feat(tools): L10 ludic-fmt enforces a project's style
lint lines in package.ludic - one_statement, max_file_lines, max_function_lines, max_comment_lines, max_header_lines, paths, baseline - checked by ludic-fmt --check <files> and ludic-fmt --lint (the project), at the line; a baseline ratchet lets a rule arrive in a codebase that breaks it (--init-baseline), lowered as it is fixed. check-impl reads the alias declarations too (it had been blind to every namespace L6 moved out of the compiler), and eight methods get their pages; a test holds the formatter to keeping type arguments together (L5). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e543525eb7
commit
334469ef61
16 changed files with 635 additions and 2 deletions
|
|
@ -239,6 +239,30 @@ function collect_impl_pairs(allsrc: pointer, pairs: []pointer) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
# {ns}.{method} pairs a namespace declares with `alias` (L6) - the engine's in
|
||||
# runtime/native/namespaces.ludic - which the compiler dispatches as surely as its own
|
||||
function collect_alias_pairs(pairs: []pointer) -> void {
|
||||
let src = read_file("runtime/native/namespaces.ludic")
|
||||
if src == null { return }
|
||||
var ns = ""
|
||||
let n = slen(src)
|
||||
var i = 0
|
||||
while i < n {
|
||||
let ln = s_trim(line_at(src, i))
|
||||
i = i + slen(line_at(src, i)) + 1
|
||||
if s_starts(ln, "namespace ") {
|
||||
var e = 10
|
||||
while e < slen(ln) and ln[e] != ' ' and ln[e] != '{' { e += 1 }
|
||||
ns = sslice(ln, 10, e)
|
||||
}
|
||||
if s_starts(ln, "alias ") and slen(ns) > 0 {
|
||||
var e = 6
|
||||
while e < slen(ln) and ln[e] != '(' and ln[e] != ' ' and ln[e] != '=' { e += 1 }
|
||||
set_add(pairs, ns + "." + sslice(ln, 6, e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# gather the docs facts: token set, and the (ns.member) pairs of ns-method pages
|
||||
function collect_docs(tokens: []pointer, docpairs: []pointer) -> void {
|
||||
let list = capture("find docs/language -name '*.md' ! -name '_section.md' 2>/dev/null")
|
||||
|
|
@ -298,6 +322,7 @@ function cmd_check_impl() -> int {
|
|||
let allsrc = read_all_selfhost()
|
||||
let impl = new []pointer
|
||||
collect_impl_pairs(allsrc, impl)
|
||||
collect_alias_pairs(impl)
|
||||
let tokens = new []pointer
|
||||
let docpairs = new []pointer
|
||||
collect_docs(tokens, docpairs)
|
||||
|
|
|
|||
|
|
@ -108,6 +108,26 @@ function cmd_test_tools() -> int {
|
|||
if shq(`{fmt} {tmp_dir()}/q.ludic | grep -q '\\[Pos, {{Foe}}\\]'`) { ok("query tags stay tight ({Foe})") } else { bad("query tags stay tight") }
|
||||
write_file(`{tmp_dir()}/al.ludic`, "program A {\n const R_A: int = 0 # one\n const R_BBB: int = 1 # two\n}\n")
|
||||
if shq(`{fmt} {tmp_dir()}/al.ludic | grep -q 'R_A: int = 0 # one'`) { ok("hand alignment preserved") } else { bad("hand alignment preserved") }
|
||||
write_file(`{tmp_dir()}/gen.ludic`, "program G {\n function f<T>(p: Pool<T>, m: Map<string, []int>) -> Pool<Pool<T>> {\n let ok = a < b and c > d\n return null\n }\n}\n")
|
||||
if shq(`{fmt} {tmp_dir()}/gen.ludic | cmp -s - {tmp_dir()}/gen.ludic`) { ok("type arguments stay together, comparisons keep their spaces (L5)") } else { bad("type arguments stay together (L5)") }
|
||||
|
||||
# --- the style rules a project states (L10) --------------------------------
|
||||
let lp = `{tmp_dir()}/lintproj`
|
||||
let lfmt = `{capture_line("pwd")}/{fmt}` # the tests below run from inside the project
|
||||
shell(`rm -rf {lp} && mkdir -p {lp}/src`)
|
||||
write_file(`{lp}/package.ludic`, "package \"t\"\nlint one_statement\nlint max_file_lines 20\nlint max_function_lines 5\nlint max_comment_lines 2\nlint max_header_lines 3\nlint paths \"src\"\nlint baseline \"lint-baseline.txt\"\n")
|
||||
write_file(`{lp}/src/a.ludic`, "# a.ludic - a file\nfunction f() -> int {\n # one\n # two\n # three\n let a = 1; return a\n}\n")
|
||||
if not shq(`cd {lp} && {lfmt} --lint > out.txt 2>&1`) and shq(`grep -q 'src/a.ludic:3: max_comment_lines' {lp}/out.txt`) and shq(`grep -q 'src/a.ludic:6: one_statement' {lp}/out.txt`) {
|
||||
ok("--lint fails a comment over the limit and two statements on a line, at their lines")
|
||||
} else { bad("--lint fails a comment over the limit and two statements on a line") }
|
||||
if shq(`cd {lp} && {lfmt} --init-baseline > /dev/null && {lfmt} --lint > /dev/null`) { ok("--init-baseline allows what is there") } else { bad("--init-baseline allows what is there") }
|
||||
write_file(`{lp}/src/b.ludic`, "function g() -> int {\n let b = 2; return b\n}\n")
|
||||
if not shq(`cd {lp} && {lfmt} --lint > /dev/null 2>&1`) { ok("a new violation fails, whatever the baseline allows elsewhere") } else { bad("a new violation fails") }
|
||||
write_file(`{lp}/src/b.ludic`, "function g() -> int { return 2 }\n")
|
||||
write_file(`{lp}/src/a.ludic`, "# a.ludic - a file\nfunction f() -> int {\n let a = 1; return a\n}\n")
|
||||
if shq(`cd {lp} && {lfmt} --lint > /dev/null && ! grep -q max_comment_lines lint-baseline.txt`) { ok("fixing one lowers the baseline, and it stays lowered") } else { bad("fixing one lowers the baseline") }
|
||||
write_file(`{lp}/src/c.ludic`, "function h() -> string { return \"a; b\" }\n")
|
||||
if shq(`cd {lp} && {lfmt} --lint > /dev/null`) { ok("a ; inside a string is not a statement") } else { bad("a ; inside a string is not a statement") }
|
||||
|
||||
# --- --check exit status, for CI and pre-commit hooks ----------------------
|
||||
let chk1 = sh(`{fmt} --check -q {tmp_dir()}/m.ludic`)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue