repos / gbc

GBC - Go B Compiler
git clone https://github.com/xplshn/gbc.git

gbc / pkg / parser
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}