ometa BSJSParser <: Parser { fromTo :x :y = seq(x) (~seq(y) char)* seq(y), space = super(#space) | fromTo('//', '\n') | fromTo('/*', '*/'), nameFirst = letter | '$' | '_', nameRest = nameFirst | digit, iName = firstAndRest(#nameFirst, #nameRest):r -> r.join(''), isKeyword :x = ?BSJSParser._isKeyword(x), name = iName:n ~isKeyword(n) -> [#name, n=='self' ? '$elf' : n], keyword = iName:k isKeyword(k) -> [k, k], digits = digit+:xs -> xs.join(''), number = (digits | empty -> ''):ws ( '.' digits:d -> ('.' + d) | ?(ws.length > 0) empty -> ''):fs ( ?(ws.length + fs.length > 0) 'e' digits:d -> ('e' + d) | empty -> ''):e -> [#number, parseFloat(ws+fs+e)], escapeChar = '\\' char:c -> ometaUnescape('\\' + c), str = seq('"""') (escapeChar | ~seq('"""') char)*:cs seq('"""') -> [#string, cs.join('')] | '\'' (escapeChar | ~'\'' char)*:cs '\'' -> [#string, cs.join('')] | '"' (escapeChar | ~'"' char)*:cs '"' -> [#string, cs.join('')] | ('#' | '`') iName:n -> [#string, n], regexp = '/' (escapeChar | ~'/' ~'\n' char)*:cs '/' ( ('m' | 'g' | 'i' | 'y')+:fs -> fs.join('') | empty -> ''):flag -> [#regexp, '/'+cs.join('')+'/'+flag], special = ( '(' | ')' | '{' | '}' | '[' | ']' | ',' | ';' | '?' | ':' | ``!=='' | ``!='' | ``==='' | ``=='' | ``='' | ``>='' | '>' | ``<='' | '<' | ``++'' | ``+='' | '+' | ``--'' | ``-='' | '-' | ``*='' | '*' | ``/='' | '/' | ``%='' | '%' | ``&&='' | ``&&'' | ``||='' | ``||'' | '.' | '!' ):s -> [s, s], tok = spaces (name | keyword | number | str | regexp | special), toks = token*:ts spaces end -> ts, token :tt = tok:t ?(t[0] == tt) -> t[1], spacesNoNl = (~'\n' space)*, expr = orExpr:e ( "?" expr:t ":" expr:f -> [#condExpr, e, t, f] | "=" expr:rhs -> [#set, e, rhs] | "+=" expr:rhs -> [#mset, e, "+", rhs] | "-=" expr:rhs -> [#mset, e, "-", rhs] | "*=" expr:rhs -> [#mset, e, "*", rhs] | "/=" expr:rhs -> [#mset, e, "/", rhs] | "%=" expr:rhs -> [#mset, e, "%", rhs] | "&&=" expr:rhs -> [#mset, e, "&&", rhs] | "||=" expr:rhs -> [#mset, e, "||", rhs] | empty -> e ), orExpr = orExpr:x "||" andExpr:y -> [#binop, "||", x, y] | andExpr, andExpr = andExpr:x "&&" eqExpr:y -> [#binop, "&&", x, y] | eqExpr, eqExpr = eqExpr:x ( "==" relExpr:y -> [#binop, "==", x, y] | "!=" relExpr:y -> [#binop, "!=", x, y] | "===" relExpr:y -> [#binop, "===", x, y] | "!==" relExpr:y -> [#binop, "!==", x, y] ) | relExpr, relExpr = relExpr:x ( ">" addExpr:y -> [#binop, ">", x, y] | ">=" addExpr:y -> [#binop, ">=", x, y] | "<" addExpr:y -> [#binop, "<", x, y] | "<=" addExpr:y -> [#binop, "<=", x, y] | "instanceof" addExpr:y -> [#binop, "instanceof", x, y] | "in" addExpr:y -> [#binop, "in", x, y] ) | addExpr, addExpr = addExpr:x "+" mulExpr:y -> [#binop, "+", x, y] | addExpr:x "-" mulExpr:y -> [#binop, "-", x, y] | mulExpr, mulExpr = mulExpr:x "*" unary:y -> [#binop, "*", x, y] | mulExpr:x "/" unary:y -> [#binop, "/", x, y] | mulExpr:x "%" unary:y -> [#binop, "%", x, y] | unary, unary = "-" postfix:p -> [#unop, "-", p] | "+" postfix:p -> p | "++" postfix:p -> [#preop, "++", p] | "--" postfix:p -> [#preop, "--", p] | "!" unary:p -> [#unop, "!", p] | "typeof" unary:p -> [#preopSpace, "typeof", p] | "delete" unary:p -> [#preopSpace, "delete", p] | postfix, postfix = primExpr:p ( spacesNoNl "++" -> [#postop, "++", p] | spacesNoNl "--" -> [#postop, "--", p] | empty -> p ), primExpr = primExpr:p ( "[" expr:i "]" -> [#getp, i, p] | "." "name":m "(" listOf(#expr, ','):as ")" -> [#send, m, p].concat(as) | "." "name":f -> [#getp, [#string, f], p] | "(" listOf(#expr, ','):as ")" -> [#call, p].concat(as) ) | primExprHd, primExprHd = "(" expr:e ")" -> e | "this" -> [#this] | "name":n -> [#get, n] | "number":n -> [#number, n] | "string":s -> [#string, s] | "regexp":r -> [#regexp, r] | "function" funcRest | "function" "name":n funcRest:f -> [#var, n, f] | "new" ("name":n ('.' | empty) -> n)*:name "(" listOf(#expr, ','):as ")" -> [#new, name.join('.')].concat(as) | "new" "(" expr:newExpr ")" "(" listOf(#expr, ','):as ")" -> [#newExpr, newExpr].concat(as) | "[" listOf(#expr, ','):es (',' | empty ) "]" -> [#arr].concat(es) | json, json = "{" listOf(#jsonBinding, ','):bs (',' | empty ) "}" -> [#json].concat(bs), jsonBinding = jsonPropName:n ":" expr:v -> [#binding, n, v], jsonPropName = "name" | "number" | "string", formal = spaces "name", funcRest = "(" listOf(#formal, ','):fs ")" "{" srcElems:body "}" -> [#func, fs, body], sc = spacesNoNl ('\n' | &'}' | end) | ";", varBinding = "name":n ( "=" expr | empty -> [#get, 'undefined'] ):v -> [#var, n, v], block = "{" srcElems:ss "}" -> ss, stmt = block | "var" listOf(#varBinding, ','):bs sc -> [#begin].concat(bs) | "if" "(" expr:c ")" stmt:t ( "else" stmt | empty -> [#get, 'undefined'] ):f -> [#if, c, t, f] | "while" "(" expr:c ")" stmt:s -> [#while, c, s] | "do" stmt:s "while" "(" expr:c ")" sc -> [#doWhile, s, c] | "for" "(" ( "var" listOf(#varBinding, ','):vars -> [#multiVar, vars] | expr | empty -> [#get, 'undefined'] ):i ";" ( expr | empty -> [#get, 'true'] ):c ";" ( expr | empty -> [#get, 'undefined'] ):u ")" stmt:s -> [#for, i, c, u, s] | "for" "(" ( "var" "name":n -> [#var, n, [#get, 'undefined']] | expr ):v "in" expr:e ")" stmt:s -> [#forIn, v, e, s] | "switch" "(" expr:e ")" "{" ( "case" expr:c ":" srcElems:cs -> [#case, c, cs] | "default" ":" srcElems:cs -> [#default, cs] )*:cs "}" -> [#switch, e].concat(cs) | "break" sc -> [#break] | "continue" sc -> [#continue] | "throw" spacesNoNl expr:e sc -> [#throw, e] | "try" block:t ("catch" "(" "name":e ")" block:c -> [e,c] | empty -> ['',[#get, 'undefined']]):ca ( "finally" block | empty -> [#get, 'undefined'] ):f -> [#try, t].concat(ca).concat([f]) | "return" ( expr | empty -> [#get, 'undefined'] ):e sc -> [#return, e] | "with" "(" expr:x ")" stmt:s -> [#with, x, s] | expr:e sc -> e | ";" -> [#get, "undefined"], srcElem = "function" "name":n funcRest:f -> [#var, n, f] | stmt, srcElems = srcElem*:ss -> [#begin].concat(ss), topLevel = srcElems:r spaces end -> r, curlySemAction = "{" (srcElem:s &srcElem -> s)+:ss expr:r sc "}" spaces -> { ss.push([#return, r]) [#call, [#func, [], [#begin].concat(ss)]] } | "{" expr:r "}" spaces -> r, semAction = curlySemAction | primExpr:r spaces -> r } BSJSParser.keywords = { } keywords = ["break", "case", "catch", "continue", "default", "delete", "do", "else", "finally", "for", "function", "if", "in", "instanceof", "new", "return", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with", "ometa"] for (var idx = 0; idx < keywords.length; idx++) BSJSParser.keywords[keywords[idx]] = true BSJSParser._isKeyword = function(k) { return hasProperty(this.keywords,k) && !hasProperty(Object.prototype,k) } ometa BSJSTranslator { trans = [:t apply(t):ans] -> ans, curlyTrans = [#begin curlyTrans:r] -> r | [#begin trans*:rs] -> ('{' + rs.join(';') + '}') | trans:r -> ('{' + r + '}'), this -> 'this', break -> 'break', continue -> 'continue', number :n -> ('(' + n + ')'), string :s -> s.toProgramString(), regexp :re -> re, arr trans*:xs -> ('[' + xs.join(',') + ']'), unop :op trans:x -> ('(' + op + x + ')'), getp trans:fd trans:x -> (x + '[' + fd + ']'), get :x -> x, set trans:lhs trans:rhs -> (lhs + '=' + rhs), mset trans:lhs :op trans:rhs -> (lhs + op + '=' + rhs), binop :op trans:x trans:y -> ('(' + x + ' ' + op + ' ' + y + ')'), preop :op trans:x -> (op + x), preopSpace :op trans:x -> (op + ' ' + x), postop :op trans:x -> (x + op), return trans:x -> ('return ' + x), with trans:x curlyTrans:s -> ('with(' + x + ')' + s), if trans:cond curlyTrans:t curlyTrans:e -> ('if(' + cond + ')' + t + 'else' + e), condExpr trans:cond trans:t trans:e -> ('(' + cond + '?' + t + ':' + e + ')'), while trans:cond curlyTrans:body -> ('while(' + cond + ')' + body), doWhile curlyTrans:body trans:cond -> ('do' + body + 'while(' + cond + ')'), for trans:init trans:cond trans:upd curlyTrans:body -> ('for(' + init + ';' + cond + ';' + upd + ')' + body), forIn trans:x trans:arr curlyTrans:body -> ('for(' + x + ' in ' + arr + ')' + body), begin trans:x end -> x, begin (trans:x ( (?(x[x.length - 1] == '}') | end) -> x | empty -> (x + ';') ) )*:xs -> ('{' + xs.join('') + '}'), func :args curlyTrans:body -> ('(function (' + args.join(',') + ')' + body + ')'), call trans:fn trans*:args -> (fn + '(' + args.join(',') + ')'), send :msg trans:recv trans*:args -> (recv + '.' + msg + '(' + args.join(',') + ')'), new :cls trans*:args -> ('new ' + cls + '(' + args.join(',') + ')'), newExpr trans:newExpr trans*:args -> ('new ' + '(' + newExpr + ')'+ '(' + args.join(',') + ')'), var :name trans:val -> ('var ' + name + '=' + val), singleVar [#var :name trans:val] -> (name + '=' + val), multiVar [singleVar*:xs] -> ('var ' + xs.join(',')), throw trans:x -> ('throw ' + x), try curlyTrans:x :name curlyTrans:c curlyTrans:f -> ('try ' + x + (name ? 'catch(' + name + ')' + c : '') + 'finally' + f), json trans*:props -> ('({' + props.join(',') + '})'), binding :name trans:val -> (name.toProgramString() + ': ' + val), switch trans:x trans*:cases -> ('switch(' + x + '){' + cases.join(';') + '}'), case trans:x trans:y -> ('case ' + x + ': '+ y), default trans:y -> ('default: ' + y) }