Skip to content

Commit 7cb4511

Browse files
Phase G: real module resolution for import statements
`import core;` actually loads now. The interpreter searches for the named module on a configurable path, parses it, and executes its statements (registering any fn definitions in the global function table). Idempotent re-imports via a tracked HashSet. ## Resolution Search path (in order): - OMC_STDLIB_PATH env var (colon-separated) - Canonical Python OMC stdlib at Sovereign_Lattice/omninet_package/ omnicode_stdlib/ and its std/ subdir - Current dir, omc-stdlib/, omc-stdlib/std/ (project-local) Per-dir naming variants: `NAME.omc`, `NAME/init.omc`, `std/NAME.omc`. ## Dispatch priority change User-defined functions now win over built-ins. This lets `import core;` override `is_fibonacci`, `fold`, etc. with the canonical Phase 6 implementations. Previously the built-ins shadowed user-defined functions of the same name — matches Python OMC's behavior. The `alias` in `import NAME as ALIAS;` is currently informational only; imports merge into a flat function namespace. ## Verified working import core; is_fibonacci(89) -> 1 (user-defined HInt, not built-in Bool) import wave; harmonic_interfere(34,55) -> 42.02 (harmonic mean, from wave.omc) import portal; safe_divide_fold(89, 0) -> 89 (canonical singularity recovery) ## Notes The Phase 6 std/* canonical modules (`core`, `wave`, `portal`, `ica`) from the Python OMC tree are now loadable and callable from Rust OMC. This unblocks a large fraction of canonical .omc programs that previously failed at the import-statement level. The 30-file canonical compat sweep stays at 21/30 — the file that specifically tests imports (`test_modules.omc`) uses an aspirational `wave(...)` constructor call that doesn't exist anywhere in wave.omc, so it remains failing despite imports working perfectly otherwise. ## Tests 111 still passing. Nothing regressed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 5169069 commit 7cb4511

2 files changed

Lines changed: 140 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,23 @@ All notable changes to OMNIcode will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added (Phase G: real module resolution, 2026-05-13)
8+
**`import core;` actually loads now.** The interpreter searches for the named module on a search path, parses it, and executes its statements (which registers any `fn` definitions in the global function table). Idempotent re-import via an `imported_modules: HashSet<String>` tracked on the interpreter.
9+
10+
**Search path** (in order):
11+
1. `OMC_STDLIB_PATH` env var (colon-separated)
12+
2. `/home/thearchitect/Sovereign_Lattice/omninet_package/omnicode_stdlib/` — canonical Python OMC stdlib
13+
3. `/home/thearchitect/Sovereign_Lattice/omninet_package/omnicode_stdlib/std/` — Phase 6 modules
14+
4. `.`, `omc-stdlib/`, `omc-stdlib/std/` (project-local)
15+
16+
Resolution tries `NAME.omc`, `NAME/init.omc`, and `std/NAME.omc` in each dir.
17+
18+
**Dispatch priority change:** user-defined functions now win over built-ins. This lets `import core;` override `is_fibonacci`, `fold`, etc. with the canonical Phase 6 implementations. Previously the built-ins shadowed any user-defined function with the same name; matches Python OMC behavior.
19+
20+
`alias` in `import NAME as ALIAS;` is currently informational — imports merge into the flat function namespace (also matching canonical Python OMC).
21+
22+
**Verified working:** `import core; is_fibonacci(89)` returns the user-defined `1` (not the built-in `Bool(true)`). `import wave; harmonic_interfere(34, 55)` returns `42.02` from the canonical `wave.omc`. `import portal; safe_divide_fold(89, 0)` returns `89`.
23+
724
### Added (Phase F: syntax + stdlib alignment for canonical compat, 2026-05-13)
825
Pushing the Rust interpreter's compatibility with real-world canonical `.omc` programs from 4/N to **21 of 30 (70%)** in a sampled sweep.
926

omnimcode-core/src/interpreter.rs

Lines changed: 123 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use crate::ast::*;
44
use crate::value::{HInt, HArray, Value, fibonacci, is_fibonacci};
5-
use std::collections::HashMap;
5+
use std::collections::{HashMap, HashSet};
66

77
pub struct Interpreter {
88
globals: HashMap<String, Value>,
@@ -11,6 +11,8 @@ pub struct Interpreter {
1111
return_value: Option<Value>,
1212
break_flag: bool,
1313
continue_flag: bool,
14+
/// Names of modules already imported (idempotent re-import).
15+
imported_modules: HashSet<String>,
1416
}
1517

1618
impl Interpreter {
@@ -22,9 +24,83 @@ impl Interpreter {
2224
return_value: None,
2325
break_flag: false,
2426
continue_flag: false,
27+
imported_modules: HashSet::new(),
2528
}
2629
}
2730

31+
/// Module search path used by `import NAME;`.
32+
/// Honors `OMC_STDLIB_PATH` (colon-separated), then falls back to a
33+
/// small built-in list that includes the canonical Python OMC stdlib.
34+
fn module_search_path() -> Vec<std::path::PathBuf> {
35+
let mut paths = Vec::new();
36+
if let Ok(env) = std::env::var("OMC_STDLIB_PATH") {
37+
for p in env.split(':') {
38+
if !p.is_empty() {
39+
paths.push(std::path::PathBuf::from(p));
40+
}
41+
}
42+
}
43+
// Canonical Python OMC stdlib (when present on this machine).
44+
paths.push(std::path::PathBuf::from(
45+
"/home/thearchitect/Sovereign_Lattice/omninet_package/omnicode_stdlib",
46+
));
47+
paths.push(std::path::PathBuf::from(
48+
"/home/thearchitect/Sovereign_Lattice/omninet_package/omnicode_stdlib/std",
49+
));
50+
// Current working directory and a relative `omc-stdlib/`.
51+
paths.push(std::path::PathBuf::from("."));
52+
paths.push(std::path::PathBuf::from("omc-stdlib"));
53+
paths.push(std::path::PathBuf::from("omc-stdlib/std"));
54+
paths
55+
}
56+
57+
fn resolve_module(name: &str) -> Option<std::path::PathBuf> {
58+
// Try each search dir with a few naming variants.
59+
// For `import std/core;` allow the slashed form too.
60+
for dir in Self::module_search_path() {
61+
for variant in [
62+
format!("{}.omc", name),
63+
format!("{}/init.omc", name),
64+
format!("std/{}.omc", name),
65+
] {
66+
let candidate = dir.join(&variant);
67+
if candidate.is_file() {
68+
return Some(candidate);
69+
}
70+
}
71+
}
72+
None
73+
}
74+
75+
fn import_module(&mut self, name: &str) -> Result<(), String> {
76+
if self.imported_modules.contains(name) {
77+
return Ok(()); // Already loaded.
78+
}
79+
let path = Self::resolve_module(name).ok_or_else(|| {
80+
format!(
81+
"Could not resolve module `{}` (set OMC_STDLIB_PATH or place {}.omc on the search path)",
82+
name, name
83+
)
84+
})?;
85+
let source = std::fs::read_to_string(&path)
86+
.map_err(|e| format!("import {}: read failed: {}", name, e))?;
87+
// Mark as imported BEFORE executing to avoid infinite recursion on
88+
// cyclic imports.
89+
self.imported_modules.insert(name.to_string());
90+
let mut parser = crate::parser::Parser::new(&source);
91+
let stmts = parser
92+
.parse()
93+
.map_err(|e| format!("import {}: parse error: {}", name, e))?;
94+
for stmt in &stmts {
95+
self.execute_stmt(stmt)?;
96+
// Don't propagate `return` / `break` / `continue` past module boundary.
97+
self.return_value = None;
98+
self.break_flag = false;
99+
self.continue_flag = false;
100+
}
101+
Ok(())
102+
}
103+
28104
pub fn execute(&mut self, statements: Vec<Statement>) -> Result<(), String> {
29105
for stmt in statements {
30106
self.execute_stmt(&stmt)?;
@@ -197,6 +273,11 @@ impl Interpreter {
197273
self.continue_flag = true;
198274
Ok(())
199275
}
276+
Statement::Import { module, alias: _ } => {
277+
// Alias is currently informational only — imports merge into a
278+
// flat function namespace (matching canonical Python OMC).
279+
self.import_module(module)
280+
}
200281
_ => Ok(()),
201282
}
202283
}
@@ -421,6 +502,12 @@ impl Interpreter {
421502
if let Some((module, func)) = name.split_once('.') {
422503
return self.call_module_function(module, func, args);
423504
}
505+
// User-defined functions win over built-ins so that `import core;`
506+
// can override built-in implementations with the canonical Phase 6
507+
// versions. Match Python OMC behavior.
508+
if let Some((params, body)) = self.functions.get(name).cloned() {
509+
return self.invoke_user_function(name, &params, &body, args);
510+
}
424511
// Built-in functions
425512
match name {
426513
"fold" => {
@@ -1239,48 +1326,47 @@ impl Interpreter {
12391326
Err("arr_resonance: requires an array".to_string())
12401327
}
12411328
}
1242-
// User-defined functions
1243-
_ => {
1244-
if let Some((params, body)) = self.functions.get(name).cloned() {
1245-
let mut eval_args = Vec::new();
1246-
for arg in args {
1247-
eval_args.push(self.eval_expr(arg)?);
1248-
}
1249-
1250-
if params.len() != eval_args.len() {
1251-
return Err(format!(
1252-
"Function {} expects {} arguments, got {}",
1253-
name,
1254-
params.len(),
1255-
eval_args.len()
1256-
));
1257-
}
1258-
1259-
// Create new scope
1260-
self.locals.push(HashMap::new());
1261-
for (param, arg) in params.iter().zip(eval_args) {
1262-
self.set_var(param.clone(), arg);
1263-
}
1329+
// Unknown name — already checked user-defined functions at the top.
1330+
_ => Err(format!("Undefined function: {}", name)),
1331+
}
1332+
}
12641333

1265-
// Execute function body
1266-
for stmt in &body {
1267-
self.execute_stmt(stmt)?;
1268-
if self.return_value.is_some() {
1269-
break;
1270-
}
1271-
}
1334+
fn invoke_user_function(
1335+
&mut self,
1336+
name: &str,
1337+
params: &[String],
1338+
body: &[Statement],
1339+
args: &[Expression],
1340+
) -> Result<Value, String> {
1341+
let mut eval_args = Vec::new();
1342+
for arg in args {
1343+
eval_args.push(self.eval_expr(arg)?);
1344+
}
12721345

1273-
let result = self.return_value.take().unwrap_or(Value::Null);
1346+
if params.len() != eval_args.len() {
1347+
return Err(format!(
1348+
"Function {} expects {} arguments, got {}",
1349+
name,
1350+
params.len(),
1351+
eval_args.len()
1352+
));
1353+
}
12741354

1275-
// Restore scope
1276-
self.locals.pop();
1355+
self.locals.push(HashMap::new());
1356+
for (param, arg) in params.iter().zip(eval_args) {
1357+
self.set_var(param.clone(), arg);
1358+
}
12771359

1278-
Ok(result)
1279-
} else {
1280-
Err(format!("Undefined function: {}", name))
1281-
}
1360+
for stmt in body {
1361+
self.execute_stmt(stmt)?;
1362+
if self.return_value.is_some() {
1363+
break;
12821364
}
12831365
}
1366+
1367+
let result = self.return_value.take().unwrap_or(Value::Null);
1368+
self.locals.pop();
1369+
Ok(result)
12841370
}
12851371

12861372
fn get_var(&self, name: &str) -> Option<Value> {

0 commit comments

Comments
 (0)