2026-01-01 22:18:25 -05:00
# include "parser.h"
# include <stdexcept>
Parser : : Parser ( Lexer lex ) : m_lex ( std : : move ( lex ) ) { }
Module Parser : : parseModule ( ) {
Module m ;
while ( peek ( ) . kind ! = TokenKind : : End ) {
TypeDef t = parseTypeDef ( ) ;
if ( m . types . contains ( t . name ) ) {
throw std : : runtime_error ( ( " Duplicate type: " + t . name ) . toStdString ( ) ) ;
}
m . types . insert ( t . name , std : : move ( t ) ) ;
}
return m ;
}
UiFlags Parser : : parseUiFlags ( bool defaultUi )
{
UiFlags f ;
f . ui = defaultUi ;
if ( ! match ( TokenKind : : LBracket ) )
return f ;
while ( true ) {
Token a = expect ( TokenKind : : Identifier , " Expected attribute name " ) ;
const QString raw = a . text ;
const QString s = raw . toLower ( ) ;
if ( s = = " ui " ) f . ui = true ;
else if ( s = = " readonly " ) f . readOnly = true ;
else if ( s = = " hidden " ) f . hidden = true ;
else if ( s = = " display " ) {
expect ( TokenKind : : Assign , " Expected '=' after display " ) ;
Token v = expect ( TokenKind : : String , " Expected string after display= " ) ;
f . display = v . text ;
}
else if ( s = = " table " ) {
expect ( TokenKind : : Assign , " Expected '=' after table " ) ;
Token v = expect ( TokenKind : : String , " Expected string after table= " ) ;
f . tableTitle = v . text ;
}
else if ( s = = " columns " ) {
expect ( TokenKind : : Assign , " Expected '=' after columns " ) ;
Token v = expect ( TokenKind : : String , " Expected string after columns= " ) ;
f . columnsCsv = v . text ; // parse later into QStringList
}
else if ( s . startsWith ( " format_ " ) ) {
// format_assetPtr="hex"
const QString colKey = raw . mid ( QString ( " format_ " ) . size ( ) ) ; // preserve case after prefix if you want
expect ( TokenKind : : Assign , " Expected '=' after format_<col> " ) ;
Token v = expect ( TokenKind : : String , " Expected string after format_<col>= " ) ;
f . formats . insert ( colKey , v . text . toLower ( ) ) ; // "hex"
}
else {
throw std : : runtime_error ( ( " Unknown attribute: " + a . text ) . toStdString ( ) ) ;
}
if ( match ( TokenKind : : Comma ) ) continue ;
break ;
}
expect ( TokenKind : : RBracket , " Expected ']' " ) ;
return f ;
}
TypeAttrs Parser : : parseTypeAttrs ( )
{
TypeAttrs a ;
if ( ! match ( TokenKind : : LBracket ) )
return a ;
while ( true ) {
Token t = expect ( TokenKind : : Identifier , " Expected type attribute " ) ;
const QString s = t . text . toLower ( ) ;
if ( s = = " root " ) a . root = true ;
else if ( s = = " display " ) {
expect ( TokenKind : : Assign , " Expected '=' after display " ) ;
Token v = expect ( TokenKind : : String , " Expected string after display= " ) ;
a . display = v . text ;
} else {
throw std : : runtime_error ( ( " Unknown type attribute: " + t . text ) . toStdString ( ) ) ;
}
if ( match ( TokenKind : : Comma ) ) continue ;
break ;
}
expect ( TokenKind : : RBracket , " Expected ']' " ) ;
return a ;
}
TypeDef Parser : : parseTypeDef ( ) {
expect ( TokenKind : : KwType , " Expected 'type' " ) ;
Token nameTok = expect ( TokenKind : : Identifier , " Expected type name " ) ;
TypeDef t ;
t . name = nameTok . text ;
const TypeAttrs attrs = parseTypeAttrs ( ) ;
t . isRoot = attrs . root ;
t . display = attrs . display ;
// optional: byteorder LE|BE
if ( match ( TokenKind : : KwByteOrder ) ) {
t . hasExplicitByteOrder = true ; // <<< add
if ( match ( TokenKind : : KwLE ) ) t . order = ByteOrder : : LE ;
else if ( match ( TokenKind : : KwBE ) ) t . order = ByteOrder : : BE ;
else throw std : : runtime_error ( " Expected LE or BE after byteorder " ) ;
}
expect ( TokenKind : : LBrace , " Expected '{' after type header " ) ;
while ( peek ( ) . kind = = TokenKind : : KwCriteria ) {
// only allow one criteria block; you can also merge multiple blocks
if ( ! t . criteria . isEmpty ( ) )
throw std : : runtime_error ( " Duplicate criteria block " ) ;
t . criteria = parseCriteriaBlock ( ) ;
}
t . body = parseBlock ( ) ;
expect ( TokenKind : : RBrace , " Expected '}' after type body " ) ;
return t ;
}
QVector < StmtPtr > Parser : : parseBlock ( ) {
QVector < StmtPtr > out ;
while ( peek ( ) . kind ! = TokenKind : : RBrace & & peek ( ) . kind ! = TokenKind : : End ) {
out . push_back ( parseStatement ( ) ) ;
}
return out ;
}
StmtPtr Parser : : parseStatement ( ) {
switch ( peek ( ) . kind ) {
case TokenKind : : KwU8 :
case TokenKind : : KwU16 :
case TokenKind : : KwU32 :
case TokenKind : : KwU64 :
case TokenKind : : KwI8 :
case TokenKind : : KwI16 :
case TokenKind : : KwI32 :
case TokenKind : : KwI64 :
2026-01-07 16:35:35 -05:00
case TokenKind : : KwF32 :
case TokenKind : : KwF64 :
2026-01-01 22:18:25 -05:00
return parseScalarStmt ( ) ;
case TokenKind : : KwSkip :
return parseSkipStmt ( ) ;
case TokenKind : : KwAlign :
return parseAlignStmt ( ) ;
case TokenKind : : KwSeek :
return parseSeekStmt ( ) ;
case TokenKind : : KwIf :
return parseIfStmt ( ) ;
case TokenKind : : KwWhile :
return parseWhileStmt ( ) ;
case TokenKind : : KwRepeat :
return parseRepeatStmt ( ) ;
case TokenKind : : KwFor :
return parseForStmt ( ) ;
case TokenKind : : Identifier :
return parseAssignStmt ( ) ;
case TokenKind : : KwRequire :
return parseRequireStmt ( ) ;
case TokenKind : : KwByteOrder :
return parseByteOrderStmt ( ) ;
2026-01-07 16:35:35 -05:00
case TokenKind : : KwBreak :
return parseBreakStmt ( ) ;
2026-01-01 22:18:25 -05:00
case TokenKind : : KwBool :
return parseScalarStmt ( ) ;
2026-01-11 12:08:26 -05:00
case TokenKind : : KwInline :
return parseInlineStmt ( ) ;
case TokenKind : : KwArray :
return parseArrayStmt ( ) ;
case TokenKind : : KwConst :
return parseConstStmt ( ) ;
case TokenKind : : KwMatch :
return parseMatchStmt ( ) ;
2026-01-01 22:18:25 -05:00
default :
throw std : : runtime_error ( ( " Unexpected token in statement: " + peek ( ) . text ) . toStdString ( ) ) ;
}
}
ScalarType Parser : : scalarFrom ( TokenKind k ) const {
switch ( k ) {
case TokenKind : : KwU8 : return ScalarType : : U8 ;
case TokenKind : : KwI8 : return ScalarType : : I8 ;
case TokenKind : : KwU16 : return ScalarType : : U16 ;
case TokenKind : : KwI16 : return ScalarType : : I16 ;
case TokenKind : : KwU32 : return ScalarType : : U32 ;
case TokenKind : : KwI32 : return ScalarType : : I32 ;
case TokenKind : : KwU64 : return ScalarType : : U64 ;
case TokenKind : : KwI64 : return ScalarType : : I64 ;
2026-01-07 16:35:35 -05:00
case TokenKind : : KwF32 : return ScalarType : : F32 ;
case TokenKind : : KwF64 : return ScalarType : : F64 ;
2026-01-01 22:18:25 -05:00
case TokenKind : : KwBool : return ScalarType : : Bool ;
default : break ;
}
throw std : : runtime_error ( " Not a scalar token " ) ;
}
StmtPtr Parser : : parseScalarStmt ( ) {
Token tTok = m_lex . next ( ) ;
Token nameTok = expect ( TokenKind : : Identifier , " Expected variable name after scalar " ) ;
2026-01-11 12:08:26 -05:00
// Bracket attributes on scalars are no longer supported - use ui() function instead
if ( peek ( ) . kind = = TokenKind : : LBracket ) {
throw std : : runtime_error ( " Bracket attributes [ui, ...] are no longer supported. Use ui( \" " + nameTok . text . toStdString ( ) + " \" , \" Display Name \" ); instead " ) ;
}
UiFlags flags ;
flags . ui = false ;
2026-01-01 22:18:25 -05:00
expect ( TokenKind : : Semicolon , " Expected ';' after scalar statement " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > node = Stmt : : ReadScalar { scalarFrom ( tTok . kind ) , nameTok . text , flags } ;
return s ;
}
StmtPtr Parser : : parseSkipStmt ( ) {
Token kw = expect ( TokenKind : : KwSkip , " Expected skip " ) ;
ExprPtr e = parseExpr ( ) ;
expect ( TokenKind : : Semicolon , " Expected ';' after skip " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ; s - > col = kw . col ;
s - > node = Stmt : : Skip { std : : move ( e ) } ;
return s ;
}
StmtPtr P arser : : parseAlignStmt ( ) {
Token kw = expect ( TokenKind : : KwAlign , " Expected align " ) ;
expect ( TokenKind : : LParen , " Expected '(' after align " ) ;
ExprPtr n = parseExpr ( ) ;
expect ( TokenKind : : RParen , " Expected ')' after align(arg) " ) ;
expect ( TokenKind : : Semicolon , " Expected ';' after align " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ; s - > col = kw . col ;
s - > node = Stmt : : Align { std : : move ( n ) } ;
return s ;
}
StmtPtr Parser : : parseSeekStmt ( ) {
Token kw = expect ( TokenKind : : KwSeek , " Expected seek " ) ;
expect ( TokenKind : : LParen , " Expected '(' after seek " ) ;
ExprPtr p = parseExpr ( ) ;
expect ( TokenKind : : RParen , " Expected ')' after seek(arg) " ) ;
expect ( TokenKind : : Semicolon , " Expected ';' after seek " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ; s - > col = kw . col ;
s - > node = Stmt : : Seek { std : : move ( p ) } ;
return s ;
}
StmtPtr Parser : : parseAssignStmt ( ) {
Token nameTok = expect ( TokenKind : : Identifier , " Expected identifier " ) ;
// Check if this is a function call statement or an assignment
if ( peek ( ) . kind = = TokenKind : : LParen ) {
// Function call statement: functionName(args);
// Parse function arguments
expect ( TokenKind : : LParen , " Expected '(' " ) ;
QVector < ExprPtr > args ;
if ( peek ( ) . kind ! = TokenKind : : RParen ) {
args . push_back ( parseExpr ( ) ) ;
while ( peek ( ) . kind = = TokenKind : : Comma ) {
m_lex . next ( ) ; // consume comma
args . push_back ( parseExpr ( ) ) ;
}
}
expect ( TokenKind : : RParen , " Expected ')' " ) ;
expect ( TokenKind : : Semicolon , " Expected ';' after function call " ) ;
// Create function call expression
auto callExpr = QSharedPointer < Expr > : : create ( ) ;
callExpr - > node = Expr : : Call { nameTok . text , std : : move ( args ) } ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > node = Stmt : : CallStmt { std : : move ( callExpr ) } ;
return s ;
}
// Regular assignment: name = expr;
Token eq = expect ( TokenKind : : Assign , " Expected '=' " ) ;
ExprPtr e = parseExpr ( ) ;
2026-01-11 12:08:26 -05:00
// Bracket attributes on assignments are no longer supported - use ui() function instead
if ( peek ( ) . kind = = TokenKind : : LBracket ) {
throw std : : runtime_error ( " Bracket attributes [ui, ...] are no longer supported. Use ui( \" " + nameTok . text . toStdString ( ) + " \" , \" Display Name \" ); instead " ) ;
}
UiFlags flags ;
flags . ui = false ;
2026-01-01 22:18:25 -05:00
expect ( TokenKind : : Semicolon , " Expected ';' after assignment " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > node = Stmt : : Assign { nameTok . text , std : : move ( e ) , flags } ;
return s ;
}
QVector < StmtPtr > Parser : : parseCriteriaBlock ( )
{
expect ( TokenKind : : KwCriteria , " Expected criteria " ) ;
expect ( TokenKind : : LBrace , " Expected '{' after criteria " ) ;
QVector < StmtPtr > blk = parseBlock ( ) ; // allow u16, if, seek, assign, etc.
expect ( TokenKind : : RBrace , " Expected '}' after criteria block " ) ;
return blk ;
}
StmtPtr Parser : : parseRequireStmt ( )
{
Token kw = expect ( TokenKind : : KwRequire , " Expected require " ) ;
ExprPtr cond = parseExpr ( ) ;
expect ( TokenKind : : Semicolon , " Expected ';' after require " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ; s - > col = kw . col ;
s - > node = Stmt : : Require { std : : move ( cond ) } ;
return s ;
}
StmtPtr Parser : : parseIfStmt ( ) {
Token kw = expect ( TokenKind : : KwIf , " Expected if " ) ;
expect ( TokenKind : : LParen , " Expected '(' after if " ) ;
ExprPtr cond = parseExpr ( ) ;
expect ( TokenKind : : RParen , " Expected ')' after if condition " ) ;
expect ( TokenKind : : LBrace , " Expected '{' after if " ) ;
QVector < StmtPtr > thenBody = parseBlock ( ) ;
expect ( TokenKind : : RBrace , " Expected '}' after if body " ) ;
QVector < StmtPtr > elseBody ;
if ( match ( TokenKind : : KwElse ) ) {
2026-01-07 16:35:35 -05:00
// Support 'else if' by recursively parsing if statement
if ( peek ( ) . kind = = TokenKind : : KwIf ) {
elseBody . push_back ( parseIfStmt ( ) ) ;
} else {
expect ( TokenKind : : LBrace , " Expected '{' after else " ) ;
elseBody = parseBlock ( ) ;
expect ( TokenKind : : RBrace , " Expected '}' after else body " ) ;
}
2026-01-01 22:18:25 -05:00
}
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ; s - > col = kw . col ;
s - > node = Stmt : : If { std : : move ( cond ) , std : : move ( thenBody ) , std : : move ( elseBody ) } ;
return s ;
}
StmtPtr Parser : : parseWhileStmt ( ) {
Token kw = expect ( TokenKind : : KwWhile , " Expected while " ) ;
expect ( TokenKind : : LParen , " Expected '(' after while " ) ;
ExprPtr cond = parseExpr ( ) ;
expect ( TokenKind : : RParen , " Expected ')' after while condition " ) ;
expect ( TokenKind : : LBrace , " Expected '{' after while " ) ;
QVector < StmtPtr > body = parseBlock ( ) ;
expect ( TokenKind : : RBrace , " Expected '}' after while body " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ; s - > col = kw . col ;
s - > node = Stmt : : While { std : : move ( cond ) , std : : move ( body ) } ;
return s ;
}
StmtPtr Parser : : parseRepeatStmt ( ) {
Token kw = expect ( TokenKind : : KwRepeat , " Expected repeat " ) ;
expect ( TokenKind : : LParen , " Expected '(' after repeat " ) ;
ExprPtr count = parseExpr ( ) ;
expect ( TokenKind : : RParen , " Expected ')' after repeat(count) " ) ;
expect ( TokenKind : : LBrace , " Expected '{' after repeat " ) ;
QVector < StmtPtr > body = parseBlock ( ) ;
expect ( TokenKind : : RBrace , " Expected '}' after repeat body " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ; s - > col = kw . col ;
s - > node = Stmt : : Repeat { std : : move ( count ) , std : : move ( body ) } ;
return s ;
}
StmtPtr Parser : : parseForStmt ( ) {
Token kw = expect ( TokenKind : : KwFor , " Expected for " ) ;
Token varTok = expect ( TokenKind : : Identifier , " Expected loop variable name after for " ) ;
expect ( TokenKind : : KwIn , " Expected 'in' " ) ;
// parse: start .. end
ExprPtr start = parseExpr ( ) ;
expect ( TokenKind : : DotDot , " Expected '..' in for range " ) ;
ExprPtr end = parseExpr ( ) ;
expect ( TokenKind : : LBrace , " Expected '{' after for range " ) ;
QVector < StmtPtr > body = parseBlock ( ) ;
expect ( TokenKind : : RBrace , " Expected '}' after for body " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ; s - > col = kw . col ;
s - > node = Stmt : : ForRange { varTok . text , std : : move ( start ) , std : : move ( end ) , std : : move ( body ) } ;
return s ;
}
// --- Expression parsing (Pratt) ---
int Parser : : precedence ( const Token & t ) const {
switch ( t . kind ) {
case TokenKind : : OrOr : return 1 ;
case TokenKind : : AndAnd : return 2 ;
case TokenKind : : EqEq :
case TokenKind : : NotEq : return 3 ;
case TokenKind : : Lt :
case TokenKind : : Lte :
case TokenKind : : Gt :
case TokenKind : : Gte : return 4 ;
case TokenKind : : Bar : return 5 ;
case TokenKind : : Caret : return 6 ;
case TokenKind : : Amp : return 7 ;
case TokenKind : : LShift :
case TokenKind : : RShift : return 8 ;
case TokenKind : : Plus :
case TokenKind : : Minus : return 9 ;
case TokenKind : : Star :
case TokenKind : : Slash :
case TokenKind : : Percent : return 10 ;
default : return 0 ;
}
}
QString Parser : : tokenOpText ( const Token & t ) const {
return t . text ;
}
ExprPtr Parser : : parseExpr ( int minPrec ) {
ExprPtr lhs = parsePrimary ( ) ;
lhs = parsePostfix ( std : : move ( lhs ) ) ;
// pipeline binds tighter than binary ops (so parse it right away)
lhs = parsePipeIfAny ( std : : move ( lhs ) ) ;
while ( true ) {
const Token & op = peek ( ) ;
const int prec = precedence ( op ) ;
if ( prec < minPrec | | prec = = 0 ) break ;
Token opTok = m_lex . next ( ) ;
ExprPtr rhs = parseExpr ( prec + 1 ) ;
auto b = QSharedPointer < Expr > : : create ( ) ;
b - > line = opTok . line ; b - > col = opTok . col ;
b - > node = Expr : : Binary { opTok . text , std : : move ( lhs ) , std : : move ( rhs ) } ;
lhs = std : : move ( b ) ;
// allow pipe after a binary expression too (e.g. read(x+1)|zlib)
lhs = parsePipeIfAny ( std : : move ( lhs ) ) ;
}
return lhs ;
}
ExprPtr Parser : : parsePrimary ( ) {
Token t = peek ( ) ;
if ( match ( TokenKind : : Number ) ) {
auto e = QSharedPointer < Expr > : : create ( ) ;
e - > line = t . line ; e - > col = t . col ;
e - > node = Expr : : Int { t . number } ;
return e ;
}
if ( match ( TokenKind : : String ) ) {
auto e = QSharedPointer < Expr > : : create ( ) ;
e - > line = t . line ; e - > col = t . col ;
e - > node = Expr : : String { t . text } ;
return e ;
}
if ( match ( TokenKind : : KwEOF ) ) {
// represent EOF as variable-like identifier "EOF" to be used only inside read(...)
auto e = QSharedPointer < Expr > : : create ( ) ;
e - > line = t . line ; e - > col = t . col ;
e - > node = Expr : : Var { " EOF " } ;
return e ;
}
2026-01-11 12:08:26 -05:00
if ( match ( TokenKind : : KwTrue ) ) {
auto e = QSharedPointer < Expr > : : create ( ) ;
e - > line = t . line ; e - > col = t . col ;
e - > node = Expr : : Int { 1 } ;
return e ;
}
if ( match ( TokenKind : : KwFalse ) ) {
auto e = QSharedPointer < Expr > : : create ( ) ;
e - > line = t . line ; e - > col = t . col ;
e - > node = Expr : : Int { 0 } ;
return e ;
}
2026-01-01 22:18:25 -05:00
if ( match ( TokenKind : : Identifier ) ) {
auto e = QSharedPointer < Expr > : : create ( ) ;
e - > line = t . line ; e - > col = t . col ;
e - > node = Expr : : Var { t . text } ;
return e ;
}
if ( match ( TokenKind : : LParen ) ) {
ExprPtr e = parseExpr ( ) ;
expect ( TokenKind : : RParen , " Expected ')' " ) ;
return e ;
}
// unary
if ( match ( TokenKind : : Bang ) | | match ( TokenKind : : Minus ) | | match ( TokenKind : : Plus ) ) {
Token op = t ;
ExprPtr rhs = parseExpr ( 11 ) ;
auto e = QSharedPointer < Expr > : : create ( ) ;
e - > line = op . line ; e - > col = op . col ;
e - > node = Expr : : Unary { op . text , std : : move ( rhs ) } ;
return e ;
}
throw std : : runtime_error ( ( " Unexpected token in expression: " + t . text ) . toStdString ( ) ) ;
}
ExprPtr Parser : : parsePostfix ( ExprPtr lhs ) {
2026-01-11 12:08:26 -05:00
while ( true ) {
// function call: var(...)
if ( peek ( ) . kind = = TokenKind : : LParen ) {
// lhs must be Var for function calls
if ( ! std : : holds_alternative < Expr : : Var > ( lhs - > node ) ) {
throw std : : runtime_error ( " Only identifiers can be called as functions " ) ;
}
const QString fn = std : : get < Expr : : Var > ( lhs - > node ) . name ;
expect ( TokenKind : : LParen , " Expected '(' " ) ;
QVector < ExprPtr > args ;
if ( peek ( ) . kind ! = TokenKind : : RParen ) {
while ( true ) {
args . push_back ( parseExpr ( ) ) ;
if ( match ( TokenKind : : Comma ) ) continue ;
break ;
}
}
expect ( TokenKind : : RParen , " Expected ')' after args " ) ;
auto call = QSharedPointer < Expr > : : create ( ) ;
call - > line = lhs - > line ; call - > col = lhs - > col ;
call - > node = Expr : : Call { fn , std : : move ( args ) } ;
lhs = std : : move ( call ) ;
continue ;
2026-01-01 22:18:25 -05:00
}
2026-01-11 12:08:26 -05:00
// member access: expr.field
if ( peek ( ) . kind = = TokenKind : : Dot ) {
Token dotTok = m_lex . next ( ) ; // consume dot
Token fieldTok = expect ( TokenKind : : Identifier , " Expected field name after '.' " ) ;
auto member = QSharedPointer < Expr > : : create ( ) ;
member - > line = dotTok . line ; member - > col = dotTok . col ;
member - > node = Expr : : Member { std : : move ( lhs ) , fieldTok . text } ;
lhs = std : : move ( member ) ;
continue ;
2026-01-01 22:18:25 -05:00
}
2026-01-11 12:08:26 -05:00
break ;
2026-01-01 22:18:25 -05:00
}
return lhs ;
}
ExprPtr Parser : : parsePipeIfAny ( ExprPtr base ) {
if ( peek ( ) . kind ! = TokenKind : : Pipe ) return base ;
auto p = QSharedPointer < Expr > : : create ( ) ;
p - > line = base - > line ; p - > col = base - > col ;
Expr : : Pipe pipe ;
pipe . base = std : : move ( base ) ;
while ( match ( TokenKind : : Pipe ) ) {
// stage can be: identifier (e.g. zlib)
// or: parse <TypeName> (as two identifiers "parse" "zonefile")
Token stage = peek ( ) ;
if ( match ( TokenKind : : Identifier ) ) {
Expr : : Call c ;
c . fn = stage . text ;
// special case: "parse <TypeName>"
if ( c . fn = = " parse " ) {
Token typeTok = expect ( TokenKind : : Identifier , " Expected type name after 'parse' in pipeline " ) ;
c . args . push_back ( QSharedPointer < Expr > : : create ( Expr { Expr : : String { typeTok . text } , typeTok . line , typeTok . col } ) ) ;
}
pipe . stages . push_back ( std : : move ( c ) ) ;
continue ;
}
throw std : : runtime_error ( " Expected stage identifier after '|' " ) ;
}
p - > node = std : : move ( pipe ) ;
return p ;
}
StmtPtr Parser : : parseByteOrderStmt ( ) {
Token kw = expect ( TokenKind : : KwByteOrder , " Expected byteorder " ) ;
ByteOrder bo ;
if ( match ( TokenKind : : KwLE ) )
bo = ByteOrder : : LE ;
else if ( match ( TokenKind : : KwBE ) )
bo = ByteOrder : : BE ;
else
throw std : : runtime_error ( " Expected LE or BE after byteorder " ) ;
expect ( TokenKind : : Semicolon , " Expected ';' after byteorder " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ;
s - > col = kw . col ;
s - > node = Stmt : : SetByteOrder { bo } ;
return s ;
}
2026-01-07 16:35:35 -05:00
StmtPtr Parser : : parseBreakStmt ( ) {
Token kw = expect ( TokenKind : : KwBreak , " Expected break " ) ;
expect ( TokenKind : : Semicolon , " Expected ';' after break " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ;
s - > col = kw . col ;
s - > node = Stmt : : Break { } ;
return s ;
}
2026-01-11 12:08:26 -05:00
// inline typename varName @ ptrField [ui="Display", set_name];
StmtPtr Parser : : parseInlineStmt ( ) {
Token kw = expect ( TokenKind : : KwInline , " Expected inline " ) ;
Token typeTok = expect ( TokenKind : : Identifier , " Expected type name after inline " ) ;
Token varTok = expect ( TokenKind : : Identifier , " Expected variable name " ) ;
QString ptrField ;
if ( match ( TokenKind : : KwAt ) ) {
// For backwards compatibility, also support @ syntax
Token ptrTok = expect ( TokenKind : : Identifier , " Expected pointer field after @ " ) ;
ptrField = ptrTok . text ;
} else if ( match ( TokenKind : : KwWhen ) ) {
// inline cstring name when name_ptr == -1;
Token ptrTok = expect ( TokenKind : : Identifier , " Expected pointer field after when " ) ;
ptrField = ptrTok . text ;
// Consume the "== -1" part (optional, for clarity)
if ( peek ( ) . kind = = TokenKind : : EqEq ) {
m_lex . next ( ) ; // consume ==
parseExpr ( ) ; // consume the -1 or value (we ignore it, always check == -1)
}
} else {
throw std : : runtime_error ( " Expected 'when' or '@' after inline variable name " ) ;
}
// Parse optional attributes
UiFlags flags ;
bool setName = false ;
if ( peek ( ) . kind = = TokenKind : : LBracket ) {
m_lex . next ( ) ; // consume [
while ( true ) {
Token attr = expect ( TokenKind : : Identifier , " Expected attribute " ) ;
QString attrName = attr . text . toLower ( ) ;
if ( attrName = = " ui " ) {
flags . ui = true ;
if ( match ( TokenKind : : Assign ) ) {
Token v = expect ( TokenKind : : String , " Expected string after ui= " ) ;
flags . display = v . text ;
}
} else if ( attrName = = " set_name " ) {
setName = true ;
} else if ( attrName = = " hidden " ) {
flags . hidden = true ;
} else {
throw std : : runtime_error ( ( " Unknown inline attribute: " + attr . text ) . toStdString ( ) ) ;
}
if ( ! match ( TokenKind : : Comma ) ) break ;
}
expect ( TokenKind : : RBracket , " Expected ']' " ) ;
}
expect ( TokenKind : : Semicolon , " Expected ';' after inline statement " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ;
s - > col = kw . col ;
s - > node = Stmt : : Inline { typeTok . text , varTok . text , ptrField , flags , setName } ;
return s ;
}
// array[count] varName: elementType;
// array[count] varName: elementType @ ptrField;
StmtPtr Parser : : parseArrayStmt ( ) {
Token kw = expect ( TokenKind : : KwArray , " Expected array " ) ;
expect ( TokenKind : : LBracket , " Expected '[' after array " ) ;
ExprPtr count = parseExpr ( ) ;
expect ( TokenKind : : RBracket , " Expected ']' after array count " ) ;
Token varTok = expect ( TokenKind : : Identifier , " Expected variable name " ) ;
expect ( TokenKind : : Colon , " Expected ':' after variable name " ) ;
Token typeTok = expect ( TokenKind : : Identifier , " Expected element type " ) ;
QString ptrField ;
if ( match ( TokenKind : : KwAt ) ) {
Token ptrTok = expect ( TokenKind : : Identifier , " Expected pointer field after @ " ) ;
ptrField = ptrTok . text ;
} else if ( match ( TokenKind : : KwWhen ) ) {
Token ptrTok = expect ( TokenKind : : Identifier , " Expected pointer field after when " ) ;
ptrField = ptrTok . text ;
// Consume optional "== -1"
if ( peek ( ) . kind = = TokenKind : : EqEq ) {
m_lex . next ( ) ;
parseExpr ( ) ;
}
}
// Parse optional UI flags
UiFlags flags = parseUiFlags ( false ) ;
expect ( TokenKind : : Semicolon , " Expected ';' after array statement " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ;
s - > col = kw . col ;
s - > node = Stmt : : ArrayDecl { std : : move ( count ) , varTok . text , typeTok . text , ptrField , flags } ;
return s ;
}
// const NAME = value;
StmtPtr Parser : : parseConstStmt ( ) {
Token kw = expect ( TokenKind : : KwConst , " Expected const " ) ;
Token nameTok = expect ( TokenKind : : Identifier , " Expected constant name " ) ;
expect ( TokenKind : : Assign , " Expected '=' after constant name " ) ;
ExprPtr value = parseExpr ( ) ;
expect ( TokenKind : : Semicolon , " Expected ';' after const " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ;
s - > col = kw . col ;
s - > node = Stmt : : Const { nameTok . text , std : : move ( value ) } ;
return s ;
}
// match(expr) {
// pattern | pattern => { body }
// default => { body }
// }
StmtPtr Parser : : parseMatchStmt ( ) {
Token kw = expect ( TokenKind : : KwMatch , " Expected match " ) ;
expect ( TokenKind : : LParen , " Expected '(' after match " ) ;
ExprPtr expr = parseExpr ( ) ;
expect ( TokenKind : : RParen , " Expected ')' after match expression " ) ;
expect ( TokenKind : : LBrace , " Expected '{' after match " ) ;
QVector < QPair < QVector < ExprPtr > , QVector < StmtPtr > > > arms ;
QVector < StmtPtr > defaultBody ;
while ( peek ( ) . kind ! = TokenKind : : RBrace & & peek ( ) . kind ! = TokenKind : : End ) {
// Check for default arm
if ( match ( TokenKind : : KwDefault ) ) {
expect ( TokenKind : : Arrow , " Expected '=>' after default " ) ;
expect ( TokenKind : : LBrace , " Expected '{' after => " ) ;
defaultBody = parseBlock ( ) ;
expect ( TokenKind : : RBrace , " Expected '}' after default body " ) ;
continue ;
}
// Parse patterns: pattern | pattern | pattern => { body }
QVector < ExprPtr > patterns ;
patterns . push_back ( parseExpr ( ) ) ;
while ( match ( TokenKind : : Bar ) ) {
patterns . push_back ( parseExpr ( ) ) ;
}
expect ( TokenKind : : Arrow , " Expected '=>' after pattern(s) " ) ;
expect ( TokenKind : : LBrace , " Expected '{' after => " ) ;
QVector < StmtPtr > body = parseBlock ( ) ;
expect ( TokenKind : : RBrace , " Expected '}' after match arm body " ) ;
arms . push_back ( qMakePair ( std : : move ( patterns ) , std : : move ( body ) ) ) ;
}
expect ( TokenKind : : RBrace , " Expected '}' after match " ) ;
auto s = QSharedPointer < Stmt > : : create ( ) ;
s - > line = kw . line ;
s - > col = kw . col ;
s - > node = Stmt : : Match { std : : move ( expr ) , std : : move ( arms ) , std : : move ( defaultBody ) } ;
return s ;
}