var parse = function(tokens, src, filename, tokenizer) { var _src_len = length(src) // ============================================================ // Parser Cursor // ============================================================ var cursor = 0 var tok = null var got_lf = false var prev_tok = null var _control_depth = 0 var _control_type = null var _expecting_body = false var advance = function() { var t = null var k = null prev_tok = tok cursor = cursor + 1 got_lf = false while (cursor < length(tokens)) { t = tokens[cursor] k = t.kind if (k == "space" || k == "comment") { cursor = cursor + 1 continue } if (k == "newline") { got_lf = true cursor = cursor + 1 continue } tok = t return null } tok = tokens[length(tokens) - 1] } var peek_ahead = function(n) { var c = cursor + 1 var count = 0 var t = null var k = null while (c < length(tokens)) { t = tokens[c] k = t.kind if (k != "space" && k != "comment" && k != "newline") { count = count + 1 if (count == n) return t } c = c + 1 } return tokens[length(tokens) - 1] } var init_cursor = function() { cursor = -1 advance() } // ============================================================ // AST Helpers // ============================================================ var errors = [] var error_count = 0 var fn_counter = 1 var ast_node = function(kind, token) { return { kind: kind, at: token.at, from_row: token.from_row, from_column: token.from_column } } var ast_node_end = function(node) { node.to_row = prev_tok.to_row node.to_column = prev_tok.to_column return node } var parse_error = function(token, msg) { if (error_count >= 5) return null error_count = error_count + 1 push(errors, { message: msg, line: token.from_row + 1, column: token.from_column + 1, offset: token.at }) } var _keywords = { "if": true, in: true, "do": true, go: true, "var": true, def: true, "for": true, "else": true, "this": true, "null": true, "true": true, "false": true, "while": true, "break": true, "return": true, "delete": true, disrupt: true, "function": true, "continue": true, disruption: true } var is_keyword = function(kind) { return _keywords[kind] == true } // ============================================================ // Expression Parsing // ============================================================ // Forward declarations via var var parse_expr = null var parse_assign_expr = null var parse_assign = null var parse_statement = null var parse_block_statements = null var parse_function_inner = null var parse_arrow_function = null var is_arrow_function = function() { if (tok.kind != "(") return false var c = cursor + 1 var depth = 1 var k = null while (c < length(tokens) && depth > 0) { k = tokens[c].kind if (k == "(") { depth = depth + 1 } else if (k == ")") { depth = depth - 1 } else if (k == "text" || k == "number") { null } c = c + 1 } while (c < length(tokens)) { k = tokens[c].kind if (k != "space" && k != "newline" && k != "comment") break c = c + 1 } if (c >= length(tokens)) return false return tokens[c].kind == "=>" } var parse_primary = function() { var start = tok var node = null var k = tok.kind var list = null var pair = null var left = null var right = null var is_ident = false var is_kw = false var p1 = null var elem = null var fn_start = null var fn = null var name_item = null var params = null var param = null var rpos = 0 var pattern_parts = null var flags_parts = null var tv = null var has_interp = false var ti = 0 var tpl_list = null var fmt_parts = null var idx = 0 var tvi = 0 var tvlen = 0 var depth = 0 var expr_parts = null var expr_str = null var tc = null var tq = null var esc_ch = null var expr_tokens = null var sub_ast = null var sub_stmt = null var sub_expr = null var meth_old_cd = 0 var meth_old_ct = null var meth_old_eb = false if (k == "number") { node = ast_node("number", start) node.value = tok.value node.number = tok.number advance() ast_node_end(node) return node } if (k == "text") { // Check for template interpolation: ${...} tv = tok.value has_interp = false ti = 0 while (ti < length(tv) - 1) { if (tv[ti] == "$" && tv[ti + 1] == "{") { if (ti == 0 || tv[ti - 1] != "\\") { has_interp = true break } } ti = ti + 1 } if (!has_interp || tokenizer == null) { node = ast_node("text", start) node.value = tok.value advance() ast_node_end(node) return node } // Template literal with interpolation node = ast_node("text literal", start) tpl_list = [] node.list = tpl_list fmt_parts = [] idx = 0 tvi = 0 tvlen = length(tv) while (tvi < tvlen) { if (tv[tvi] == "\\" && tvi + 1 < tvlen) { esc_ch = tv[tvi + 1] if (esc_ch == "n") { push(fmt_parts, "\n") } else if (esc_ch == "t") { push(fmt_parts, "\t") } else if (esc_ch == "r") { push(fmt_parts, "\r") } else if (esc_ch == "\\") { push(fmt_parts, "\\") } else if (esc_ch == "`") { push(fmt_parts, "`") } else if (esc_ch == "$") { push(fmt_parts, "$") } else if (esc_ch == "0") { push(fmt_parts, character(0)) } else { push(fmt_parts, esc_ch) } tvi = tvi + 2 } else if (tv[tvi] == "$" && tvi + 1 < tvlen && tv[tvi + 1] == "{") { tvi = tvi + 2 depth = 1 expr_parts = [] while (tvi < tvlen && depth > 0) { tc = tv[tvi] if (tc == "{") { depth = depth + 1; push(expr_parts, tc); tvi = tvi + 1 } else if (tc == "}") { depth = depth - 1 if (depth > 0) { push(expr_parts, tc) } tvi = tvi + 1 } else if (tc == "'" || tc == "\"" || tc == "`") { tq = tc push(expr_parts, tc) tvi = tvi + 1 while (tvi < tvlen && tv[tvi] != tq) { if (tv[tvi] == "\\" && tvi + 1 < tvlen) { push(expr_parts, tv[tvi]) tvi = tvi + 1 } push(expr_parts, tv[tvi]) tvi = tvi + 1 } if (tvi < tvlen) { push(expr_parts, tv[tvi]); tvi = tvi + 1 } } else { push(expr_parts, tc) tvi = tvi + 1 } } expr_str = text(expr_parts) expr_tokens = tokenizer(expr_str, "