lua metaprogram: Delete dead code, some more lifting to duffle

This commit is contained in:
ed
2026-07-11 14:16:29 -04:00
parent 072231c46b
commit 45ac85c038
10 changed files with 94 additions and 280 deletions
+14
View File
@@ -342,6 +342,20 @@ end
-- Not normally needed since Lua state is per-process.
function M._reset_ensured_dirs() _ensured_dirs = {} end
-- Group a list of `SourceFile`-shaped records by their `dir` field.
-- Used by the annotation / static-analysis / report passes to partition sources into per-DIRECTORY (per-module) buckets
-- before emitting per-module reports. Insertion order preserved within each bucket (matches source order in `ctx.sources`).
-- @param sources table[] -- list of source records (each having a `dir` string field)
-- @return table<string, table[]> -- map of `dir` -> sources in that dir
function M.group_sources_by_dir(sources)
local by_dir = {}
for _, src in ipairs(sources) do
by_dir[src.dir] = by_dir[src.dir] or {}
table.insert(by_dir[src.dir], src)
end
return by_dir
end
-- ════════════════════════════════════════════════════════════════════════════
-- Section 4: C-language scanner primitives
-- ════════════════════════════════════════════════════════════════════════════
+16 -6
View File
@@ -1,13 +1,21 @@
--- duffle_paths.lua — Single-line bootstrap helper for the tape-atom Lua scripts.
---
--- Each entry script (ps1_meta.lua, word_count_eval.lua, and the 5 passes/*.lua files) starts with:
--- Each entry script (ps1_meta.lua + the 7 passes/*.lua files) starts with one of:
--- ```lua
--- -- Entry script (ps1_meta.lua — `arg[0]` is set):
--- local duffle = dofile((arg[0]:match("(.*[/\\])") or "./") .. "duffle_paths.lua")
---
--- -- Pass module (debug.getinfo path resolution; works both standalone and when require'd):
--- local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
--- local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
--- ```
--- That single line: (a) locates this helper via `arg[0]`,
--- (b) loads it (which sets `package.path` + `package.cpath` via `git rev-parse`),
--- (c) returns the `M` table (a wrapper around the setup function).
--- After this line, `require("duffle")` and `require("passes.X")` both resolve normally.
---
--- That small bootstrap: (a) locates this helper via `arg[0]` / `debug.getinfo`,
--- (b) loads it (which sets `package.path` + `package.cpath` via cached `git rev-parse`),
--- (c) at the bottom calls `require("duffle")` (now resolvable since `package.path` was just set) and returns the duffle M.
--- Net effect: the caller gets the duffle module in one statement; no separate `dofile(...)` + `require("duffle")` dance.
---
--- Replaces the prior 2-line (entry) or 4-line (pass) pattern that had the call site do its own path resolution + duplicated setup.
local M = {}
@@ -63,4 +71,6 @@ end
-- Run the setup as a side effect.
M.setup()
return M
-- Now that package.path includes scripts/, `require("duffle")` resolves. Return the duffle module
-- so callers can do `local duffle = dofile(...duffle_paths.lua)` in one line.
return require("duffle")
+5 -9
View File
@@ -18,10 +18,10 @@
-- Bootstrap: load `scripts/duffle_paths.lua` (sets package.path + package.cpath).
-- Uses `debug.getinfo` to find this file's own directory, so it works
-- both standalone and when require'd from the orchestrator.
local _src = debug.getinfo(1, "S").source:sub(2)
local _dir = _src:match("(.*[/\\])") or "./"
dofile(_dir .. "../duffle_paths.lua")
local duffle = require("duffle")
-- Bootstrap: load `duffle_paths.lua` via `debug.getinfo(1, "S").source` (works both standalone + when require'd).
-- duffle_paths.lua sets package.path then returns `require("duffle")` at the bottom, so the dofile value IS the duffle module.
local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
local write_file = duffle.write_file
local ensure_dir = duffle.ensure_dir
@@ -329,11 +329,7 @@ function M.run(ctx)
-- Per-DIRECTORY (per-module) aggregation. Group sources by `src.dir`,
-- validate every source in the dir, then emit ONE errors.h per dir.
local by_dir = {}
for _, src in ipairs(ctx.sources) do
by_dir[src.dir] = by_dir[src.dir] or {}
table.insert(by_dir[src.dir], src)
end
local by_dir = duffle.group_sources_by_dir(ctx.sources)
for dir, dir_sources in pairs(by_dir) do
local dir_basename = dir:match("([^/\\]+)$") or dir
+32 -28
View File
@@ -28,10 +28,10 @@
-- Bootstrap: load `scripts/duffle_paths.lua` (sets package.path + package.cpath).
-- Uses `debug.getinfo` to find this file's own directory, so it works
-- both standalone and when require'd from the orchestrator.
local _src = debug.getinfo(1, "S").source:sub(2)
local _dir = _src:match("(.*[/\\])") or "./"
dofile(_dir .. "../duffle_paths.lua")
local duffle = require("duffle")
-- Bootstrap: load `duffle_paths.lua` via `debug.getinfo(1, "S").source` (works both standalone + when require'd).
-- duffle_paths.lua sets package.path then returns `require("duffle")` at the bottom, so the dofile value IS the duffle module.
local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
local word_count_eval = require("word_count_eval")
-- ════════════════════════════════════════════════════════════════════════════
@@ -431,8 +431,8 @@ local function strip_mac_prefix(ident)
return ident
end
-- (internal) Recursive word-count lookup. `cache` is the memoization table across all calls to `compute_component_word_count`;
-- the in-progress -1 sentinel detects cycles (A -> B -> A).
-- (internal) Recursive word-count lookup. `cache` is the memoization table shared across all components
-- in a single source's `count_all_components` pass; the in-progress -1 sentinel detects cycles (A -> B -> A).
-- @param name string -- the component name (without `mac_`)
-- @param comp_by_name table<string, Component>
-- @param wc table<string, integer>
@@ -469,25 +469,25 @@ local function word_count_rec(name, comp_by_name, wc, cache)
return n
end
--- Compute the word count of a component body, accounting for macro expansion.
--- Each comma-separated entry in the body is a "slot" that contributes its own word count.
--- For most entries (regular MIPS instructions) the count is 1.
--- For `mac_Y(...)` calls, the count is the word count of `mac_Y` (recursive lookup through `components`).
--- For encoding macros with a known multi-word count (e.g. `mask_upper` = 2),
--- the count is taken from `word_counts`.
--- Compute word counts for every component in `components` in a single pass.
--- The name-lookup table + memoization cache are built ONCE (per source) instead of per-component,
--- so the cache survives across siblings and a component's recursive `mac_Y(...)` references hit memoized values
--- instead of re-walking the body. Previously each call rebuilt both tables (O(N) tables per call → O(N^2)).
---
--- The lookup is memoized via `word_count_rec` to avoid infinite recursion (e.g. if two components referenced each other).
--- This is the same algorithm as the original `tape_atom_annotation_pass.lua` (commit 7d20a4d).
--- Cycle detection (A -> B -> A) is preserved via the in-progress `-1` sentinel in `cache`.
---
--- @param c Component
--- @param components Component[]
--- @param wc table<string, integer>
--- @return integer
local function compute_component_word_count(c, components, wc)
--- @return table<string, integer> -- map of component name (without `mac_`) -> word count
local function count_all_components(components, wc)
local comp_by_name = {}
for _, cc in ipairs(components) do comp_by_name[cc.name] = cc end
local cache = {}
return word_count_rec(c.name, comp_by_name, wc, cache)
local cache = {}
local counts = {}
for _, c in ipairs(components) do
counts[c.name] = word_count_rec(c.name, comp_by_name, wc, cache)
end
return counts
end
-- ════════════════════════════════════════════════════════════════════════════
@@ -566,7 +566,7 @@ end
--- @param components Component[]
--- @param wc table<string, integer>
--- @return string[] -- list of lines for this component
local function build_component_lines(c, components, wc)
local function build_component_lines(c, counts)
local lines = {}
if c.comment and c.comment ~= "" then
@@ -577,7 +577,8 @@ local function build_component_lines(c, components, wc)
local tokens = tokens_from_body(c.body)
local sig = signature_from_args(c.args)
local n = compute_component_word_count(c, components, wc)
-- Direct lookup against the per-source precomputed `counts` table (built once by count_all_components).
local n = counts[c.name]
if n > 0 then
emit_macro_body(lines, c, sig, tokens)
@@ -640,16 +641,16 @@ end
--- @param ctx PassCtx
--- @param src SourceFile
--- @param components Component[]
--- @param counts table<string, integer> -- precomputed word counts (from count_all_components)
--- @return string|nil -- path to the written file (nil if no components)
local function emit_component_macros_h(ctx, src, components)
local function emit_component_macros_h(ctx, src, components, counts)
if #components == 0 then return nil end
local out_dir, out_path = compute_macs_h_path(src)
local lines = header_boilerplate(src)
local wc = ctx.shared.word_counts
for _, c in ipairs(components) do
for _, l in ipairs(build_component_lines(c, components, wc)) do
for _, l in ipairs(build_component_lines(c, counts)) do
lines[#lines + 1] = l
end
end
@@ -675,10 +676,11 @@ end
-- so offsets sees them without re-reading the file.
-- @param ctx PassCtx
-- @param components Component[]
local function update_shared_word_counts(ctx, components)
-- @param counts table<string, integer> -- precomputed word counts (from count_all_components)
local function update_shared_word_counts(ctx, components, counts)
local wc = ctx.shared.word_counts
for _, c in ipairs(components) do
wc["mac_" .. c.name] = compute_component_word_count(c, components, wc)
wc["mac_" .. c.name] = counts[c.name]
end
end
@@ -693,10 +695,12 @@ function M.run(ctx)
-- project_components reads from src.scan + does backward lookups on src.text
local components = project_components(src.text, src.scan)
if #components > 0 then
local macs_path = emit_component_macros_h(ctx, src, components)
-- Compute word counts for ALL components once (was: rebuilt per call inside the helpers).
local counts = count_all_components(components, ctx.shared.word_counts)
local macs_path = emit_component_macros_h(ctx, src, components, counts)
if macs_path then
outputs[#outputs + 1] = { macs_h = macs_path }
update_shared_word_counts(ctx, components)
update_shared_word_counts(ctx, components, counts)
end
end
end
+4 -4
View File
@@ -18,10 +18,10 @@
-- Bootstrap: load `scripts/duffle_paths.lua` (sets package.path + package.cpath).
-- Uses `debug.getinfo` to find this file's own directory, so it works
-- both standalone and when require'd from the orchestrator.
local _src = debug.getinfo(1, "S").source:sub(2)
local _dir = _src:match("(.*[/\\])") or "./"
dofile(_dir .. "../duffle_paths.lua")
local duffle = require("duffle")
-- Bootstrap: load `duffle_paths.lua` via `debug.getinfo(1, "S").source` (works both standalone + when require'd).
-- duffle_paths.lua sets package.path then returns `require("duffle")` at the bottom, so the dofile value IS the duffle module.
local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
local word_count_eval = require("word_count_eval")
local count_token_words = word_count_eval.count_token_words
+5 -17
View File
@@ -21,10 +21,10 @@
-- Bootstrap: load `scripts/duffle_paths.lua` (sets package.path + package.cpath).
-- Uses `debug.getinfo` to find this file's own directory, so it works
-- both standalone and when require'd from the orchestrator.
local _src = debug.getinfo(1, "S").source:sub(2)
local _dir = _src:match("(.*[/\\])") or "./"
dofile(_dir .. "../duffle_paths.lua")
local duffle = require("duffle")
-- Bootstrap: load `duffle_paths.lua` via `debug.getinfo(1, "S").source` (works both standalone + when require'd).
-- duffle_paths.lua sets package.path then returns `require("duffle")` at the bottom, so the dofile value IS the duffle module.
local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
-- ════════════════════════════════════════════════════════════════════════════
-- Constants
@@ -346,18 +346,6 @@ end
-- Orchestration helpers
-- ════════════════════════════════════════════════════════════════════════════
-- Group source files by their `dir` field. Used to mirror the per-DIRECTORY partitioning the annotation pass uses.
-- @param sources SourceFile[]
-- @return table<string, SourceFile[]> -- map of dir -> sources in that dir
local function group_sources_by_dir(sources)
local by_dir = {}
for _, src in ipairs(sources) do
by_dir[src.dir] = by_dir[src.dir] or {}
table.insert(by_dir[src.dir], src)
end
return by_dir
end
-- (internal) Validate each source in `dir_sources` via the annotation pass, tagging each result with `result.source = src.path` for downstream rendering.
-- Returns the list of module results + the flat list of all results (for the project-wide summary).
-- @param ctx PassCtx
@@ -413,7 +401,7 @@ function M.run(ctx)
local warnings = {}
local module_entries = (ctx.flags and ctx.flags._annot_results) or {}
local by_dir = group_sources_by_dir(ctx.sources)
local by_dir = duffle.group_sources_by_dir(ctx.sources)
if not ctx.dry_run then duffle.ensure_dir(ctx.out_root) end
+4 -4
View File
@@ -21,10 +21,10 @@
-- Bootstrap: load `scripts/duffle_paths.lua` (sets package.path + package.cpath).
-- Uses `debug.getinfo` to find this file's own directory, so it works
-- both standalone and when require'd from the orchestrator.
local _src = debug.getinfo(1, "S").source:sub(2)
local _dir = _src:match("(.*[/\\])") or "./"
dofile(_dir .. "../duffle_paths.lua")
local duffle = require("duffle")
-- Bootstrap: load `duffle_paths.lua` via `debug.getinfo(1, "S").source` (works both standalone + when require'd).
-- duffle_paths.lua sets package.path then returns `require("duffle")` at the bottom, so the dofile value IS the duffle module.
local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
-- ════════════════════════════════════════════════════════════════════════════
-- Type declarations
+6 -201
View File
@@ -22,10 +22,10 @@
-- Bootstrap: load `scripts/duffle_paths.lua` (sets package.path + package.cpath).
-- Uses `debug.getinfo` to find this file's own directory, so it works both standalone and when require'd from the orchestrator.
local _src = debug.getinfo(1, "S").source:sub(2)
local _dir = _src:match("(.*[/\\])") or "./"
dofile(_dir .. "../duffle_paths.lua")
local duffle = require("duffle")
-- Bootstrap: load `duffle_paths.lua` via `debug.getinfo(1, "S").source` (works both standalone + when require'd).
-- duffle_paths.lua sets package.path then returns `require("duffle")` at the bottom, so the dofile value IS the duffle module.
local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
-- ════════════════════════════════════════════════════════════════════════════
-- Constants
@@ -392,197 +392,6 @@ local function check_mac_yield_uniformity(atoms, findings)
end
end
end
-- ════════════════════════════════════════════════════════════════════════════
-- Source walkers: Binds_* structs + per-atom atom_info
-- ════════════════════════════════════════════════════════════════════════════
-- Parse the U4 fields from a Binds_X body. Returns (fields, byte_count).
local function parse_binds_fields(body)
local fields = {}
local byte_off = 0
local body_pos = 1
while body_pos <= #body do
body_pos = duffle.skip_ws_and_cmt(body, body_pos)
if body_pos > #body then break end
local type_ident, type_end = duffle.read_ident(body, body_pos)
if not type_ident then
body_pos = body_pos + 1
elseif type_ident == "U4" then
local field_ident, field_end = duffle.read_ident(body, duffle.skip_ws_and_cmt(body, type_end))
if field_ident then
fields[#fields + 1] = { name = field_ident, offset = byte_off }
byte_off = byte_off + 4
end
body_pos = field_end or (type_end + 1)
else
body_pos = type_end + 1
end
end
return fields, byte_off
end
--- Walk source-as-written, return a list of `{line, name, fields, bytes}` for every `typedef Struct_(Binds_X) { ... };` declaration.
--- Only U4 fields are tracked (Binds_* are always word arrays in this codebase pointers stored as U4, indices as U4, etc.).
--- Mirrors annotation.lua :: find_binds_structs but is independent (no shared cross-pass state for static-analysis; each pass re-walks source).
local function find_binds_structs(source_text)
local line_of = duffle.LineIndex(source_text)
local out = {}
local src_len = #source_text
local pos = 1
while pos <= src_len do
pos = duffle.skip_ws_and_cmt(source_text, pos)
if pos > src_len then break end
local pp_pos = duffle.skip_preprocessor_line(source_text, pos)
if pp_pos then pos = pp_pos; goto continue end
local ident, ident_end = duffle.read_ident(source_text, pos)
-- scan: <ident>
if not ident then pos = pos + 1; goto continue end
if ident ~= "typedef" then pos = ident_end; goto continue end
local after_typedef = duffle.skip_ws_and_cmt(source_text, ident_end)
local id2, id2_end = duffle.read_ident(source_text, after_typedef)
-- scan: typedef <id2>
if id2 ~= "Struct_" then pos = id2_end or (after_typedef + 1); goto continue end
local open_paren = duffle.skip_ws_and_cmt(source_text, id2_end)
if source_text:sub(open_paren, open_paren) ~= "(" then pos = open_paren + 1; goto continue end
local inner, after_paren = duffle.read_parens(source_text, open_paren)
-- scan: typedef Struct_(<name>)
local name = duffle.trim(inner)
local brace = duffle.scan_to_char(source_text, "{", after_paren)
-- scan: typedef Struct_(<name>) {
if not brace then pos = open_paren + 1; goto continue end
local body, after_brace = duffle.read_braces(source_text, brace)
-- scan: typedef Struct_(<name>) { <fields> }
local fields, byte_off = parse_binds_fields(body)
if name:sub(1, 6) == "Binds_" then
out[#out + 1] = { line = line_of(pos), name = name, fields = fields, bytes = byte_off }
end
pos = after_brace
::continue::
end
return out
end
-- Parse the register list from inside `atom_reads(...)` or `atom_writes(...)`.
local function parse_reg_list(sub_inner)
local regs = {}
local sub_inner_pos = 1
while sub_inner_pos <= #sub_inner do
sub_inner_pos = duffle.skip_ws_and_cmt(sub_inner, sub_inner_pos)
if sub_inner_pos > #sub_inner then break end
local reg_ident, reg_end = duffle.read_ident(sub_inner, sub_inner_pos)
if reg_ident then
regs[#regs + 1] = duffle.trim(reg_ident)
sub_inner_pos = reg_end
else
sub_inner_pos = sub_inner_pos + 1
end
if sub_inner_pos > #sub_inner then break end
if sub_inner:sub(sub_inner_pos, sub_inner_pos) == "," then sub_inner_pos = sub_inner_pos + 1 end
end
return regs
end
-- Parse the sub-calls inside `atom_info(atom_bind(...), atom_reads(...), atom_writes(...))`.
-- Returns (binds, reads, writes).
local function parse_atom_info_subcalls(info_inner)
local binds, reads, writes = nil, nil, nil
local sub_pos = 1
while sub_pos <= #info_inner do
sub_pos = duffle.skip_ws_and_cmt(info_inner, sub_pos)
if sub_pos > #info_inner then break end
local sub_ident, sub_end = duffle.read_ident(info_inner, sub_pos)
if not sub_ident then
sub_pos = sub_pos + 1
elseif sub_ident == "atom_bind" then
local sub_open = duffle.skip_ws_and_cmt(info_inner, sub_end)
if info_inner:sub(sub_open, sub_open) == "(" then
local sub_inner, sub_after2 = duffle.read_parens(info_inner, sub_open)
-- scan: atom_bind(<Binds_X>)
binds = duffle.trim(sub_inner)
sub_pos = sub_after2
else
sub_pos = sub_open + 1
end
elseif sub_ident == "atom_reads" or sub_ident == "atom_writes" then
local kind = sub_ident
local sub_open = duffle.skip_ws_and_cmt(info_inner, sub_end)
if info_inner:sub(sub_open, sub_open) == "(" then
local sub_inner, sub_after2 = duffle.read_parens(info_inner, sub_open)
-- scan: atom_reads(<regs>) OR atom_writes(<regs>)
local regs = parse_reg_list(sub_inner)
if kind == "atom_reads" then reads = regs else writes = regs end
sub_pos = sub_after2
else
sub_pos = sub_open + 1
end
else
sub_pos = sub_end
end
end
return binds, reads, writes
end
--- For every `MipsAtom_(name)` followed by `atom_info(...)`, parse the atom_info's `atom_bind(Binds_X)`, `atom_reads(...)`, and `atom_writes(...)` sub-calls.
--- Returns a list of `{atom_name, binds, reads, writes, info_line}` for use by check_abi_handoff.
local function find_atom_info(source_text)
local line_of = duffle.LineIndex(source_text)
local out = {}
local src_len = #source_text
local pos = 1
while pos <= src_len do
pos = duffle.skip_ws_and_cmt(source_text, pos)
if pos > src_len then break end
local pp_pos = duffle.skip_preprocessor_line(source_text, pos)
if pp_pos then pos = pp_pos; goto continue end
local ident, ident_end = duffle.read_ident(source_text, pos)
-- scan: <ident>
if not ident then pos = pos + 1; goto continue end
if ident ~= "MipsAtom_" then pos = ident_end; goto continue end
local open_paren = duffle.skip_ws_and_cmt(source_text, ident_end)
if source_text:sub(open_paren, open_paren) ~= "(" then pos = open_paren + 1; goto continue end
local inner, after_paren = duffle.read_parens(source_text, open_paren)
-- scan: MipsAtom_(<name>)
local name_start = 1
while name_start <= #inner and inner:sub(name_start, name_start):match("[%s]") do name_start = name_start + 1 end
local name_end = name_start
while name_end <= #inner and inner:sub(name_end, name_end):match("[%w_]") do name_end = name_end + 1 end
local atom_name = inner:sub(name_start, name_end - 1)
local lookahead = duffle.skip_ws_and_cmt(source_text, after_paren)
local look_ident, look_end = duffle.read_ident(source_text, lookahead)
-- scan: MipsAtom_(<name>) <look_ident>
if look_ident ~= "atom_info" then pos = after_paren; goto continue end
local info_open = duffle.skip_ws_and_cmt(source_text, look_end)
if source_text:sub(info_open, info_open) ~= "(" then pos = info_open + 1; goto continue end
local info_inner, info_after = duffle.read_parens(source_text, info_open)
-- scan: MipsAtom_(<name>) atom_info(<binds>, <reads>, <writes>)
local binds, reads, writes = parse_atom_info_subcalls(info_inner)
out[#out + 1] = {
atom_name = atom_name, binds = binds,
reads = reads or {}, writes = writes or {},
info_line = line_of(lookahead),
}
pos = info_after
::continue::
end
return out
end
-- ════════════════════════════════════════════════════════════════════════════
-- Check #3: ABI handoff discipline
-- ════════════════════════════════════════════════════════════════════════════
@@ -1275,13 +1084,9 @@ function M.run(ctx)
-- Aggregate per-DIRECTORY (per-module). One static_analysis.txt per source-directory, emitted only if the directory contains at least one atom.
-- Empty-source directories (e.g. duffle headers with no atoms) produce no report.
--
-- Group sources by `src.dir`. The first component of `dir` is the module name (e.g. "code/duffle" -> "duffle", "code/gte_hello" -> "gte_hello").
-- Group sources by `src.dir`. The first component of `dir` is the module name (e.g. "code/duffle" -> "duffle", "code/gte_hello" -> "gte_hello").
-- Output path is `<out_root>/<module_basename>.static_analysis.txt`.
local by_dir = {}
for _, src in ipairs(ctx.sources) do
by_dir[src.dir] = by_dir[src.dir] or {}
table.insert(by_dir[src.dir], src)
end
local by_dir = duffle.group_sources_by_dir(ctx.sources)
for dir, dir_sources in pairs(by_dir) do
-- Run validate() against every source in this directory; accumulate atoms / findings / errors / warnings.
+4 -4
View File
@@ -20,10 +20,10 @@
-- Bootstrap: see `ps1_meta.lua` for the rationale.
-- Bootstrap: load `scripts/duffle_paths.lua` (sets package.path + package.cpath).
-- Uses `debug.getinfo` to find this file's own directory, so it works both standalone and when require'd from the orchestrator.
local _src = debug.getinfo(1, "S").source:sub(2)
local _dir = _src:match("(.*[/\\])") or "./"
dofile(_dir .. "../duffle_paths.lua")
local duffle = require("duffle")
-- Bootstrap: load `duffle_paths.lua` via `debug.getinfo(1, "S").source` (works both standalone + when require'd).
-- duffle_paths.lua sets package.path then returns `require("duffle")` at the bottom, so the dofile value IS the duffle module.
local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
-- ════════════════════════════════════════════════════════════════════════════
-- Constants
+4 -7
View File
@@ -19,13 +19,10 @@
-- Module-scope requires + package.path setup
-- ════════════════════════════════════════════════════════════════════════════
-- Resolve `arg[0]` to an absolute-ish script directory so that `require("duffle")` resolves against `scripts/` regardless of CWD.
-- Note: this boilerplate is duplicated in 6 other entry scripts; a
-- Phase-6 extraction target (`duffle.setup_package_path()`).
-- Bootstrap: load `duffle_paths.lua` (uses `git rev-parse` to find the repo root, then sets package.path + package.cpath).
-- After this line, `require("duffle")` and `require("passes.X")` both resolve.
dofile((arg[0]:match("(.*[/\\])") or "./") .. "duffle_paths.lua")
local duffle = require("duffle")
-- Bootstrap: load `duffle_paths.lua` via `arg[0]` (this script's own path).
-- That single statement: (a) sets `package.path` + `package.cpath` (via cached `git rev-parse`),
-- (b) at the bottom returns `require("duffle")`. So the dofile's return value is the duffle module.
local duffle = dofile((arg[0]:match("(.*[/\\])") or "./") .. "duffle_paths.lua")
-- ════════════════════════════════════════════════════════════════════════════
-- Constants