@ -0,0 +1,316 @@ | |||
# minproto.leg -- minimal prototype langauge for semantic experiments | |||
# | |||
# last edited: 2024-07-04 10:07:00 by piumarta on zora | |||
start = - ( s:stmt { global yysval = s } | |||
| !. { global yysval = 0 } | |||
| < (!EOL .)* > { syntaxError(yytext) } | |||
) | |||
stmt = WHILE LPAREN c:expr RPAREN s:stmt { $$ = newWhile(c, s) } | |||
| IF LPAREN c:expr RPAREN s:stmt | |||
( ELSE t:stmt { $$ = newIf(c, s, t ) } | |||
| { $$ = newIf(c, s, nil) } | |||
) | |||
| CONT EOS { $$ = newContinue() } | |||
| BREAK e:expr EOS { $$ = newBreak(e) } | |||
| BREAK EOS { $$ = newBreak(nil) } | |||
| RETURN e:expr EOS { $$ = newReturn(e) } | |||
| RETURN EOS { $$ = newReturn(nil) } | |||
| FOR LPAREN i:id IN e:expr RPAREN | |||
s:stmt { $$ = newForIn(i, e, s) } | |||
| FOR LPAREN i:id FROM a:expr | |||
TO b:expr RPAREN s:stmt { $$ = newForFromTo(i, a, b, s) } | |||
| FOR LPAREN i:expr SEMI c:expr SEMI | |||
u:expr RPAREN s:stmt { $$ = newFor(i, c, u, s) } | |||
| TRY t:stmt | |||
( CATCH LPAREN i:id RPAREN c:stmt { $$ = newTryCatch(t, i, c) } | |||
| ENSURE e:stmt { $$ = newTryEnsure(t, e) } | |||
) | |||
| RAISE e:expr EOS { $$ = newRaise(e) } | |||
| LOCAL i:id p:params b:block { $$ = newSetLocal (i, newLambda(p, b, nil, i)) } | |||
| GLOBAL i:id p:params b:block { $$ = newSetGlobal(i, newLambda(p, b, nil, i)) } | |||
| i:id p:params b:block { $$ = newSetVar (i, newLambda(p, b, nil, i)) } | |||
| v:proto DOT i:id p:params b:block { $$ = newSetProp(v, i, newLambda(p, b, v, i)) } | |||
| b:block { $$ = newBlock(b) } | |||
| e:expr EOS { $$ = e } | |||
proto = v:var ( DOT j:id !LPAREN { v = newGetProp(v, j) } | |||
)* { $$ = v } | |||
EOS = SEMI+ | &RBRACE | &ELSE | &CATCH | |||
expr = LOCAL i:id ASSIGN e:expr { $$ = newSetLocal (i, e) } | |||
| GLOBAL i:id ASSIGN e:expr { $$ = newSetGlobal(i, e) } | |||
| i:id ASSIGN e:expr { $$ = newSetVar (i, 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:prefix ( STAR r:prefix { l = newBinop(opMul, l, r) } | |||
| SLASH r:prefix { l = newBinop(opDiv, l, r) } | |||
| PCENT r:prefix { l = newBinop(opMod, l, r) } | |||
) * { $$ = l } | |||
prefix = PPLUS p:prefix { $$ = newBinop(opPreAdd, lvalue(p), newInteger(1)) } | |||
| MMINUS p:prefix { $$ = newBinop(opPreSub, lvalue(p), newInteger(1)) } | |||
| PLING p:prefix { $$ = newUnyop(opNot, p) } | |||
| MINUS p:prefix { $$ = newUnyop(opNeg, p) } | |||
| TILDE p:prefix { $$ = newUnyop(opCom, p) } | |||
| BQUOTE s:stmt { $$ = newUnyop(opQuasiquote, s) } | |||
| COMMAT e:expr { $$ = newUnyop(opUnquote, e) } | |||
| postfix | |||
postfix = SUPER DOT i:id a:args { $$ = newSuper(i, a) } | |||
| p:primary | |||
( LBRAK | |||
( COLON ( RBRAK { p = newGetSlice(p, nil, nil) } | |||
| e:xexpr RBRAK { p = newGetSlice(p, nil, e) } | |||
) | |||
| s:xexpr ( COLON ( RBRAK { p = newGetSlice(p, s, nil) } | |||
| e:xexpr RBRAK { p = newGetSlice(p, s, e) } | |||
) | |||
| RBRAK { p = newGetArray(p, s) } | |||
) | |||
) | |||
| DOT i:id ( a:args !LBRACE { p = newInvoke(p, i, a) } | |||
| { p = newGetProp(p, i) } | |||
) | |||
| a:args !LBRACE { p = newApply(p, a) } | |||
)* | |||
( PPLUS { p = newBinop(opPostAdd, lvalue(p), newInteger( 1)) } | |||
| MMINUS { p = newBinop(opPostAdd, lvalue(p), newInteger(-1)) } | |||
)? { $$ = p } | |||
args = LPAREN a:mkobj | |||
( RPAREN | |||
| ( k:id COLON e:xexpr { Object_put(a, k, e) } | |||
| e:xexpr { Object_push(a, e) } | |||
) | |||
( COMMA ( k:id COLON e:xexpr { Object_put(a, k, e) } | |||
| e:xexpr { Object_push(a, e) } | |||
) )* RPAREN ) { $$ = a } | |||
params = LPAREN p:mkobj | |||
( RPAREN | |||
| i:id ( COLON e:expr { Object_put(p, i, e) } | |||
| { Object_push(p, i) } | |||
) | |||
( COMMA i:id ( COLON e:expr { Object_put(p, i, e) } | |||
| { Object_push(p, i) } | |||
) | |||
)* RPAREN ) { $$ = p } | |||
mkobj = { $$ = (global new)(pObject) } | |||
primary = nil | number | string | symbol | var | lambda | subexpr | literal # | regex | |||
lambda = p:params b:block { $$ = newLambda(p, b, nil, nil) } | |||
subexpr = LPAREN e:expr RPAREN { $$ = e } | |||
| b:block { $$ = newBlock(b) } | |||
literal = LBRAK o:mkobj | |||
( RBRAK | |||
| ( ( i:id COLON e:expr { Object_put(o, i, e) } | |||
| e:expr { Object_push(o, e) } | |||
) ( COMMA ( i:id COLON e:expr { Object_put(o, i, e) } | |||
| e:expr { Object_push(o, e) } | |||
) )* )? RBRAK ) { $$ = newLiteral(o) } | |||
block = LBRACE b:mkobj | |||
( e:stmt { Object_push(b, e) } | |||
)* ( RBRACE { $$ = b } | |||
| error @{ expected("statement or \x7D", yytext) } | |||
) | |||
nil = NIL { $$ = nil } | |||
number = "-" n:unsign { $$ = neg(n) } | |||
| "+" n:number { $$ = n } | |||
| n:unsign { $$ = n } | |||
unsign = < DIGIT* '.' DIGIT+ EXP? > - { $$ = newFloat(strtod(yytext, 0)) } | |||
| "0" [bB] < BIGIT+ > - { $$ = newInteger(strtol(yytext, 0, 2)) } | |||
| "0" [xX] < HIGIT+ > - { $$ = newInteger(strtol(yytext, 0, 16)) } | |||
| "0" < OIGIT* > - { $$ = newInteger(strtol(yytext, 0, 8)) } | |||
| < DIGIT+ > - { $$ = newInteger(strtol(yytext, 0, 10)) } | |||
| "'" < char > "'" - { $$ = newInteger(_get(newStringUnescaped(yytext), String,value)[0]) } | |||
string = '"' < ( !'"' char )* > '"' - { $$ = newStringUnescaped(yytext) } | |||
char = '\\' [abefnrtv'"\[\]\\] | |||
| '\\' [0-3][0-7][0-7] | |||
| '\\' [xX] HIGIT* | |||
| '\\' [0-7][0-7]? | |||
| !'\\' . | |||
# char = "\\" ( ["'\\abfnrtv] | |||
# | [xX] HIGIT* | |||
# | [0-7][0-7]?[0-7]? | |||
# ) | |||
# | . | |||
symbol = HASH i:id { $$ = i } | |||
var = LOCAL i:id { $$ = newGetLocal (i) } | |||
| GLOBAL i:id { $$ = newGetGlobal(i) } | |||
| i:id { $$ = newGetVar (i) } | |||
id = < LETTER ALNUM* > - { $$ = intern(yytext) } | |||
# regex = SLASH a:alts SLASH { $$ = a } | |||
# alts = s:seq ( OR t:seq { s = Alt_append(t) } | |||
# )* { $$ = s } | |||
# seq = p:pre ( q:pre { s = Seq_append(t) } | |||
# )* { $$ = s } | |||
# elt = action | pre | |||
# action = b:block { $$ = newAction(b) } | |||
# pre = PLING p:pre { $$ = newNot(p) } | |||
# | AND p:pre { $$ = newAnd(p) } | |||
# | post | |||
# post = a:atom ( STAR { a = newMany(a) } | |||
# | PLUS { a = newMore(a) } | |||
# | QUERY { a = newMore(a) } | |||
# )? { $$ = a } | |||
# atom = DOT { $$ = newDot() } | |||
# | "[" ( !"]" "\\"? . )* "]" - { $$ = newClass(yytext) } | |||
# | '"' xxxxxx | |||
# class = LBRAK | |||
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 = "." ![.] - | |||
PLING = "!" ![=] - | |||
TILDE = "~" - | |||
error = - < (!EOL .)* > | |||
xexpr = expr | error @{ expected("expression", yytext) } | |||
@ -0,0 +1,73 @@ | |||
statement = b:block { b; } | |||
| e:expression SEMI { e; } | |||
expression = p:primary | |||
( DOT i:id ASSIGN e:expression # { $$ = newSetProp(p, i, e) } | |||
| LBRAK i:expression RBRAK ASSIGN e:expression { SetArray.new(object: p, index: i, value: e); } | |||
) | |||
| i:id ASSIGN e:expression { SetVar.new(name: i, value: e); } | |||
| pf:postfix { pf; } | |||
postfix = p:primary | |||
( DOT i:id a:args !ASSIGN !LBRACE { p = Invoke.new(self: p, method: i, arguments: a); } | |||
| DOT i:id !ASSIGN { p = GetProp.new(object: p, key: i); } | |||
| a:args !ASSIGN !LBRACE { p = Call.new(function: p, arguments: a); } | |||
) * { p; } | |||
args = LPAREN a:mklist | |||
( | |||
( k:id COLON e:expression { a[k] = e; } | |||
| e:expression { a.push(e); } | |||
) | |||
( COMMA | |||
( k:id COLON e:expression { a[k] = e; } | |||
| e:expression { a.push(e); } | |||
) | |||
) * | |||
) ? RPAREN { a; } | |||
mklist = { Object.new(); } | |||
primary = nil | number | var | subExpr | |||
subExpr = LPAREN e:expression RPAREN { e; } | |||
block = LBRACE b:mklist | |||
( e:statement { b.push(e); } | |||
) * RBRACE { Block.new(body: b); } | |||
nil = NIL { nil; } | |||
number = "-" n:unsign { Unyop.new(operation: __opNeg).push(n) } | |||
| "+" n:number { n } | |||
| n:unsign { n } | |||
unsign = < DIGIT+ > - { yytext.asInteger(10); } | |||
var = i:id { GetVar.new(name: i); } | |||
id = < LETTER ALNUM* > - { intern(yytext); } | |||
DIGIT = [0-9] | |||
LETTER = [A-Za-z_] | |||
ALNUM = LETTER | DIGIT | |||
NIL = "nil" !ALNUM - | |||
SEMI = ";" - | |||
COMMA = "," - | |||
LPAREN = "(" - | |||
RPAREN = ")" - | |||
LBRAK = "[" - | |||
RBRAK = "]" - | |||
LBRACE = "{" - | |||
RBRACE = "}" - | |||
COLON = ":" ![:] - | |||
ASSIGN = "=" ![=] - | |||
DOT = "." ![.] - | |||
- = ( space | comment )* | |||
space = ' ' | '\t' | end-of-line | |||
comment = '//' ( !end-of-line . )* end-of-line | |||
end-of-line = '\r\n' | '\n' | '\r' | |||
end-of-file = !. |
@ -0,0 +1,79 @@ | |||
grammar = - ( d:definition { d; } | |||
| end-of-file { nil; } | |||
) | |||
definition = i:identifier ASSIGN e:expression SEMI? { Definition.new(name: i, expression: e); } | |||
expression = s:sequence !BAR { s; } | |||
| s1:sequence { s1 = Alternation.new().push(s1); } | |||
( BAR s2:sequence { s1.push(s2); } | |||
) * { s1; } | |||
sequence = p:prefix ( q:prefix { p = Sequence.new().push(p).push(q); } | |||
( q:prefix { p.push(q); } | |||
) * ) ? { p; } | |||
prefix = AND a:action { ParseTimeAction.new(action: a); } | |||
| AT a:action { ExecuteAction.new(action: a); } | |||
| | |||
( AND s:suffix { And.new(expression: s); } | |||
| NOT s:suffix { Not.new(expression: s); } | |||
| s:suffix { s; } | |||
) | |||
suffix = p:primary | |||
( QUERY { p = Optional.new(expression: p); } | |||
| STAR { p = Star.new(expression: p); } | |||
| PLUS { p = Plus.new(expression: p); } | |||
) ? { p; } | |||
primary = i1:identifier COLON i2:ruleCall !ASSIGN { Assignment.new(name: i1, rule: i2); } | |||
| i:ruleCall !ASSIGN { i; } | |||
| LPAREN e:expression RPAREN { e; } | |||
| l:literal { l; } | |||
| c:class { c; } | |||
| DOT { Dot.new(); } | |||
| a:action { a; } | |||
| BEGIN e:expression END { Capture.new(expression: e); } | |||
identifier = < [-a-zA-Z_][-a-zA-Z_0-9]* > - { intern(yytext); } | |||
ruleCall = n:identifier CCOLON r:identifier { NamespacedRuleCall.new(namespace: n, name: r); } | |||
| i:identifier { RuleCall.new(name: i); } | |||
literal = ['] < ( !['] char )* > ['] - { StringLiteral.new(string: yytext); } | |||
| ["] < ( !["] char )* > ["] - { StringLiteral.new(string: yytext); } | |||
class = '[' < ( !']' range )* > ']' - { CharacterClass.new(value: yytext); } | |||
range = char '-' char | char | |||
char = '\\' [abefnrtv'"\[\]\\] | |||
| '\\' [0-3][0-7][0-7] | |||
| '\\' [0-7][0-7]? | |||
| !'\\' . | |||
action = m:metaLanguage::block - { Action.new(parseTree: Block.new(body: m)); } | |||
- = ( space | comment )* | |||
space = ' ' | '\t' | end-of-line | |||
comment = '#' ( !end-of-line . )* end-of-line | |||
end-of-line = '\r\n' | '\n' | '\r' | |||
end-of-file = !. | |||
BAR = '|' - | |||
NOT = '!' - | |||
QUERY = '?' - | |||
BEGIN = '<' - | |||
END = '>' - | |||
SEMI = ";" - | |||
CCOLON = "::" - | |||
LPAREN = "(" - | |||
RPAREN = ")" - | |||
ASSIGN = "=" ![=] - | |||
AND = "&" ![&=] - | |||
PLUS = "+" ![+=] - | |||
STAR = "*" ![=] - | |||
DOT = "." ![.] - | |||
COLON = ":" ![:] - | |||
AT = "@" - |
@ -0,0 +1,275 @@ | |||
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 = "~" - |