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:
Orkun ÇAKILKAYA 2026-09-25 08:10:46 +03:00
parent b401f7acc1
commit afa4d23a4f
17 changed files with 47107 additions and 46056 deletions

View file

@ -835,6 +835,9 @@ function cmd_dev_test() -> int {
reject_case("rejected/generic_mismatch", "words wants a Pool<string> and this is a Pool<int>", "two instances of one generic are two types")
reject_case("rejected/generic_arity", "Pool takes 1 type argument(s) and Pool<int, string> gives 2", "a generic takes as many type arguments as it has parameters")
reject_case("rejected/private_generic", "grow is private to module kit", "an instance of a private generic is private to its module")
feat_case("lang/long_literals", "", "4294967295 3988292384 -4294967296 1099511627776 -3750763034362895579 -1 1", "long_literals.ludic (a decimal literal past 2^31 - 1, and a hex literal of more than eight digits, is a long)")
reject_case("rejected/long_into_int", "4294967295 does not fit one; it is a long", "a long literal given to an int is refused, not wrapped")
feat_case("lang/nested_templates", "", "outer [inner 3 {x}] \"`}\" end", "nested_templates.ludic (a template literal inside another's hole)")
feat_case("lang/aliases", "", "10 5 3", "aliases.ludic (L6: a namespace method declared as an alias of a function, labelled or by its parameters)")
reject_case("rejected/alias_arity", "this call to Trail.length leaves out to, which has no default", "an alias's arguments are checked against its target")
reject_case("rejected/alias_twice", "Trail.length is declared twice", "a namespace method is declared once")

View file

@ -136,6 +136,38 @@ program LudicFmt {
return c == '&' or c == '|' or c == '^' or c == '~' # & | ^ ~
}
# the backtick that closes the template opening at s[i0] (or the end): a `{...}` hole is code, so
# a string, a char or another template inside it is skipped whole - as the compiler reads it
function fmt_tmpl_end(s: pointer, i0: int) -> int {
var i = i0 + 1
while s[i] != 0 and s[i] != '`' {
if s[i] == '\\' and s[i + 1] != 0 { i += 2; continue }
if s[i] == '{' and s[i + 1] == '{' { i += 2; continue }
if s[i] == '{' { i = fmt_hole_end(s, i + 1) }
if s[i] != 0 { i += 1 }
}
return i
}
function fmt_hole_end(s: pointer, i0: int) -> int {
var i = i0
var depth = 1
while s[i] != 0 {
let d = s[i]
if d == '"' or d == '\'' {
i += 1
while s[i] != 0 and s[i] != d { if s[i] == '\\' and s[i + 1] != 0 { i += 1 }; i += 1 }
}
else if d == '`' { i = fmt_tmpl_end(s, i) }
else if d == '{' { depth += 1 }
else if d == '}' {
depth -= 1
if depth == 0 { return i }
}
if s[i] != 0 { i += 1 }
}
return i
}
function lex(s: pointer) -> void {
src = s
tk_kind = new []int; tk_start = new []int; tk_end = new []int; tk_line = new []int
@ -155,9 +187,9 @@ program LudicFmt {
if s[i] == '"' { i += 1 }
push_tok(LT_STR, st, i, line); continue
}
if c == '`' { # `interpolated`
let st = i; i += 1
while s[i] != 0 and s[i] != '`' { if s[i] == '\\' and s[i + 1] != 0 { i += 2 } else { i += 1 } }
if c == '`' { # `interpolated`, holes and all
let st = i
i = fmt_tmpl_end(s, i)
if s[i] == '`' { i += 1 }
push_tok(LT_STR, st, i, line); continue
}