ref: 27ce19d25de84f23f10905db49bf83e95fea4257
dir: /js-parse.c/
#include "js.h" #include "js-parse.h" #include "js-ast.h" #define A1(x,a) jsP_newnode(J, x, a, 0, 0, 0) #define A2(x,a,b) jsP_newnode(J, x, a, b, 0, 0) #define A3(x,a,b,c) jsP_newnode(J, x, a, b, c, 0) #define LIST(h) jsP_newnode(J, AST_LIST, h, 0, 0, 0); #define EXP0(x) jsP_newnode(J, EXP_ ## x, 0, 0, 0, 0) #define EXP1(x,a) jsP_newnode(J, EXP_ ## x, a, 0, 0, 0) #define EXP2(x,a,b) jsP_newnode(J, EXP_ ## x, a, b, 0, 0) #define EXP3(x,a,b,c) jsP_newnode(J, EXP_ ## x, a, b, c, 0) #define STM0(x) jsP_newnode(J, STM_ ## x, 0, 0, 0, 0) #define STM1(x,a) jsP_newnode(J, STM_ ## x, a, 0, 0, 0) #define STM2(x,a,b) jsP_newnode(J, STM_ ## x, a, b, 0, 0) #define STM3(x,a,b,c) jsP_newnode(J, STM_ ## x, a, b, c, 0) #define STM4(x,a,b,c,d) jsP_newnode(J, STM_ ## x, a, b, c, d) static js_Ast *expression(js_State *J, int notin); static js_Ast *assignment(js_State *J, int notin); static js_Ast *memberexp(js_State *J); static js_Ast *statement(js_State *J); static js_Ast *funcbody(js_State *J); static const char *tokenstring[] = { "(end-of-file)", "'\\x01'", "'\\x02'", "'\\x03'", "'\\x04'", "'\\x05'", "'\\x06'", "'\\x07'", "'\\x08'", "'\\x09'", "'\\x0A'", "'\\x0B'", "'\\x0C'", "'\\x0D'", "'\\x0E'", "'\\x0F'", "'\\x10'", "'\\x11'", "'\\x12'", "'\\x13'", "'\\x14'", "'\\x15'", "'\\x16'", "'\\x17'", "'\\x18'", "'\\x19'", "'\\x1A'", "'\\x1B'", "'\\x1C'", "'\\x1D'", "'\\x1E'", "'\\x1F'", "' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'\\''", "'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'", "'0'", "'1'", "'2'", "'3'", "'4'", "'5'", "'6'", "'7'", "'8'", "'9'", "':'", "';'", "'<'", "'='", "'>'", "'?'", "'@'", "'A'", "'B'", "'C'", "'D'", "'E'", "'F'", "'G'", "'H'", "'I'", "'J'", "'K'", "'L'", "'M'", "'N'", "'O'", "'P'", "'Q'", "'R'", "'S'", "'T'", "'U'", "'V'", "'W'", "'X'", "'Y'", "'Z'", "'['", "'\'", "']'", "'^'", "'_'", "'`'", "'a'", "'b'", "'c'", "'d'", "'e'", "'f'", "'g'", "'h'", "'i'", "'j'", "'k'", "'l'", "'m'", "'n'", "'o'", "'p'", "'q'", "'r'", "'s'", "'t'", "'u'", "'v'", "'w'", "'x'", "'y'", "'z'", "'{'", "'|'", "'}'", "'~'", "'\\x7F'", 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, "(identifier)", "(number)", "(string)", "(regexp)", "'<='", "'>='", "'=='", "'!='", "'==='", "'!=='", "'<<'", "'>>'", "'>>>'", "'&&'", "'||'", "'+='", "'-='", "'*='", "'/='", "'%='", "'<<='", "'>>='", "'>>>='", "'&='", "'|='", "'^='", "'++'", "'--'", "'break'", "'case'", "'catch'", "'continue'", "'debugger'", "'default'", "'delete'", "'do'", "'else'", "'false'", "'finally'", "'for'", "'function'", "'if'", "'in'", "'instanceof'", "'new'", "'null'", "'return'", "'switch'", "'this'", "'throw'", "'true'", "'try'", "'typeof'", "'var'", "'void'", "'while'", "'with'", }; static inline void next(js_State *J) { J->lookahead = jsP_lex(J); } static inline int accept(js_State *J, int t) { if (J->lookahead == t) { next(J); return 1; } return 0; } static inline void expect(js_State *J, int t) { if (accept(J, t)) return; jsP_error(J, "unexpected token: %s (expected %s)", tokenstring[J->lookahead], tokenstring[t]); } static void semicolon(js_State *J) { if (J->lookahead == ';') { next(J); return; } if (J->newline || J->lookahead == '}' || J->lookahead == 0) return; jsP_error(J, "unexpected token: %s (expected ';')", tokenstring[J->lookahead]); } static js_Ast *identifier(js_State *J) { if (J->lookahead == TK_IDENTIFIER) { js_Ast *a = jsP_newstrnode(J, AST_IDENTIFIER, J->text); next(J); return a; } jsP_error(J, "unexpected token: %s (expected identifier)", tokenstring[J->lookahead]); return NULL; } static js_Ast *identifieropt(js_State *J) { if (J->lookahead == TK_IDENTIFIER) return identifier(J); return NULL; } static js_Ast *identifiername(js_State *J) { if (J->lookahead == TK_IDENTIFIER || J->lookahead >= TK_BREAK) { js_Ast *a = jsP_newstrnode(J, AST_IDENTIFIER, J->text); next(J); return a; } jsP_error(J, "unexpected token: %s (expected identifier or keyword)", tokenstring[J->lookahead]); return NULL; } static js_Ast *arguments(js_State *J) { js_Ast *head, *tail, *node; if (J->lookahead == ')') return NULL; node = assignment(J, 0); head = tail = LIST(node); while (accept(J, ',')) { node = assignment(J, 0); tail = tail->b = LIST(node); } return head; } static js_Ast *arrayliteral(js_State *J) { js_Ast *head, *tail, *node; while (J->lookahead == ',') next(J); if (J->lookahead == ']') return NULL; node = assignment(J, 0); head = tail = LIST(node); while (accept(J, ',')) { while (J->lookahead == ',') next(J); if (J->lookahead == ']') break; node = assignment(J, 0); tail = tail->b = LIST(node); } return head; } static js_Ast *propname(js_State *J) { js_Ast *name; if (J->lookahead == TK_NUMBER) { name = jsP_newnumnode(J, AST_NUMBER, J->number); next(J); } else if (J->lookahead == TK_STRING) { name = jsP_newstrnode(J, AST_STRING, J->text); next(J); } else { name = identifiername(J); } return name; } static js_Ast *propassign(js_State *J) { js_Ast *name, *value, *arg, *body; if (J->lookahead == TK_IDENTIFIER && !strcmp(J->text, "get")) { next(J); name = propname(J); expect(J, '('); expect(J, ')'); body = funcbody(J); return EXP2(PROP_GET, name, body); } if (J->lookahead == TK_IDENTIFIER && !strcmp(J->text, "set")) { next(J); name = propname(J); expect(J, '('); arg = identifier(J); expect(J, ')'); body = funcbody(J); return EXP3(PROP_SET, name, arg, body); } name = propname(J); expect(J, ':'); value = assignment(J, 0); return EXP2(PROP_VAL, name, value); } static js_Ast *objectliteral(js_State *J) { js_Ast *head, *tail, *node; if (J->lookahead == '}') return NULL; node = propassign(J); head = tail = LIST(node); while (accept(J, ',')) { if (J->lookahead == '}') break; node = propassign(J); tail = tail->b = LIST(node); } return head; } static js_Ast *primary(js_State *J) { js_Ast *a; if (J->lookahead == TK_IDENTIFIER) { a = jsP_newstrnode(J, AST_IDENTIFIER, J->text); next(J); return a; } if (J->lookahead == TK_STRING) { a = jsP_newstrnode(J, AST_STRING, J->text); next(J); return a; } if (J->lookahead == TK_REGEXP) { a = jsP_newstrnode(J, AST_REGEXP, J->text); // TODO: flags next(J); return a; } if (J->lookahead == TK_NUMBER) { a = jsP_newnumnode(J, AST_NUMBER, J->number); next(J); return a; } if (accept(J, TK_THIS)) return EXP0(THIS); if (accept(J, TK_NULL)) return EXP0(NULL); if (accept(J, TK_TRUE)) return EXP0(TRUE); if (accept(J, TK_FALSE)) return EXP0(FALSE); if (accept(J, '{')) { a = EXP1(OBJECT, objectliteral(J)); expect(J, '}'); return a; } if (accept(J, '[')) { a = EXP1(ARRAY, arrayliteral(J)); expect(J, ']'); return a; } if (accept(J, '(')) { a = expression(J, 0); expect(J, ')'); return a; } jsP_error(J, "unexpected token in expression: %s", tokenstring[J->lookahead]); return NULL; } static js_Ast *paramlist(js_State *J) { js_Ast *head, *tail, *node; if (J->lookahead == ')') return NULL; node = identifier(J); head = tail = LIST(node); while (accept(J, ',')) { node = identifier(J); tail = tail->b = LIST(node); } return head; } static js_Ast *newexp(js_State *J) { js_Ast *a, *b, *c; if (accept(J, TK_NEW)) { a = memberexp(J); if (accept(J, '(')) { b = arguments(J); expect(J, ')'); return EXP2(NEW, a, b); } return EXP1(NEW, a); } if (accept(J, TK_FUNCTION)) { a = identifieropt(J); expect(J, '('); b = paramlist(J); expect(J, ')'); c = funcbody(J); return EXP3(FUNC, a, b, c); } return primary(J); } static js_Ast *memberexp(js_State *J) { js_Ast *a = newexp(J); loop: if (accept(J, '.')) { a = EXP2(MEMBER, a, identifiername(J)); goto loop; } if (accept(J, '[')) { a = EXP2(INDEX, a, expression(J, 0)); expect(J, ']'); goto loop; } return a; } static js_Ast *callexp(js_State *J) { js_Ast *a = newexp(J); loop: if (accept(J, '.')) { a = EXP2(MEMBER, a, identifiername(J)); goto loop; } if (accept(J, '[')) { a = EXP2(INDEX, a, expression(J, 0)); expect(J, ']'); goto loop; } if (accept(J, '(')) { a = EXP2(CALL, a, arguments(J)); expect(J, ')'); goto loop; } return a; } static js_Ast *postfix(js_State *J) { js_Ast *a = callexp(J); if (!J->newline && accept(J, TK_INC)) return EXP1(POSTINC, a); if (!J->newline && accept(J, TK_DEC)) return EXP1(POSTDEC, a); return a; } static js_Ast *unary(js_State *J) { if (accept(J, TK_DELETE)) return EXP1(DELETE, unary(J)); if (accept(J, TK_VOID)) return EXP1(VOID, unary(J)); if (accept(J, TK_TYPEOF)) return EXP1(TYPEOF, unary(J)); if (accept(J, TK_INC)) return EXP1(PREINC, unary(J)); if (accept(J, TK_DEC)) return EXP1(PREDEC, unary(J)); if (accept(J, '+')) return EXP1(POS, unary(J)); if (accept(J, '-')) return EXP1(NEG, unary(J)); if (accept(J, '~')) return EXP1(BITNOT, unary(J)); if (accept(J, '!')) return EXP1(LOGNOT, unary(J)); return postfix(J); } static js_Ast *multiplicative(js_State *J) { js_Ast *a = unary(J); loop: if (accept(J, '*')) { a = EXP2(MUL, a, unary(J)); goto loop; } if (accept(J, '/')) { a = EXP2(DIV, a, unary(J)); goto loop; } if (accept(J, '%')) { a = EXP2(MOD, a, unary(J)); goto loop; } return a; } static js_Ast *additive(js_State *J) { js_Ast *a = multiplicative(J); loop: if (accept(J, '+')) { a = EXP2(ADD, a, multiplicative(J)); goto loop; } if (accept(J, '-')) { a = EXP2(SUB, a, multiplicative(J)); goto loop; } return a; } static js_Ast *shift(js_State *J) { js_Ast *a = additive(J); loop: if (accept(J, TK_SHL)) { a = EXP2(SHL, a, additive(J)); goto loop; } if (accept(J, TK_SHR)) { a = EXP2(SHR, a, additive(J)); goto loop; } if (accept(J, TK_USHR)) { a = EXP2(USHR, a, additive(J)); goto loop; } return a; } static js_Ast *relational(js_State *J, int notin) { js_Ast *a = shift(J); loop: if (accept(J, '<')) { a = EXP2(LT, a, shift(J)); goto loop; } if (accept(J, '>')) { a = EXP2(GT, a, shift(J)); goto loop; } if (accept(J, TK_LE)) { a = EXP2(LE, a, shift(J)); goto loop; } if (accept(J, TK_GE)) { a = EXP2(GE, a, shift(J)); goto loop; } if (accept(J, TK_INSTANCEOF)) { a = EXP2(INSTANCEOF, a, shift(J)); goto loop; } if (!notin && accept(J, TK_IN)) { a = EXP2(IN, a, shift(J)); goto loop; } return a; } static js_Ast *equality(js_State *J, int notin) { js_Ast *a = relational(J, notin); loop: if (accept(J, TK_EQ)) { a = EXP2(EQ, a, relational(J, notin)); goto loop; } if (accept(J, TK_NE)) { a = EXP2(NE, a, relational(J, notin)); goto loop; } if (accept(J, TK_EQ3)) { a = EXP2(EQ3, a, relational(J, notin)); goto loop; } if (accept(J, TK_NE3)) { a = EXP2(NE3, a, relational(J, notin)); goto loop; } return a; } static js_Ast *bitand(js_State *J, int notin) { js_Ast *a = equality(J, notin); while (accept(J, '&')) a = EXP2(BITAND, a, equality(J, notin)); return a; } static js_Ast *bitxor(js_State *J, int notin) { js_Ast *a = bitand(J, notin); while (accept(J, '^')) a = EXP2(BITXOR, a, bitand(J, notin)); return a; } static js_Ast *bitor(js_State *J, int notin) { js_Ast *a = bitxor(J, notin); while (accept(J, '|')) a = EXP2(BITOR, a, bitxor(J, notin)); return a; } static js_Ast *logand(js_State *J, int notin) { js_Ast *a = bitor(J, notin); while (accept(J, TK_AND)) a = EXP2(LOGAND, a, bitor(J, notin)); return a; } static js_Ast *logor(js_State *J, int notin) { js_Ast *a = logand(J, notin); while (accept(J, TK_OR)) a = EXP2(LOGOR, a, logand(J, notin)); return a; } static js_Ast *conditional(js_State *J, int notin) { js_Ast *a, *b, *c; a = logor(J, notin); if (accept(J, '?')) { b = assignment(J, notin); expect(J, ':'); c = assignment(J, notin); return EXP3(COND, a, b, c); } return a; } static js_Ast *assignment(js_State *J, int notin) { js_Ast *a = conditional(J, notin); if (accept(J, '=')) return EXP2(ASS, a, assignment(J, notin)); if (accept(J, TK_MUL_ASS)) return EXP2(ASS_MUL, a, assignment(J, notin)); if (accept(J, TK_DIV_ASS)) return EXP2(ASS_DIV, a, assignment(J, notin)); if (accept(J, TK_MOD_ASS)) return EXP2(ASS_MOD, a, assignment(J, notin)); if (accept(J, TK_ADD_ASS)) return EXP2(ASS_ADD, a, assignment(J, notin)); if (accept(J, TK_SUB_ASS)) return EXP2(ASS_SUB, a, assignment(J, notin)); if (accept(J, TK_SHL_ASS)) return EXP2(ASS_SHL, a, assignment(J, notin)); if (accept(J, TK_SHR_ASS)) return EXP2(ASS_SHR, a, assignment(J, notin)); if (accept(J, TK_USHR_ASS)) return EXP2(ASS_USHR, a, assignment(J, notin)); if (accept(J, TK_AND_ASS)) return EXP2(ASS_BITAND, a, assignment(J, notin)); if (accept(J, TK_XOR_ASS)) return EXP2(ASS_BITXOR, a, assignment(J, notin)); if (accept(J, TK_OR_ASS)) return EXP2(ASS_BITOR, a, assignment(J, notin)); return a; } static js_Ast *expression(js_State *J, int notin) { js_Ast *a = assignment(J, notin); while (accept(J, ',')) a = EXP2(COMMA, a, assignment(J, notin)); return a; } static js_Ast *vardec(js_State *J, int notin) { js_Ast *a, *b; a = identifier(J); if (accept(J, '=')) b = assignment(J, notin); else b = NULL; return A2(AST_INIT, a, b); } static js_Ast *vardeclist(js_State *J, int notin) { js_Ast *head, *tail, *node; node = vardec(J, notin); head = tail = LIST(node); while (accept(J, ',')) { node = vardec(J, notin); tail = tail->b = LIST(node); } return head; } static js_Ast *expopt(js_State *J, int end) { js_Ast *a = NULL; if (J->lookahead != end) a = expression(J, 0); expect(J, end); return a; } static js_Ast *casestatementlist(js_State *J) { js_Ast *head, *tail, *node; if (J->lookahead == '}' || J->lookahead == TK_CASE || J->lookahead == TK_DEFAULT) return NULL; node = statement(J); head = tail = LIST(node); while (J->lookahead != '}' && J->lookahead != TK_CASE && J->lookahead != TK_DEFAULT) { node = statement(J); tail = tail->b = LIST(node); } return head; } static js_Ast *caseclause(js_State *J) { js_Ast *a, *b; if (accept(J, TK_CASE)) { a = expression(J, 0); expect(J, ':'); b = casestatementlist(J); return STM2(CASE, a, b); } if (accept(J, TK_DEFAULT)) { expect(J, ':'); a = casestatementlist(J); return STM1(DEFAULT, a); } jsP_error(J, "unexpected token in switch: %s", tokenstring[J->lookahead]); return NULL; } static js_Ast *caseblock(js_State *J) { js_Ast *head, *tail, *node; expect(J, '{'); if (accept(J, '}')) return NULL; node = caseclause(J); head = tail = LIST(node); while (J->lookahead != '}') { node = caseclause(J); tail = tail->b = LIST(node); } expect(J, '}'); return head; } static js_Ast *block(js_State *J) { js_Ast *head, *tail, *node; expect(J, '{'); if (accept(J, '}')) return STM1(BLOCK, NULL); node = statement(J); head = tail = LIST(node); while (J->lookahead != '}') { node = statement(J); tail = tail->b = LIST(node); } expect(J, '}'); return STM1(BLOCK, head); } static js_Ast *statement(js_State *J) { js_Ast *a, *b, *c, *d; if (J->lookahead == '{') { return block(J); } if (accept(J, ';')) { return STM0(NOP); } if (accept(J, TK_CONTINUE)) { a = identifieropt(J); semicolon(J); return STM1(CONTINUE, a); } if (accept(J, TK_BREAK)) { a = identifieropt(J); semicolon(J); return STM1(BREAK, a); } if (accept(J, TK_RETURN)) { if (J->lookahead != ';' && J->lookahead != '}' && J->lookahead != 0) a = expression(J, 0); else a = NULL; semicolon(J); return STM1(RETURN, a); } if (accept(J, TK_WITH)) { expect(J, '('); a = expression(J, 0); expect(J, ')'); b = statement(J); return STM2(WITH, a, b); } if (accept(J, TK_THROW)) { a = expression(J, 0); semicolon(J); return STM1(THROW, a); } if (accept(J, TK_TRY)) { a = block(J); b = c = d = NULL; if (accept(J, TK_CATCH)) { expect(J, '('); b = identifier(J); expect(J, ')'); c = block(J); } if (accept(J, TK_FINALLY)) { d = block(J); } if (!b && !d) jsP_error(J, "unexpected token: %s (expected 'catch' or 'finally')", tokenstring[J->lookahead]); return STM4(TRY, a, b, c, d); } if (accept(J, TK_DEBUGGER)) { semicolon(J); return STM0(DEBUGGER); } if (accept(J, TK_IF)) { expect(J, '('); a = expression(J, 0); expect(J, ')'); b = statement(J); if (accept(J, TK_ELSE)) c = statement(J); else c = NULL; return STM3(IF, a, b, c); } if (accept(J, TK_DO)) { a = statement(J); expect(J, TK_WHILE); expect(J, '('); b = expression(J, 0); expect(J, ')'); semicolon(J); return STM2(DO, a, b); } if (accept(J, TK_WHILE)) { expect(J, '('); a = expression(J, 0); expect(J, ')'); b = statement(J); return STM2(WHILE, a, b); } if (accept(J, TK_VAR)) { a = vardeclist(J, 0); semicolon(J); return STM1(VAR, a); } if (accept(J, TK_FOR)) { expect(J, '('); if (accept(J, TK_VAR)) { a = vardeclist(J, 1); if (accept(J, ';')) { b = expopt(J, ';'); c = expopt(J, ')'); d = statement(J); return STM4(FOR_VAR, a, b, c, d); } if (accept(J, TK_IN)) { b = expression(J, 0); expect(J, ')'); c = statement(J); return STM3(FOR_IN_VAR, a, b, c); } jsP_error(J, "unexpected token in for-var-statement: %s", tokenstring[J->lookahead]); return NULL; } if (J->lookahead != ';') { a = expression(J, 1); } if (accept(J, ';')) { b = expopt(J, ';'); c = expopt(J, ')'); d = statement(J); return STM4(FOR, a, b, c, d); } if (accept(J, TK_IN)) { b = expression(J, 0); expect(J, ')'); c = statement(J); return STM3(FOR_IN, a, b, c); } jsP_error(J, "unexpected token in for-statement: %s", tokenstring[J->lookahead]); return NULL; } if (accept(J, TK_SWITCH)) { expect(J, '('); a = expression(J, 0); expect(J, ')'); b = caseblock(J); return STM2(SWITCH, a, b); } /* LabelledStatement : Identifier ':' Statement */ /* ExpressionStatement : Expression ';' */ if (J->lookahead == TK_IDENTIFIER) { a = expression(J, 0); if (a->type == AST_IDENTIFIER && accept(J, ':')) { b = statement(J); return STM2(LABEL, a, b); } semicolon(J); return a; } /* ExpressionStatement : [lookahead not 'function' or '{'] Expression ';' */ if (J->lookahead != TK_FUNCTION && J->lookahead != '{') { a = expression(J, 0); semicolon(J); return a; } jsP_error(J, "unexpected token in statement: %s", tokenstring[J->lookahead]); return NULL; } static js_Ast *chunknode(js_State *J) { js_Ast *a, *b, *c; if (accept(J, TK_FUNCTION)) { a = identifier(J); expect(J, '('); b = paramlist(J); expect(J, ')'); c = funcbody(J); return STM3(FUNC, a, b, c); } return statement(J); } static js_Ast *chunk(js_State *J) { js_Ast *head, *tail, *node; if (J->lookahead == '}' || J->lookahead == 0) return NULL; node = chunknode(J); head = tail = LIST(node); while (J->lookahead != '}' && J->lookahead != 0) { node = chunknode(J); tail = tail->b = LIST(node); } return head; } static js_Ast *funcbody(js_State *J) { js_Ast *a; expect(J, '{'); a = chunk(J); expect(J, '}'); return a; } int jsP_error(js_State *J, const char *fmt, ...) { va_list ap; fprintf(stderr, "syntax error: %s:%d: ", J->filename, J->line); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); longjmp(J->jb, 1); return 0; } int jsP_parse(js_State *J, const char *filename, const char *source) { jsP_initlex(J, filename, source); if (setjmp(J->jb)) { jsP_freeast(J); return 1; } next(J); printblock(chunk(J), 0); putchar('\n'); // TODO: compile to bytecode jsP_freeast(J); return 0; }