fix(lang): a template inside another's hole, and integer literals past 2^31 - 1
A template literal nested in a {...} hole crashed the parser: the outer
literal ended at the inner backtick. The lexer (and ludic-fmt's) now
reads a hole as code, taking strings, chars and templates in it whole.
A decimal literal past 2147483647, or a hex one of more than eight
digits, was wrapped into a negative int; it is a long with its value
now, and giving one to an int is refused. Reseed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b401f7acc1
commit
afa4d23a4f
17 changed files with 47107 additions and 46056 deletions
|
|
@ -985,7 +985,10 @@ function emit_call(e: Node) -> Val {
|
|||
|
||||
function emit_expr(e: Node) -> Val {
|
||||
if (e == null) { return val("0", "int") }
|
||||
if e.kind == E_INT { return val(itoa(e.ival), "int") }
|
||||
if e.kind == E_INT {
|
||||
if e.s != null { return val(e.s, "long") } # a literal past 2^31 - 1
|
||||
return val(itoa(e.ival), "int")
|
||||
}
|
||||
if e.kind == E_FLOAT and is_float_file(e.file) {
|
||||
let ff = val(fp_lit_code(e.s, "float"), "float")
|
||||
ff.lit = e
|
||||
|
|
|
|||
|
|
@ -360,7 +360,10 @@ function static_type(e: Node) -> pointer {
|
|||
if (et != null) { return "[]" + et }
|
||||
}
|
||||
if e.kind == E_STR or e.kind == E_SLICE { return "string" }
|
||||
if e.kind == E_INT { return "int" }
|
||||
if e.kind == E_INT {
|
||||
if e.s != null { return "long" }
|
||||
return "int"
|
||||
}
|
||||
if e.kind == E_FLOAT and is_float_file(e.file) { return "float" }
|
||||
if e.kind == E_FLOAT { return "fixed" }
|
||||
if e.kind == E_BOOL { return "bool" }
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ function fp_result(a: pointer, b: pointer) -> pointer {
|
|||
# a literal-only expression (Val.lit) evaluated exactly in float type t
|
||||
function fp_const(e: Node, t: pointer) -> pointer {
|
||||
if e.kind == E_FLOAT { return fp_lit_code(e.s, t) }
|
||||
if e.kind == E_INT { return fp_lit_code(itoa(e.ival), t) }
|
||||
if e.kind == E_INT { return fp_lit_code(int_lit_code(e), t) }
|
||||
if e.kind == E_UN {
|
||||
let x = fp_const(e.a, t)
|
||||
return emit_bind(`fneg {t} {x}`)
|
||||
|
|
|
|||
|
|
@ -30,12 +30,18 @@ function emit_escaped(s: pointer) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
# an int literal's LLVM constant: its value, or a long literal's own digits
|
||||
function int_lit_code(e: Node) -> pointer {
|
||||
if e.s != null { return e.s }
|
||||
return itoa(e.ival)
|
||||
}
|
||||
# the constant initializer for a global var: a literal, or 0/null
|
||||
function global_init(d: Node) -> pointer {
|
||||
if (d.a == null) or is_fp(d.ty) { return zero_of(llty(d.ty)) } # a float's value is set by L_init_globals
|
||||
let e = d.a
|
||||
if e.kind == E_INT or e.kind == E_FLOAT or e.kind == E_BOOL { return itoa(e.ival) }
|
||||
if e.kind == E_UN and (e.s == ("-")) and e.a.kind == E_INT { return (("-") + itoa(e.a.ival)) }
|
||||
if e.kind == E_INT { return int_lit_code(e) }
|
||||
if e.kind == E_FLOAT or e.kind == E_BOOL { return itoa(e.ival) }
|
||||
if e.kind == E_UN and (e.s == ("-")) and e.a.kind == E_INT { return (("-") + int_lit_code(e.a)) }
|
||||
if e.kind == E_MEMBER and e.a.kind == E_ID { # `Enum.Variant` is a compile-time int
|
||||
let ord = enum_ordinal(e.a.s, e.s)
|
||||
if ord >= 0 { return itoa(ord) }
|
||||
|
|
|
|||
|
|
@ -51,6 +51,11 @@ function ck_err(cat: pointer, n: Node, msg: pointer) -> void {
|
|||
}
|
||||
# a value given where a type is wanted: `what` says where ("argument 2 of f", "x")
|
||||
function ck_give(to: pointer, from: pointer, e: Node, what: pointer) -> void {
|
||||
# a literal past 2^31 - 1 given to an int would lose its value: say so, rather than wrap it
|
||||
if e != null and e.kind == E_INT and e.s != null and ((to == "int") or (to == "byte") or (to == "fixed")) {
|
||||
ck_err("literal", e, `{what} wants {ck_a(to)} and {e.s} does not fit one; it is a long`)
|
||||
return
|
||||
}
|
||||
let cat = ck_mismatch(to, from, e)
|
||||
if cat == null { return }
|
||||
if (cat == "slice-pointer") {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,10 @@ function ck_expr(e: Node) -> pointer {
|
|||
let ex = ck_expect
|
||||
ck_expect = null
|
||||
let k = e.kind
|
||||
if k == E_INT { return "int" }
|
||||
if k == E_INT {
|
||||
if e.s != null { return "long" } # a literal past 2^31 - 1
|
||||
return "int"
|
||||
}
|
||||
if k == E_FLOAT {
|
||||
if is_float_file(e.file) { return "float" }
|
||||
return "fixed"
|
||||
|
|
|
|||
|
|
@ -60,6 +60,41 @@ function lex_error(line: int, c: int) -> void {
|
|||
lex_fail(line, `unexpected character '{ch}' (byte {c})`)
|
||||
}
|
||||
|
||||
# the closing backtick of the template literal that opens at s[i]: a hole's `{...}` is code, so a
|
||||
# string, a char or another template inside it is skipped whole - `a {f(`b {c}`)} d` is one literal
|
||||
function tmpl_end(s: pointer, i0: int, n: int) -> int {
|
||||
var i = i0 + 1
|
||||
while i < n and s[i] != '`' {
|
||||
if s[i] == CH_BACKSLASH { i += 2; continue }
|
||||
if s[i] == '{' and i + 1 < n and s[i + 1] == '{' { i += 2; continue }
|
||||
if s[i] == '{' {
|
||||
i = hole_end(s, i + 1, n)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return i
|
||||
}
|
||||
# the `}` that closes a hole whose code starts at s[i]; n when there is none
|
||||
function hole_end(s: pointer, i0: int, n: int) -> int {
|
||||
var i = i0
|
||||
var depth = 1
|
||||
while i < n {
|
||||
let d = s[i]
|
||||
if d == '"' or d == CH_SQUOTE {
|
||||
i += 1
|
||||
while i < n and s[i] != d { if s[i] == CH_BACKSLASH { i += 1 }; i += 1 }
|
||||
}
|
||||
else if d == '`' { i = tmpl_end(s, i, n) }
|
||||
else if d == '{' { depth += 1 }
|
||||
else if d == '}' {
|
||||
depth -= 1
|
||||
if depth == 0 { return i }
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
# report a lexical error in the standard `file:line: error: msg` shape and stop
|
||||
function lex_fail(line: int, msg: pointer) -> void {
|
||||
let m = `{g_parse_file}:{line}: error: {msg}\n`
|
||||
|
|
@ -100,11 +135,16 @@ function lex_at(src: pointer, first_line: int) -> void {
|
|||
continue
|
||||
}
|
||||
if c == '`' { # `interpolated string` — captured raw
|
||||
i += 1
|
||||
let e = tmpl_end(src, i, n)
|
||||
let out = bytes(n)
|
||||
var j = 0
|
||||
while i < n and src[i] != '`' { out[j] = src[i]; j += 1; i += 1 }
|
||||
i += 1
|
||||
while i < e {
|
||||
out[j] = src[i]
|
||||
j += 1
|
||||
i += 1
|
||||
}
|
||||
i = e + 1
|
||||
out[j] = 0
|
||||
tok_push(TK_INTERP, out, 0, line)
|
||||
continue
|
||||
|
|
@ -122,6 +162,8 @@ function lex_at(src: pointer, first_line: int) -> void {
|
|||
if char_is_digit(c) {
|
||||
if c == '0' and src[i + 1] == 'x' { # 0x hex
|
||||
var v = 0
|
||||
var lv: long = 0
|
||||
var digits = 0
|
||||
i += 2
|
||||
while i < n {
|
||||
let h = src[i]
|
||||
|
|
@ -130,14 +172,24 @@ function lex_at(src: pointer, first_line: int) -> void {
|
|||
else { if h >= 'a' and h <= 'f' { d = h - 87 }
|
||||
else { if h >= 'A' and h <= 'F' { d = h - 55 } else { break } } }
|
||||
v = v * 16 + d
|
||||
lv = lv * long(16) + long(d)
|
||||
if digits > 0 or d > 0 { digits += 1 }
|
||||
i += 1
|
||||
}
|
||||
tok_push(TK_INT, null, v, line)
|
||||
# up to 8 hex digits is a 32-bit pattern (0xFFFFFFFF is -1, as it always was); more is a long
|
||||
var big: pointer = null
|
||||
if digits > 8 { big = string(lv) }
|
||||
tok_push(TK_INT, big, v, line)
|
||||
continue
|
||||
}
|
||||
var v = 0
|
||||
var lv: long = 0
|
||||
let nstart = i
|
||||
while i < n and char_is_digit(src[i]) { v = v * 10 + (src[i] - 48); i += 1 }
|
||||
while i < n and char_is_digit(src[i]) {
|
||||
v = v * 10 + (src[i] - 48)
|
||||
lv = lv * long(10) + long(src[i] - 48)
|
||||
i += 1
|
||||
}
|
||||
# a fractional part makes it a Q16.16 fixed literal (its text is kept: in a
|
||||
# float context the literal is exactly that decimal instead)
|
||||
if i < n and src[i] == '.' and char_is_digit(src[i + 1]) {
|
||||
|
|
@ -154,7 +206,10 @@ function lex_at(src: pointer, first_line: int) -> void {
|
|||
tok_push(TK_FLOAT, src[nstart..i], bits, line)
|
||||
continue
|
||||
}
|
||||
tok_push(TK_INT, null, v, line)
|
||||
# a decimal past 2^31 - 1 does not fit an int: it is a long, and keeps its value as text
|
||||
var big: pointer = null
|
||||
if lv > long(2147483647) { big = string(lv) }
|
||||
tok_push(TK_INT, big, v, line)
|
||||
continue
|
||||
}
|
||||
if char_is_alpha(c) {
|
||||
|
|
|
|||
|
|
@ -199,19 +199,9 @@ function parse_interp(raw: pointer) -> Node {
|
|||
if lj > 0 { acc = interp_add(acc, interp_lit(lit, lj)); lj = 0 }
|
||||
i += 1
|
||||
let hs = i
|
||||
# find the hole's closing brace: braces nest, and a brace inside a
|
||||
# "string" or 'char' literal in the hole is text, not structure
|
||||
var depth = 1
|
||||
while i < n and depth > 0 {
|
||||
let d = raw[i]
|
||||
if d == '"' or d == CH_SQUOTE {
|
||||
i += 1
|
||||
while i < n and raw[i] != d { if raw[i] == CH_BACKSLASH { i += 1 }; i += 1 }
|
||||
}
|
||||
else if d == '{' { depth += 1 }
|
||||
else if d == '}' { depth -= 1; if depth == 0 { break } }
|
||||
i += 1
|
||||
}
|
||||
# find the hole's closing brace: braces nest, and a brace inside a "string", a 'char' or a
|
||||
# `template` in the hole is text, not structure (lex.ludic, hole_end)
|
||||
i = hole_end(raw, i, n)
|
||||
acc = interp_add(acc, interp_str(parse_hole(raw[hs..i])))
|
||||
i += 1 # skip the closing '}'
|
||||
} else {
|
||||
|
|
@ -259,7 +249,7 @@ function p_primary() -> Node {
|
|||
if t.kind == TK_INTERP { pi += 1; g_interp_line = t.line; return parse_interp(t.text) }
|
||||
if is_op("[") { return parse_list() }
|
||||
if t.kind == TK_ID and (t.text == "emit") and (toks[pi + 1].kind == TK_ID) { return parse_emit() }
|
||||
if t.kind == TK_INT { let n = node(E_INT); n.ival = t.ival; pi += 1; return n }
|
||||
if t.kind == TK_INT { let n = node(E_INT); n.ival = t.ival; n.s = t.text; pi += 1; return n } # s: a long literal's digits
|
||||
if t.kind == TK_FLOAT { let n = node(E_FLOAT); n.ival = t.ival; n.s = t.text; pi += 1; return n }
|
||||
if t.kind == TK_STR { let n = node(E_STR); n.s = t.text; pi += 1; return n }
|
||||
if t.kind == TK_ID {
|
||||
|
|
|
|||
46477
selfhost/ludicc.seed.ll
46477
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue