xplshn
·
2025-09-14
parser.go
Go
1package parser
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7
8 "github.com/xplshn/gbc/pkg/ast"
9 "github.com/xplshn/gbc/pkg/config"
10 "github.com/xplshn/gbc/pkg/token"
11 "github.com/xplshn/gbc/pkg/util"
12)
13
14type Parser struct {
15 tokens []token.Token
16 pos int
17 current token.Token
18 previous token.Token
19 cfg *config.Config
20 isTypedPass bool
21 typeNames map[string]bool
22}
23
24func NewParser(tokens []token.Token, cfg *config.Config) *Parser {
25 p := &Parser{
26 tokens: tokens,
27 cfg: cfg,
28 isTypedPass: cfg.IsFeatureEnabled(config.FeatTyped),
29 typeNames: make(map[string]bool),
30 }
31 if len(tokens) > 0 {
32 p.current = p.tokens[0]
33 }
34
35 if p.isTypedPass {
36 for keyword, tokType := range token.KeywordMap {
37 if tokType >= token.Void && tokType <= token.Any {
38 p.typeNames[keyword] = true
39 }
40 }
41 }
42 return p
43}
44
45func (p *Parser) advance() {
46 if p.pos < len(p.tokens) {
47 p.previous = p.current
48 p.pos++
49 if p.pos < len(p.tokens) {
50 p.current = p.tokens[p.pos]
51 }
52 }
53}
54
55func (p *Parser) peek() token.Token {
56 if p.pos+1 < len(p.tokens) {
57 return p.tokens[p.pos+1]
58 }
59 return p.tokens[len(p.tokens)-1]
60}
61
62func (p *Parser) check(tokType token.Type) bool { return p.current.Type == tokType }
63
64func (p *Parser) match(tokTypes ...token.Type) bool {
65 for _, tokType := range tokTypes {
66 if p.check(tokType) {
67 p.advance()
68 return true
69 }
70 }
71 return false
72}
73
74func (p *Parser) expect(tokType token.Type, message string) {
75 if p.check(tokType) {
76 p.advance()
77 return
78 }
79 util.Error(p.current, message)
80}
81
82func (p *Parser) isTypeName(name string) bool {
83 if !p.isTypedPass { return false }
84 _, exists := p.typeNames[name]
85 return exists
86}
87
88func isLValue(node *ast.Node) bool {
89 if node == nil { return false }
90 switch node.Type {
91 case ast.Ident, ast.Indirection, ast.Subscript, ast.MemberAccess: return true
92 default: return false
93 }
94}
95
96func (p *Parser) Parse() *ast.Node {
97 var stmts []*ast.Node
98 tok := p.current
99 for !p.check(token.EOF) {
100 for p.match(token.Semi) {}
101 if p.check(token.EOF) { break }
102
103 stmt := p.parseTopLevel()
104 if stmt != nil {
105 if stmt.Type == ast.MultiVarDecl {
106 stmts = append(stmts, stmt.Data.(ast.MultiVarDeclNode).Decls...)
107 } else {
108 stmts = append(stmts, stmt)
109 }
110 }
111 }
112 return ast.NewBlock(tok, stmts, true)
113}
114
115func (p *Parser) parseTopLevel() *ast.Node {
116 currentTok := p.current
117 var stmt *ast.Node
118
119 switch currentTok.Type {
120 case token.Directive:
121 directiveVal := currentTok.Value
122 if strings.HasPrefix(directiveVal, "requires:") {
123 flagStr := strings.TrimSpace(strings.TrimPrefix(directiveVal, "requires:"))
124 if err := p.cfg.ProcessDirectiveFlags(flagStr, currentTok); err != nil {
125 util.Error(currentTok, err.Error())
126 }
127 } else {
128 util.Error(currentTok, "Unknown directive '[b]: %s'", directiveVal)
129 }
130 stmt = ast.NewDirective(currentTok, directiveVal)
131 p.advance()
132 case token.TypeKeyword: p.advance(); stmt = p.parseTypeDecl()
133 case token.Extrn: p.advance(); stmt = p.parseUntypedDeclarationList(token.Extrn, currentTok)
134 case token.Auto:
135 if p.isBxDeclarationAhead() {
136 stmt = p.parseDeclaration(true)
137 } else {
138 p.advance()
139 stmt = p.parseUntypedDeclarationList(token.Auto, p.previous)
140 }
141 case token.Ident:
142 identTok := p.current
143 peekTok := p.peek()
144
145 if peekTok.Type == token.LParen {
146 p.advance()
147 stmt = p.parseFuncDecl(nil, identTok)
148 } else if peekTok.Type == token.Asm {
149 p.advance()
150 stmt = p.parseAsmFuncDef(identTok)
151 } else if peekTok.Type == token.Extrn {
152 // Handle typed external declaration: type_name extrn function_name;
153 if p.isTypedPass && p.isTypeName(identTok.Value) {
154 returnType := p.typeFromName(identTok.Value)
155 if returnType != nil {
156 p.advance() // consume type name
157 p.advance() // consume 'extrn'
158 stmt = p.parseTypedExtrnDecl(identTok, returnType)
159 } else {
160 // Fallback to regular parsing if type not found
161 stmt = p.parseUntypedGlobalDefinition(identTok)
162 }
163 } else {
164 stmt = p.parseUntypedGlobalDefinition(identTok)
165 }
166 } else if p.isTypedPass && p.isTypeName(identTok.Value) && peekTok.Type != token.Define {
167 stmt = p.parseTypedVarOrFuncDecl(true)
168 } else if p.isBxDeclarationAhead() {
169 stmt = p.parseDeclaration(false)
170 } else if p.isMultiAssignmentAhead() {
171 stmt = p.parseExpr()
172 if stmt != nil {
173 p.expect(token.Semi, "Expected ';' after multi-assignment statement")
174 }
175 } else {
176 stmt = p.parseUntypedGlobalDefinition(identTok)
177 }
178 default:
179 if p.isTypedPass && (p.isBuiltinType(p.current) || p.check(token.Const) || p.isPointerTypeAhead()) {
180 stmt = p.parseTypedVarOrFuncDecl(true)
181 } else {
182 stmt = p.parseExpr()
183 if stmt != nil {
184 p.expect(token.Semi, "Expected ';' after top-level expression statement")
185 } else {
186 util.Error(p.current, "Expected a top-level definition or expression")
187 p.advance()
188 }
189 }
190 }
191 return stmt
192}
193
194func (p *Parser) isBxDeclarationAhead() bool {
195 originalPos, originalCurrent := p.pos, p.current
196 defer func() { p.pos, p.current = originalPos, originalCurrent }()
197
198 hasAuto := p.match(token.Auto)
199 if !p.check(token.Ident) {
200 return false
201 }
202 p.advance()
203
204 for p.match(token.Comma) {
205 if !p.check(token.Ident) {
206 return false
207 }
208 p.advance()
209 }
210
211 if p.check(token.Define) {
212 return true
213 }
214 if p.check(token.Eq) {
215 return hasAuto
216 }
217 return false
218}
219
220func (p *Parser) isMultiAssignmentAhead() bool {
221 originalPos, originalCurrent := p.pos, p.current
222 defer func() { p.pos, p.current = originalPos, originalCurrent }()
223
224 if !p.check(token.Ident) {
225 return false
226 }
227 p.advance()
228
229 hasMultipleVars := false
230 for p.match(token.Comma) {
231 hasMultipleVars = true
232 if !p.check(token.Ident) {
233 return false
234 }
235 p.advance()
236 }
237
238 return hasMultipleVars && p.check(token.Eq)
239}
240
241func (p *Parser) isBuiltinType(tok token.Token) bool {
242 return tok.Type >= token.Void && tok.Type <= token.Any
243}
244
245// isPointerCastAhead checks if the current position looks like a pointer cast: (*type)
246// This allows complex pointer casts while disallowing simple scalar C-style casts
247func (p *Parser) isPointerCastAhead() bool {
248 if !p.isTypedPass { return false }
249
250 originalPos, originalCurrent := p.pos, p.current
251 defer func() { p.pos, p.current = originalPos, originalCurrent }()
252
253 if p.match(token.Const) {}
254
255 // Check for Go-style pointer syntax: *type
256 hasPointer := false
257 if p.match(token.Star) {
258 hasPointer = true
259 for p.match(token.Star) {} // Allow multiple stars for pointer-to-pointer
260 }
261
262 if p.isBuiltinType(p.current) {
263 p.advance()
264 } else if p.check(token.Ident) && p.isTypeName(p.current.Value) {
265 p.advance()
266 } else if p.match(token.LBracket) {
267 hasPointer = true
268 for !p.check(token.RBracket) && !p.check(token.EOF) { p.advance() }
269 if p.check(token.RBracket) { p.advance() }
270 } else {
271 return false
272 }
273
274 return hasPointer && p.check(token.RParen)
275}
276
277func (p *Parser) isPointerTypeAhead() bool {
278 if !p.check(token.Star) { return false }
279
280 originalPos, originalCurrent := p.pos, p.current
281 defer func() { p.pos, p.current = originalPos, originalCurrent }()
282
283 // Skip all consecutive * tokens
284 for p.check(token.Star) {
285 p.advance()
286 }
287
288 // Check if what follows the * is actually a type name
289 return p.isBuiltinType(p.current) || (p.check(token.Ident) && p.isTypeName(p.current.Value))
290}
291
292func (p *Parser) parseStmt() *ast.Node {
293 tok := p.current
294
295 isLabelAhead := (p.check(token.Ident) || p.current.Type >= token.Auto) && p.peek().Type == token.Colon
296
297 if isLabelAhead {
298 var labelName string
299 if p.check(token.Ident) {
300 labelName = p.current.Value
301 } else {
302 for kw, typ := range token.KeywordMap {
303 if p.current.Type == typ {
304 labelName = kw
305 break
306 }
307 }
308 }
309 p.advance()
310 p.advance()
311 if p.check(token.RBrace) { return ast.NewLabel(tok, labelName, ast.NewBlock(p.current, nil, true)) }
312 return ast.NewLabel(tok, labelName, p.parseStmt())
313 }
314
315 if p.isTypedPass && (p.isBuiltinType(p.current) || (p.isTypeName(p.current.Value) && p.peek().Type != token.Define) || p.check(token.Const) || p.isPointerTypeAhead()) {
316 return p.parseTypedVarOrFuncDecl(false)
317 }
318
319 switch {
320 case p.match(token.If):
321 p.expect(token.LParen, "Expected '(' after 'if'")
322 cond := p.parseExpr()
323 p.expect(token.RParen, "Expected ')' after if condition")
324 thenBody := p.parseStmt()
325 var elseBody *ast.Node
326 if p.match(token.Else) {
327 elseBody = p.parseStmt()
328 }
329 return ast.NewIf(tok, cond, thenBody, elseBody)
330 case p.match(token.While):
331 p.expect(token.LParen, "Expected '(' after 'while'")
332 cond := p.parseExpr()
333 p.expect(token.RParen, "Expected ')' after while condition")
334 body := p.parseStmt()
335 return ast.NewWhile(tok, cond, body)
336 case p.match(token.Switch):
337 hasParen := p.match(token.LParen)
338 expr := p.parseExpr()
339 if hasParen {
340 p.expect(token.RParen, "Expected ')' after switch expression")
341 }
342 body := p.parseStmt()
343 return ast.NewSwitch(tok, expr, body)
344 case p.check(token.LBrace):
345 return p.parseBlockStmt()
346 case p.check(token.Auto):
347 if p.isBxDeclarationAhead() {
348 return p.parseDeclaration(true)
349 }
350 p.advance()
351 return p.parseUntypedDeclarationList(token.Auto, p.previous)
352 case p.match(token.Extrn):
353 return p.parseUntypedDeclarationList(token.Extrn, p.previous)
354 case p.match(token.Case):
355 var values []*ast.Node
356 for {
357 values = append(values, p.parseExpr())
358 if !p.match(token.Comma) {
359 break
360 }
361 }
362 p.expect(token.Colon, "Expected ':' after case value")
363 body := p.parseStmt()
364 return ast.NewCase(tok, values, body)
365 case p.match(token.Default):
366 p.expect(token.Colon, "Expected ':' after 'default'")
367 body := p.parseStmt()
368 return ast.NewDefault(tok, body)
369 case p.match(token.Goto):
370 var labelName string
371 if p.check(token.Ident) {
372 labelName = p.current.Value
373 p.advance()
374 } else {
375 isKeyword := false
376 for kw, typ := range token.KeywordMap {
377 if p.current.Type == typ {
378 labelName, isKeyword = kw, true
379 break
380 }
381 }
382 if !isKeyword {
383 util.Error(p.current, "Expected label name after 'goto'")
384 for !p.check(token.Semi) && !p.check(token.EOF) {
385 p.advance()
386 }
387 } else {
388 if labelName == "continue" {
389 util.Warn(p.cfg, config.WarnExtra, p.current, "'goto continue' is a workaround for a limitation of -std=B; please avoid this construct")
390 }
391 p.advance()
392 }
393 }
394 node := ast.NewGoto(tok, labelName)
395 p.expect(token.Semi, "Expected ';' after goto statement")
396 return node
397 case p.match(token.Return):
398 var expr *ast.Node
399 if !p.check(token.Semi) {
400 p.expect(token.LParen, "Expected '(' after 'return' with value")
401 if !p.check(token.RParen) {
402 expr = p.parseExpr()
403 }
404 p.expect(token.RParen, "Expected ')' after return value")
405 }
406 p.expect(token.Semi, "Expected ';' after return statement")
407 return ast.NewReturn(tok, expr)
408 case p.match(token.Break):
409 p.expect(token.Semi, "Expected ';' after 'break'")
410 return ast.NewBreak(tok)
411 case p.match(token.Continue):
412 if !p.cfg.IsFeatureEnabled(config.FeatContinue) {
413 util.Error(p.previous, "'continue' is a Bx extension, not available in -std=B")
414 }
415 p.expect(token.Semi, "Expected ';' after 'continue'")
416 return ast.NewContinue(tok)
417 case p.match(token.Semi): return ast.NewBlock(tok, nil, true)
418 default:
419 if p.check(token.Ident) {
420 isShortDecl := false
421 originalPos, originalCurrent := p.pos, p.current
422 p.advance()
423 for p.match(token.Comma) {
424 if !p.check(token.Ident) {
425 break
426 }
427 p.advance()
428 }
429 if p.check(token.Define) {
430 isShortDecl = true
431 }
432 p.pos, p.current = originalPos, originalCurrent
433 if isShortDecl {
434 return p.parseDeclaration(false)
435 }
436 }
437
438 expr := p.parseExpr()
439 if expr != nil {
440 p.expect(token.Semi, "Expected ';' after expression statement")
441 }
442 return expr
443 }
444}
445
446func (p *Parser) parseBlockStmt() *ast.Node {
447 tok := p.current
448 p.expect(token.LBrace, "Expected '{' to start a block")
449 var stmts []*ast.Node
450 for !p.check(token.RBrace) && !p.check(token.EOF) {
451 stmt := p.parseStmt()
452 if stmt != nil {
453 if stmt.Type == ast.MultiVarDecl {
454 stmts = append(stmts, stmt.Data.(ast.MultiVarDeclNode).Decls...)
455 } else {
456 stmts = append(stmts, stmt)
457 }
458 }
459 }
460 p.expect(token.RBrace, "Expected '}' after block")
461 return ast.NewBlock(tok, stmts, false)
462}
463
464func (p *Parser) parseDeclaration(hasAuto bool) *ast.Node {
465 declTok := p.current
466 if hasAuto {
467 p.expect(token.Auto, "Expected 'auto' keyword")
468 declTok = p.previous
469 }
470
471 var names []*ast.Node
472 for {
473 p.expect(token.Ident, "Expected identifier in declaration")
474 names = append(names, ast.NewIdent(p.previous, p.previous.Value))
475 if !p.match(token.Comma) {
476 break
477 }
478 }
479
480 var op token.Type
481 var inits []*ast.Node
482 isDefine := false
483
484 if p.match(token.Define) {
485 if hasAuto {
486 util.Error(p.previous, "Cannot use ':=' in a typed declaration; use '=' instead")
487 return ast.NewVarDecl(declTok, "", ast.TypeUntyped, nil, nil, false, false, false)
488 }
489 op, isDefine = token.Define, true
490 } else if p.match(token.Eq) {
491 op = token.Eq
492 }
493
494 if op != 0 {
495 for {
496 inits = append(inits, p.parseAssignmentExpr())
497 if !p.match(token.Comma) {
498 break
499 }
500 }
501 if len(names) != len(inits) {
502 util.Error(declTok, "Mismatched number of variables and initializers (%d vs %d)", len(names), len(inits))
503 }
504 } else if !p.cfg.IsFeatureEnabled(config.FeatAllowUninitialized) {
505 util.Error(declTok, "Uninitialized declaration is not allowed in this mode")
506 }
507
508 var decls []*ast.Node
509 for i, nameNode := range names {
510 var initList []*ast.Node
511 if i < len(inits) {
512 initList = append(initList, inits[i])
513 }
514 name := nameNode.Data.(ast.IdentNode).Name
515 decls = append(decls, ast.NewVarDecl(nameNode.Tok, name, ast.TypeUntyped, initList, nil, false, false, isDefine))
516 }
517
518 p.expect(token.Semi, "Expected ';' after declaration")
519
520 if len(decls) == 1 {
521 return decls[0]
522 }
523 return ast.NewMultiVarDecl(declTok, decls)
524}
525
526func (p *Parser) parseUntypedDeclarationList(declType token.Type, declTok token.Token) *ast.Node {
527 if declType == token.Extrn {
528 var names []*ast.Node
529 for {
530 p.expect(token.Ident, "Expected identifier in 'extrn' list")
531 names = append(names, ast.NewIdent(p.previous, p.previous.Value))
532 if !p.match(token.Comma) {
533 break
534 }
535 }
536 p.expect(token.Semi, "Expected ';' after 'extrn' declaration")
537 return ast.NewExtrnDecl(declTok, names, nil)
538 }
539
540 var decls []*ast.Node
541 for {
542 var name string
543 var itemToken token.Token
544
545 if p.check(token.Ident) {
546 itemToken, name = p.current, p.current.Value
547 p.advance()
548 } else if p.check(token.TypeKeyword) {
549 itemToken, name = p.current, "type"
550 util.Warn(p.cfg, config.WarnExtra, itemToken, "Using keyword 'type' as an identifier")
551 p.advance()
552 } else {
553 p.expect(token.Ident, "Expected identifier in declaration")
554 if p.check(token.Comma) || p.check(token.Semi) {
555 continue
556 }
557 break
558 }
559
560 var sizeExpr *ast.Node
561 isVector, isBracketed := false, false
562
563 if p.match(token.LBracket) {
564 if declType == token.Auto {
565 util.Error(p.previous, "Classic B 'auto' vectors use 'auto name size', not 'auto name[size]'")
566 }
567 isVector, isBracketed = true, true
568 if !p.check(token.RBracket) {
569 sizeExpr = p.parseExpr()
570 }
571 p.expect(token.RBracket, "Expected ']' after array size")
572 } else if p.check(token.Number) {
573 isVector = true
574 sizeExpr = p.parsePrimaryExpr()
575 }
576
577 if sizeExpr == nil && !isBracketed && !p.cfg.IsFeatureEnabled(config.FeatAllowUninitialized) {
578 util.Error(itemToken, "Uninitialized declaration of '%s' is not allowed in this mode", name)
579 }
580
581 decls = append(decls, ast.NewVarDecl(itemToken, name, nil, nil, sizeExpr, isVector, isBracketed, false))
582 if !p.match(token.Comma) {
583 break
584 }
585 }
586 p.expect(token.Semi, "Expected ';' after declaration list")
587
588 if len(decls) == 1 {
589 return decls[0]
590 }
591 return ast.NewMultiVarDecl(declTok, decls)
592}
593
594func (p *Parser) parseUntypedGlobalDefinition(nameToken token.Token) *ast.Node {
595 name := nameToken.Value
596 if p.isTypeName(name) {
597 util.Error(nameToken, "Variable name '%s' shadows a type", name)
598 }
599 p.advance()
600
601 var sizeExpr *ast.Node
602 isVector, isBracketed := false, false
603
604 if p.match(token.LBracket) {
605 isVector, isBracketed = true, true
606 if !p.check(token.RBracket) {
607 sizeExpr = p.parseExpr()
608 }
609 p.expect(token.RBracket, "Expected ']' for vector definition")
610 }
611
612 var initList []*ast.Node
613 if !p.check(token.Semi) {
614 // Check if this is an attempt to assign without declaration
615 if p.check(token.Eq) {
616 util.Error(nameToken, "Assignment without declaration is not allowed at global scope. Use ':=' or make it a typed declaration and initialization")
617 return nil
618 }
619 initList = append(initList, p.parseUnaryExpr())
620 if isBracketed || p.match(token.Comma) || (!p.check(token.Semi) && !p.check(token.EOF)) {
621 isVector = true
622 if p.previous.Type != token.Comma {
623 p.match(token.Comma)
624 }
625 for !p.check(token.Semi) && !p.check(token.EOF) {
626 initList = append(initList, p.parseUnaryExpr())
627 if p.check(token.Semi) || p.check(token.EOF) {
628 break
629 }
630 p.match(token.Comma)
631 }
632 }
633 }
634
635 if len(initList) == 0 && sizeExpr == nil && !isBracketed && !p.cfg.IsFeatureEnabled(config.FeatAllowUninitialized) {
636 util.Error(nameToken, "Uninitialized declaration of '%s' is not allowed in this mode", name)
637 }
638
639 p.expect(token.Semi, "Expected ';' after global definition")
640 return ast.NewVarDecl(nameToken, name, nil, initList, sizeExpr, isVector, isBracketed, false)
641}
642
643func (p *Parser) parseFuncDecl(returnType *ast.BxType, nameToken token.Token) *ast.Node {
644 name := nameToken.Value
645 if p.isTypeName(name) {
646 util.Error(nameToken, "Function name '%s' shadows a type", name)
647 }
648 p.expect(token.LParen, "Expected '(' after function name")
649
650 var params []*ast.Node
651 var hasVarargs bool
652 isTyped := p.isTypedPass && p.isTypedParameterList()
653
654 if isTyped {
655 params, hasVarargs = p.parseTypedParameters()
656 } else {
657 params, hasVarargs = p.parseUntypedParameters()
658 }
659 p.expect(token.RParen, "Expected ')' after parameters")
660
661 var decls []*ast.Node
662 for p.check(token.Auto) || p.check(token.Extrn) {
663 decl := p.parseStmt()
664 if decl != nil {
665 if decl.Type == ast.MultiVarDecl {
666 decls = append(decls, decl.Data.(ast.MultiVarDeclNode).Decls...)
667 } else {
668 decls = append(decls, decl)
669 }
670 }
671 }
672
673 var body *ast.Node
674 if p.check(token.LBrace) {
675 body = p.parseBlockStmt()
676 } else {
677 body = p.parseStmt()
678 }
679
680 if len(decls) > 0 {
681 var allStmts []*ast.Node
682 allStmts = append(allStmts, decls...)
683 if body != nil {
684 if body.Type == ast.Block && !body.Data.(ast.BlockNode).IsSynthetic {
685 allStmts = append(allStmts, body.Data.(ast.BlockNode).Stmts...)
686 } else {
687 allStmts = append(allStmts, body)
688 }
689 }
690 body = ast.NewBlock(nameToken, allStmts, false)
691 }
692
693 if returnType == nil {
694 returnType = ast.TypeUntyped
695 if isTyped {
696 returnType = ast.TypeInt
697 }
698 }
699
700 return ast.NewFuncDecl(nameToken, name, params, body, hasVarargs, isTyped, returnType)
701}
702
703func (p *Parser) parseAsmFuncDef(nameToken token.Token) *ast.Node {
704 name := nameToken.Value
705 if p.isTypeName(name) {
706 util.Error(nameToken, "Function name '%s' shadows a type", name)
707 }
708
709 p.expect(token.Asm, "Expected '__asm__' keyword")
710 asmTok := p.previous
711
712 p.expect(token.LParen, "Expected '(' after '__asm__'")
713 var codeParts []string
714 for !p.check(token.RParen) && !p.check(token.EOF) {
715 p.expect(token.String, "Expected string literal in '__asm__' block")
716 codeParts = append(codeParts, p.previous.Value)
717 p.match(token.Comma)
718 }
719 p.expect(token.RParen, "Expected ')' to close '__asm__' block")
720 asmCode := strings.Join(codeParts, "\n")
721 body := ast.NewAsmStmt(asmTok, asmCode)
722
723 if !p.check(token.LBrace) {
724 p.expect(token.Semi, "Expected ';' or '{' after '__asm__' definition")
725 } else {
726 p.parseBlockStmt()
727 }
728
729 return ast.NewFuncDecl(nameToken, nameToken.Value, nil, body, false, false, nil)
730}
731
732func (p *Parser) parseTypeDecl() *ast.Node {
733 typeTok := p.previous
734
735 if p.match(token.Enum) {
736 return p.parseEnumDef(typeTok)
737 }
738
739 if p.match(token.Struct) {
740 underlyingType := p.parseStructDef()
741 var name string
742 if p.check(token.Ident) {
743 name = p.current.Value
744 p.advance()
745 } else {
746 if underlyingType.StructTag == "" {
747 util.Error(typeTok, "Typedef for anonymous struct must have a name")
748 return nil
749 }
750 name = underlyingType.StructTag
751 }
752
753 p.typeNames[name] = true
754 underlyingType.Name = name
755
756 p.expect(token.Semi, "Expected ';' after type declaration")
757 return ast.NewTypeDecl(typeTok, name, underlyingType)
758 }
759
760 // Handle type alias: type <underlying_type> <new_name>;
761 underlyingType := p.parseType()
762 if underlyingType == nil {
763 util.Error(p.current, "Expected a type for type alias after 'type'")
764 for !p.check(token.Semi) && !p.check(token.EOF) {
765 p.advance()
766 }
767 return nil
768 }
769
770 p.expect(token.Ident, "Expected new type name for alias")
771 nameToken := p.previous
772 name := nameToken.Value
773
774 if p.isTypeName(name) {
775 util.Error(nameToken, "Redefinition of type '%s'", name)
776 }
777 p.typeNames[name] = true
778
779 p.expect(token.Semi, "Expected ';' after type alias declaration")
780 return ast.NewTypeDecl(typeTok, name, underlyingType)
781}
782
783func (p *Parser) parseEnumDef(typeTok token.Token) *ast.Node {
784 p.expect(token.Ident, "Expected enum name")
785 nameToken := p.previous
786 name := nameToken.Value
787 p.typeNames[name] = true
788
789 p.expect(token.LBrace, "Expected '{' to open enum definition")
790
791 var members []*ast.Node
792 var currentValue int64 = 0
793
794 for !p.check(token.RBrace) && !p.check(token.EOF) {
795 p.expect(token.Ident, "Expected enum member name")
796 memberToken := p.previous
797 memberName := memberToken.Value
798
799 if p.match(token.Eq) {
800 valExpr := p.parseExpr()
801 foldedVal := ast.FoldConstants(valExpr)
802 if foldedVal.Type != ast.Number {
803 util.Error(valExpr.Tok, "Enum member initializer must be a constant integer")
804 currentValue++
805 } else {
806 currentValue = foldedVal.Data.(ast.NumberNode).Value
807 }
808 }
809
810 initExpr := ast.NewNumber(memberToken, currentValue)
811 memberDecl := ast.NewVarDecl(memberToken, memberName, ast.TypeInt, []*ast.Node{initExpr}, nil, false, false, true)
812 members = append(members, memberDecl)
813
814 currentValue++
815
816 if !p.match(token.Comma) {
817 break
818 }
819 }
820
821 p.expect(token.RBrace, "Expected '}' to close enum definition")
822 p.expect(token.Semi, "Expected ';' after enum declaration")
823
824 return ast.NewEnumDecl(typeTok, name, members)
825}
826
827func (p *Parser) parseTypedVarOrFuncDecl(isTopLevel bool) *ast.Node {
828 startTok := p.current
829 declType := p.parseType()
830
831 if p.match(token.Define) {
832 util.Error(p.previous, "Cannot use ':=' in a typed declaration; use '=' instead")
833 return p.parseTypedVarDeclBody(startTok, declType, p.previous)
834 }
835
836 // Check for typed external declaration: type extrn function_name;
837 if p.match(token.Extrn) {
838 return p.parseTypedExtrnDecl(startTok, declType)
839 }
840
841 p.expect(token.Ident, "Expected identifier after type")
842 nameToken := p.previous
843
844 if p.check(token.LParen) {
845 return p.parseFuncDecl(declType, nameToken)
846 }
847
848 return p.parseTypedVarDeclBody(startTok, declType, nameToken)
849}
850
851func (p *Parser) parseTypedVarDeclBody(startTok token.Token, declType *ast.BxType, nameToken token.Token) *ast.Node {
852 var decls []*ast.Node
853 currentNameToken := nameToken
854
855 for {
856 name := currentNameToken.Value
857 finalType := declType
858 var sizeExpr *ast.Node
859 isArr, isBracketed := false, false
860
861 if p.match(token.LBracket) {
862 isArr, isBracketed = true, true
863 if !p.check(token.RBracket) {
864 sizeExpr = p.parseExpr()
865 }
866 p.expect(token.RBracket, "Expected ']' after array size")
867 finalType = &ast.BxType{Kind: ast.TYPE_ARRAY, Base: declType, ArraySize: sizeExpr, IsConst: declType.IsConst}
868 }
869
870 var initList []*ast.Node
871 if p.match(token.Eq) {
872 initList = append(initList, p.parseAssignmentExpr())
873 } else if p.check(token.Define) {
874 util.Error(p.current, "Cannot use ':=' in a typed declaration; use '=' instead")
875 return ast.NewVarDecl(currentNameToken, name, finalType, nil, sizeExpr, isArr, isBracketed, false)
876 } else if !p.cfg.IsFeatureEnabled(config.FeatAllowUninitialized) {
877 util.Error(nameToken, "Initialized typed declaration is required in this mode")
878 }
879
880 decls = append(decls, ast.NewVarDecl(currentNameToken, name, finalType, initList, sizeExpr, isArr, isBracketed, false))
881
882 if !p.match(token.Comma) {
883 break
884 }
885
886 p.expect(token.Ident, "Expected identifier after comma in declaration list")
887 currentNameToken = p.previous
888 }
889
890 p.expect(token.Semi, "Expected ';' after typed variable declaration")
891
892 if len(decls) == 1 {
893 return decls[0]
894 }
895 return ast.NewMultiVarDecl(startTok, decls)
896}
897
898func (p *Parser) parseType() *ast.BxType {
899 if !p.isTypedPass {
900 return nil
901 }
902
903 isConst := p.match(token.Const)
904 var baseType *ast.BxType
905
906 // Handle Go-style pointer syntax (*int)
907 if p.match(token.Star) {
908 elemType := p.parseType()
909 baseType = &ast.BxType{Kind: ast.TYPE_POINTER, Base: elemType}
910 } else if p.match(token.LBracket) {
911 p.expect(token.RBracket, "Expected ']' to complete array type specifier")
912 elemType := p.parseType()
913 baseType = &ast.BxType{Kind: ast.TYPE_ARRAY, Base: elemType}
914 } else {
915 tok := p.current
916 if p.match(token.Struct) {
917 if p.check(token.Ident) && p.peek().Type != token.LBrace {
918 tagName := p.current.Value
919 p.advance()
920 baseType = &ast.BxType{Kind: ast.TYPE_STRUCT, Name: tagName, StructTag: tagName}
921 } else {
922 baseType = p.parseStructDef()
923 }
924 } else if p.match(token.Enum) {
925 if p.check(token.Ident) {
926 tagName := p.current.Value
927 p.advance()
928 baseType = &ast.BxType{Kind: ast.TYPE_ENUM, Name: tagName}
929 } else {
930 util.Error(tok, "Anonymous enums are not supported as types")
931 baseType = ast.TypeUntyped
932 }
933 } else if p.isBuiltinType(tok) {
934 p.advance()
935 var typeName string
936 for keyword, t := range token.KeywordMap {
937 if t == p.previous.Type {
938 typeName = keyword
939 break
940 }
941 }
942
943 if p.previous.Type == token.Void {
944 baseType = ast.TypeVoid
945 } else if p.previous.Type == token.StringKeyword {
946 baseType = ast.TypeString
947 } else if p.previous.Type >= token.Float && p.previous.Type <= token.Float64 {
948 baseType = &ast.BxType{Kind: ast.TYPE_FLOAT, Name: typeName}
949 } else {
950 if typeName == "" {
951 util.Error(tok, "Internal parser error: could not find string for builtin type %v", tok.Type)
952 return ast.TypeUntyped
953 }
954 baseType = &ast.BxType{Kind: ast.TYPE_PRIMITIVE, Name: typeName}
955 }
956 } else if p.check(token.Ident) {
957 typeName := p.current.Value
958 if !p.isTypeName(typeName) {
959 util.Error(p.current, "Unknown type name '%s'", typeName)
960 p.advance()
961 return ast.TypeUntyped
962 }
963 p.advance()
964 baseType = &ast.BxType{Kind: ast.TYPE_PRIMITIVE, Name: typeName}
965 } else {
966 util.Error(p.current, "Expected a type name, 'struct', 'enum', or '[]'")
967 p.advance()
968 return ast.TypeUntyped
969 }
970 }
971
972 if isConst {
973 newType := *baseType
974 newType.IsConst = true
975 return &newType
976 }
977 return baseType
978}
979
980func (p *Parser) parseStructDef() *ast.BxType {
981 structType := &ast.BxType{Kind: ast.TYPE_STRUCT}
982
983 if p.check(token.Ident) {
984 structType.StructTag = p.current.Value
985 if p.isTypedPass {
986 p.typeNames[structType.StructTag] = true
987 }
988 p.advance()
989 }
990
991 p.expect(token.LBrace, "Expected '{' to open struct definition")
992
993 for !p.check(token.RBrace) && !p.check(token.EOF) {
994 var names []token.Token
995 p.expect(token.Ident, "Expected field name in struct")
996 names = append(names, p.previous)
997
998 for p.match(token.Comma) {
999 if p.isBuiltinType(p.current) || p.isTypeName(p.current.Value) || p.check(token.LBracket) || p.check(token.Star) || p.check(token.Struct) {
1000 p.pos--
1001 p.current = p.tokens[p.pos-1]
1002 break
1003 }
1004 p.expect(token.Ident, "Expected field name after comma")
1005 names = append(names, p.previous)
1006 }
1007
1008 fieldType := p.parseType()
1009
1010 for _, nameToken := range names {
1011 fieldDecl := ast.NewVarDecl(nameToken, nameToken.Value, fieldType, nil, nil, false, false, false)
1012 structType.Fields = append(structType.Fields, fieldDecl)
1013 }
1014
1015 p.expect(token.Semi, "Expected ';' after struct field declaration")
1016 }
1017
1018 p.expect(token.RBrace, "Expected '}' to close struct definition")
1019 if structType.StructTag != "" {
1020 structType.Name = structType.StructTag
1021 }
1022 return structType
1023}
1024
1025func (p *Parser) isTypedParameterList() bool {
1026 originalPos, originalCurrent := p.pos, p.current
1027 defer func() { p.pos, p.current = originalPos, originalCurrent }()
1028
1029 if p.check(token.RParen) {
1030 return false
1031 }
1032 if p.check(token.Void) && p.peek().Type == token.RParen {
1033 return true
1034 }
1035 if p.isBuiltinType(p.current) || p.isTypeName(p.current.Value) {
1036 return true
1037 }
1038
1039 for {
1040 if !p.check(token.Ident) {
1041 return false
1042 }
1043 p.advance()
1044 if !p.match(token.Comma) {
1045 break
1046 }
1047 }
1048 return p.isBuiltinType(p.current) || p.isTypeName(p.current.Value) || p.check(token.LBracket) || p.check(token.Star)
1049}
1050
1051func (p *Parser) parseUntypedParameters() ([]*ast.Node, bool) {
1052 var params []*ast.Node
1053 var hasVarargs bool
1054 if !p.check(token.RParen) {
1055 for {
1056 if p.match(token.Dots) {
1057 hasVarargs = true
1058 break
1059 }
1060 p.expect(token.Ident, "Expected parameter name or '...'")
1061 params = append(params, ast.NewIdent(p.previous, p.previous.Value))
1062 if !p.match(token.Comma) {
1063 break
1064 }
1065 }
1066 }
1067 return params, hasVarargs
1068}
1069
1070func (p *Parser) parseTypedParameters() ([]*ast.Node, bool) {
1071 var params []*ast.Node
1072 var hasVarargs bool
1073 if p.check(token.RParen) {
1074 return params, false
1075 }
1076 if p.check(token.Void) && p.peek().Type == token.RParen {
1077 p.advance()
1078 return params, false
1079 }
1080
1081 for {
1082 if p.check(token.RParen) {
1083 break
1084 }
1085 if p.match(token.Dots) {
1086 hasVarargs = true
1087 break
1088 }
1089
1090 if p.isBuiltinType(p.current) || p.isTypeName(p.current.Value) {
1091 paramType := p.parseType()
1092 name := fmt.Sprintf("anonparam%d", len(params))
1093 paramNode := ast.NewVarDecl(p.previous, name, paramType, nil, nil, false, false, false)
1094 params = append(params, paramNode)
1095 } else {
1096 var names []token.Token
1097 p.expect(token.Ident, "Expected parameter name")
1098 names = append(names, p.previous)
1099 for p.match(token.Comma) {
1100 if p.isBuiltinType(p.current) || p.isTypeName(p.current.Value) || p.check(token.LBracket) || p.check(token.Star) || p.check(token.RParen) || p.check(token.Dots) {
1101 p.pos--
1102 p.current = p.tokens[p.pos-1]
1103 break
1104 }
1105 p.expect(token.Ident, "Expected parameter name")
1106 names = append(names, p.previous)
1107 }
1108
1109 paramType := p.parseType()
1110 for _, nameTok := range names {
1111 paramNode := ast.NewVarDecl(nameTok, nameTok.Value, paramType, nil, nil, false, false, false)
1112 params = append(params, paramNode)
1113 }
1114 }
1115
1116 if !p.match(token.Comma) {
1117 break
1118 }
1119 }
1120 return params, hasVarargs
1121}
1122
1123func getBinaryOpPrecedence(op token.Type) int {
1124 switch op {
1125 case token.OrOr: return 4
1126 case token.AndAnd: return 5
1127 case token.Or: return 6
1128 case token.Xor: return 7
1129 case token.And: return 8
1130 case token.EqEq, token.Neq: return 9
1131 case token.Lt, token.Gt, token.Lte, token.Gte: return 10
1132 case token.Shl, token.Shr: return 11
1133 case token.Plus, token.Minus: return 12
1134 case token.Star, token.Slash, token.Rem: return 13
1135 default:
1136 return -1
1137 }
1138}
1139
1140func (p *Parser) parseExpr() *ast.Node { return p.parseAssignmentExpr() }
1141
1142func (p *Parser) parseAssignmentExpr() *ast.Node {
1143 // Try to detect multi-assignment pattern: lhs1, lhs2, ... = rhs1, rhs2, ...
1144 startPos := p.pos
1145 startCurrent := p.current
1146
1147 // Parse first expression
1148 left := p.parseTernaryExpr()
1149
1150 // Check if this could be a multi-assignment (comma followed by more expressions then equals)
1151 if p.check(token.Comma) {
1152 var lhsList []*ast.Node
1153 lhsList = append(lhsList, left)
1154
1155 // Parse comma-separated lvalues
1156 for p.match(token.Comma) {
1157 expr := p.parseTernaryExpr()
1158 lhsList = append(lhsList, expr)
1159 }
1160
1161 // Check if we have an assignment operator
1162 if op := p.current.Type; op >= token.Eq && op <= token.EqShr {
1163 // Validate all lhs expressions are lvalues
1164 for _, lhs := range lhsList {
1165 if !isLValue(lhs) {
1166 util.Error(p.current, "Invalid target for assignment")
1167 }
1168 }
1169
1170 tok := p.current
1171 p.advance()
1172
1173 // Parse comma-separated rvalues
1174 var rhsList []*ast.Node
1175 for {
1176 rhs := p.parseAssignmentExpr()
1177 rhsList = append(rhsList, rhs)
1178 if !p.match(token.Comma) {
1179 break
1180 }
1181 }
1182
1183 // Check that number of lhs and rhs match
1184 if len(lhsList) != len(rhsList) {
1185 util.Error(tok, "Mismatched number of variables and values in assignment (%d vs %d)", len(lhsList), len(rhsList))
1186 }
1187
1188 return ast.NewMultiAssign(tok, op, lhsList, rhsList)
1189 }
1190
1191 // Not a multi-assignment, backtrack and treat as comma expression
1192 p.pos = startPos
1193 p.current = startCurrent
1194 left = p.parseTernaryExpr()
1195 }
1196
1197 // Regular single assignment
1198 if op := p.current.Type; op >= token.Eq && op <= token.EqShr {
1199 if !isLValue(left) {
1200 util.Error(p.current, "Invalid target for assignment")
1201 }
1202 tok := p.current
1203 p.advance()
1204 right := p.parseAssignmentExpr()
1205 return ast.NewAssign(tok, op, left, right)
1206 }
1207 return left
1208}
1209
1210func (p *Parser) parseTernaryExpr() *ast.Node {
1211 cond := p.parseBinaryExpr(0)
1212 if p.match(token.Question) {
1213 tok := p.previous
1214 thenExpr := p.parseExpr()
1215 p.expect(token.Colon, "Expected ':' for ternary operator")
1216 elseExpr := p.parseAssignmentExpr()
1217 return ast.NewTernary(tok, cond, thenExpr, elseExpr)
1218 }
1219 return cond
1220}
1221
1222func (p *Parser) parseBinaryExpr(minPrec int) *ast.Node {
1223 left := p.parseUnaryExpr()
1224 for {
1225 if left == nil {
1226 return nil
1227 }
1228 op := p.current.Type
1229 prec := getBinaryOpPrecedence(op)
1230 if prec < minPrec {
1231 break
1232 }
1233 opTok := p.current
1234 p.advance()
1235 right := p.parseBinaryExpr(prec + 1)
1236 left = ast.NewBinaryOp(opTok, op, left, right)
1237 }
1238 return left
1239}
1240
1241func (p *Parser) parseUnaryExpr() *ast.Node {
1242 tok := p.current
1243 if p.match(token.Not, token.Complement, token.Minus, token.Plus, token.Inc, token.Dec, token.Star, token.And) {
1244 op, opToken := p.previous.Type, p.previous
1245 operand := p.parseUnaryExpr()
1246
1247 if op == token.Star {
1248 return ast.NewIndirection(tok, operand)
1249 }
1250 if op == token.And {
1251 if !isLValue(operand) {
1252 util.Error(opToken, "Address-of operator '&' requires an l-value")
1253 }
1254 return ast.NewAddressOf(tok, operand)
1255 }
1256 if (op == token.Inc || op == token.Dec) && !isLValue(operand) {
1257 util.Error(opToken, "Prefix '++' or '--' requires an l-value")
1258 }
1259 return ast.NewUnaryOp(tok, op, operand)
1260 }
1261 return p.parsePostfixExpr()
1262}
1263
1264func (p *Parser) parsePostfixExpr() *ast.Node {
1265 expr := p.parsePrimaryExpr()
1266 for {
1267 if expr == nil {
1268 return nil
1269 }
1270 tok := p.current
1271 if p.match(token.LParen) {
1272 var args []*ast.Node
1273 if !p.check(token.RParen) {
1274 for {
1275 args = append(args, p.parseAssignmentExpr())
1276 if !p.match(token.Comma) {
1277 break
1278 }
1279 }
1280 }
1281 p.expect(token.RParen, "Expected ')' after function arguments")
1282 expr = ast.NewFuncCall(tok, expr, args)
1283 } else if p.match(token.LBracket) {
1284 index := p.parseExpr()
1285 p.expect(token.RBracket, "Expected ']' after array index")
1286 expr = ast.NewSubscript(tok, expr, index)
1287 } else if p.isTypedPass && p.match(token.Dot) {
1288 p.expect(token.Ident, "Expected member name after '.'")
1289 member := ast.NewIdent(p.previous, p.previous.Value)
1290 expr = ast.NewMemberAccess(tok, expr, member)
1291 } else if p.match(token.Inc, token.Dec) {
1292 if !isLValue(expr) {
1293 util.Error(p.previous, "Postfix '++' or '--' requires an l-value")
1294 }
1295 expr = ast.NewPostfixOp(p.previous, p.previous.Type, expr)
1296 } else {
1297 break
1298 }
1299 }
1300 return expr
1301}
1302
1303func (p *Parser) parseStructLiteral(typeNode *ast.Node) *ast.Node {
1304 startTok := p.current
1305 p.expect(token.LBrace, "Expected '{' for struct literal")
1306
1307 var values []*ast.Node
1308 var names []*ast.Node
1309 hasNames, hasPositional := false, false
1310
1311 for !p.check(token.RBrace) && !p.check(token.EOF) {
1312 if p.check(token.Ident) && p.peek().Type == token.Colon {
1313 hasNames = true
1314 if hasPositional {
1315 util.Error(p.current, "Cannot mix named and positional fields in struct literal")
1316 }
1317 p.expect(token.Ident, "Expected field name")
1318 names = append(names, ast.NewIdent(p.previous, p.previous.Value))
1319 p.expect(token.Colon, "Expected ':' after field name")
1320 values = append(values, p.parseAssignmentExpr())
1321 } else {
1322 hasPositional = true
1323 if hasNames {
1324 util.Error(p.current, "Cannot mix named and positional fields in struct literal")
1325 }
1326 names = append(names, nil)
1327 values = append(values, p.parseAssignmentExpr())
1328 }
1329
1330 if !p.match(token.Comma) {
1331 break
1332 }
1333 }
1334
1335 p.expect(token.RBrace, "Expected '}' to close struct literal")
1336
1337 if hasPositional && !hasNames {
1338 names = nil
1339 }
1340
1341 return ast.NewStructLiteral(startTok, typeNode, values, names)
1342}
1343
1344func (p *Parser) parseArrayLiteral(startTok token.Token, elemType *ast.BxType) *ast.Node {
1345 p.expect(token.LBrace, "Expected '{' for array literal")
1346
1347 var values []*ast.Node
1348 for !p.check(token.RBrace) && !p.check(token.EOF) {
1349 values = append(values, p.parseAssignmentExpr())
1350 if !p.match(token.Comma) {
1351 break
1352 }
1353 }
1354
1355 p.expect(token.RBrace, "Expected '}' to close array literal")
1356
1357 return ast.NewArrayLiteral(startTok, elemType, values)
1358}
1359
1360func (p *Parser) parsePrimaryExpr() *ast.Node {
1361 tok := p.current
1362 if p.match(token.Number) {
1363 valStr := p.previous.Value
1364 val, err := strconv.ParseInt(valStr, 0, 64)
1365 if err != nil {
1366 uval, uerr := strconv.ParseUint(valStr, 0, 64)
1367 if uerr != nil {
1368 util.Error(tok, "Invalid integer literal: %s", valStr)
1369 }
1370 val = int64(uval)
1371 }
1372 return ast.NewNumber(tok, val)
1373 }
1374 if p.match(token.FloatNumber) {
1375 val, _ := strconv.ParseFloat(p.previous.Value, 64)
1376 return ast.NewFloatNumber(tok, val)
1377 }
1378 if p.match(token.String) {
1379 return ast.NewString(tok, p.previous.Value)
1380 }
1381 if p.match(token.Nil) {
1382 return ast.NewNil(tok)
1383 }
1384 if p.match(token.Null) {
1385 util.Warn(p.cfg, config.WarnExtra, tok, "Use of 'null' is discouraged, prefer 'nil' for idiomatic Bx code")
1386 return ast.NewNil(tok)
1387 }
1388 if p.match(token.Ident) {
1389 identTok := p.previous
1390 if p.isTypedPass && p.isTypeName(identTok.Value) && p.check(token.LBrace) {
1391 typeNode := ast.NewIdent(identTok, identTok.Value)
1392 return p.parseStructLiteral(typeNode)
1393 }
1394 return ast.NewIdent(tok, p.previous.Value)
1395 }
1396 if p.isTypedPass && p.isBuiltinType(p.current) {
1397 tokType := p.current.Type
1398 p.advance()
1399 if keyword, ok := token.TypeStrings[tokType]; ok {
1400 return ast.NewIdent(tok, keyword)
1401 }
1402 }
1403 if p.match(token.TypeKeyword) {
1404 util.Warn(p.cfg, config.WarnExtra, p.previous, "Using keyword 'type' as an identifier")
1405 return ast.NewIdent(tok, "type")
1406 }
1407 if p.match(token.TypeOf) {
1408 p.expect(token.LParen, "Expected '(' after 'typeof'")
1409 expr := p.parseExpr()
1410 p.expect(token.RParen, "Expected ')' after typeof expression")
1411 return ast.NewTypeOf(tok, expr)
1412 }
1413 if p.match(token.LParen) {
1414 // Only allow C-style casts for pointer types, not simple scalar types
1415 if p.isTypedPass && p.isPointerCastAhead() {
1416 castType := p.parseType()
1417 p.expect(token.RParen, "Expected ')' after type in cast")
1418 exprToCast := p.parseUnaryExpr()
1419 return ast.NewTypeCast(tok, exprToCast, castType)
1420 }
1421 expr := p.parseExpr()
1422 p.expect(token.RParen, "Expected ')' after expression")
1423 return expr
1424 }
1425 if p.match(token.Auto) {
1426 if p.check(token.LBracket) {
1427 allocTok := p.previous
1428 p.advance()
1429 sizeExpr := p.parseExpr()
1430 p.expect(token.RBracket, "Expected ']' after auto allocation size")
1431 return ast.NewAutoAlloc(allocTok, sizeExpr)
1432 }
1433 p.pos--
1434 p.current = p.previous
1435 }
1436
1437 // Handle array literals: []type{ ... }
1438 if p.isTypedPass && p.match(token.LBracket) {
1439 arrayTok := p.previous
1440 p.expect(token.RBracket, "Expected ']' for array literal")
1441 if p.isBuiltinType(p.current) || p.isTypeName(p.current.Value) || p.check(token.Star) {
1442 elemType := p.parseType()
1443 if elemType != nil && p.check(token.LBrace) {
1444 return p.parseArrayLiteral(arrayTok, elemType)
1445 }
1446 }
1447 // Not an array literal, backtrack
1448 util.Error(arrayTok, "Expected type after '[]' for array literal")
1449 return nil
1450 }
1451
1452 if !p.check(token.EOF) && !p.check(token.RBrace) && !p.check(token.Semi) {
1453 util.Error(tok, "Expected an expression")
1454 }
1455 return nil
1456}
1457
1458// typeFromName converts a type name string to a BxType
1459func (p *Parser) typeFromName(name string) *ast.BxType {
1460 // Check if it's a built-in type keyword
1461 if tokType, isKeyword := token.KeywordMap[name]; isKeyword {
1462 if tokType == token.Void {
1463 return ast.TypeVoid
1464 } else if tokType == token.StringKeyword {
1465 return ast.TypeString
1466 } else if tokType >= token.Float && tokType <= token.Float64 {
1467 return &ast.BxType{Kind: ast.TYPE_FLOAT, Name: name}
1468 } else if tokType >= token.Byte && tokType <= token.Any {
1469 return &ast.BxType{Kind: ast.TYPE_PRIMITIVE, Name: name}
1470 }
1471 }
1472
1473 // Check if it's a user-defined type name
1474 if p.isTypeName(name) {
1475 return &ast.BxType{Kind: ast.TYPE_PRIMITIVE, Name: name}
1476 }
1477
1478 return nil
1479}
1480
1481// parseTypedExtrnDecl parses typed external function declarations like "tm extrn localtime;"
1482func (p *Parser) parseTypedExtrnDecl(typeTok token.Token, returnType *ast.BxType) *ast.Node {
1483 var names []*ast.Node
1484 for {
1485 p.expect(token.Ident, "Expected identifier in typed 'extrn' declaration")
1486 names = append(names, ast.NewIdent(p.previous, p.previous.Value))
1487 if !p.match(token.Comma) {
1488 break
1489 }
1490 }
1491 p.expect(token.Semi, "Expected ';' after typed 'extrn' declaration")
1492 return ast.NewExtrnDecl(typeTok, names, returnType)
1493}