`ludic syntax [--json] [-o FILE]` prints what `ludicc --emit-syntax` does (a line per entry, or the JSON). `ludic-dev syntax` writes, between "ludic-dev syntax: begin" / "end" lines, the keyword, type, phase and attribute tables of ludic_syntax.h, LudicVocabulary's sets (JetBrains), ludic-mode.el's lists and the language server's word tests (is_keyword_word and the rest; is_contextual_word is every word the parser does not reserve, and every declaring or modifying one), and every TextMate pattern marked "comment": "ludic-dev syntax: <group>" (shared and the VS Code copy). The grammars gain module uses port bind action reducer dispatch registry def open component prop view alias friend unsafe numbers of as from mut system; import and extern colour as declarations; the phase clause knows Overlay; the bitwise pattern matches | and ^ on their own again. `ludic-dev syntax --check` - and check-vocabulary, whose old parser comparison it replaces, and the regression suite (syntax_cases, one line per file) - fails when a written list is behind, when a grammar lacks a keyword, type or phase, when docs/language has no page for a keyword, type, phase or attribute, or when the parser (a scan of selfhost/frontend: is_id / text == words, a == / ann == attributes) tests a word or reads an attribute vocab.ludic lacks, or the reverse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
3 KiB
Text
86 lines
3 KiB
Text
# ---- ludic syntax -------------------------------------------------------------
|
|
# The language's vocabulary as the compiler holds it (`ludicc --emit-syntax`,
|
|
# selfhost/frontend/vocab.ludic): every keyword with its role and whether it is
|
|
# reserved, every declaration's form, the built-in types, the phases, every
|
|
# attribute with where it goes, what it takes and what it means, the operators and
|
|
# the literal forms. An editor's grammar is written from it (`ludic-dev syntax`).
|
|
#
|
|
# ludic syntax one line per entry: kind, name, role
|
|
# ludic syntax --json the JSON, as the compiler writes it
|
|
# ludic syntax ... -o FILE into FILE
|
|
|
|
# the vocabulary's JSON from the compiler, or null when it cannot say
|
|
function syntax_json_text() -> pointer {
|
|
ensure_ludicc()
|
|
let js = tmp_path("syntax.json")
|
|
if not shq(`{ludicc()} --emit-syntax > {sh_single(js)} 2> {tmp_path("syntax.err")}`) { return null }
|
|
let s = read_file(js)
|
|
if s == null or slen(s) == 0 or s[0] != '{' { return null }
|
|
return s
|
|
}
|
|
|
|
function cmd_syntax() -> int {
|
|
var json = false
|
|
var dest = ""
|
|
var ai = 2
|
|
while ai < arg_count() {
|
|
let a = arg(ai)
|
|
if a == "--json" { json = true }
|
|
else if a == "-o" or a == "--out" {
|
|
if ai + 1 >= arg_count() { err(`ludic syntax: {a} needs a file\n`); return 2 }
|
|
ai += 1
|
|
dest = arg(ai)
|
|
}
|
|
else {
|
|
err(`ludic syntax: unknown argument {a}\n`)
|
|
err(" usage: ludic syntax [--json] [-o FILE]\n")
|
|
return 2
|
|
}
|
|
ai += 1
|
|
}
|
|
let s = syntax_json_text()
|
|
if s == null { err("ludic syntax: the compiler wrote no vocabulary (is it older than ludic syntax?)\n"); return 1 }
|
|
var text = s
|
|
if not json { text = syntax_lines(s) }
|
|
if dest == "" { out(text); return 0 }
|
|
if not write_file(dest, text) { err(`ludic syntax: cannot write {dest}\n`); return 1 }
|
|
return 0
|
|
}
|
|
|
|
# the plain listing, `keyword module declaration`, one entry a line - read off the JSON's own
|
|
# layout (a list opens on ` "name": [`, one entry per line after it) rather than parsed
|
|
function syntax_lines(s: pointer) -> pointer {
|
|
var o = ""
|
|
var kind = ""
|
|
let n = slen(s)
|
|
var i = 0
|
|
while i < n {
|
|
let ln = line_at(s, i)
|
|
i = i + slen(ln) + 1
|
|
if s_starts(ln, " \"") and s_contains(ln, "[") {
|
|
kind = sslice(ln, 3, s_index(ln, "\"", 3) - 1) # "keywords" -> keyword
|
|
continue
|
|
}
|
|
if not s_starts(ln, " {\"") { continue }
|
|
var name = syntax_field(ln, s_index(ln, "\": ", 0) + 3)
|
|
if kind == "attribute" { name = "@" + name }
|
|
var role = ""
|
|
let r = s_index(ln, "\"role\": ", 0)
|
|
if r >= 0 { role = syntax_field(ln, r + 8) }
|
|
let f = s_index(ln, "\"form\": ", 0)
|
|
if f >= 0 { role = syntax_field(ln, f + 8) }
|
|
o = o + kind + "\t" + name + "\t" + role + "\n"
|
|
}
|
|
return o
|
|
}
|
|
# the JSON string starting at ln[q] (its opening quote), unescaped
|
|
function syntax_field(ln: pointer, q: int) -> pointer {
|
|
var o = ""
|
|
var i = q + 1
|
|
while ln[i] != 0 and ln[i] != '"' {
|
|
if ln[i] == '\\' { i += 1 }
|
|
o = o + sslice(ln, i, i + 1)
|
|
i += 1
|
|
}
|
|
return o
|
|
}
|