Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions GAPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,9 @@ Each entry should name the gap, the workaround used, and a thought on the upstre
- **Symptom:** `chr of 97` returns `"a"`, but there's no `ord of "a"` to go the other way. This makes char-class compilation awkward — without numeric codes, ranges and bitmaps are hard to encode efficiently.
- **Workaround:** Now unnecessary.
- **Upstream fix:** Shipped alongside gap #1 — `ord of s` returns byte 0..255, or -1 for empty / non-string.

### load_file cross-module write asymmetry — FILED upstream 2026-07-03 (EigenScript#373)
- **Encountered:** verifying #5 (engine internals clobbering caller globals)
- **Symptom:** whether a lib function's bare `name is` clobbers a caller global depends on whether the global was declared **before** the `load_file` (clobbered) or after (insulated). Made #5 look like a non-repro — its example used the safe order — while the before-order corrupted the engine's own state from caller globals.
- **Workaround:** `local` on every function-internal first assignment (the #5 fix, `tests/test_s9_scope.eigs`); the discipline is load-order-proof.
- **Upstream:** EigenScript#373 — remove or document the asymmetry.
28 changes: 14 additions & 14 deletions lib/regex.eigs
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ load_file of "lib/regex_vm.eigs"
# ---- re_compile: pattern → program ----
# Returns null on parse error.
define re_compile(pattern) as:
ast is regex_parse of pattern
local ast is regex_parse of pattern
if ast == null:
return null
return regex_compile_ast of ast

# ---- re_match: anchored full-string match ----
# Returns 1 if pattern matches the entire string, else 0.
define re_match(args) as:
prog is args[0]
text is args[1]
local prog is args[0]
local text is args[1]
if prog == null:
return 0
caps is regex_vm_run of [prog, text, 0, 1]
local caps is regex_vm_run of [prog, text, 0, 1]
if caps == null:
return 0
return 1
Expand All @@ -49,8 +49,8 @@ define re_match(args) as:
# Returns [start, end, g1s, g1e, g2s, g2e, ...] or null. Non-participating
# groups report -1, -1.
define re_search(args) as:
prog is args[0]
text is args[1]
local prog is args[0]
local text is args[1]
if prog == null:
return null
return regex_vm_run of [prog, text, 0, 0]
Expand All @@ -76,9 +76,9 @@ define re_replace(args) as:
break
local ms is caps[0]
local me is caps[1]
out is out + text[pos:ms] + rep
local out is out + text[pos:ms] + rep
if me > ms:
pos is me
local pos is me
else:
if me < n:
out is out + text[me:me + 1]
Expand All @@ -90,15 +90,15 @@ define re_replace(args) as:
# ---- re_find_all: all non-overlapping matches ----
# Returns a list of capture lists (same shape as re_search results).
define re_find_all(args) as:
prog is args[0]
text is args[1]
results is []
local prog is args[0]
local text is args[1]
local results is []
if prog == null:
return results
pos is 0
n is len of text
local pos is 0
local n is len of text
loop while pos <= n:
caps is regex_vm_run of [prog, text, pos, 0]
local caps is regex_vm_run of [prog, text, pos, 0]
if caps == null:
return results
append of [results, caps]
Expand Down
54 changes: 27 additions & 27 deletions lib/regex_compile.eigs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@

# ---- _emit: append one instruction and return its index ----
define _emit(prog, inst) as:
idx is len of prog
local idx is len of prog
append of [prog, inst]
return idx

# ---- _compile_node: recursively emit for an AST node ----
define _compile_node(node, prog) as:
tag is node[0]
local tag is node[0]
if tag == "lit":
_emit of [prog, ["char", node[1]]]
return 0
Expand All @@ -38,7 +38,7 @@ define _compile_node(node, prog) as:
if tag == "empty":
return 0
if tag == "concat":
children is node[1]
local children is node[1]
for i in range of (len of children):
_compile_node of [children[i], prog]
return 0
Expand Down Expand Up @@ -70,7 +70,7 @@ define _compile_node(node, prog) as:
_emit of [prog, ["assert_end"]]
return 0
if tag == "group":
idx is node[1]
local idx is node[1]
_emit of [prog, ["save", 2 * idx]]
_compile_node of [node[2], prog]
_emit of [prog, ["save", 2 * idx + 1]]
Expand All @@ -90,12 +90,12 @@ define _compile_node(node, prog) as:
# jmp L1
# L3:
define _compile_star(child, prog, greedy) as:
l1 is len of prog
split_idx is _emit of [prog, ["split", -1, -1]]
l2 is len of prog
local l1 is len of prog
local split_idx is _emit of [prog, ["split", -1, -1]]
local l2 is len of prog
_compile_node of [child, prog]
_emit of [prog, ["jmp", l1]]
l3 is len of prog
local l3 is len of prog
if greedy == 1:
prog[split_idx][1] is l2
prog[split_idx][2] is l3
Expand All @@ -114,10 +114,10 @@ define _compile_star(child, prog, greedy) as:
# split L2, L1 (try exit first)
# L2:
define _compile_plus(child, prog, greedy) as:
l1 is len of prog
local l1 is len of prog
_compile_node of [child, prog]
split_idx is _emit of [prog, ["split", -1, -1]]
l2 is len of prog
local split_idx is _emit of [prog, ["split", -1, -1]]
local l2 is len of prog
if greedy == 1:
prog[split_idx][1] is l1
prog[split_idx][2] is l2
Expand All @@ -136,10 +136,10 @@ define _compile_plus(child, prog, greedy) as:
# L1: <body>
# L2:
define _compile_quest(child, prog, greedy) as:
split_idx is _emit of [prog, ["split", -1, -1]]
l1 is len of prog
local split_idx is _emit of [prog, ["split", -1, -1]]
local l1 is len of prog
_compile_node of [child, prog]
l2 is len of prog
local l2 is len of prog
if greedy == 1:
prog[split_idx][1] is l1
prog[split_idx][2] is l2
Expand All @@ -160,27 +160,27 @@ define _compile_quest(child, prog, greedy) as:
# L_END:
# 2-way is the same with the recursion collapsed.
define _compile_alt(branches, prog) as:
n is len of branches
local n is len of branches
if n == 1:
_compile_node of [branches[0], prog]
return 0
# Emit split placeholder; first target is next pc, second is fixed up
# after we know where the rest goes.
split_idx is _emit of [prog, ["split", -1, -1]]
first_pc is len of prog
local split_idx is _emit of [prog, ["split", -1, -1]]
local first_pc is len of prog
prog[split_idx][1] is first_pc
_compile_node of [branches[0], prog]
jmp_idx is _emit of [prog, ["jmp", -1]]
rest_pc is len of prog
local jmp_idx is _emit of [prog, ["jmp", -1]]
local rest_pc is len of prog
prog[split_idx][2] is rest_pc
# Compile remaining branches as a sub-alt.
rest is []
i is 1
local rest is []
local i is 1
loop while i < n:
append of [rest, branches[i]]
i is i + 1
_compile_alt of [rest, prog]
end_pc is len of prog
local end_pc is len of prog
prog[jmp_idx][1] is end_pc
return 0

Expand All @@ -190,18 +190,18 @@ define _compile_alt(branches, prog) as:
# VM can grab both pieces atomically; null patterns still propagate as null
# from the parser, before we ever get here.
define regex_compile_ast(ast) as:
prog is []
local prog is []
_compile_node of [ast, prog]
_emit of [prog, ["match"]]
n_slots is 0
i is 0
local n_slots is 0
local i is 0
loop while i < (len of prog):
inst is prog[i]
local inst is prog[i]
if inst[0] == "save":
if inst[1] >= n_slots:
n_slots is inst[1] + 1
i is i + 1
out is {}
local out is {}
out["code"] is prog
out["n_slots"] is n_slots
return out
Loading
Loading