start = - ( s:stmt { s; } | !. # { yysval = 0 } | < (!EOL .)* > # { fatal("syntax error near: %s", yytext) } ) stmt = WHILE LPAREN c:expr RPAREN s:stmt { While.new(condition: c, body: s) } | IF LPAREN c:expr RPAREN s:stmt ( ELSE t:stmt { If.new(condition: c, consequent: s, alternate: t ) } | { If.new(condition: c, consequent: s, alternate: nil) } ) | CONT EOS { Continue.new() } | BREAK e:expr EOS { Break.new(value: e) } | BREAK EOS { Break.new(value: nil) } | RETURN e:expr EOS { Return.new(value: e) } | RETURN EOS { Return.new(value: nil) } | FOR LPAREN i:id IN e:expr RPAREN s:stmt { ForIn.new(identifier: i, expression: e, body: s) } | FOR LPAREN i:id FROM a:expr TO b:expr RPAREN s:stmt { ForFromTo.new(identifier: i, first: a, last: b, body: s) } | FOR LPAREN i:expr SEMI c:expr SEMI u:expr RPAREN s:stmt { For.new(initialise: i, condition: c, update: u, body: s) } | TRY t:stmt ( CATCH LPAREN i:id RPAREN c:stmt { TryCatch.new(statement: t, identifier: i, handler: c) } | ENSURE e:stmt { TryEnsure.new(statement: t, handler: e) } ) | RAISE e:expr EOS { Raise.new(value: e) } | LOCAL i:id p:params b:block { SetLocal.new(name: i, value: Lambda.new(parameters: p, body: b)) } | GLOBAL i:id p:params b:block { SetGlobal.new(name: i, value: Lambda.new(parameters: p, body: b)) } | i:id p:params b:block { SetVar.new(name: i, value: Lambda.new(parameters: p, body: b)) } | v:proto DOT i:id p:params b:block { SetProp.new(object: v, key: i, value: Lambda.new(parameters: p, body: b)) } | b:block { Block.new(body: b) } | e:expr EOS { e } proto = v:var ( DOT j:id !LPAREN { v = GetProp.new(object: v, key: j) } )* { v } EOS = SEMI+ | &RBRACE | &ELSE | &CATCH expr = LOCAL i:id ASSIGN e:expr { SetLocal.new(name: i, value: e) } | GLOBAL i:id ASSIGN e:expr { SetGlobal.new(name: i, value: e) } | i:id ASSIGN e:expr { SetVar.new(name: i, value: e) } | l:logor ( ASSIGN r:expr { l = assign(l, r) } | PLUSEQ r:expr { l = newBinop(__opPreAdd, lvalue(l), r) } | MINUSEQ r:expr { l = newBinop(__opPreSub, lvalue(l), r) } | STAREQ r:expr { l = newBinop(__opPreMul, lvalue(l), r) } | SLASHEQ r:expr { l = newBinop(__opPreDiv, lvalue(l), r) } | PCENTEQ r:expr { l = newBinop(__opPreMod, lvalue(l), r) } | SHLEQ r:expr { l = newBinop(__opPreShl, lvalue(l), r) } | SHREQ r:expr { l = newBinop(__opPreShr, lvalue(l), r) } | ANDEQ r:expr { l = newBinop(__opPreAnd, lvalue(l), r) } | XOREQ r:expr { l = newBinop(__opPreXor, lvalue(l), r) } | OREQ r:expr { l = newBinop(__opPreOr, lvalue(l), r) } )? { l } logor = l:logand ( BARBAR r:logand { l = newBinop(__opLogOr, l, r) } )* { l } logand = l:bitor ( ANDAND r:bitor { l = newBinop(__opLogAnd, l, r) } )* { l } bitor = l:bitxor ( OR r:bitxor { l = newBinop(__opBitOr, l, r) } )* { l } bitxor = l:bitand ( XOR r:bitand { l = newBinop(__opBitXor, l, r) } )* { l } bitand = l:eq ( AND r:eq { l = newBinop(__opBitAnd, l, r) } )* { l } eq = l:ineq ( EQ r:ineq { l = newBinop(__opEq, l, r) } | NOTEQ r:ineq { l = newBinop(__opNotEq, l, r) } )* { l } ineq = l:shift ( LESS r:shift { l = newBinop(__opLess, l, r) } | LESSEQ r:shift { l = newBinop(__opLessEq, l, r) } | GRTREQ r:shift { l = newBinop(__opGrtrEq, l, r) } | GRTR r:shift { l = newBinop(__opGrtr, l, r) } )* { l } shift = l:sum ( SHL r:sum { l = newBinop(__opShl, l, r) } | SHR r:sum { l = newBinop(__opShr, l, r) } )* { l } sum = l:prod ( PLUS r:prod { l = newBinop(__opAdd, l, r) } | MINUS r:prod { l = newBinop(__opSub, l, r) } )* { l } prod = l:range ( STAR r:range { l = newBinop(__opMul, l, r) } | SLASH r:range { l = newBinop(__opDiv, l, r) } | PCENT r:range { l = newBinop(__opMod, l, r) } ) * { l } range = i1:prefix ( DOTDOT i2:prefix { i1 = Range.new(start: i1, end: i2) } ) ? { i1 } prefix = PPLUS p:prefix { newBinop(__opPreAdd, lvalue(p), 1) } | MMINUS p:prefix { newBinop(__opPreSub, lvalue(p), 1) } | PLING p:prefix { Unyop.new(operation: __opNot).push(p) } | MINUS p:prefix { Unyop.new(operation: __opNeg).push(p) } | TILDE p:prefix { Unyop.new(operation: __opCom).push(p) } | BQUOTE s:stmt { Unyop.new(operation: __opQuasiquote).push(s) } | COMMAT e:expr { Unyop.new(operation: __opUnquote).push(e) } | postfix postfix = SUPER DOT i:id a:args { Super.new(method: i, arguments: a) } | p:primary ( LBRAK e:expr RBRAK { p = GetArray.new(object: p, index: e) } | DOT i:id ( a:args !LBRACE { p = Invoke.new(self: p, method: i, arguments: a) } | { p = GetProp.new(object: p, key: i) } ) | a:args !LBRACE { p = newApply(p, a) } )* ( PPLUS { p = newBinop(__opPostAdd, lvalue(p), 1) } | MMINUS { p = newBinop(__opPostAdd, lvalue(p), -1) } )? { p } args = LPAREN a:mkobj ( RPAREN | ( k:id COLON e:expr { a[k] = e } | e:expr { a.push(e) } ) ( COMMA ( k:id COLON e:expr { a[k] = e } | e:expr { a.push(e) } ) )* RPAREN ) { a } params = LPAREN p:mkobj ( RPAREN | i:id ( COLON e:expr { p[i] = e } | { p.push(i) } ) ( COMMA i:id ( COLON e:expr { p[i] = e } | { p.push(i) } ) )* RPAREN ) { p } mkobj = { Object.new() } primary = nil | number | string | symbol | var | lambda | subexpr | literal lambda = p:params b:block { Lambda.new(parameters: p, body: b) } subexpr = LPAREN e:expr RPAREN { e } | b:block { Block.new(body: b) } literal = LBRAK o:mkobj ( RBRAK | ( ( i:id COLON e:expr { o[i] = e } | e:expr { o.push(e) } ) ( COMMA ( i:id COLON e:expr { o[i] = e } | e:expr { o.push(e) } ) )* )? RBRAK ) { Literal.new(object: o) } block = LBRACE b:mkobj ( e:stmt { b.push(e) } )* RBRACE { b } nil = NIL { nil } number = "-" n:unsign { Unyop.new(operation: __opNeg).push(n) } | "+" n:number { n } | n:unsign { n } unsign = < DIGIT* '.' DIGIT+ EXP? > - { yytext.asFloat() } | "0" [bB] < BIGIT+ > - { yytext.asInteger(2) } | "0" [xX] < HIGIT+ > - { yytext.asInteger(16) } | "0" < OIGIT* > - { yytext.asInteger(8) } | < DIGIT+ > - { yytext.asInteger() } | "'" < char > "'" - { ord(yytext.unescaped()) } string = '"' < ( !'"' char )* > '"' - { yytext } # Version originale, qui ne parse pas deux antislash pour une raison obscure # char = "\\" ( ["'\\abfnrtv] # | [xX] HIGIT* # | [0-7][0-7]?[0-7]? # ) # | . # Version de rawgrammar.leg, qui fonctionne sans problème char = '\\' [abefnrtv'"\[\]\\] | '\\' [0-3][0-7][0-7] | '\\' [0-7][0-7]? | '\\' [xX] HIGIT+ | !'\\' . symbol = HASH i:id { i } var = LOCAL i:id { GetLocal.new(name: i) } | GLOBAL i:id { GetGlobal.new(name: i) } | i:id { GetVar.new(name: i) } id = < LETTER ALNUM* > - { intern(yytext) } BIGIT = [0-1] OIGIT = [0-7] DIGIT = [0-9] HIGIT = [0-9A-Fa-f] LETTER = [A-Za-z_$?] ALNUM = LETTER | DIGIT SIGN = [-+] EXP = [eE] SIGN DIGIT+ - = SPACE* SPACE = [ \t] | EOL | SLC | MLC EOL = [\n\r] # { ++lineno } SLC = "//" (!EOL .)* MLC = "/*" ( MLC | !"*/" (EOL | .))* "*/" - NIL = "nil" !ALNUM - WHILE = "while" !ALNUM - IF = "if" !ALNUM - ELSE = "else" !ALNUM - FOR = "for" !ALNUM - IN = "in" !ALNUM - FROM = "from" !ALNUM - TO = "to" !ALNUM - CONT = "continue" !ALNUM - BREAK = "break" !ALNUM - RETURN = "return" !ALNUM - TRY = "try" !ALNUM - CATCH = "catch" !ALNUM - ENSURE = "ensure" !ALNUM - RAISE = "raise" !ALNUM - GLOBAL = "global" !ALNUM - LOCAL = "local" !ALNUM - SUPER = "super" !ALNUM - BQUOTE = "`" - COMMAT = "@" - HASH = "#" - SEMI = ";" - ASSIGN = "=" ![=] - COMMA = "," - COLON = ":" ![:] - LPAREN = "(" - RPAREN = ")" - LBRAK = "[" - RBRAK = "]" - LBRACE = "{" - RBRACE = "}" - BARBAR = "||" ![=] - ANDAND = "&&" ![=] - OR = "|" ![|=] - OREQ = "|=" - XOR = "^" ![=] - XOREQ = "^=" - AND = "&" ![&=] - ANDEQ = "&=" - EQ = "==" - NOTEQ = "!=" - LESS = "<" ![<=] - LESSEQ = "<=" - GRTREQ = ">=" - GRTR = ">" ![=] - SHL = "<<" ![=] - SHLEQ = "<<=" - SHR = ">>" ![=] - SHREQ = ">>=" - PLUS = "+" ![+=] - PLUSEQ = "+=" - PPLUS = "++" - MINUS = "-" ![-=] - MINUSEQ = "-=" - MMINUS = "--" - STAR = "*" ![=] - STAREQ = "*=" - SLASH = "/" ![/=] - SLASHEQ = "/=" - PCENT = "%" ![=] - PCENTEQ = "%=" - DOT = "." ![.] - DOTDOT = ".." - PLING = "!" ![=] - TILDE = "~" -