schema: ludicc --emit-schema / ludic schema, --check --diagnostics=json, and editor attributes
--emit-schema FILE writes the compiler's resolved view once the program type-checks: every
record (fields, types, defaults as written, docs, places, attributes), every registry with its
entries in their final order after the open-registry merge (key, constant, index, file:line:col
of the entry and of each field value, and which file contributed which keys), every const, and
the zero-argument functions a fn value can name. Deterministic, schema_version 1; the runtime is
left out. `ludic schema [file] [-o FILE]` wraps it.
--check --diagnostics=json prints every error as one JSON array on stdout: the checker's and the
module rules' all, a parse or lowering error as the last. Tokens and nodes now carry a column.
Fields take several @attributes; @Ref(Registry), @OneOf(PREFIX_), @Range(lo, hi), @Unit("..."),
@Asset("..."), @Color on a field and @AppendOnly / @ByKey on a registry change nothing but go into
the schema, and @Ref naming no registry is an error (every one reported). Fixtures:
examples/lang/attributes.ludic, examples/rejected/ref_unknown.ludic, cases in ludic-dev test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f8d811aa97
commit
42deb76c28
27 changed files with 1220 additions and 23 deletions
|
|
@ -11,12 +11,15 @@ const TK_EOF: int = 5
|
|||
const TK_FLOAT: int = 6
|
||||
const TK_INTERP: int = 7 # `text {expr} text` — raw content, split by the parser
|
||||
|
||||
property Tok { kind: int = 0, text: pointer = null, ival: int = 0, line: int = 0, pos: int = -1, end: int = -1 }
|
||||
property Tok { kind: int = 0, text: pointer = null, ival: int = 0, line: int = 0, pos: int = -1, end: int = -1, col: int = 0, off: int = -1, oend: int = -1 }
|
||||
# col: the token's column (1-based) on its line; off / oend: where it starts and ends in the text
|
||||
# lexed, whatever the base (a resource file's own offsets, for the schema's spans - never a rewrite)
|
||||
|
||||
# where the tokens are in their file (0.S's migration rewrites source by position): a file's own
|
||||
# text starts at 0, an interpolation hole at its place in the file, and generated code has none (-1)
|
||||
var g_lex_base: int = -1
|
||||
var lx_start: int = 0
|
||||
var lx_line0: int = 0 # where the line being lexed starts in the text
|
||||
|
||||
var toks: []Tok
|
||||
|
||||
|
|
@ -24,6 +27,8 @@ function tok_push(kind: int, text: pointer, ival: int, line: int) -> void {
|
|||
let t = new Tok
|
||||
t.kind = kind; t.text = text; t.ival = ival; t.line = line
|
||||
if g_lex_base >= 0 { t.pos = g_lex_base + lx_start }
|
||||
t.off = lx_start
|
||||
t.col = lx_start - lx_line0 + 1
|
||||
push(toks, t)
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +108,10 @@ function hole_end(s: pointer, i0: int, n: int) -> int {
|
|||
|
||||
# report a lexical error in the standard `file:line: error: msg` shape and stop
|
||||
function lex_fail(line: int, msg: pointer) -> void {
|
||||
if g_diag_json {
|
||||
diag_add(g_parse_file, line, lx_start - lx_line0 + 1, "error", msg)
|
||||
diag_exit(1)
|
||||
}
|
||||
let m = `{g_parse_file}:{line}: error: {msg}\n`
|
||||
file_write(file_stderr(), m, len(m))
|
||||
exit(1)
|
||||
|
|
@ -119,6 +128,7 @@ function lex(src: pointer) -> void {
|
|||
# re-lexed on its own, and keeps the line of the string it sits in)
|
||||
function lex_at(src: pointer, first_line: int) -> void {
|
||||
toks = new []Tok
|
||||
lx_line0 = 0
|
||||
var i = 0
|
||||
var line = first_line
|
||||
let n = len(src)
|
||||
|
|
@ -127,11 +137,12 @@ function lex_at(src: pointer, first_line: int) -> void {
|
|||
# the token the last turn pushed ends where this one starts looking
|
||||
if len(toks) > lx_n {
|
||||
if g_lex_base >= 0 { toks[len(toks) - 1].end = g_lex_base + i }
|
||||
toks[len(toks) - 1].oend = i
|
||||
lx_n = len(toks)
|
||||
}
|
||||
lx_start = i
|
||||
let c = src[i]
|
||||
if c == '\n' { tok_push(TK_NL, null, 0, line); line += 1; i += 1; continue }
|
||||
if c == '\n' { tok_push(TK_NL, null, 0, line); line += 1; i += 1; lx_line0 = i; continue }
|
||||
if c == ' ' or c == '\t' or c == '\r' { i += 1; continue }
|
||||
if c == '#' { # '#' comment to end of line
|
||||
while i < n and src[i] != '\n' { i += 1 }
|
||||
|
|
@ -255,6 +266,7 @@ function lex_at(src: pointer, first_line: int) -> void {
|
|||
lex_error(line, c)
|
||||
}
|
||||
if len(toks) > lx_n and g_lex_base >= 0 { toks[len(toks) - 1].end = g_lex_base + i }
|
||||
if len(toks) > lx_n { toks[len(toks) - 1].oend = i }
|
||||
lx_start = i
|
||||
tok_push(TK_EOF, null, 0, line)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue