better parse errors

This commit is contained in:
2026-02-10 06:51:26 -06:00
parent 3f7e34cd7a
commit 747227de40
3 changed files with 255 additions and 1 deletions

View File

@@ -9,6 +9,9 @@ var parse = function(tokens, src, filename, tokenizer) {
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
@@ -176,6 +179,9 @@ var parse = function(tokens, src, filename, tokenizer) {
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)
@@ -399,6 +405,12 @@ var parse = function(tokens, src, filename, tokenizer) {
else if (tok.kind == "eof") parse_error(tok, "unterminated method parameter list")
if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters")
fn.arity = length(params)
meth_old_cd = _control_depth
meth_old_ct = _control_type
meth_old_eb = _expecting_body
_control_depth = 0
_control_type = null
_expecting_body = false
if (tok.kind == "{") {
advance()
fn.statements = parse_block_statements()
@@ -407,6 +419,9 @@ var parse = function(tokens, src, filename, tokenizer) {
} else {
parse_error(tok, "expected '{' for method body")
}
_control_depth = meth_old_cd
_control_type = meth_old_ct
_expecting_body = meth_old_eb
fn.function_nr = function_nr
function_nr = function_nr + 1
ast_node_end(fn)
@@ -788,6 +803,9 @@ var parse = function(tokens, src, filename, tokenizer) {
var prev_names = null
var pname = null
var old_dis = 0
var old_cd = _control_depth
var old_ct = _control_type
var old_eb = _expecting_body
if (in_disruption) {
parse_error(tok, "cannot define function inside disruption clause")
@@ -834,6 +852,9 @@ var parse = function(tokens, src, filename, tokenizer) {
if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters")
node.arity = length(params)
_control_depth = 0
_control_type = null
_expecting_body = false
if (tok.kind == "{") {
advance()
stmts = parse_block_statements()
@@ -859,6 +880,9 @@ var parse = function(tokens, src, filename, tokenizer) {
}
}
_control_depth = old_cd
_control_type = old_ct
_expecting_body = old_eb
node.function_nr = function_nr
function_nr = function_nr + 1
ast_node_end(node)
@@ -875,6 +899,9 @@ var parse = function(tokens, src, filename, tokenizer) {
var expr = null
var prev_names = null
var pname = null
var old_cd = _control_depth
var old_ct = _control_type
var old_eb = _expecting_body
node.arrow = true
if (in_disruption) {
@@ -925,6 +952,9 @@ var parse = function(tokens, src, filename, tokenizer) {
advance()
}
_control_depth = 0
_control_type = null
_expecting_body = false
if (tok.kind == "{") {
advance()
stmts = parse_block_statements()
@@ -940,6 +970,9 @@ var parse = function(tokens, src, filename, tokenizer) {
node.statements = stmts
}
_control_depth = old_cd
_control_type = old_ct
_expecting_body = old_eb
node.function_nr = function_nr
function_nr = function_nr + 1
ast_node_end(node)
@@ -971,8 +1004,25 @@ var parse = function(tokens, src, filename, tokenizer) {
var elif = null
var p1_tok = null
var labeled_stmt = null
var depth = 0
var saved_ct = null
var saved_cd = 0
var saved_eb = false
if (k == "{") {
if (!_expecting_body) {
parse_error(start, "bare block '{ ... }' is not a valid statement; use a function, if, while, or for instead")
advance()
depth = 1
while (tok.kind != "eof" && depth > 0) {
if (tok.kind == "{") depth = depth + 1
else if (tok.kind == "}") depth = depth - 1
if (depth > 0) advance()
}
if (tok.kind == "}") advance()
return null
}
_expecting_body = false
node = ast_node("block", start)
advance()
stmts = parse_block_statements()
@@ -983,6 +1033,9 @@ var parse = function(tokens, src, filename, tokenizer) {
}
if (k == "var" || k == "def") {
if (_control_depth > 0) {
parse_error(start, "'" + k + "' declarations must appear at function body level, not inside '" + _control_type + "'; move this declaration before the '" + _control_type + "' statement")
}
kind_name = k
is_def = (k == "def")
advance()
@@ -1009,6 +1062,8 @@ var parse = function(tokens, src, filename, tokenizer) {
}
} else if (is_def) {
parse_error(start, "missing initializer for constant '" + var_name + "'")
} else {
parse_error(start, "'var' declarations must be initialized; use 'var " + var_name + " = null' if no value is needed")
}
ast_node_end(node)
push(decls, node)
@@ -1037,6 +1092,11 @@ var parse = function(tokens, src, filename, tokenizer) {
else parse_error(tok, "expected ')' after if condition")
then_stmts = []
node.then = then_stmts
saved_ct = _control_type
saved_cd = _control_depth
_control_type = "if"
_control_depth = _control_depth + 1
_expecting_body = true
body = parse_statement()
if (body != null) push(then_stmts, body)
else_ifs = []
@@ -1044,15 +1104,22 @@ var parse = function(tokens, src, filename, tokenizer) {
if (tok.kind == "else") {
advance()
if (tok.kind == "if") {
_control_depth = saved_cd
_control_type = saved_ct
elif = parse_statement()
if (elif != null) push(else_ifs, elif)
ast_node_end(node)
return node
} else {
else_stmts = []
node.else = else_stmts
_expecting_body = true
body = parse_statement()
if (body != null) push(else_stmts, body)
}
}
_control_depth = saved_cd
_control_type = saved_ct
ast_node_end(node)
return node
}
@@ -1068,8 +1135,15 @@ var parse = function(tokens, src, filename, tokenizer) {
else parse_error(tok, "expected ')' after while condition")
stmts = []
node.statements = stmts
saved_ct = _control_type
saved_cd = _control_depth
_control_type = "while"
_control_depth = _control_depth + 1
_expecting_body = true
body = parse_statement()
if (body != null) push(stmts, body)
_control_depth = saved_cd
_control_type = saved_ct
ast_node_end(node)
return node
}
@@ -1079,8 +1153,15 @@ var parse = function(tokens, src, filename, tokenizer) {
advance()
stmts = []
node.statements = stmts
saved_ct = _control_type
saved_cd = _control_depth
_control_type = "do"
_control_depth = _control_depth + 1
_expecting_body = true
body = parse_statement()
if (body != null) push(stmts, body)
_control_depth = saved_cd
_control_type = saved_ct
if (tok.kind == "while") advance()
else parse_error(tok, "expected 'while' after do body")
if (tok.kind == "(") advance()
@@ -1101,6 +1182,7 @@ var parse = function(tokens, src, filename, tokenizer) {
else parse_error(tok, "expected '(' after for")
if (tok.kind != ";") {
if (tok.kind == "var" || tok.kind == "def") {
parse_error(tok, "'" + tok.kind + "' declarations cannot appear in the for initializer; declare variables before the for loop")
init = parse_statement()
node.init = init
} else {
@@ -1124,8 +1206,15 @@ var parse = function(tokens, src, filename, tokenizer) {
else parse_error(tok, "expected ')' after for clauses")
stmts = []
node.statements = stmts
saved_ct = _control_type
saved_cd = _control_depth
_control_type = "for"
_control_depth = _control_depth + 1
_expecting_body = true
body = parse_statement()
if (body != null) push(stmts, body)
_control_depth = saved_cd
_control_type = saved_ct
ast_node_end(node)
return node
}