UNPKG

peasy

Version:

an easy but powerful parser

217 lines (195 loc) 8.25 kB
peasy = require "../peasy" {in_, charset, letterDigits} = peasy _in_ = in_ identifierChars = '$_'+letterDigits identifierCharSet = charset(identifierChars) exports.Parser = class Parser extends peasy.Parser constructor: -> super self = @ number = -> text = self.data start = cur = self.cur base = 10 c = text[cur] if c=='+' or c=='-' then cur++ if text[cur]=='0' c = text[++cur] if c=='x' or c=='X' then base = 16; cur++ if base==16 while c = text[cur] if not ('0'<=c<='9' or 'a'<=c<='f' or 'A'<=c<='F') self.cur = cur return text[start...cur] cur++ while c = text[cur] if not ('0'<=c<='9') then break cur++ if text[cur]=='.' cur++ while c = text[cur] if not ('0'<=c<='9') then break cur++ if text[cur-1]=='.' and (c = text[cur-2]) and not ('0'<=c<='9') then return c = text[cur] if c=='E' or c=='e' cur++ while c = text[cur] if not ('0'<=c<='9') then break cur++ if (c =text[cur-1]) and (c=='E' or c=='e') then return self.cur = cur text[start...cur] string = -> text = self.data cur = self.cur c = text[cur] if c=='"' or c=="'" then quote = c else return result = '' cur++ while 1 c = text[cur] if c=='\\' then result += text[cur+1]; cur += 2; else if c==quote self.cur = cur+1 return quote+result+quote else if not c then error('expect '+quote) else result += c; cur++ {orp, rec, memo, wrap, char, literal, spaces, eoi, identifier} = self = @ question = char('?'); colon = char(':'); comma = char(','); dot = char('.') lpar = char('('); rpar = char(')') lbracket = char('['); rbracket = char(']') myop = (op) -> if op.length==1 then opFn = char(op) else opFn = literal(op) if _in_(op[0], identifierCharSet) -> spaces() and (op=opFn()) and spaces() and not _in_(data[self.cur], identifierCharSet) and ' '+op+' ' else -> spaces() and (op=opFn()) and spaces() and op posNeg = (op) -> opFn = char(op) -> spaces() and (op=opFn()) and (c = self.data[self.cur]) and c!='.' and not('0'<=c<='9') and spaces() and op positive = posNeg('+'); negative = posNeg('-') new_ = myop('new') inc = myop('++'); dec = myop('--') not1 = orp(myop('!'), myop('not')) not_ = -> not1() and '!' bitnot = myop('~') typeof_ = myop('typeof'); void_ = myop('void'); delete_ = myop('delete') unaryOp = orp(not_, bitnot, positive, negative, typeof_, void_) plus = myop('+'); minus = myop('-') mul = myop('*'); div = myop('/'); idiv = myop('//'); mod = myop('%') lshift = myop('<<'); rshift = myop('>>'); zrshift = myop('>>>') lt = myop('<'); le = myop('<='); gt = myop('>'); ge = myop('>=') in_ = myop('in'); instanceof_ = myop('instanceof') eq = myop('=='); ne = myop('!='); eq2 = myop('==='); ne2 = myop('!==') bitand = myop('&'); bitxor = myop('^'); bitor = myop('|') and1 = orp(myop('&&'), myop('and')) and_ = -> and1() and '&&' or1 = orp(myop('||'), myop('or')) or_ = -> or1() and '||' comma = myop(',') assign = myop('='); addassign = myop('+='); subassign = myop('-=') mulassign = myop('*='); divassign = myop('/='); modassign = myop('%='); idivassign = myop('//=') rshiftassign = myop('>>='); lshiftassign = myop('<<='); zrshiftassign = myop('>>>=') bitandassign = myop('&='); bitxorassign = myop('^='); bitorassign = myop('|=') error = (msg) -> throw self.data[self.cur-20..self.cur+20]+' '+self.cur+': '+msg expect = (fn, msg) -> fn() or error(msg) incDec = orp(inc, dec) prefixOperation = -> (op=incDec()) and (x=headExpr()) and op+x suffixOperation = -> (x=headExpr()) and (op=incDec()) and x+op paren = (item, left=lpar, right=rpar, msg='expect ) to match (') -> if (typeof item)=='string' then left = self.literal(item) if (typeof left)=='string' then left = self.literal(left) if (typeof right)=='string' then right = self.literal(right) -> start=self.cur; left() and (x=item()) and expect(right, msg+' at: '+start) and x paren1 = paren(-> (spaces() and (x=expr()) and spaces() and x)) parenExpr = memo -> (x=paren1()) and '('+x+')' literalExpr = orp(number, string, identifier) atom = memo orp(parenExpr, literalExpr) unaryTail = orp(prefixSuffixExpr, atom) unary_ = -> (op=unaryOp()) and (x=unaryTail()) and op+x bracketExpr1 = wrap(paren(wrap(-> commaExpr()), lbracket, rbracket, 'expect ) to match (')) bracketExpr = -> (x=bracketExpr1()) and '['+x+']' wrapDot = wrap(dot) dotIdentifier = -> wrapDot() and (id=expect(identifier, 'expect identifier')) and '.'+id attr = orp(bracketExpr, dotIdentifier) param = paren -> (spaces() and (x=expr()) and spaces() and expect(rpar,'expect )')) or ' ' paramExpr = memo -> (x=param()) and '('+x+')' callPropTail = orp(paramExpr, attr) callProp = rec -> (h=headExpr()) and (((e=callPropTail()) and h+e) or h) property = rec -> (h=headExpr()) and (((e=attr()) and h+e) or h) headAtom = memo orp(parenExpr, identifier) headExpr = memo orp(callProp, headAtom) wrapQuestion = wrap(question) wrapColon = wrap(colon) condition_ = -> (x=logicOrExpr()) and wrapQuestion() and (y=assignExpr()) and expect(wrapColon, 'expect :') and (z=assignExpr()) and x+'? '+y+': '+z assignLeft = property assignOperator = orp(assign, addassign, subassign, mulassign, divassign, modassign, idivassign,\ rshiftassign, lshiftassign, zrshiftassign, bitandassign, bitxorassign, bitorassign) assignExpr_ = -> (v=assignLeft()) and (op=assignOperator()) and (e=assignExpr()) and v+op+e #https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence ''' Precedence Operator type Associativity Individual operators 1 new right-to-left new 2 function call left-to-right () property access left-to-right . left-to-right [] 3 ++ n/a -- 4 right-to-left ! ~ + - typeof void delete 5 * / % // 6 + - 7 << >> >>> 8 < <= > >= in instanceof 9 == != === !== 10 bitwise-and left-to-right & 11 bitwise-xor left-to-right ^ 12 bitwise-or left-to-right | 13 logical-and left-to-right && 14 logical-or left-to-right || 15 condition right-to-left ?: 16 yield right-to-left yield 17 assignment right-to-left = += -= *= /= %= <<= >>= >>>= &= ^= |= 18 comma left-to-right , ''' operations = 0: atom 1: -> new_() and (x=expr()) and 'new '+x 2: callProp 3: orp(prefixOperation, suffixOperation) 4: unary_ 5: [mul, div, idiv] 6: [plus, minus] 7: [lshift, rshift, zrshift] 8: [lt, le, gt, ge, in_, instanceof_] 9: [eq, ne, eq2, ne2] 10: [bitand] 11: [bitxor] 12: [bitor] 13: [and_] 14: [or_] 15: condition_ 16: assignExpr_ 17: [comma] operationFnList = [atom] getExpr = (n) -> operation = operations[n] lower = operationFnList[n-1] if typeof operation == 'function' then orp(operation, lower) else operator = if operation.length==1 then operation[0] else orp(operation...) binary = rec -> n start = self.cur; if (x = binary()) if (op=operator()) and (y=lower()) then x+op+y else x else self.cur=start; lower() for i in [1..17] then operationFnList[i] = getExpr(i) prefixSuffixExpr = operationFnList[3] logicOrExpr = operationFnList[14] assignExpr = operationFnList[16] expr = operationFnList[16] @root = -> (x=expr()) and expect(eoi, 'expect end of input') and x exports.parser = parser = new Parser exports.parse = (text) -> parser.parse(text)