Page MenuHomePhorge

ast.c
No OneTemporary

Size
53 KB
Referenced Files
None
Subscribers
None
#include "silver/ast.h"
#include "silver/lexer.h"
#include "silver/directives.h"
#include "escape.h"
#include "errors.h"
#include "internal.h"
#include "tokens.h"
#include <runtime.h>
#include <stdlib.h>
#include <string.h>
sv_ast_t *sv_ast_new(sv_node_type_t type) {
sv_ast_t *n = code_arena_bump(sizeof(sv_ast_t));
if (!n) return NULL;
memset(n, 0, sizeof(sv_ast_t));
n->type = type;
return n;
}
void sv_ast_list_push(sv_ast_list_t *list, sv_ast_t *node) {
if (list->count >= list->cap) {
int new_cap = list->cap ? list->cap * 2 : 4;
sv_ast_t **new_items = code_arena_bump((size_t)new_cap * sizeof(sv_ast_t *));
if (!new_items) return;
if (list->items)
memcpy(new_items, list->items, (size_t)list->count * sizeof(sv_ast_t *));
list->items = new_items;
list->cap = new_cap;
}
list->items[list->count++] = node;
}
bool sv_ast_can_be_expression_statement(const sv_ast_t *node) {
if (!node) return false;
static const uint8_t expr_stmt_nodes[N__COUNT] = {
[N_NUMBER] = 1, [N_STRING] = 1, [N_BIGINT] = 1, [N_BOOL] = 1,
[N_NULL] = 1, [N_UNDEF] = 1, [N_THIS] = 1, [N_GLOBAL_THIS] = 1,
[N_TEMPLATE] = 1, [N_REGEXP] = 1, [N_IDENT] = 1, [N_BINARY] = 1,
[N_UNARY] = 1, [N_UPDATE] = 1, [N_ASSIGN] = 1, [N_TERNARY] = 1,
[N_CALL] = 1, [N_NEW] = 1, [N_MEMBER] = 1, [N_OPTIONAL] = 1,
[N_ARRAY] = 1, [N_OBJECT] = 1, [N_PROPERTY] = 1, [N_SPREAD] = 1,
[N_SEQUENCE] = 1, [N_ARROW] = 1, [N_YIELD] = 1, [N_AWAIT] = 1,
[N_TYPEOF] = 1, [N_DELETE] = 1, [N_VOID] = 1, [N_TAGGED_TEMPLATE] = 1,
[N_IMPORT] = 1,
};
if (node->type == N_FUNC)
return !(node->str && !(node->flags & FN_ARROW));
if ((unsigned)node->type >= N__COUNT) return false;
return expr_stmt_nodes[node->type] != 0;
}
typedef struct {
ant_t *js;
sv_lexer_t lx;
bool no_in;
} sv_parser_t;
#define P sv_parser_t *p
#define JS p->js
#define TOK p->lx.st.tok
#define TOFF p->lx.st.toff
#define TLEN p->lx.st.tlen
#define TVAL p->lx.st.tval
#define POS p->lx.st.pos
#define CONSUMED p->lx.st.consumed
#define HAD_NEWLINE p->lx.st.had_newline
#define CODE p->lx.code
#define CLEN p->lx.clen
#define CONSUME() ((void)(CONSUMED = 1))
#define NEXT() sv_lexer_next(&p->lx)
#define LA() sv_lexer_lookahead(&p->lx)
static sv_ast_t *parse_stmt(P);
static sv_ast_t *parse_expr(P);
static sv_ast_t *parse_assign(P);
static sv_ast_t *parse_ternary(P);
static sv_ast_t *parse_binary(P, int min_prec);
static sv_ast_t *parse_unary(P);
static sv_ast_t *parse_postfix(P);
static sv_ast_t *parse_call(P);
static sv_ast_t *parse_primary(P);
static sv_ast_t *parse_block(P, bool directive_ctx);
static sv_ast_t *parse_func(P);
static sv_ast_t *parse_class(P);
static sv_ast_t *parse_object(P);
static sv_ast_t *parse_array(P);
static sv_ast_t *parse_import_stmt(P);
static sv_ast_t *parse_export_stmt(P);
static sv_ast_t *parse_arrow_body(P);
static sv_ast_t *parse_binding_pattern(P);
#define SV_SYNC_ERR() ((void)sv_lexer_set_error_site(&p->lx))
#define SV_MKERR(...) (SV_SYNC_ERR(), js_mkerr(__VA_ARGS__))
#define SV_MKERR_TYPED(...) (SV_SYNC_ERR(), js_mkerr_typed(__VA_ARGS__))
static inline const char *tok_str(P) {
return &CODE[TOFF];
}
static inline bool eat(P, uint8_t tok) {
NEXT();
if (TOK == tok) { CONSUME(); return true; }
return false;
}
static inline void expect(P, uint8_t tok) {
NEXT();
if (TOK == tok) CONSUME();
}
static inline sv_ast_t *mk_plain(sv_node_type_t type) {
return sv_ast_new(type);
}
#define mk(type) ({ \
sv_ast_t *_n = mk_plain(type); \
_n->src_off = (uint32_t)TOFF; _n; \
})
static inline sv_ast_t *mk_num(double val) {
sv_ast_t *n = mk_plain(N_NUMBER);
n->num = val;
return n;
}
static inline sv_ast_t *mk_ident(const char *s, uint32_t len) {
sv_ast_t *n = mk_plain(N_IDENT);
n->str = s;
n->len = len;
return n;
}
static inline void sv_ast_set_string(sv_ast_t *n, sv_lex_string_t s) {
if (!n || !s.ok) return;
n->str = s.str;
n->len = s.len;
}
typedef struct {
const char *str;
uint32_t len;
bool ok;
bool valid_cooked;
} sv_tpl_cooked_t;
static inline const char *decode_ident_into_arena(const char *src, uint32_t len, uint32_t *out_len) {
if (!src || len == 0 || !memchr(src, '\\', len)) {
if (out_len) *out_len = len;
return src;
}
char *dst = code_arena_bump((size_t)len + 1);
if (!dst) {
if (out_len) *out_len = len;
return src;
}
size_t di = 0; size_t i = 0;
const uint8_t *in = (const uint8_t *)src;
uint8_t *out = (uint8_t *)dst;
while (i < len) {
if (in[i] == '\\' && i + 1 < len && in[i + 1] == 'u') {
size_t adv = decode_escape(in, i, len, out, &di, 0);
if (adv > 0) { i += 2 + adv; continue; }
}
out[di++] = in[i++];
}
out[di] = '\0';
if (out_len) *out_len = (uint32_t)di;
return dst;
}
static inline bool is_contextual_ident_tok(uint8_t tok) {
return
tok == TOK_AS
|| tok == TOK_FROM
|| tok == TOK_OF
|| tok == TOK_ASYNC;
}
static inline const char *tok_ident_str(P, uint32_t *out_len) {
return decode_ident_into_arena(tok_str(p), (uint32_t)TLEN, out_len);
}
static inline bool is_ident_like_tok(uint8_t tok) {
return tok == TOK_IDENTIFIER || tok == TOK_DEFAULT || is_contextual_ident_tok(tok);
}
static inline bool sv_strict_forbidden_binding_ident(const char *s, uint32_t len) {
return is_eval_or_arguments_name(s, len) || is_strict_reserved_name(s, len);
}
static inline bool sv_is_strict_restricted_assign_target(P, sv_ast_t *n) {
return p->lx.strict && n && n->type == N_IDENT && is_eval_or_arguments_name(n->str, n->len);
}
static inline sv_ast_t *mk_ident_from_tok(P) {
uint32_t len = 0;
const char *name = tok_ident_str(p, &len);
sv_ast_t *n = mk_ident(name, len);
n->src_off = (uint32_t)TOFF;
n->src_end = (uint32_t)(TOFF + TLEN);
return n;
}
static inline sv_ast_t *mk_private_ident_from_tok(P) {
sv_ast_t *n = mk(N_IDENT);
n->str = &CODE[TOFF - 1];
n->len = (uint32_t)(TLEN + 1);
n->src_off = (uint32_t)(TOFF > 0 ? TOFF - 1 : TOFF);
n->src_end = (uint32_t)(TOFF + TLEN);
return n;
}
static inline void sv_strict_check_binding_ident(P, const char *s, uint32_t len) {
if (!p->lx.strict || !s || len == 0) return;
if (sv_strict_forbidden_binding_ident(s, len)) SV_MKERR_TYPED(
JS, JS_ERR_SYNTAX, "Invalid binding identifier '%.*s' in strict mode",
(int)len, s
);
}
static inline void sv_parse_unexpected_token(P) {
size_t tok_len = 0;
if (TOFF < CLEN && TLEN > 0) {
jsoff_t rem = CLEN - TOFF;
tok_len = (size_t)TLEN;
if ((jsoff_t)tok_len > rem) tok_len = (size_t)rem;
}
if (tok_len == 0) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Unexpected token 'EOF'");
return;
}
SV_MKERR_TYPED(
JS, JS_ERR_SYNTAX,
"Unexpected token '%.*s'",
(int)tok_len, &CODE[TOFF]
);
}
static sv_ast_t *parse_arrow_body(P) {
if (NEXT() == TOK_LBRACE) return parse_block(p, true);
return parse_assign(p);
}
static void sv_parse_stmt_list(P, sv_ast_list_t *out, bool stop_at_rbrace, bool directive_ctx) {
bool strict_mode = p->lx.strict;
bool saved_lexer_strict = p->lx.strict;
bool in_directive_prologue = directive_ctx;
p->lx.strict = strict_mode;
for (;;) {
NEXT();
if (TOK == TOK_EOF) break;
if (stop_at_rbrace && TOK == TOK_RBRACE) break;
if (TOK == TOK_ERR) {
sv_parse_unexpected_token(p);
break;
}
sv_ast_t *stmt = parse_stmt(p);
if (stmt) sv_ast_list_push(out, stmt);
if (JS->thrown_exists) break;
if (!in_directive_prologue) continue;
if (!stmt || stmt->type == N_EMPTY) continue;
if (!sv_ast_can_be_expression_statement(stmt)) {
in_directive_prologue = false;
continue;
}
if (sv_ast_is_use_strict(JS, stmt)) {
strict_mode = true;
p->lx.strict = true;
continue;
}
in_directive_prologue = false;
}
p->lx.strict = saved_lexer_strict;
}
static sv_ast_t *parse_binding_pattern(P) {
NEXT();
if (TOK == TOK_LBRACKET) return parse_array(p);
if (TOK == TOK_LBRACE) return parse_object(p);
if (is_ident_like_tok(TOK)) {
sv_ast_t *id = mk_ident_from_tok(p);
sv_strict_check_binding_ident(p, id->str, id->len);
CONSUME();
return id;
}
CONSUME();
return mk(N_EMPTY);
}
static void push_arrow_params_from_expr(sv_ast_t *fn, sv_ast_t *expr) {
if (!fn || !expr) return;
if (expr->type == N_SEQUENCE) {
push_arrow_params_from_expr(fn, expr->left);
push_arrow_params_from_expr(fn, expr->right);
return;
}
if (expr->type == N_ASSIGN && expr->op == TOK_ASSIGN) {
sv_ast_t *def = mk_plain(N_ASSIGN_PAT);
def->left = expr->left;
def->right = expr->right;
def->src_off = expr->src_off;
sv_ast_list_push(&fn->args, def);
return;
}
if (expr->type == N_SPREAD) {
sv_ast_t *rest = mk_plain(N_REST);
rest->right = expr->right;
rest->src_off = expr->src_off;
sv_ast_list_push(&fn->args, rest);
return;
}
sv_ast_list_push(&fn->args, expr);
}
static sv_tpl_cooked_t decode_template_segment(P, const uint8_t *in, size_t start, size_t end) {
sv_tpl_cooked_t outv = { .str = NULL, .len = 0, .ok = true, .valid_cooked = true };
size_t raw_len = (end > start) ? (end - start) : 0;
if (raw_len == 0) {
outv.str = "";
return outv;
}
uint8_t *out = code_arena_bump(raw_len);
if (!out) {
(void)SV_MKERR(JS, "oom");
outv.ok = false;
return outv;
}
size_t out_len = 0;
for (size_t i = start; i < end; i++) {
if (in[i] == '\r') {
out[out_len++] = '\n';
if (i + 1 < end && in[i + 1] == '\n') i++;
continue;
}
if (in[i] != '\\' || i + 1 >= end) {
out[out_len++] = in[i];
continue;
}
uint8_t c = in[i + 1];
if (c >= '1' && c <= '9') { outv.valid_cooked = false; return outv; }
if (c == '0' && i + 2 < end && in[i + 2] >= '0' && in[i + 2] <= '9') {
outv.valid_cooked = false; return outv;
}
if (c == 'x' && !(i + 3 < end && is_xdigit(in[i + 2]) && is_xdigit(in[i + 3]))) {
outv.valid_cooked = false; return outv;
}
if (c == 'u') {
if (i + 2 < end && in[i + 2] == '{') {
uint32_t cp = 0; size_t j = i + 3;
while (j < end && is_xdigit(in[j])) { cp = (cp << 4) | unhex(in[j]); j++; }
if (!(j < end && in[j] == '}' && j > i + 3 && cp <= 0x10FFFF)) {
outv.valid_cooked = false; return outv;
}
} else if (
!(i + 5 < end && is_xdigit(in[i + 2])
&& is_xdigit(in[i + 3])
&& is_xdigit(in[i + 4])
&& is_xdigit(in[i + 5]))) { outv.valid_cooked = false; return outv; }
}
i += 1 + decode_escape(in, i, end, out, &out_len, '`');
}
outv.str = (const char *)out;
outv.len = (uint32_t)out_len;
return outv;
}
static sv_ast_t *try_parse_async_arrow(P) {
uint8_t la = LA();
uint32_t async_off = (uint32_t)TOFF;
if (la == TOK_LPAREN) {
sv_lexer_state_t saved;
sv_lexer_save_state(&p->lx, &saved);
NEXT(); CONSUME();
if (NEXT() == TOK_RPAREN) {
CONSUME();
if (LA() == TOK_ARROW) {
NEXT(); CONSUME();
sv_ast_t *fn = mk(N_FUNC);
fn->flags = FN_ARROW | FN_ASYNC;
fn->body = parse_arrow_body(p);
fn->src_off = async_off;
fn->src_end = (uint32_t)(TOFF + TLEN);
return fn;
}
}
sv_lexer_restore_state(&p->lx, &saved);
NEXT(); CONSUME();
sv_ast_t *expr = parse_expr(p);
expect(p, TOK_RPAREN);
if (LA() == TOK_ARROW) {
NEXT(); CONSUME();
sv_ast_t *fn = mk(N_FUNC);
fn->flags = FN_ARROW | FN_ASYNC;
push_arrow_params_from_expr(fn, expr);
fn->body = parse_arrow_body(p);
fn->src_off = async_off;
fn->src_end = (uint32_t)(TOFF + TLEN);
return fn;
}
sv_lexer_restore_state(&p->lx, &saved);
return NULL;
}
if (la == TOK_IDENTIFIER) {
sv_lexer_state_t saved;
sv_lexer_save_state(&p->lx, &saved);
NEXT(); CONSUME();
sv_ast_t *id = mk_ident_from_tok(p);
if (LA() == TOK_ARROW) {
NEXT(); CONSUME();
sv_ast_t *fn = mk(N_FUNC);
fn->flags = FN_ARROW | FN_ASYNC;
sv_ast_list_push(&fn->args, id);
fn->body = parse_arrow_body(p);
fn->src_off = async_off;
fn->src_end = (uint32_t)(TOFF + TLEN);
return fn;
}
sv_lexer_restore_state(&p->lx, &saved);
return NULL;
}
return NULL;
}
static sv_ast_t *parse_primary(P) {
NEXT();
static const void *dispatch[TOK_MAX] = {
[TOK_NUMBER] = &&l_number,
[TOK_STRING] = &&l_string,
[TOK_BIGINT] = &&l_bigint,
[TOK_TRUE] = &&l_true,
[TOK_FALSE] = &&l_false,
[TOK_NULL] = &&l_null,
[TOK_UNDEF] = &&l_undef,
[TOK_THIS] = &&l_this,
[TOK_IDENTIFIER] = &&l_ident,
[TOK_AS] = &&l_ident,
[TOK_FROM] = &&l_ident,
[TOK_OF] = &&l_ident,
[TOK_LPAREN] = &&l_paren,
[TOK_LBRACKET] = &&l_array,
[TOK_LBRACE] = &&l_object,
[TOK_FUNC] = &&l_func,
[TOK_CLASS] = &&l_class,
[TOK_ASYNC] = &&l_async,
[TOK_TEMPLATE] = &&l_template,
[TOK_NEW] = &&l_new,
[TOK_TYPEOF] = &&l_typeof,
[TOK_VOID] = &&l_void,
[TOK_DELETE] = &&l_delete,
[TOK_YIELD] = &&l_yield,
[TOK_AWAIT] = &&l_await,
[TOK_SUPER] = &&l_super,
[TOK_REST] = &&l_spread,
[TOK_IMPORT] = &&l_import_expr,
[TOK_DIV] = &&l_regex,
[TOK_DIV_ASSIGN] = &&l_regex,
[TOK_GLOBAL_THIS] = &&l_globalthis,
[TOK_WINDOW] = &&l_globalthis,
[TOK_HASH] = &&l_private_name,
};
if (TOK < TOK_MAX && dispatch[TOK])
goto *dispatch[TOK];
sv_parse_unexpected_token(p);
return mk(N_EMPTY);
l_number: {
CONSUME();
return mk_num(tod(TVAL));
}
l_string: {
sv_ast_t *n = mk(N_STRING);
sv_ast_set_string(n, sv_lexer_str_literal(&p->lx));
CONSUME();
return n;
}
l_bigint: {
CONSUME();
sv_ast_t *n = mk(N_BIGINT);
n->str = tok_str(p);
n->len = (uint32_t)TLEN;
return n;
}
l_true: { CONSUME(); sv_ast_t *n = mk(N_BOOL); n->num = 1; return n; }
l_false: { CONSUME(); sv_ast_t *n = mk(N_BOOL); n->num = 0; return n; }
l_null: { CONSUME(); return mk(N_NULL); }
l_undef: { CONSUME(); return mk(N_UNDEF); }
l_this: { CONSUME(); return mk(N_THIS); }
l_globalthis: { CONSUME(); return mk(N_GLOBAL_THIS); }
l_ident: {
uint32_t ident_off = (uint32_t)TOFF;
CONSUME();
sv_ast_t *id = mk_ident_from_tok(p);
if (LA() == TOK_ARROW) {
NEXT(); CONSUME();
sv_ast_t *fn = mk(N_FUNC);
fn->flags = FN_ARROW;
fn->src_off = ident_off;
sv_ast_list_push(&fn->args, id);
fn->body = parse_arrow_body(p);
fn->src_end = (uint32_t)(TOFF + TLEN);
return fn;
}
return id;
}
l_paren: {
uint32_t paren_off = (uint32_t)TOFF;
CONSUME();
if (NEXT() == TOK_RPAREN) {
CONSUME();
if (LA() == TOK_ARROW) {
NEXT(); CONSUME();
sv_ast_t *fn = mk(N_FUNC);
fn->flags = FN_ARROW;
fn->src_off = paren_off;
fn->body = parse_arrow_body(p);
fn->src_end = (uint32_t)(TOFF + TLEN);
return fn;
}
return mk(N_UNDEF);
}
sv_ast_t *expr = parse_expr(p);
expect(p, TOK_RPAREN);
if (LA() == TOK_ARROW) {
NEXT(); CONSUME();
sv_ast_t *fn = mk(N_FUNC);
fn->flags = FN_ARROW;
fn->src_off = paren_off;
push_arrow_params_from_expr(fn, expr);
fn->body = parse_arrow_body(p);
fn->src_end = (uint32_t)(TOFF + TLEN);
return fn;
}
expr->flags |= FN_PAREN;
return expr;
}
l_array: return parse_array(p);
l_object: return parse_object(p);
l_func: { CONSUME(); return parse_func(p); }
l_class: {
CONSUME();
return parse_class(p);
}
l_async: {
uint32_t async_off = (uint32_t)TOFF;
CONSUME();
if (LA() == TOK_FUNC) {
NEXT(); CONSUME();
sv_ast_t *fn = parse_func(p);
fn->flags |= FN_ASYNC;
fn->src_off = async_off;
return fn;
}
sv_ast_t *arrow = try_parse_async_arrow(p);
if (arrow) return arrow;
return mk_ident_from_tok(p);
}
l_template: {
CONSUME();
sv_ast_t *n = mk(N_TEMPLATE);
const uint8_t *in = (const uint8_t *)&CODE[TOFF];
size_t tpl_len = TLEN;
size_t i = 1;
for (;;) {
size_t seg_start = i;
while (i < tpl_len - 1) {
if (in[i] == '\\' && i + 1 < tpl_len - 1) { i += 2; continue; }
if (in[i] == '$' && i + 1 < tpl_len - 1 && in[i + 1] == '{') break;
i++;
}
sv_ast_t *s = mk(N_STRING);
s->aux = (const char *)&in[seg_start];
s->aux_len = (uint32_t)(i - seg_start);
sv_tpl_cooked_t cooked = decode_template_segment(p, in, seg_start, i);
if (cooked.ok && cooked.valid_cooked) {
s->str = cooked.str;
s->len = cooked.len;
} else if (cooked.ok) s->flags |= FN_INVALID_COOKED;
sv_ast_list_push(&n->args, s);
if (i >= tpl_len - 1 || in[i] != '$') break;
i += 2;
size_t expr_start = i;
size_t expr_max_len = (
tpl_len > 0 && expr_start < tpl_len - 1)
? (tpl_len - 1 - expr_start) : 0;
sv_lexer_checkpoint_t cp;
sv_lexer_push_source(&p->lx, &cp, (const char *)&in[expr_start], (jsoff_t)expr_max_len);
sv_ast_t *expr = parse_expr(p);
sv_ast_list_push(&n->args, expr);
if (NEXT() != TOK_RBRACE) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Unterminated template expression");
sv_lexer_pop_source(&p->lx, &cp);
return n;
}
CONSUME();
size_t consumed_expr = (size_t)POS;
sv_lexer_pop_source(&p->lx, &cp);
if (consumed_expr == 0) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Unterminated template expression");
return n;
} i = expr_start + consumed_expr;
}
return n;
}
l_new: {
CONSUME();
if (NEXT() == TOK_DOT) {
CONSUME();
if (NEXT() == TOK_IDENTIFIER && TLEN == 6 && memcmp(tok_str(p), "target", 6) == 0) {
CONSUME();
return mk(N_NEW_TARGET);
}
sv_parse_unexpected_token(p);
return mk(N_EMPTY);
}
sv_ast_t *n = mk(N_NEW);
sv_ast_t *callee = parse_primary(p);
for (;;) {
uint8_t la = NEXT();
if (la == TOK_DOT) {
CONSUME();
NEXT();
sv_ast_t *mem = mk(N_MEMBER);
mem->left = callee;
mem->right = mk_ident_from_tok(p);
CONSUME();
callee = mem;
} else if (la == TOK_LBRACKET) {
CONSUME();
sv_ast_t *mem = mk(N_MEMBER);
mem->left = callee;
mem->right = parse_expr(p);
mem->flags = 1;
expect(p, TOK_RBRACKET);
callee = mem;
} else break;
}
n->left = callee;
if (NEXT() == TOK_LPAREN) {
CONSUME();
while (NEXT() != TOK_RPAREN && TOK != TOK_EOF) {
if (TOK == TOK_REST) {
CONSUME();
sv_ast_t *spread = mk(N_SPREAD);
spread->right = parse_assign(p);
sv_ast_list_push(&n->args, spread);
} else sv_ast_list_push(&n->args, parse_assign(p));
if (NEXT() == TOK_COMMA) CONSUME();
else break;
} expect(p, TOK_RPAREN);
}
return n;
}
l_typeof: {
CONSUME();
sv_ast_t *n = mk(N_TYPEOF);
n->right = parse_unary(p);
return n;
}
l_void: {
CONSUME();
sv_ast_t *n = mk(N_VOID);
n->right = parse_unary(p);
return n;
}
l_delete: {
CONSUME();
sv_ast_t *n = mk(N_DELETE);
n->right = parse_unary(p);
if (p->lx.strict && n->right && n->right->type == N_IDENT) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "cannot delete bindings in strict mode");
return mk(N_EMPTY);
}
return n;
}
l_yield: {
CONSUME();
sv_ast_t *n = mk(N_YIELD);
if (NEXT() == TOK_MUL) {
CONSUME();
n->flags = 1;
}
if (TOK != TOK_SEMICOLON && TOK != TOK_RBRACE &&
TOK != TOK_RPAREN && TOK != TOK_RBRACKET &&
TOK != TOK_EOF && TOK != TOK_COMMA)
n->right = parse_assign(p);
return n;
}
l_await: {
CONSUME();
sv_ast_t *n = mk(N_AWAIT);
n->right = parse_unary(p);
return n;
}
l_super: {
CONSUME();
return mk_ident("super", 5);
}
l_spread: {
CONSUME();
sv_ast_t *n = mk(N_SPREAD);
n->right = parse_assign(p);
return n;
}
l_import_expr: {
CONSUME();
if (NEXT() != TOK_LPAREN) return mk_ident("import", 6);
CONSUME();
sv_ast_t *n = mk(N_IMPORT);
n->right = parse_expr(p);
expect(p, TOK_RPAREN);
return n;
}
l_regex: {
CONSUME();
jsoff_t pattern_start = (TOK == TOK_DIV_ASSIGN) ? (TOFF + 1) : POS;
if (TOK == TOK_DIV_ASSIGN) POS = pattern_start;
bool in_class = false;
while (POS < CLEN) {
char c = CODE[POS];
if (c == '\\' && POS + 1 < CLEN) {
POS += 2;
continue;
}
if (c == '[') in_class = true;
else if (c == ']') in_class = false;
else if (c == '/' && !in_class) break;
POS++;
}
jsoff_t pattern_end = POS;
if (POS < CLEN) POS++;
jsoff_t flags_start = POS;
while (POS < CLEN) {
char c = CODE[POS];
if (c == 'g' || c == 'i' || c == 'm' || c == 's' || c == 'u' || c == 'y')
POS++;
else break;
}
jsoff_t flags_end = POS;
sv_ast_t *n = mk(N_REGEXP);
n->str = &CODE[pattern_start];
n->len = (uint32_t)(pattern_end - pattern_start);
n->aux = &CODE[flags_start];
n->aux_len = (uint32_t)(flags_end - flags_start);
CONSUMED = 1;
return n;
}
l_private_name: {
CONSUME();
NEXT();
if (!is_ident_like_tok(TOK)) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "private field name expected");
return mk(N_EMPTY);
}
sv_ast_t *n = mk_private_ident_from_tok(p);
CONSUME();
return n;
}
}
static sv_ast_t *parse_array(P) {
CONSUME();
sv_ast_t *n = mk(N_ARRAY);
while (NEXT() != TOK_RBRACKET && TOK != TOK_EOF) {
if (TOK == TOK_COMMA) {
CONSUME();
sv_ast_list_push(&n->args, mk(N_EMPTY));
continue;
}
if (TOK == TOK_REST) {
CONSUME();
sv_ast_t *spread = mk(N_SPREAD);
spread->right = parse_assign(p);
sv_ast_list_push(&n->args, spread);
} else {
sv_ast_list_push(&n->args, parse_assign(p));
}
if (NEXT() == TOK_COMMA) CONSUME();
else break;
}
expect(p, TOK_RBRACKET);
return n;
}
static sv_ast_t *parse_object(P) {
CONSUME();
sv_ast_t *n = mk(N_OBJECT);
bool proto_set = false;
while (NEXT() != TOK_RBRACE && TOK != TOK_EOF) {
sv_ast_t *prop = mk(N_PROPERTY);
if (TOK == TOK_REST) {
CONSUME();
sv_ast_t *spread = mk(N_SPREAD);
spread->right = parse_assign(p);
sv_ast_list_push(&n->args, spread);
if (NEXT() == TOK_COMMA) CONSUME();
continue;
}
if (TOK == TOK_LBRACKET) {
CONSUME();
prop->left = parse_assign(p);
expect(p, TOK_RBRACKET);
prop->flags |= FN_COMPUTED;
}
else if (
(TLEN == 3 && memcmp(tok_str(p), "get", 3) == 0) ||
(TLEN == 3 && memcmp(tok_str(p), "set", 3) == 0)) {
uint8_t gs = tok_str(p)[0];
uint8_t la = LA();
if (la != TOK_COLON && la != TOK_LPAREN && la != TOK_COMMA &&
la != TOK_RBRACE) {
CONSUME();
prop->flags |= (gs == 'g') ? FN_GETTER : FN_SETTER;
NEXT();
if (TOK == TOK_LBRACKET) {
CONSUME();
prop->left = parse_assign(p);
expect(p, TOK_RBRACKET);
prop->flags |= FN_COMPUTED;
} else if (TOK == TOK_NUMBER) {
CONSUME();
prop->left = mk_num(tod(TVAL));
} else if (TOK == TOK_STRING) {
prop->left = mk(N_STRING);
sv_ast_set_string(prop->left, sv_lexer_str_literal(&p->lx));
CONSUME();
} else {
prop->left = mk_ident_from_tok(p);
CONSUME();
}
prop->right = parse_func(p);
sv_ast_list_push(&n->args, prop);
if (NEXT() == TOK_COMMA) CONSUME();
continue;
}
prop->left = mk_ident_from_tok(p);
CONSUME();
}
else if (TOK == TOK_ASYNC) {
uint8_t la = LA();
if (la != TOK_COLON && la != TOK_LPAREN && la != TOK_COMMA && la != TOK_RBRACE) {
CONSUME();
prop->flags |= FN_ASYNC;
NEXT();
if (TOK == TOK_MUL) {
prop->flags |= FN_GENERATOR;
CONSUME();
NEXT();
}
if (TOK == TOK_LBRACKET) {
CONSUME();
prop->left = parse_assign(p);
expect(p, TOK_RBRACKET);
prop->flags |= FN_COMPUTED;
} else if (TOK == TOK_NUMBER) {
CONSUME();
prop->left = mk_num(tod(TVAL));
} else if (TOK == TOK_STRING) {
prop->left = mk(N_STRING);
sv_ast_set_string(prop->left, sv_lexer_str_literal(&p->lx));
CONSUME();
} else {
prop->left = mk_ident_from_tok(p);
CONSUME();
}
prop->right = parse_func(p);
prop->right->flags |= FN_ASYNC;
if (prop->flags & FN_GENERATOR)
prop->right->flags |= FN_GENERATOR;
sv_ast_list_push(&n->args, prop);
if (NEXT() == TOK_COMMA) CONSUME();
continue;
}
prop->left = mk_ident_from_tok(p);
CONSUME();
}
else if (TOK == TOK_NUMBER) {
CONSUME();
prop->left = mk_num(tod(TVAL));
}
else if (TOK == TOK_STRING) {
prop->left = mk(N_STRING);
sv_ast_set_string(prop->left, sv_lexer_str_literal(&p->lx));
CONSUME();
}
else {
prop->left = mk_ident_from_tok(p);
CONSUME();
}
if (NEXT() == TOK_COLON) {
CONSUME();
prop->flags |= FN_COLON;
if (prop->left && prop->left->type == N_IDENT &&
prop->left->len == 9 && memcmp(prop->left->str, "__proto__", 9) == 0) {
if (proto_set) { SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Duplicate __proto__ fields are not allowed in object literals"); return n; }
proto_set = true;
}
prop->right = parse_assign(p);
} else if (TOK == TOK_LPAREN) {
prop->right = parse_func(p);
} else {
prop->right = mk_ident(prop->left->str, prop->left->len);
if (NEXT() == TOK_ASSIGN) {
CONSUME();
sv_ast_t *def = mk(N_ASSIGN);
def->op = TOK_ASSIGN;
def->left = prop->right;
def->right = parse_assign(p);
prop->right = def;
}
}
sv_ast_list_push(&n->args, prop);
if (NEXT() == TOK_COMMA) CONSUME();
else break;
}
expect(p, TOK_RBRACE);
return n;
}
static sv_ast_t *parse_call(P) {
sv_ast_t *n = parse_primary(p);
for (;;) {
uint8_t la = NEXT();
if (la == TOK_LPAREN) {
CONSUME();
sv_ast_t *call = mk(N_CALL);
call->left = n;
while (NEXT() != TOK_RPAREN && TOK != TOK_EOF) {
if (TOK == TOK_REST) {
CONSUME();
sv_ast_t *spread = mk(N_SPREAD);
spread->right = parse_assign(p);
sv_ast_list_push(&call->args, spread);
} else {
sv_ast_list_push(&call->args, parse_assign(p));
}
if (NEXT() == TOK_COMMA) CONSUME();
else break;
}
expect(p, TOK_RPAREN);
n = call;
} else if (la == TOK_DOT) {
CONSUME();
NEXT();
sv_ast_t *mem = mk(N_MEMBER);
mem->left = n;
if (TOK == TOK_HASH) {
CONSUME();
NEXT();
if (!is_ident_like_tok(TOK)) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "private field name expected");
return mk(N_EMPTY);
}
mem->right = mk_private_ident_from_tok(p);
CONSUME();
} else {
mem->right = mk_ident_from_tok(p);
CONSUME();
}
n = mem;
} else if (la == TOK_LBRACKET) {
CONSUME();
sv_ast_t *mem = mk(N_MEMBER);
mem->left = n;
mem->right = parse_expr(p);
mem->flags = 1;
expect(p, TOK_RBRACKET);
n = mem;
} else if (la == TOK_OPTIONAL_CHAIN) {
CONSUME();
sv_ast_t *opt = mk(N_OPTIONAL);
opt->left = n;
if (NEXT() == TOK_LBRACKET) {
CONSUME();
opt->right = parse_expr(p);
opt->flags = 1;
expect(p, TOK_RBRACKET);
} else if (TOK == TOK_LPAREN) {
sv_ast_t *call = mk(N_CALL);
call->left = opt;
CONSUME();
while (NEXT() != TOK_RPAREN && TOK != TOK_EOF) {
sv_ast_list_push(&call->args, parse_assign(p));
if (NEXT() == TOK_COMMA) CONSUME();
else break;
}
expect(p, TOK_RPAREN);
n = call;
continue;
} else if (TOK == TOK_HASH) {
CONSUME();
NEXT();
if (!is_ident_like_tok(TOK)) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "private field name expected");
return mk(N_EMPTY);
}
opt->right = mk_private_ident_from_tok(p);
CONSUME();
} else {
opt->right = mk_ident_from_tok(p);
CONSUME();
}
n = opt;
} else if (la == TOK_TEMPLATE) {
sv_ast_t *tagged = mk(N_TAGGED_TEMPLATE);
tagged->left = n;
tagged->right = parse_primary(p);
n = tagged;
} else break;
}
return n;
}
static sv_ast_t *parse_postfix(P) {
sv_ast_t *n = parse_call(p);
uint8_t la = NEXT();
if ((la == TOK_POSTINC || la == TOK_POSTDEC) && !HAD_NEWLINE) {
if (sv_is_strict_restricted_assign_target(p, n)) {
SV_MKERR_TYPED(
JS, JS_ERR_SYNTAX,
"cannot modify eval or arguments in strict mode");
return mk(N_EMPTY);
}
CONSUME();
sv_ast_t *u = mk(N_UPDATE);
u->op = la;
u->right = n;
return u;
}
return n;
}
static sv_ast_t *parse_unary(P) {
uint8_t la = NEXT();
if (la == TOK_NOT || la == TOK_TILDA ||
la == TOK_UPLUS || la == TOK_UMINUS ||
la == TOK_PLUS || la == TOK_MINUS) {
CONSUME();
sv_ast_t *n = mk(N_UNARY);
n->op = (la == TOK_PLUS) ? TOK_UPLUS :
(la == TOK_MINUS) ? TOK_UMINUS : la;
n->right = parse_unary(p);
if (NEXT() == TOK_EXP) {
SV_MKERR_TYPED(
JS, JS_ERR_SYNTAX,
"Unary operator used immediately before exponentiation expression. "
"Parenthesis must be used to disambiguate operator precedence");
return mk(N_EMPTY);
}
return n;
}
if (la == TOK_POSTINC || la == TOK_POSTDEC) {
CONSUME();
sv_ast_t *target = parse_unary(p);
if (sv_is_strict_restricted_assign_target(p, target)) {
SV_MKERR_TYPED(
JS, JS_ERR_SYNTAX,
"cannot modify eval or arguments in strict mode");
return mk(N_EMPTY);
}
sv_ast_t *n = mk(N_UPDATE);
n->op = la;
n->right = target;
n->flags = 1;
return n;
}
return parse_postfix(p);
}
static sv_ast_t *parse_binary(P, int min_prec) {
sv_ast_t *left = parse_unary(p);
for (;;) {
uint8_t op = NEXT();
if (op >= TOK_MAX) break;
if (op == TOK_IN && p->no_in) break;
int prec = prec_table[op];
if (prec == 0 || prec < min_prec) break;
CONSUME();
int next_prec = (op == TOK_EXP) ? prec : prec + 1;
sv_ast_t *right = parse_binary(p, next_prec);
sv_ast_t *bin = mk(N_BINARY);
bin->op = op;
bin->left = left;
bin->right = right;
left = bin;
}
return left;
}
static sv_ast_t *parse_ternary(P) {
sv_ast_t *cond = parse_binary(p, 1);
if (NEXT() == TOK_Q) {
CONSUME();
sv_ast_t *n = mk(N_TERNARY);
n->cond = cond;
n->left = parse_assign(p);
expect(p, TOK_COLON);
n->right = parse_assign(p);
return n;
}
return cond;
}
static bool is_assign_op(uint8_t tok) {
return tok >= TOK_ASSIGN && tok <= TOK_NULLISH_ASSIGN;
}
static sv_ast_t *parse_assign(P) {
sv_ast_t *left = parse_ternary(p);
uint8_t op = NEXT();
if (is_assign_op(op)) {
if (left->type == N_NEW_TARGET) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Invalid left-hand side in assignment");
return mk(N_EMPTY);
}
if ((left->flags & FN_PAREN) && op == TOK_ASSIGN &&
(left->type == N_OBJECT || left->type == N_OBJECT_PAT ||
left->type == N_ARRAY || left->type == N_ARRAY_PAT)) {
SV_MKERR_TYPED(
JS, JS_ERR_SYNTAX,
"Invalid destructuring assignment target");
return mk(N_EMPTY);
}
if (sv_is_strict_restricted_assign_target(p, left)) {
SV_MKERR_TYPED(
JS, JS_ERR_SYNTAX,
"cannot modify eval or arguments in strict mode");
return mk(N_EMPTY);
}
CONSUME();
sv_ast_t *n = mk(N_ASSIGN);
n->op = op;
n->left = left;
n->right = parse_assign(p);
return n;
}
return left;
}
static sv_ast_t *parse_expr(P) {
sv_ast_t *left = parse_assign(p);
while (NEXT() == TOK_COMMA) {
CONSUME();
sv_ast_t *n = mk(N_SEQUENCE);
n->left = left;
n->right = parse_assign(p);
left = n;
}
return left;
}
bool ast_references_arguments(const sv_ast_t *node) {
if (!node) return false;
if (node->type == N_IDENT && node->len == 9 && memcmp(node->str, "arguments", 9) == 0)
return true;
if (node->type == N_FUNC && !(node->flags & FN_ARROW)) {
for (int i = 0; i < node->args.count; i++)
if (ast_references_arguments(node->args.items[i])) return true;
return false;
}
if (ast_references_arguments(node->left)) return true;
if (ast_references_arguments(node->right)) return true;
if (ast_references_arguments(node->body)) return true;
if (ast_references_arguments(node->catch_body)) return true;
if (ast_references_arguments(node->finally_body)) return true;
for (int i = 0; i < node->args.count; i++)
if (ast_references_arguments(node->args.items[i])) return true;
return false;
}
static sv_ast_t *parse_func(P) {
sv_ast_t *fn = mk(N_FUNC);
if (NEXT() == TOK_MUL) {
CONSUME();
fn->flags |= FN_GENERATOR;
}
if (NEXT() == TOK_IDENTIFIER) {
fn->str = tok_ident_str(p, &fn->len);
sv_strict_check_binding_ident(p, fn->str, fn->len);
CONSUME();
}
expect(p, TOK_LPAREN);
while (NEXT() != TOK_RPAREN && TOK != TOK_EOF) {
if (TOK == TOK_REST) {
CONSUME();
sv_ast_t *rest = mk(N_REST);
rest->right = parse_binding_pattern(p);
sv_ast_list_push(&fn->args, rest);
break;
}
sv_ast_t *param = parse_binding_pattern(p);
if (NEXT() == TOK_ASSIGN) {
CONSUME();
sv_ast_t *def = mk(N_ASSIGN_PAT);
def->left = param;
def->right = parse_assign(p);
param = def;
}
sv_ast_list_push(&fn->args, param);
if (NEXT() == TOK_COMMA) {
CONSUME();
if (NEXT() == TOK_RPAREN) break;
} else break;
}
expect(p, TOK_RPAREN);
fn->body = parse_block(p, true);
fn->src_end = (uint32_t)(TOFF + TLEN);
if (!(fn->flags & FN_ARROW) && ast_references_arguments(fn->body))
fn->flags |= FN_USES_ARGS;
return fn;
}
static sv_ast_t *parse_class(P) {
sv_ast_t *cls = mk(N_CLASS);
if (NEXT() == TOK_IDENTIFIER &&
!(TLEN == 7 && memcmp(tok_str(p), "extends", 7) == 0)) {
cls->str = tok_ident_str(p, &cls->len);
CONSUME();
}
if (NEXT() == TOK_IDENTIFIER && TLEN == 7 && memcmp(tok_str(p), "extends", 7) == 0) {
CONSUME();
cls->left = parse_assign(p);
}
expect(p, TOK_LBRACE);
while (NEXT() != TOK_RBRACE && TOK != TOK_EOF) {
if (TOK == TOK_SEMICOLON) { CONSUME(); continue; }
uint8_t flags = 0;
if (TOK == TOK_STATIC) {
flags |= FN_STATIC;
CONSUME();
NEXT();
if (TOK == TOK_LBRACE) {
sv_ast_t *block = parse_block(p, false);
block->type = N_STATIC_BLOCK;
block->flags = FN_STATIC;
sv_ast_list_push(&cls->args, block);
continue;
}
}
sv_ast_t *method = mk(N_METHOD);
uint32_t method_src_off = (uint32_t)TOFF;
if (TOK == TOK_ASYNC && LA() != TOK_LPAREN) {
flags |= FN_ASYNC;
CONSUME();
NEXT();
}
if (TOK == TOK_MUL) {
flags |= FN_GENERATOR;
CONSUME();
NEXT();
}
if ((TLEN == 3 && memcmp(tok_str(p), "get", 3) == 0) ||
(TLEN == 3 && memcmp(tok_str(p), "set", 3) == 0)) {
uint8_t la = LA();
if (la != TOK_LPAREN) {
flags |= (tok_str(p)[0] == 'g') ? FN_GETTER : FN_SETTER;
CONSUME();
NEXT();
}
}
if (TOK == TOK_LBRACKET) {
CONSUME();
method->left = parse_assign(p);
expect(p, TOK_RBRACKET);
flags |= FN_COMPUTED;
} else if (TOK == TOK_HASH) {
CONSUME();
NEXT();
if (!is_ident_like_tok(TOK)) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "private field name expected");
return mk(N_EMPTY);
}
method->left = mk_private_ident_from_tok(p);
CONSUME();
} else {
method->left = mk_ident_from_tok(p);
CONSUME();
}
method->flags = flags;
if (NEXT() == TOK_LPAREN) {
method->right = parse_func(p);
method->right->flags |= (flags & (FN_ASYNC | FN_GENERATOR)) | FN_METHOD;
method->right->src_off = method_src_off;
} else if (TOK == TOK_ASSIGN) {
CONSUME();
method->right = parse_assign(p);
if (NEXT() == TOK_SEMICOLON) CONSUME();
} else {
method->right = mk(N_UNDEF);
if (TOK == TOK_SEMICOLON) CONSUME();
}
sv_ast_list_push(&cls->args, method);
}
expect(p, TOK_RBRACE);
return cls;
}
static sv_ast_t *parse_block(P, bool directive_ctx) {
expect(p, TOK_LBRACE);
sv_ast_t *block = mk(N_BLOCK);
sv_parse_stmt_list(p, &block->args, true, directive_ctx);
if (JS->thrown_exists) return block;
expect(p, TOK_RBRACE);
return block;
}
static sv_ast_t *parse_var_decl(P, sv_var_kind_t kind, bool allow_uninit_const) {
sv_ast_t *var = mk(N_VAR);
var->var_kind = kind;
do {
NEXT();
sv_ast_t *decl = mk(N_VARDECL);
if (TOK == TOK_LBRACKET) {
decl->left = parse_array(p);
} else if (TOK == TOK_LBRACE) {
decl->left = parse_object(p);
} else if (TOK == TOK_ERR) {
sv_parse_unexpected_token(p);
return var;
} else {
decl->left = mk_ident_from_tok(p);
sv_strict_check_binding_ident(p, decl->left->str, decl->left->len);
CONSUME();
}
if (NEXT() == TOK_ASSIGN) {
CONSUME();
decl->right = parse_assign(p);
} else if (kind == VAR_CONST && !allow_uninit_const) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Missing initializer in const declaration");
}
sv_ast_list_push(&var->args, decl);
} while (NEXT() == TOK_COMMA && (CONSUME(), 1));
return var;
}
static sv_ast_t *skip_import_stmt(P) {
while (NEXT() != TOK_SEMICOLON && TOK != TOK_EOF) CONSUME();
if (TOK == TOK_SEMICOLON) CONSUME();
return mk(N_EMPTY);
}
enum {
IMPORT_BIND_DEFAULT = 1 << 0,
IMPORT_BIND_NAMESPACE = 1 << 1,
};
static inline void import_decl_add_binding(
sv_ast_t *decl,
const char *import_name, uint32_t import_len,
const char *local_name, uint32_t local_len,
uint8_t flags
) {
sv_ast_t *spec = mk_plain(N_IMPORT_SPEC);
spec->flags = flags;
if (import_name)
spec->left = mk_ident(import_name, import_len);
spec->right = mk_ident(local_name, local_len);
sv_ast_list_push(&decl->args, spec);
}
static sv_ast_t *parse_import_stmt(P) {
static const char default_name[] = "default";
sv_ast_t *decl = mk(N_IMPORT_DECL);
if (NEXT() == TOK_STRING) {
sv_ast_t *spec = mk(N_STRING);
sv_ast_set_string(spec, sv_lexer_str_literal(&p->lx));
CONSUME();
if (NEXT() == TOK_SEMICOLON) CONSUME();
decl->right = spec;
return decl;
}
bool saw_clause = false;
if (is_ident_like_tok(NEXT())) {
saw_clause = true;
uint32_t local_len = 0;
const char *local_name = tok_ident_str(p, &local_len);
import_decl_add_binding(
decl,
default_name, (uint32_t)(sizeof(default_name) - 1),
local_name, local_len,
IMPORT_BIND_DEFAULT
);
CONSUME();
if (NEXT() == TOK_COMMA) CONSUME();
else goto parse_from;
}
if (NEXT() == TOK_MUL) {
saw_clause = true;
CONSUME();
expect(p, TOK_AS);
NEXT();
if (!is_ident_like_tok(TOK)) return skip_import_stmt(p);
uint32_t ns_len = 0;
const char *ns_name = tok_ident_str(p, &ns_len);
import_decl_add_binding(
decl,
NULL, 0,
ns_name, ns_len,
IMPORT_BIND_NAMESPACE
);
CONSUME();
} else if (NEXT() == TOK_LBRACE) {
saw_clause = true;
CONSUME();
while (NEXT() != TOK_RBRACE && TOK != TOK_EOF) {
const char *import_name;
uint32_t import_len;
if (TOK == TOK_STRING) {
sv_lex_string_t s = sv_lexer_str_literal(&p->lx);
import_name = s.str;
import_len = s.len;
CONSUME();
} else if (!(TOK >= TOK_IDENTIFIER && TOK < TOK_IDENT_LIKE_END)) {
CONSUME();
continue;
} else {
import_name = tok_ident_str(p, &import_len);
CONSUME();
}
const char *local_name = import_name;
uint32_t local_len = import_len;
if (NEXT() == TOK_AS) {
CONSUME();
NEXT();
if (!is_ident_like_tok(TOK)) return skip_import_stmt(p);
local_name = tok_ident_str(p, &local_len);
CONSUME();
}
import_decl_add_binding(decl, import_name, import_len, local_name, local_len, 0);
if (NEXT() == TOK_COMMA) {
CONSUME();
if (NEXT() == TOK_RBRACE) break;
}
}
expect(p, TOK_RBRACE);
}
parse_from:
if (!saw_clause) return skip_import_stmt(p);
expect(p, TOK_FROM);
if (NEXT() != TOK_STRING) return skip_import_stmt(p);
sv_ast_t *spec = mk(N_STRING);
sv_ast_set_string(spec, sv_lexer_str_literal(&p->lx));
CONSUME();
decl->right = spec;
if (NEXT() == TOK_SEMICOLON) CONSUME();
return decl;
}
static sv_ast_t *parse_export_name(P) {
NEXT();
if (TOK == TOK_STRING) {
sv_lex_string_t s = sv_lexer_str_literal(&p->lx);
sv_ast_t *name = mk(N_IDENT);
name->str = s.str;
name->len = s.len;
CONSUME();
return name;
}
if (!(TOK >= TOK_IDENTIFIER && TOK < TOK_IDENT_LIKE_END)) {
sv_parse_unexpected_token(p);
return NULL;
}
sv_ast_t *name = mk_ident_from_tok(p);
CONSUME();
return name;
}
static sv_ast_t *parse_export_stmt(P) {
sv_ast_t *decl = mk(N_EXPORT);
NEXT();
if (TOK == TOK_DEFAULT) {
CONSUME();
decl->flags |= EX_DEFAULT;
if (NEXT() == TOK_ASYNC && LA() == TOK_FUNC) {
uint32_t async_off = (uint32_t)TOFF;
CONSUME();
NEXT(); CONSUME();
decl->left = parse_func(p);
decl->left->flags |= FN_ASYNC;
decl->left->src_off = async_off;
if (NEXT() == TOK_SEMICOLON) CONSUME();
return decl;
}
if (TOK == TOK_FUNC) {
CONSUME();
decl->left = parse_func(p);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return decl;
}
if (TOK == TOK_CLASS) {
CONSUME();
decl->left = parse_class(p);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return decl;
}
decl->left = parse_assign(p);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return decl;
}
if (TOK == TOK_ASYNC && LA() == TOK_FUNC) {
decl->flags |= EX_DECL;
uint32_t async_off = (uint32_t)TOFF;
CONSUME();
NEXT(); CONSUME();
decl->left = parse_func(p);
decl->left->flags |= FN_ASYNC;
decl->left->src_off = async_off;
if (!decl->left->str || decl->left->len == 0)
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "exported function declarations require a name");
return decl;
}
if (TOK == TOK_FUNC) {
decl->flags |= EX_DECL;
CONSUME();
decl->left = parse_func(p);
if (!decl->left->str || decl->left->len == 0)
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "exported function declarations require a name");
return decl;
}
if (TOK == TOK_CLASS) {
decl->flags |= EX_DECL;
CONSUME();
decl->left = parse_class(p);
if (!decl->left->str || decl->left->len == 0)
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "exported class declarations require a name");
return decl;
}
if (TOK == TOK_VAR || TOK == TOK_LET || TOK == TOK_CONST) {
decl->flags |= EX_DECL;
sv_var_kind_t kind = (
TOK == TOK_VAR) ? VAR_VAR :
(TOK == TOK_LET) ? VAR_LET : VAR_CONST;
CONSUME();
decl->left = parse_var_decl(p, kind, false);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return decl;
}
if (TOK == TOK_LBRACE) {
decl->flags |= EX_NAMED;
CONSUME();
while (NEXT() != TOK_RBRACE && TOK != TOK_EOF) {
sv_ast_t *local_name = parse_export_name(p);
if (!local_name) return decl;
sv_ast_t *export_name = local_name;
if (NEXT() == TOK_AS) {
CONSUME();
export_name = parse_export_name(p);
if (!export_name) return decl;
}
sv_ast_t *spec = mk(N_IMPORT_SPEC);
spec->left = local_name;
spec->right = export_name;
sv_ast_list_push(&decl->args, spec);
if (NEXT() == TOK_COMMA) {
CONSUME();
if (NEXT() == TOK_RBRACE) break;
} else {
break;
}
}
expect(p, TOK_RBRACE);
if (NEXT() == TOK_FROM) {
CONSUME();
if (NEXT() != TOK_STRING) {
sv_parse_unexpected_token(p);
return decl;
}
sv_ast_t *spec = mk(N_STRING);
sv_ast_set_string(spec, sv_lexer_str_literal(&p->lx));
decl->right = spec;
decl->flags |= EX_FROM;
CONSUME();
}
if (NEXT() == TOK_SEMICOLON) CONSUME();
return decl;
}
if (TOK == TOK_MUL) {
decl->flags |= EX_STAR;
CONSUME();
if (NEXT() == TOK_AS) {
decl->flags |= EX_NAMESPACE;
CONSUME();
sv_ast_t *name = parse_export_name(p);
if (!name) return decl;
sv_ast_t *spec = mk(N_IMPORT_SPEC);
spec->right = name;
sv_ast_list_push(&decl->args, spec);
}
expect(p, TOK_FROM);
if (NEXT() != TOK_STRING) {
sv_parse_unexpected_token(p);
return decl;
}
sv_ast_t *spec = mk(N_STRING);
sv_ast_set_string(spec, sv_lexer_str_literal(&p->lx));
decl->right = spec;
decl->flags |= EX_FROM;
CONSUME();
if (NEXT() == TOK_SEMICOLON) CONSUME();
return decl;
}
sv_parse_unexpected_token(p);
return decl;
}
static sv_ast_t *parse_stmt(P) {
NEXT();
static const void *dispatch[TOK_MAX] = {
[TOK_SEMICOLON] = &&l_semi,
[TOK_LBRACE] = &&l_block,
[TOK_VAR] = &&l_var,
[TOK_LET] = &&l_let,
[TOK_CONST] = &&l_const,
[TOK_IF] = &&l_if,
[TOK_WHILE] = &&l_while,
[TOK_DO] = &&l_do,
[TOK_FOR] = &&l_for,
[TOK_RETURN] = &&l_return,
[TOK_THROW] = &&l_throw,
[TOK_BREAK] = &&l_break,
[TOK_CONTINUE] = &&l_continue,
[TOK_TRY] = &&l_try,
[TOK_SWITCH] = &&l_switch,
[TOK_DEBUGGER] = &&l_debugger,
[TOK_WITH] = &&l_with,
[TOK_FUNC] = &&l_func,
[TOK_CLASS] = &&l_class,
[TOK_ASYNC] = &&l_async,
[TOK_EXPORT] = &&l_export,
[TOK_IMPORT] = &&l_import,
};
if (TOK < TOK_MAX && dispatch[TOK])
goto *dispatch[TOK];
goto l_expr_stmt;
l_semi: { CONSUME(); return mk(N_EMPTY); }
l_block: return parse_block(p, false);
l_var: {
CONSUME();
sv_ast_t *n = parse_var_decl(p, VAR_VAR, false);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return n;
}
l_let: {
CONSUME();
sv_ast_t *n = parse_var_decl(p, VAR_LET, false);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return n;
}
l_const: {
CONSUME();
sv_ast_t *n = parse_var_decl(p, VAR_CONST, false);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return n;
}
l_if: {
CONSUME();
sv_ast_t *n = mk(N_IF);
expect(p, TOK_LPAREN);
n->cond = parse_expr(p);
expect(p, TOK_RPAREN);
n->left = parse_stmt(p);
if (NEXT() == TOK_ELSE) {
CONSUME();
n->right = parse_stmt(p);
}
return n;
}
l_while: {
CONSUME();
sv_ast_t *n = mk(N_WHILE);
expect(p, TOK_LPAREN);
n->cond = parse_expr(p);
expect(p, TOK_RPAREN);
n->body = parse_stmt(p);
return n;
}
l_do: {
CONSUME();
sv_ast_t *n = mk(N_DO_WHILE);
n->body = parse_stmt(p);
expect(p, TOK_WHILE);
expect(p, TOK_LPAREN);
n->cond = parse_expr(p);
expect(p, TOK_RPAREN);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return n;
}
l_for: {
CONSUME();
bool is_for_await = false;
if (NEXT() == TOK_AWAIT) {
CONSUME();
is_for_await = true;
}
expect(p, TOK_LPAREN);
sv_ast_t *init_node = NULL;
NEXT();
if (TOK == TOK_VAR || TOK == TOK_LET || TOK == TOK_CONST) {
sv_var_kind_t kind = (
TOK == TOK_VAR) ? VAR_VAR :
(TOK == TOK_LET) ? VAR_LET : VAR_CONST;
CONSUME();
p->no_in = true;
init_node = parse_var_decl(p, kind, true);
p->no_in = false;
} else if (TOK != TOK_SEMICOLON) {
p->no_in = true;
init_node = parse_expr(p);
p->no_in = false;
}
uint8_t la = NEXT();
if (la == TOK_IN) {
CONSUME();
sv_ast_t *n = mk(N_FOR_IN);
n->left = init_node;
n->right = parse_expr(p);
expect(p, TOK_RPAREN);
n->body = parse_stmt(p);
return n;
}
if (la == TOK_OF || (la == TOK_IDENTIFIER && TLEN == 2 &&
memcmp(tok_str(p), "of", 2) == 0)) {
CONSUME();
sv_ast_t *n = mk(is_for_await ? N_FOR_AWAIT_OF : N_FOR_OF);
n->left = init_node;
n->right = parse_assign(p);
expect(p, TOK_RPAREN);
n->body = parse_stmt(p);
return n;
}
if (init_node && init_node->type == N_VAR && init_node->var_kind == VAR_CONST) {
for (int i = 0; i < init_node->args.count; i++) {
sv_ast_t *decl = init_node->args.items[i];
if (decl && decl->type == N_VARDECL && !decl->right) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Missing initializer in const declaration");
return mk(N_EMPTY);
}
}
}
sv_ast_t *n = mk(N_FOR);
n->init = init_node;
expect(p, TOK_SEMICOLON);
if (NEXT() != TOK_SEMICOLON)
n->cond = parse_expr(p);
expect(p, TOK_SEMICOLON);
if (NEXT() != TOK_RPAREN)
n->update = parse_expr(p);
expect(p, TOK_RPAREN);
n->body = parse_stmt(p);
return n;
}
l_return: {
CONSUME();
sv_ast_t *n = mk(N_RETURN);
if (NEXT() != TOK_SEMICOLON && TOK != TOK_RBRACE && TOK != TOK_EOF &&
!HAD_NEWLINE)
n->right = parse_expr(p);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return n;
}
l_throw: {
CONSUME();
sv_ast_t *n = mk(N_THROW);
n->right = parse_expr(p);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return n;
}
l_break: {
CONSUME();
sv_ast_t *n = mk(N_BREAK);
if (NEXT() == TOK_IDENTIFIER && !HAD_NEWLINE) {
n->str = tok_ident_str(p, &n->len);
CONSUME();
}
if (NEXT() == TOK_SEMICOLON) CONSUME();
return n;
}
l_continue: {
CONSUME();
sv_ast_t *n = mk(N_CONTINUE);
if (NEXT() == TOK_IDENTIFIER && !HAD_NEWLINE) {
n->str = tok_ident_str(p, &n->len);
CONSUME();
}
if (NEXT() == TOK_SEMICOLON) CONSUME();
return n;
}
l_try: {
CONSUME();
sv_ast_t *n = mk(N_TRY);
n->body = parse_block(p, false);
if (NEXT() == TOK_CATCH) {
CONSUME();
if (NEXT() == TOK_LPAREN) {
CONSUME();
n->catch_param = parse_binding_pattern(p);
if (n->catch_param->type == N_IDENT)
sv_strict_check_binding_ident(p, n->catch_param->str, n->catch_param->len);
expect(p, TOK_RPAREN);
}
n->catch_body = parse_block(p, false);
}
if (NEXT() == TOK_FINALLY) {
CONSUME();
n->finally_body = parse_block(p, false);
}
return n;
}
l_switch: {
CONSUME();
sv_ast_t *n = mk(N_SWITCH);
expect(p, TOK_LPAREN);
n->cond = parse_expr(p);
expect(p, TOK_RPAREN);
expect(p, TOK_LBRACE);
while (NEXT() != TOK_RBRACE && TOK != TOK_EOF) {
sv_ast_t *c = mk(N_CASE);
if (TOK == TOK_CASE) {
CONSUME();
c->left = parse_expr(p);
} else if (TOK == TOK_DEFAULT) {
CONSUME();
}
expect(p, TOK_COLON);
while (NEXT() != TOK_CASE && TOK != TOK_DEFAULT &&
TOK != TOK_RBRACE && TOK != TOK_EOF)
sv_ast_list_push(&c->args, parse_stmt(p));
sv_ast_list_push(&n->args, c);
}
expect(p, TOK_RBRACE);
return n;
}
l_debugger: { CONSUME(); if (NEXT() == TOK_SEMICOLON) CONSUME(); return mk(N_DEBUGGER); }
l_with: {
if (p->lx.strict) {
SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "with statement not allowed in strict mode");
return mk(N_EMPTY);
}
CONSUME();
sv_ast_t *n = mk(N_WITH);
expect(p, TOK_LPAREN);
n->left = parse_expr(p);
expect(p, TOK_RPAREN);
n->body = parse_stmt(p);
return n;
}
l_func: {
CONSUME();
return parse_func(p);
}
l_class: {
CONSUME();
return parse_class(p);
}
l_async: {
uint8_t la = LA();
uint32_t async_off = (uint32_t)TOFF;
if (la == TOK_FUNC) {
CONSUME();
NEXT(); CONSUME();
sv_ast_t *fn = parse_func(p);
fn->flags |= FN_ASYNC;
fn->src_off = async_off;
return fn;
}
goto l_expr_stmt;
}
l_import: {
CONSUME();
return parse_import_stmt(p);
}
l_export: {
CONSUME();
return parse_export_stmt(p);
}
l_expr_stmt: {
if (TOK == TOK_IDENTIFIER) {
uint8_t la = LA();
if (la == TOK_COLON) {
sv_ast_t *label = mk(N_LABEL);
label->str = tok_ident_str(p, &label->len);
CONSUME();
NEXT(); CONSUME();
label->body = parse_stmt(p);
return label;
}
}
sv_ast_t *expr = parse_expr(p);
if (NEXT() == TOK_SEMICOLON) CONSUME();
return expr;
}
}
sv_ast_t *sv_parse(ant_t *js, const char *code, jsoff_t clen, bool strict){
sv_parser_t parser = { .js = js };
sv_parser_t *p = &parser;
sv_lexer_init(&p->lx, js, code, clen, strict);
sv_ast_t *program = mk(N_PROGRAM);
sv_parse_stmt_list(p, &program->args, false, true);
if (js->thrown_exists) return NULL;
if (p->lx.strict) program->flags |= FN_PARSE_STRICT;
return program;
}

File Metadata

Mime Type
text/x-c
Expires
Thu, Mar 26, 4:42 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
511988
Default Alt Text
ast.c (53 KB)

Event Timeline