import { parse } from "../compiler/parser.js"; import { checkModule, moduleInfoFromNative } from "../compiler/checker.js"; import { NATIVE_MODULES } from "../compiler/builtins.js"; import { emitBundle } from "../compiler/codegen.js"; import { renderDiagnostic } from "../compiler/diagnostics.js"; import { writeFileSync, readFileSync } from "node:fs"; import { execFileSync } from "node:child_process"; const sources: Record = { "src/main.xan": ` import std.string import std.math import std.io import std.test struct Point { x: Int, y: Int } enum Color { Red, Green, Blue } type Shape = | Circle(r: Float) | Rect(w: Float, h: Float) schema User { name: String(minLen: 1, maxLen: 50) age: Int(min: 0, max: 150) = 0 email: String? = None } class Counter { export mut count: Int = 0 fun increment() { this.count += 1 } fun peek() -> Int { this.count } } let mut total = 0 fun area(s: Shape) -> Float { match s { Circle(r) -> 3.14159 * r * r Rect(w, h) -> w * h } } fun safeDiv(a: Int, b: Int) -> Int! { if b == 0 { raise "division by zero" } a / b } fun greet(name: String?) -> String { let n = name or "stranger" "hello, {n}" } fun falliblePair() -> (Int, String)! { let x = 5 ok((x, "ten")) } fun describe(p: Point) -> String { if p.x > 0 { "positive" } else { "non-positive" } } fun sumTo(n: Int) -> Int { let mut s = 0 for i in 1..=n { s += i } s } fun countdown() -> List { (1..=5).toList().reverse() } fun withCounter() -> Int { let c = Counter { count: 5 } c.increment() c.peek() } fun internals() -> Int { let mut m = {"a": 1, "b": 2} m["c"] = 3 (m["a"] or 0) + (m["c"] or 0) } fun joinNames() -> String { let xs = ["alice", "bob"] xs.join(", ") } test "math works" { expect(2 + 2).eq(4) expect(sumTo(100)).eq(5050) } export fun main() -> Int! { io.println("area rect: {area(Rect(2.0, 3.0))}") io.println("area circle: {area(Circle(1.0))}") let d = safeDiv(10, 3) or raise "division by zero" io.println("div: {d}") let bad = safeDiv(1, 0) match bad { Ok(v) -> io.println("bad div got {v}") Err(e) -> io.println("bad div: {e}") } io.println(greet(None)) io.println(greet(Some("bob"))) let u = User.parse("\\{\\"name\\": \\"alice\\", \\"age\\": 30\\}") match u { Ok(user) -> io.println("user: {user.name} ({user.age})") Err(e) -> io.println("user error: {e}") } let u2 = User.from({"name": "x"}) match u2 { Ok(user) -> io.println("u2: {user.name} age={user.age}") Err(e) -> io.println("u2 error: {e}") } io.println("sumTo(100) = {sumTo(100)}") io.println("countdown: {countdown()}") io.println("counter: {withCounter()}") io.println("internals: {internals()}") io.println("join: {joinNames()}") io.println("color: {Color.Green}") let f = falliblePair() match f { Ok(t) -> io.println("pair ok: {t}") Err(e) -> io.println("pair err: {e}") } io.println("done") 0 } `, }; const checkers = new Map>(); function resolveImport(ref: { kind: "std"; segments: string[] } | { kind: "rel"; path: string } | { kind: "pkg"; name: string } | { kind: "js"; name: string }): ReturnType | null { if (ref.kind === "std") { const key = "std." + ref.segments.join("."); const mod = NATIVE_MODULES[key]; if (mod) return moduleInfoFromNative(mod); return null; } if (ref.kind === "rel") { const id = ref.path.replace(/\.xan$/, ""); const src = sources[id]; if (src) { return { id, kind: "ex", name: id, exports: checkers.get(id)?.exports ?? [] }; } } return null; } const src = sources["src/main.xan"]!; const program = parse(src, "src/main.xan"); const cm = checkModule({ moduleId: "src/main", file: "src/main.xan", source: src, program, resolveImport, resolveChecked: (id) => checkers.get(id) ?? null, }); checkers.set("src/main", cm); if (cm.diagnostics.length > 0) { console.log(`FAIL: ${cm.diagnostics.length} diagnostic(s)`); for (const d of cm.diagnostics) console.log(renderDiagnostic(d)); process.exit(1); } const bundle = emitBundle(new Map([["src/main", { program, checked: cm }]]), { entry: "src/main", mode: "run" }); const outPath = "/tmp/opencode/ex-smoke2.mjs"; writeFileSync(outPath, bundle); const expected = [ "area rect: 6", "area circle: 3.14159", "div: 3", "bad div: division by zero", "hello, stranger", "hello, bob", "user: alice (30)", "u2: x age=0", "sumTo(100) = 5050", "countdown: [5, 4, 3, 2, 1]", "counter: 6", "internals: 4", "join: alice, bob", "color: Green", "pair ok: [5, ten]", "done", ].join("\n"); const stdout = execFileSync("node", [outPath], { encoding: "utf8" }); if (stdout.trim() === expected) { console.log("OK: bundle output matches"); } else { console.log("MISMATCH"); console.log("--- expected ---"); console.log(expected); console.log("--- got ---"); console.log(stdout); process.exit(1); }