def CP_SLASH = 47 def CP_BSLASH = 92 var is_alpha = function(c) { return (c >= 65 && c <= 90) || (c >= 97 && c <= 122) } var parse = function(tokens, src, filename, tokenizer) { var _src_len = length(src) var cp = [] var _i = 0 while (_i < _src_len) { push(cp, codepoint(src[_i])) _i = _i + 1 } // ============================================================ // Parser Cursor // ============================================================ var cursor = 0 var tok = null var got_lf = false var prev_tok = null 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 function_nr = 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 is_keyword = function(kind) { return kind == "if" || kind == "in" || kind == "do" || kind == "go" || kind == "var" || kind == "def" || kind == "for" || kind == "else" || kind == "this" || kind == "null" || kind == "true" || kind == "false" || kind == "while" || kind == "break" || kind == "return" || kind == "delete" || kind == "disrupt" || kind == "function" || kind == "continue" || kind == "disruption" } // ============================================================ // 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_str = "" var flags = "" var tv = null var has_interp = false var ti = 0 var tpl_list = null var fmt = null var idx = 0 var tvi = 0 var tvlen = 0 var depth = 0 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 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 = "" 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") { fmt = fmt + "\n" } else if (esc_ch == "t") { fmt = fmt + "\t" } else if (esc_ch == "r") { fmt = fmt + "\r" } else if (esc_ch == "\\") { fmt = fmt + "\\" } else if (esc_ch == "`") { fmt = fmt + "`" } else if (esc_ch == "$") { fmt = fmt + "$" } else if (esc_ch == "0") { fmt = fmt + character(0) } else { fmt = fmt + esc_ch } tvi = tvi + 2 } else if (tv[tvi] == "$" && tvi + 1 < tvlen && tv[tvi + 1] == "{") { tvi = tvi + 2 depth = 1 expr_str = "" while (tvi < tvlen && depth > 0) { tc = tv[tvi] if (tc == "{") { depth = depth + 1; expr_str = expr_str + tc; tvi = tvi + 1 } else if (tc == "}") { depth = depth - 1 if (depth > 0) { expr_str = expr_str + tc } tvi = tvi + 1 } else if (tc == "'" || tc == "\"" || tc == "`") { tq = tc expr_str = expr_str + tc tvi = tvi + 1 while (tvi < tvlen && tv[tvi] != tq) { if (tv[tvi] == "\\" && tvi + 1 < tvlen) { expr_str = expr_str + tv[tvi] tvi = tvi + 1 } expr_str = expr_str + tv[tvi] tvi = tvi + 1 } if (tvi < tvlen) { expr_str = expr_str + tv[tvi]; tvi = tvi + 1 } } else { expr_str = expr_str + tc tvi = tvi + 1 } } expr_tokens = tokenizer(expr_str, "