// AST factories { const attr = (name, args) => ({ name, args }) const stmt = (stmt_type, args) => ({ type: "stmt", stmt_type, args }) const decl = (decl_type, name, attrs, args) => ({ type: "decl", decl_type, name, attrs: attrs || [], args }) const uexpr = (op, arg) => ({ type: "unary", op, arg }) const bexpr = (op, left, right) => ({ type: "binary", op, left, right }) const assocl = (head, tail) => tail.reduce((left, arg) => { const [op, right] = arg return bexpr(op, left, right) }, head) } Program = WS @(Decl / Stmt)* WS // Declarations Decl = VarDecl VarDecl = attrs:Attrs? type:Type name:Ident SEMI { return decl("var", name, attrs, { type }) } // Statements Stmt = GotoStmt / LabelStmt / AssignStmt GotoStmt = GOTO name:Ident SEMI { return stmt("goto", { name }) } LabelStmt = name:Ident COLON { return stmt("label", { name }) } AssignStmt = name:Ident ASSIGN expr:Expr SEMI { return stmt("assign", { name, expr }) } // Expressions Expr = BitOrExpr BitOrExpr = head:BitXorExpr tail:(op:BitOrOp e:BitXorExpr)* { return assocl(head, tail || []) } BitOrOp = PIPE { return "bit_or" } BitXorExpr = head:BitAndExpr tail:(op:BitXorOp e:BitAndExpr)* { return assocl(head, tail || []) } BitXorOp = CARET { return "bit_xor" } BitAndExpr = head:ShiftExpr tail:(op:BitAndOp e:ShiftExpr)* { return assocl(head, tail || []) } BitAndOp = AMPERSAND { return "bit_and" } ShiftExpr = head:AddExpr op:ShiftOp n:Number { return bexpr(op, head, n) } / AddExpr ShiftOp = LTLT { return "shift_left" } / GTGT { return "shift_right" } AddExpr = head:UnaryExpr tail:(op:AddOp e:UnaryExpr)* { return assocl(head, tail || []) } AddOp = PLUS { return "add" } / MINUS { return "subtract" } UnaryExpr = op:UnaryOp? e:BaseExpr { return op ? uexpr(op, e) : e } UnaryOp = TILDE { return "bit_negate" } / MINUS { return "arith_negate" } BaseExpr = Ident / Number / LPAREN @Expr RPAREN // Attributes Attrs = LATTR @AttrList RATTR AttrList = h:Attr t:(COMMA @Attr)* { return [h].concat(t) } / '' { return [] } Attr = name:Ident args:(LPAREN @AttrArgs RPAREN)? { return attr(name, args || []) } AttrArgs = h:AttrArg t:(COMMA @AttrArg)* { return [h].concat(t) } / '' { return [] } AttrArg = Ident / Number // Types Type = PrimitiveType PrimitiveType = S8 / U8 / U16 // Terminals Ident = h:[a-zA-Z_] t:[0-9a-zA-Z_]* WS { return h + t.join('') } Number = digits:[0-9]+ WS { return parseInt(digits.join(''), 10) } / '0x' digits:[a-fA-F0-9]+ WS { return parseInt(digits.join(''), 16) } / '0b' digits:[01]+ WS { return parseInt(digits.join(''), 2) } // Terminal keywords GOTO = 'goto' WS S8 = @'s8' WS U8 = @'u8' WS U16 = @'u16' WS // Terminal symbols AMPERSAND = '&' WS ASSIGN = '<-' WS CARET = '^' WS COLON = ':' WS COMMA = ',' WS GTGT = '>>' WS LATTR = '[[' WS LPAREN = '(' WS LTLT = '<<' WS MINUS = '-' WS PIPE = '|' WS PLUS = '+' WS RATTR = ']]' WS RPAREN = ')' WS SEMI = ';' WS TILDE = '~' WS // Misc WS = [ \t\r\n]*