feat(lang): L3 module scope - module, export, friend module

A barrel's 'module NAME' makes its directory a module; a declaration other modules
use says 'export'. Private use from another module is an error naming the module
and where to mark it; 'friend module' sees everything (a test harness); a file in
no module is public and a package keeps its own module. LUDIC_VIS_REPORT=1 lists
every violation instead of stopping, so a codebase can be given its exports first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-24 00:52:06 +03:00
parent 0c73287e35
commit 0677aee93f
20 changed files with 55913 additions and 54132 deletions

View file

@ -0,0 +1,37 @@
# emit_vis.ludic — L3: module scope. A file belongs to the module its own `module NAME` line or
# its importer's names (frontend/parse.ludic, module_of); a file in no module is public. A
# reference from one module to another module's function, global, record or event needs that
# declaration `export`ed, unless the referring module is a `friend module` (a test harness).
# LUDIC_VIS_REPORT=1 prints every violation as `vis: <file>: <module>.<name>` and goes on, so a
# tool can add the exports a codebase needs before the rule is switched on for it.
var g_vis_report: int = -1
function vis_reporting() -> bool {
if g_vis_report < 0 {
g_vis_report = 0
if getenv("LUDIC_VIS_REPORT") != null { g_vis_report = 1 }
}
return g_vis_report == 1
}
function vis_allowed(d: Node, from: pointer) -> bool {
if d.vis == 1 { return true }
if d.file == null { return true }
let to = module_of(d.file)
if to == "" { return true }
if from == to { return true }
return module_is_friend(from)
}
# `what` is the name as written at the reference
function vis_check(d: Node, what: pointer) -> void {
if d == null { return }
var here = g_err_file
if here == null { return }
let from = module_of(here)
if vis_allowed(d, from) { return }
let to = module_of(d.file)
if vis_reporting() {
let m = `vis: {d.file}:{itoa(d.line)}: {to}.{what} used from {here}\n`
file_write(file_stderr(), m, len(m))
return
}
perr(`{what} is private to module {to}; mark it 'export' where it is declared ({d.file})`)
}