Compare commits
34
Commits
main
...
8ac59dfaa1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ac59dfaa1 | ||
|
|
74d7376039 | ||
|
|
a4ec5656d2 | ||
|
|
b43042c88d | ||
|
|
a77eade06f | ||
|
|
89d4bae8db | ||
|
|
02095524b1 | ||
|
|
da9dae734c | ||
|
|
a2d5dd9ce8 | ||
|
|
7c5bb41e09 | ||
|
|
459b6188a9 | ||
|
|
21f35b30fa | ||
|
|
ab63c8ed90 | ||
|
|
f7f71c5f52 | ||
|
|
b67edc5cee | ||
|
|
1ceda90207 | ||
|
|
9b9b25ce0f | ||
|
|
118c153280 | ||
|
|
f1b4225c92 | ||
|
|
53ae30f1ca | ||
|
|
147f26e063 | ||
|
|
463177d3be | ||
|
|
1df3e3bcb4 | ||
|
|
3cf11f922e | ||
|
|
69cea030dc | ||
|
|
0892c084ee | ||
|
|
ad473f245c | ||
|
|
0182b8ed5c | ||
|
|
777b6b42d1 | ||
|
|
5dadf6d6ee | ||
|
|
67c8a137dd | ||
|
|
e2e0ebc21f | ||
|
|
51d8510b79 | ||
|
|
50b07074fb |
No files matched your search
@@ -2,3 +2,11 @@
|
|||||||
!.gitignore
|
!.gitignore
|
||||||
|
|
||||||
build/
|
build/
|
||||||
|
|
||||||
|
*.sir
|
||||||
|
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
@@ -0,0 +1,417 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""SPL build system — .c → .o → exe, .spl → .sir, deps auto-resolved."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parent
|
||||||
|
BUILD_DIR = ROOT / "build"
|
||||||
|
CACHE_FILE = BUILD_DIR / ".build_cache.json"
|
||||||
|
DESC_FILE = ROOT / "project_desc.py"
|
||||||
|
EXE_EXT = ".exe" if os.name == "nt" else ""
|
||||||
|
|
||||||
|
CC = os.environ.get("CC", "gcc")
|
||||||
|
CFLAGS = os.environ.get("CFLAGS", "-Wall -Wextra -O0 -g").split()
|
||||||
|
|
||||||
|
def load_desc():
|
||||||
|
ns = {"__builtins__": __builtins__}
|
||||||
|
exec(DESC_FILE.read_text(), ns)
|
||||||
|
return {
|
||||||
|
"exe": ns.get("exe", {}),
|
||||||
|
"spl": ns.get("spl", {}),
|
||||||
|
"pipeline": ns.get("pipeline", {}),
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def obj_key(src):
|
||||||
|
return src.rsplit(".", 1)[0].replace("/", "_").replace("\\", "_")
|
||||||
|
|
||||||
|
def obj_path(src):
|
||||||
|
return BUILD_DIR / (obj_key(src) + ".o")
|
||||||
|
|
||||||
|
def exe_path(name):
|
||||||
|
return BUILD_DIR / (name + EXE_EXT)
|
||||||
|
|
||||||
|
def sir_path(name):
|
||||||
|
return BUILD_DIR / (name + ".sir")
|
||||||
|
|
||||||
|
# ── Cache ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def load_cache():
|
||||||
|
if CACHE_FILE.is_file():
|
||||||
|
try:
|
||||||
|
return json.loads(CACHE_FILE.read_text())
|
||||||
|
except (json.JSONDecodeError, OSError):
|
||||||
|
pass
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def save_cache(cache):
|
||||||
|
BUILD_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
CACHE_FILE.write_text(json.dumps(cache, indent=2))
|
||||||
|
|
||||||
|
def hash_file(p):
|
||||||
|
h = hashlib.sha256()
|
||||||
|
with p.open("rb") as f:
|
||||||
|
while chunk := f.read(65536):
|
||||||
|
h.update(chunk)
|
||||||
|
return h.hexdigest()
|
||||||
|
|
||||||
|
# ── C compilation ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
_built_objs = set()
|
||||||
|
|
||||||
|
def compile_c(src, cache, force):
|
||||||
|
key = f"obj:{src}"
|
||||||
|
dst = obj_path(src)
|
||||||
|
src_path = ROOT / src
|
||||||
|
|
||||||
|
if not src_path.is_file():
|
||||||
|
print(f" ERR {src} not found"); return False
|
||||||
|
|
||||||
|
h = hashlib.sha256()
|
||||||
|
h.update(hash_file(src_path).encode())
|
||||||
|
h.update(" ".join(CFLAGS).encode())
|
||||||
|
ch = h.hexdigest()
|
||||||
|
|
||||||
|
cached = cache.get(key)
|
||||||
|
if not force and cached and cached["h"] == ch and dst.is_file():
|
||||||
|
_built_objs.add(src); return True
|
||||||
|
|
||||||
|
BUILD_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
cmd = [CC] + CFLAGS + ["-c", str(src_path), "-o", str(dst)]
|
||||||
|
print(f" CC {src}")
|
||||||
|
if subprocess.run(cmd).returncode: return False
|
||||||
|
cache[key] = {"h": ch}
|
||||||
|
_built_objs.add(src)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def link_exe(name, sources, cache, force):
|
||||||
|
key = f"exe:{name}"
|
||||||
|
dst = exe_path(name)
|
||||||
|
|
||||||
|
# hash = concat of all input obj hashes + name
|
||||||
|
h = hashlib.sha256(name.encode())
|
||||||
|
for src in sources:
|
||||||
|
cached = cache.get(f"obj:{src}", {})
|
||||||
|
h.update(cached.get("h", "?").encode())
|
||||||
|
ch = h.hexdigest()
|
||||||
|
|
||||||
|
cached = cache.get(key)
|
||||||
|
if not force and cached and cached["h"] == ch and dst.is_file():
|
||||||
|
return True
|
||||||
|
|
||||||
|
cmd = [CC] + CFLAGS + [str(obj_path(s)) for s in sources] + ["-o", str(dst)]
|
||||||
|
print(f" LINK {name}")
|
||||||
|
if subprocess.run(cmd).returncode: return False
|
||||||
|
cache[key] = {"h": ch}
|
||||||
|
return True
|
||||||
|
|
||||||
|
# ── SPL compilation ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def find_imports(src_path):
|
||||||
|
"""Scan source for @import("path") directives, return resolved paths."""
|
||||||
|
deps = []
|
||||||
|
try:
|
||||||
|
text = src_path.read_text(errors="replace")
|
||||||
|
except OSError:
|
||||||
|
return deps
|
||||||
|
idx = 0
|
||||||
|
while True:
|
||||||
|
idx = text.find('@import("', idx)
|
||||||
|
if idx < 0:
|
||||||
|
break
|
||||||
|
start = idx + 9
|
||||||
|
end = text.find('")', start)
|
||||||
|
if end < 0:
|
||||||
|
break
|
||||||
|
imp_path = text[start:end]
|
||||||
|
imp_file = (src_path.parent / imp_path).resolve()
|
||||||
|
if imp_file.is_file():
|
||||||
|
deps.append(imp_file)
|
||||||
|
idx = end + 2
|
||||||
|
return deps
|
||||||
|
|
||||||
|
def collect_deps(src_path):
|
||||||
|
"""Collect all transitive @import dependencies."""
|
||||||
|
seen = set()
|
||||||
|
result = []
|
||||||
|
def walk(p):
|
||||||
|
p = p.resolve()
|
||||||
|
if p in seen:
|
||||||
|
return
|
||||||
|
seen.add(p)
|
||||||
|
for dep in find_imports(p):
|
||||||
|
walk(dep)
|
||||||
|
result.append(dep)
|
||||||
|
walk(src_path)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def compile_spl(name, src, compiler, desc, cache, force):
|
||||||
|
key = f"spl:{name}"
|
||||||
|
dst = sir_path(name)
|
||||||
|
src_path = ROOT / src
|
||||||
|
|
||||||
|
if not src_path.is_file():
|
||||||
|
print(f" ERR {src} not found"); return False
|
||||||
|
|
||||||
|
h = hashlib.sha256()
|
||||||
|
h.update(hash_file(src_path).encode())
|
||||||
|
|
||||||
|
# Hash @import dependencies (transitive)
|
||||||
|
for dep in collect_deps(src_path):
|
||||||
|
h.update(hash_file(dep).encode())
|
||||||
|
|
||||||
|
# Determine how to run the compiler
|
||||||
|
if compiler in desc.get("spl", {}):
|
||||||
|
# SPL-based compiler: run via VM runner
|
||||||
|
cinfo = desc["spl"][compiler]
|
||||||
|
pl = desc.get("pipeline", {}).get(cinfo.get("pipeline", ""), {})
|
||||||
|
runner = pl.get("runner") or cinfo.get("runner", "spl_cli")
|
||||||
|
runner_exe = exe_path(runner)
|
||||||
|
sir = sir_path(compiler)
|
||||||
|
if runner_exe.is_file():
|
||||||
|
h.update(hash_file(runner_exe).encode())
|
||||||
|
if os.path.isfile(sir):
|
||||||
|
h.update(hash_file(Path(sir)).encode())
|
||||||
|
ch = h.hexdigest()
|
||||||
|
cached = cache.get(key)
|
||||||
|
if not force and cached and cached["h"] == ch and dst.is_file():
|
||||||
|
return True
|
||||||
|
BUILD_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
cmd = [str(runner_exe), str(sir), str(src_path), str(dst)]
|
||||||
|
print(f" SPL {name} ({compiler} via {runner})")
|
||||||
|
else:
|
||||||
|
# Native exe compiler
|
||||||
|
ce = exe_path(compiler)
|
||||||
|
if ce.is_file():
|
||||||
|
h.update(hash_file(ce).encode())
|
||||||
|
ch = h.hexdigest()
|
||||||
|
cached = cache.get(key)
|
||||||
|
if not force and cached and cached["h"] == ch and dst.is_file():
|
||||||
|
return True
|
||||||
|
BUILD_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
cmd = [str(ce), str(src_path), str(dst)]
|
||||||
|
print(f" SPL {name} ({compiler})")
|
||||||
|
|
||||||
|
if subprocess.run(cmd).returncode: return False
|
||||||
|
cache[key] = {"h": ch}
|
||||||
|
return True
|
||||||
|
|
||||||
|
# ── Dependency-aware build ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
_built = set()
|
||||||
|
|
||||||
|
def build(name, desc, cache, force):
|
||||||
|
"""Build target + all deps (auto-resolved DAG traversal)."""
|
||||||
|
if name in _built:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if name in desc.get("exe", {}):
|
||||||
|
sources = desc["exe"][name]
|
||||||
|
for src in sources:
|
||||||
|
if not compile_c(src, cache, force):
|
||||||
|
return False
|
||||||
|
if not link_exe(name, sources, cache, force):
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif name in desc.get("spl", {}):
|
||||||
|
info = desc["spl"][name]
|
||||||
|
pl = desc.get("pipeline", {}).get(info.get("pipeline", ""), {})
|
||||||
|
compiler = pl.get("compiler") or info.get("compiler")
|
||||||
|
if not compiler:
|
||||||
|
print(f" ERR '{name}': no compiler in pipeline '{info.get('pipeline')}'")
|
||||||
|
return False
|
||||||
|
if not build(compiler, desc, cache, force):
|
||||||
|
return False
|
||||||
|
if not compile_spl(name, info["src"], compiler, desc, cache, force):
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(f" ERR unknown target '{name}'")
|
||||||
|
return False
|
||||||
|
|
||||||
|
_built.add(name)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def build_all(desc, cache, force):
|
||||||
|
targets = list(desc.get("exe", {})) + list(desc.get("spl", {}))
|
||||||
|
for t in targets:
|
||||||
|
if not build(t, desc, cache, force):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
# ── Run ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def run_target(name, desc, cache, force, extra_args):
|
||||||
|
if name in desc.get("exe", {}):
|
||||||
|
if not build(name, desc, cache, force):
|
||||||
|
return 1
|
||||||
|
exe = exe_path(name)
|
||||||
|
print(f" RUN {name}")
|
||||||
|
return subprocess.run([str(exe)] + extra_args).returncode
|
||||||
|
|
||||||
|
if name in desc.get("spl", {}):
|
||||||
|
info = desc["spl"][name]
|
||||||
|
pl = desc.get("pipeline", {}).get(info.get("pipeline", ""), {})
|
||||||
|
runner = pl.get("runner") or info.get("runner", "spl_cli")
|
||||||
|
if not runner:
|
||||||
|
print(f" ERR '{name}': no runner"); return 1
|
||||||
|
if not build(name, desc, cache, force):
|
||||||
|
return 1
|
||||||
|
if not build(runner, desc, cache, force):
|
||||||
|
return 1
|
||||||
|
print(f" RUN {name} (via {runner})")
|
||||||
|
return subprocess.run(
|
||||||
|
[str(exe_path(runner)), str(sir_path(name))] + extra_args
|
||||||
|
).returncode
|
||||||
|
|
||||||
|
if name in desc.get("pipeline", {}):
|
||||||
|
pl = desc["pipeline"][name]
|
||||||
|
compiler = pl.get("compiler")
|
||||||
|
runner = pl.get("runner")
|
||||||
|
if not compiler or not runner:
|
||||||
|
print(f" ERR pipeline '{name}' missing compiler or runner"); return 1
|
||||||
|
if not extra_args:
|
||||||
|
print(" ERR usage: run <pipeline> <src> [args]"); return 1
|
||||||
|
src = extra_args[0]
|
||||||
|
|
||||||
|
if not build(compiler, desc, cache, force): return 1
|
||||||
|
if not build(runner, desc, cache, force): return 1
|
||||||
|
|
||||||
|
sir_name = "pipeline_" + hashlib.sha256(str(ROOT / src).encode()).hexdigest()[:8]
|
||||||
|
if not compile_spl(sir_name, src, compiler, desc, cache, force):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(f" RUN pipeline {name} ({src})")
|
||||||
|
return subprocess.run(
|
||||||
|
[str(exe_path(runner)), str(sir_path(sir_name))] + extra_args[1:]
|
||||||
|
).returncode
|
||||||
|
|
||||||
|
print(f" ERR unknown target '{name}'"); return 1
|
||||||
|
|
||||||
|
# ── Clean / List ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def clean():
|
||||||
|
if BUILD_DIR.is_dir():
|
||||||
|
shutil.rmtree(BUILD_DIR)
|
||||||
|
print(f" CLEAN {BUILD_DIR}")
|
||||||
|
|
||||||
|
def list_targets(desc):
|
||||||
|
print("Executables (.c → .o → exe):")
|
||||||
|
for name, sources in desc.get("exe", {}).items():
|
||||||
|
print(f" {name}")
|
||||||
|
for s in sources:
|
||||||
|
print(f" {s}")
|
||||||
|
if desc.get("spl"):
|
||||||
|
print("\nSPL programs (.spl → .sir via pipeline):")
|
||||||
|
for name, info in desc["spl"].items():
|
||||||
|
pl_name = info.get("pipeline", "-")
|
||||||
|
print(f" {name} pipeline={pl_name} src={info['src']}")
|
||||||
|
if desc.get("pipeline"):
|
||||||
|
print("\nPipelines:")
|
||||||
|
for name, pl in desc["pipeline"].items():
|
||||||
|
print(f" {name} compiler={pl['compiler']} runner={pl.get('runner', '-')}")
|
||||||
|
|
||||||
|
# ── CLI ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="SPL build system")
|
||||||
|
sub = parser.add_subparsers(dest="command")
|
||||||
|
|
||||||
|
bp = sub.add_parser("build", help="build targets (default: all)")
|
||||||
|
bp.add_argument("targets", nargs="*")
|
||||||
|
bp.add_argument("-f", "--force", action="store_true")
|
||||||
|
|
||||||
|
rp = sub.add_parser("run", help="build and run a target, or run pipeline <name> <src> [args]")
|
||||||
|
rp.add_argument("target")
|
||||||
|
rp.add_argument("extra", nargs="*")
|
||||||
|
|
||||||
|
sub.add_parser("test", help="build and run VM unit tests")
|
||||||
|
sub.add_parser("clean", help="remove build artifacts")
|
||||||
|
sub.add_parser("list", help="list all targets")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not DESC_FILE.is_file():
|
||||||
|
print(f"ERR {DESC_FILE} not found"); return 1
|
||||||
|
|
||||||
|
# Default: build all
|
||||||
|
if args.command is None:
|
||||||
|
desc = load_desc(); cache = load_cache()
|
||||||
|
ok = build_all(desc, cache, False)
|
||||||
|
save_cache(cache)
|
||||||
|
return 0 if ok else 1
|
||||||
|
|
||||||
|
# Build
|
||||||
|
if args.command == "build":
|
||||||
|
desc = load_desc(); cache = load_cache()
|
||||||
|
if args.targets:
|
||||||
|
ok = all(build(t, desc, cache, args.force) for t in args.targets)
|
||||||
|
else:
|
||||||
|
ok = build_all(desc, cache, args.force)
|
||||||
|
save_cache(cache)
|
||||||
|
return 0 if ok else 1
|
||||||
|
|
||||||
|
# Run
|
||||||
|
if args.command == "run":
|
||||||
|
desc = load_desc(); cache = load_cache()
|
||||||
|
rc = run_target(args.target, desc, cache, False, args.extra)
|
||||||
|
save_cache(cache)
|
||||||
|
return rc
|
||||||
|
|
||||||
|
# Pipeline: build compiler → compile .spl → run via runner
|
||||||
|
if args.command == "pipeline":
|
||||||
|
desc = load_desc(); cache = load_cache()
|
||||||
|
pl = desc.get("pipeline", {}).get(args.name)
|
||||||
|
if not pl:
|
||||||
|
print(f" ERR unknown pipeline '{args.name}'"); return 1
|
||||||
|
compiler = pl.get("compiler")
|
||||||
|
runner = pl.get("runner")
|
||||||
|
if not compiler or not runner:
|
||||||
|
print(f" ERR pipeline '{args.name}' missing compiler or runner"); return 1
|
||||||
|
src_path = ROOT / args.src
|
||||||
|
if not src_path.is_file():
|
||||||
|
print(f" ERR source not found: {args.src}"); return 1
|
||||||
|
|
||||||
|
# Build compiler + runner
|
||||||
|
if not build(compiler, desc, cache, False): return 1
|
||||||
|
if not build(runner, desc, cache, False): return 1
|
||||||
|
|
||||||
|
# Compile .spl → .sir (temp name = src path hash)
|
||||||
|
sir_name = "pipeline_" + hashlib.sha256(str(src_path).encode()).hexdigest()[:8]
|
||||||
|
if not compile_spl(sir_name, args.src, compiler, desc, cache, False):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Run .sir via runner
|
||||||
|
print(f" PIPELINE {args.name} ({args.src})")
|
||||||
|
rc = subprocess.run(
|
||||||
|
[str(exe_path(runner)), str(sir_path(sir_name))] + args.extra
|
||||||
|
).returncode
|
||||||
|
save_cache(cache)
|
||||||
|
return rc
|
||||||
|
|
||||||
|
# Test
|
||||||
|
if args.command == "test":
|
||||||
|
desc = load_desc(); cache = load_cache()
|
||||||
|
ok = build("test", desc, cache, False)
|
||||||
|
save_cache(cache)
|
||||||
|
if not ok: return 1
|
||||||
|
return subprocess.run([str(exe_path("test"))]).returncode
|
||||||
|
|
||||||
|
# Clean / List
|
||||||
|
if args.command == "clean":
|
||||||
|
clean(); return 0
|
||||||
|
if args.command == "list":
|
||||||
|
list_targets(load_desc()); return 0
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
vm = [
|
||||||
|
"stage0/spl_ir.c",
|
||||||
|
"stage0/spl_syscall.c",
|
||||||
|
"stage0/spl_vm.c",
|
||||||
|
]
|
||||||
|
|
||||||
|
splc0_part = [
|
||||||
|
"stage1/spl_comp.c",
|
||||||
|
"stage1/spl_lexer.c",
|
||||||
|
"stage1/spl_type.c",
|
||||||
|
"stage1/spl_parser.c",
|
||||||
|
"stage1/spl_lex_util.c",
|
||||||
|
"stage1/spl_expr.c",
|
||||||
|
"stage1/spl_stmt.c",
|
||||||
|
]
|
||||||
|
|
||||||
|
exe = {
|
||||||
|
"spl_cli": ["stage0/spl_cli.c"] + vm,
|
||||||
|
"splc_cli": ["stage1/splc_cli.c"] + vm + splc0_part,
|
||||||
|
"splc0": ["stage1/splc0.c"] + vm + splc0_part,
|
||||||
|
"test": ["stage0/test_spl_vm.c"] + vm,
|
||||||
|
"spl_disasm": ["stage0/spl_disasm.c"] + vm,
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline = {
|
||||||
|
"splc0b": {"compiler": "splc0", "runner": "spl_cli"},
|
||||||
|
"splc0r": {"compiler": "splc0", "runner": "splc_cli"},
|
||||||
|
"splc0d": {"compiler": "splc0", "runner": "spl_disasm"},
|
||||||
|
|
||||||
|
"splc1b": {"compiler": "splc0", "runner": "splc_cli"},
|
||||||
|
"splc1r": {"compiler": "splc1", "runner": "splc_cli"},
|
||||||
|
"splc1d": {"compiler": "splc1", "runner": "spl_disasm"},
|
||||||
|
|
||||||
|
"splc2b": {"compiler": "splc1", "runner": "splc_cli"},
|
||||||
|
"splc2r": {"compiler": "splc2", "runner": "splc_cli"},
|
||||||
|
"splc2d": {"compiler": "splc2", "runner": "spl_disasm"},
|
||||||
|
|
||||||
|
"splc3b": {"compiler": "splc2", "runner": "splc_cli"},
|
||||||
|
"splc3r": {"compiler": "splc3", "runner": "splc_cli"},
|
||||||
|
"splc3d": {"compiler": "splc3", "runner": "spl_disasm"},
|
||||||
|
|
||||||
|
"splc4b": {"compiler": "splc3", "runner": "splc_cli"},
|
||||||
|
"splc4r": {"compiler": "splc4", "runner": "splc_cli"},
|
||||||
|
"splc4d": {"compiler": "splc4", "runner": "spl_disasm"},
|
||||||
|
}
|
||||||
|
|
||||||
|
spl = {
|
||||||
|
"splc1": {"src": "stage2/splc1.spl", "pipeline": "splc0r"},
|
||||||
|
"splc2": {"src": "stage2/splc2.spl", "pipeline": "splc1r"},
|
||||||
|
"splc3": {"src": "stage3/splc3.spl", "pipeline": "splc2r"},
|
||||||
|
"splc4": {"src": "stage4/splc4.spl", "pipeline": "splc3r"},
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* @file color.h
|
||||||
|
* @brief ANSI终端颜色控制码定义
|
||||||
|
*
|
||||||
|
* 提供跨平台的终端文本颜色和样式控制支持
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SCC_TERMINAL_COLOR_H__
|
||||||
|
#define __SCC_TERMINAL_COLOR_H__
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
/// @name 前景色控制码
|
||||||
|
/// @{
|
||||||
|
#define ANSI_FG_BLACK "\33[30m" ///< 黑色前景
|
||||||
|
#define ANSI_FG_RED "\33[31m" ///< 红色前景
|
||||||
|
#define ANSI_FG_GREEN "\33[32m" ///< 绿色前景
|
||||||
|
#define ANSI_FG_YELLOW "\33[33m" ///< 黄色前景
|
||||||
|
#define ANSI_FG_BLUE "\33[34m" ///< 蓝色前景
|
||||||
|
#define ANSI_FG_MAGENTA "\33[35m" ///< 品红色前景
|
||||||
|
#define ANSI_FG_CYAN "\33[36m" ///< 青色前景
|
||||||
|
#define ANSI_FG_WHITE "\33[37m" ///< 白色前景
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @name 背景色控制码
|
||||||
|
/// @{
|
||||||
|
#define ANSI_BG_BLACK "\33[40m" ///< 黑色背景
|
||||||
|
#define ANSI_BG_RED "\33[41m" ///< 红色背景
|
||||||
|
#define ANSI_BG_GREEN "\33[42m" ///< 绿色背景
|
||||||
|
#define ANSI_BG_YELLOW "\33[43m" ///< 黄色背景
|
||||||
|
#define ANSI_BG_BLUE "\33[44m" ///< 蓝色背景
|
||||||
|
#define ANSI_BG_MAGENTA "\33[45m" ///< 品红色背景(注:原始代码此处应为45m)
|
||||||
|
#define ANSI_BG_CYAN "\33[46m" ///< 青色背景
|
||||||
|
#define ANSI_BG_WHITE "\33[47m" ///< 白色背景
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @name 文字样式控制码
|
||||||
|
/// @{
|
||||||
|
#define ANSI_UNDERLINED "\33[4m" ///< 下划线样式
|
||||||
|
#define ANSI_BOLD "\33[1m" ///< 粗体样式
|
||||||
|
#define ANSI_NONE "\33[0m" ///< 重置所有样式
|
||||||
|
/// @}
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def ANSI_FMT
|
||||||
|
* @brief 安全文本格式化宏
|
||||||
|
* @param str 目标字符串
|
||||||
|
* @param fmt ANSI格式序列(可组合多个样式)
|
||||||
|
*
|
||||||
|
* @note 当定义ANSI_FMT_DISABLE时自动禁用颜色输出
|
||||||
|
* @code
|
||||||
|
* printf(ANSI_FMT("Warning!", ANSI_FG_YELLOW ANSI_BOLD));
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#ifndef ANSI_FMT_DISABLE
|
||||||
|
#define ANSI_FMT(str, fmt) fmt str ANSI_NONE ///< 启用样式包裹
|
||||||
|
#else
|
||||||
|
#define ANSI_FMT(str, fmt) str ///< 禁用样式输出
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SCC_TERMINAL_COLOR_H__ */
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
#ifndef __CORE_MAP_H__
|
||||||
|
#define __CORE_MAP_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef nullptr
|
||||||
|
#define nullptr NULL
|
||||||
|
#endif
|
||||||
|
typedef uintptr_t usize;
|
||||||
|
typedef intptr_t isize;
|
||||||
|
|
||||||
|
#define MAP_TYPEOF __typeof__
|
||||||
|
|
||||||
|
/* 状态常量 */
|
||||||
|
#define __MAP_SLOT_EMPTY 0
|
||||||
|
#define __MAP_SLOT_OCCUPIED 1
|
||||||
|
#define __MAP_SLOT_DELETED 2
|
||||||
|
|
||||||
|
/* 默认负载因子 factor/128 */
|
||||||
|
#define MAP_DEFAULT_LOAD_FACTOR 70
|
||||||
|
|
||||||
|
/* ---------- 默认哈希/比较函数 ---------- */
|
||||||
|
#define MAP_HASH_INT(k) ((usize)((k) * 2654435761U))
|
||||||
|
#define MAP_CMP_INT(a, b) ((a) != (b))
|
||||||
|
|
||||||
|
static inline usize map_hash_str(const char *s) {
|
||||||
|
usize h = 5381;
|
||||||
|
while (*s)
|
||||||
|
h = ((h << 5) + h) + (unsigned char)*s++;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
#define MAP_HASH_STR map_hash_str
|
||||||
|
#define MAP_CMP_STR strcmp
|
||||||
|
|
||||||
|
/* ---------- 数据结构宏 ---------- */
|
||||||
|
#define MAP_SLOT(key_t, val_t) \
|
||||||
|
struct { \
|
||||||
|
key_t key; \
|
||||||
|
val_t val; \
|
||||||
|
char state; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAP(key_t, val_t) \
|
||||||
|
struct { \
|
||||||
|
usize size; \
|
||||||
|
usize cap; \
|
||||||
|
MAP_SLOT(key_t, val_t) * data; \
|
||||||
|
usize (*hash)(key_t); \
|
||||||
|
int (*cmp)(key_t, key_t); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 操作宏 ---------- */
|
||||||
|
|
||||||
|
/** 初始化,必须提供哈希和比较函数 */
|
||||||
|
#define map_init(map, hash_fn, cmp_fn) \
|
||||||
|
do { \
|
||||||
|
(map).size = 0; \
|
||||||
|
(map).cap = 0; \
|
||||||
|
(map).data = nullptr; \
|
||||||
|
(map).hash = (hash_fn); \
|
||||||
|
(map).cmp = (cmp_fn); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/** 释放内部数组 */
|
||||||
|
#define map_free(map) \
|
||||||
|
do { \
|
||||||
|
free((map).data); \
|
||||||
|
(map).data = nullptr; \
|
||||||
|
(map).size = (map).cap = 0; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/** 遍历所有有效元素 */
|
||||||
|
#define map_for(map, idx) \
|
||||||
|
for (usize(idx) = 0; (idx) < (map).cap; ++(idx)) \
|
||||||
|
if ((map).data[(idx)].state == __MAP_SLOT_OCCUPIED)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入(若键已存在则更新值)
|
||||||
|
* 注意:扩容使用 realloc,失败会 abort(可自行修改错误处理)
|
||||||
|
*/
|
||||||
|
#define map_put(map, _key, _val) \
|
||||||
|
do { \
|
||||||
|
/* 扩容 */ \
|
||||||
|
if ((map).cap == 0 || (map).size * 128 / (map).cap >= MAP_DEFAULT_LOAD_FACTOR) { \
|
||||||
|
usize new_cap = (map).cap == 0 ? 8 : (map).cap * 2; \
|
||||||
|
MAP_SLOT(MAP_TYPEOF((map).data->key), MAP_TYPEOF((map).data->val)) *new_data = \
|
||||||
|
calloc(new_cap, sizeof(*new_data)); \
|
||||||
|
if (!new_data) \
|
||||||
|
abort(); \
|
||||||
|
/* 重新插入旧元素 */ \
|
||||||
|
for (usize _i = 0; _i < (map).cap; ++_i) { \
|
||||||
|
if ((map).data[_i].state == __MAP_SLOT_OCCUPIED) { \
|
||||||
|
usize _h = (map).hash((map).data[_i].key) & (new_cap - 1); \
|
||||||
|
while (new_data[_h].state == __MAP_SLOT_OCCUPIED) \
|
||||||
|
_h = (_h + 1) & (new_cap - 1); \
|
||||||
|
new_data[_h].key = (map).data[_i].key; \
|
||||||
|
new_data[_h].val = (map).data[_i].val; \
|
||||||
|
new_data[_h].state = __MAP_SLOT_OCCUPIED; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
free((map).data); \
|
||||||
|
(map).data = (void *)new_data; \
|
||||||
|
(map).cap = new_cap; \
|
||||||
|
} \
|
||||||
|
/* 查找或插入 */ \
|
||||||
|
usize _mask = (map).cap - 1; \
|
||||||
|
usize _idx = (map).hash(_key) & _mask; \
|
||||||
|
usize _first_del = (usize) - 1; \
|
||||||
|
while ((map).data[_idx].state != __MAP_SLOT_EMPTY) { \
|
||||||
|
if ((map).data[_idx].state == __MAP_SLOT_OCCUPIED && \
|
||||||
|
(map).cmp((map).data[_idx].key, _key) == 0) { \
|
||||||
|
(map).data[_idx].val = _val; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
if ((map).data[_idx].state == __MAP_SLOT_DELETED && _first_del == (usize) - 1) \
|
||||||
|
_first_del = _idx; \
|
||||||
|
_idx = (_idx + 1) & _mask; \
|
||||||
|
} \
|
||||||
|
if ((map).data[_idx].state == __MAP_SLOT_EMPTY) { \
|
||||||
|
usize _target = (_first_del != (usize) - 1) ? _first_del : _idx; \
|
||||||
|
(map).data[_target].key = _key; \
|
||||||
|
(map).data[_target].val = _val; \
|
||||||
|
(map).data[_target].state = __MAP_SLOT_OCCUPIED; \
|
||||||
|
++(map).size; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询:若找到,*out_val 被赋值为对应值并返回 1;否则返回 0
|
||||||
|
*/
|
||||||
|
#define map_get(map, _key, out_val) \
|
||||||
|
(({ \
|
||||||
|
int _found = 0; \
|
||||||
|
if ((map).cap > 0) { \
|
||||||
|
usize _mask = (map).cap - 1; \
|
||||||
|
usize _idx = (map).hash(_key) & _mask; \
|
||||||
|
while ((map).data[_idx].state != __MAP_SLOT_EMPTY) { \
|
||||||
|
if ((map).data[_idx].state == __MAP_SLOT_OCCUPIED && \
|
||||||
|
(map).cmp((map).data[_idx].key, _key) == 0) { \
|
||||||
|
*out_val = (map).data[_idx].val; \
|
||||||
|
_found = 1; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
_idx = (_idx + 1) & _mask; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
_found; \
|
||||||
|
}))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定键
|
||||||
|
*/
|
||||||
|
#define map_del(map, _key) \
|
||||||
|
do { \
|
||||||
|
if ((map).cap == 0) \
|
||||||
|
break; \
|
||||||
|
usize _mask = (map).cap - 1; \
|
||||||
|
usize _idx = (map).hash(_key) & _mask; \
|
||||||
|
while ((map).data[_idx].state != __MAP_SLOT_EMPTY) { \
|
||||||
|
if ((map).data[_idx].state == __MAP_SLOT_OCCUPIED && \
|
||||||
|
(map).cmp((map).data[_idx].key, _key) == 0) { \
|
||||||
|
(map).data[_idx].state = __MAP_SLOT_DELETED; \
|
||||||
|
--(map).size; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
_idx = (_idx + 1) & _mask; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif /* __CORE_MAP_H__ */
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
/**
|
||||||
|
* @file vec.h
|
||||||
|
* @brief 动态数组(Dynamic Array)实现
|
||||||
|
*
|
||||||
|
* 提供类型安全的动态数组容器实现,支持自动扩容和基本操作
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CORE_VEC_H__
|
||||||
|
#define __CORE_VEC_H__
|
||||||
|
|
||||||
|
#define __CORE_VEC_USE_STD__
|
||||||
|
#ifndef __CORE_VEC_USE_STD__
|
||||||
|
#include "core_log.h"
|
||||||
|
|
||||||
|
#include "core_impl.h"
|
||||||
|
#include "core_type.h"
|
||||||
|
#define __vec_realloc realloc
|
||||||
|
#define __vec_free free
|
||||||
|
#define __vec_memcpy memcpy
|
||||||
|
#else
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef nullptr
|
||||||
|
#define nullptr NULL
|
||||||
|
#endif
|
||||||
|
typedef size_t usize;
|
||||||
|
#define __vec_realloc realloc
|
||||||
|
#define __vec_free free
|
||||||
|
#define __vec_memcpy memcpy
|
||||||
|
|
||||||
|
#ifndef LOG_FATAL
|
||||||
|
#include <stdio.h>
|
||||||
|
#define LOG_FATAL(...) \
|
||||||
|
do { \
|
||||||
|
printf(__VA_ARGS__); \
|
||||||
|
abort(); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Assert
|
||||||
|
#include <assert.h>
|
||||||
|
#define Assert(cond) assert(cond)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @defgroup vec_struct 数据结构定义 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def VEC(type)
|
||||||
|
* @brief 声明向量结构体
|
||||||
|
* @param type 存储的数据类型
|
||||||
|
*
|
||||||
|
* 生成包含size/cap/data三个字段的结构体定义:
|
||||||
|
* - size: 当前元素数量
|
||||||
|
* - cap: 数组容量
|
||||||
|
* - data: 存储数组指针
|
||||||
|
* @example
|
||||||
|
* VEC(char) string; <=> char[dynamic_array] string;
|
||||||
|
* struct people { VEC(char) name; int age; VEC(struct people) children;
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
#define VEC(type) \
|
||||||
|
struct { \
|
||||||
|
usize size; \
|
||||||
|
usize cap; \
|
||||||
|
type *data; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @defgroup vec_operations 动态数组操作宏 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_init(vec)
|
||||||
|
* @brief 初始化向量结构体
|
||||||
|
* @param vec 要初始化的向量结构体变量
|
||||||
|
*
|
||||||
|
* @note 此宏不会分配内存,仅做零初始化
|
||||||
|
*/
|
||||||
|
#define vec_init(vec) \
|
||||||
|
do { \
|
||||||
|
(vec).size = 0, (vec).cap = 0, (vec).data = 0; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define vec_realloc(vec, new_cap) \
|
||||||
|
do { \
|
||||||
|
void *data = __vec_realloc((vec).data, new_cap * sizeof(*(vec).data)); \
|
||||||
|
if (!data) { \
|
||||||
|
LOG_FATAL("vector_push: realloc failed\n"); \
|
||||||
|
} \
|
||||||
|
(vec).cap = new_cap; \
|
||||||
|
(vec).data = data; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define vec_size(vec) ((vec).size)
|
||||||
|
#define vec_cap(vec) ((vec).cap)
|
||||||
|
#define vec_for(vec, idx) for (usize idx = 0; idx < vec_size(vec); idx += 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_push(vec, value)
|
||||||
|
* @brief 添加元素到向量末尾
|
||||||
|
* @param vec 目标向量结构体
|
||||||
|
* @param value 要添加的值(需匹配存储类型)
|
||||||
|
*
|
||||||
|
* @note 当容量不足时自动扩容为2倍(初始容量为4)
|
||||||
|
* @warning 内存分配失败时会触发LOG_FATAL
|
||||||
|
*/
|
||||||
|
#define vec_push(vec, value) \
|
||||||
|
do { \
|
||||||
|
if ((vec).size >= (vec).cap) { \
|
||||||
|
usize cap = (vec).cap ? (vec).cap * 2 : 4; \
|
||||||
|
vec_realloc(vec, cap); \
|
||||||
|
} \
|
||||||
|
Assert((vec).data != nullptr); \
|
||||||
|
(vec).data[(vec).size++] = value; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_pop(vec)
|
||||||
|
* @brief 弹出最后一个元素
|
||||||
|
* @param vec 目标向量结构体
|
||||||
|
* @return 最后元素的引用
|
||||||
|
* @warning 需确保size > 0时使用
|
||||||
|
*/
|
||||||
|
#define vec_pop(vec) ((vec).data[--(vec).size])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_at(vec, idx)
|
||||||
|
* @brief 获取指定索引元素
|
||||||
|
* @param vec 目标向量结构体
|
||||||
|
* @param idx 元素索引(0 <= idx < size)
|
||||||
|
* @return 对应元素的引用
|
||||||
|
*/
|
||||||
|
#define vec_at(vec, idx) (((vec).data)[idx])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_idx(vec, ptr)
|
||||||
|
* @brief 获取元素指针对应的索引
|
||||||
|
* @param vec 目标向量结构体
|
||||||
|
* @param ptr 元素指针(需在data数组范围内)
|
||||||
|
* @return 元素索引值
|
||||||
|
*/
|
||||||
|
#define vec_idx(vec, ptr) ((ptr) - (vec).data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_free(vec)
|
||||||
|
* @brief 释放向量内存
|
||||||
|
* @param vec 目标向量结构体
|
||||||
|
*
|
||||||
|
* @note 释放后需重新初始化才能再次使用
|
||||||
|
*/
|
||||||
|
#define vec_free(vec) \
|
||||||
|
do { \
|
||||||
|
if ((vec).data == nullptr) \
|
||||||
|
break; \
|
||||||
|
__vec_free((vec).data); \
|
||||||
|
(vec).data = nullptr; \
|
||||||
|
(vec).size = (vec).cap = 0; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define vec_unsafe_get_data(vec) ((vec).data)
|
||||||
|
|
||||||
|
#define vec_unsafe_from_buffer(vec, buffer, buffer_size) \
|
||||||
|
do { \
|
||||||
|
(vec).size = buffer_size; \
|
||||||
|
(vec).cap = (vec).size; \
|
||||||
|
(vec).data = buffer; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define vec_unsafe_from_static_array(vec, array) \
|
||||||
|
do { \
|
||||||
|
(vec).size = sizeof(array) / sizeof((array)[0]); \
|
||||||
|
(vec).cap = (vec).size; \
|
||||||
|
(vec).data = array; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_sized_realloc(vec, elem_size, new_cap)
|
||||||
|
* @brief 内部宏:按 elem_size 重新分配内存
|
||||||
|
*/
|
||||||
|
#define vec_sized_realloc(vec, elem_size, new_cap) \
|
||||||
|
do { \
|
||||||
|
void *new_data = __vec_realloc((vec).data, (new_cap) * (elem_size)); \
|
||||||
|
if (!new_data) \
|
||||||
|
LOG_FATAL("vec_sized_realloc: failed\n"); \
|
||||||
|
(vec).data = new_data; \
|
||||||
|
(vec).cap = new_cap; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_sized_push(vec, elem_size, src_ptr)
|
||||||
|
* @brief 添加一个元素(从 src_ptr 拷贝 elem_size 字节)
|
||||||
|
* @param vec VEC(type) 定义的向量变量(type 可为 char 或 void)
|
||||||
|
* @param elem_size 每个元素占用的字节数
|
||||||
|
* @param src_ptr 源数据的指针
|
||||||
|
* @param copy_size 要拷贝的字节数
|
||||||
|
*
|
||||||
|
* @note 使用前需确保 vec.data 类型与 src_ptr 无关,内部会按字节拷贝。
|
||||||
|
* 推荐声明时为 `VEC(char)` 或 `VEC(unsigned char)`。
|
||||||
|
*/
|
||||||
|
#define vec_sized_push(vec, elem_size, src_ptr, copy_size) \
|
||||||
|
do { \
|
||||||
|
if ((vec).size >= (vec).cap) { \
|
||||||
|
usize new_cap = (vec).cap ? (vec).cap * 2 : 4; \
|
||||||
|
vec_sized_realloc(vec, elem_size, new_cap); \
|
||||||
|
} \
|
||||||
|
char *slot = (char *)(vec).data + (vec).size * (elem_size); \
|
||||||
|
__vec_memcpy(slot, (src_ptr), (copy_size)); \
|
||||||
|
(vec).size++; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_sized_at_ptr(vec, elem_size, idx)
|
||||||
|
* @brief 获取第 idx 个元素的指针(void*)
|
||||||
|
* @return 指向元素的指针,需转换为具体类型使用
|
||||||
|
*/
|
||||||
|
#define vec_sized_at_ptr(vec, elem_size, idx) ((void *)((char *)(vec).data + (idx) * (elem_size)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_sized_foreach(vec, elem_size, elem_ptr_var, block)
|
||||||
|
* @brief 遍历所有元素
|
||||||
|
* @param elem_ptr_var 循环内的变量名(void* 类型)
|
||||||
|
* @param block 循环体语句块
|
||||||
|
*/
|
||||||
|
#define vec_sized_foreach(vec, elem_size, elem_ptr_var, block) \
|
||||||
|
do { \
|
||||||
|
for (usize __i = 0; __i < (vec).size; ++__i) { \
|
||||||
|
void *elem_ptr_var = vec_sized_at_ptr(vec, elem_size, __i); \
|
||||||
|
block; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_sized_pop(vec, elem_size)
|
||||||
|
* @brief 弹出最后一个元素(仅减小 size,不返回数据)
|
||||||
|
*/
|
||||||
|
#define vec_sized_pop(vec, elem_size) \
|
||||||
|
do { \
|
||||||
|
if ((vec).size == 0) \
|
||||||
|
LOG_FATAL("vec_sized_pop: empty\n"); \
|
||||||
|
(vec).size--; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_sized_clear(vec)
|
||||||
|
* @brief 清空向量(重置 size = 0,不释放内存)
|
||||||
|
*/
|
||||||
|
#define vec_sized_clear(vec) ((vec).size = 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def vec_sized_free(vec)
|
||||||
|
* @brief 释放向量内存(与原始 vec_free 相同,可复用)
|
||||||
|
* @note 注意:如果元素内部有堆资源,需在释放前自行遍历调用析构函数。
|
||||||
|
*/
|
||||||
|
#define vec_sized_free(vec) vec_free(vec)
|
||||||
|
|
||||||
|
#endif /* __CORE_VEC_H__ */
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
static inline int log_snprintf(char *s, size_t n, const char *format, ...) {
|
||||||
|
int ret;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
ret = log_vsnprintf(s, n, format, args);
|
||||||
|
va_end(args);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_default_handler(logger_t *module, log_level_t level, const char *file, int line,
|
||||||
|
const char *func, const char *fmt, ...) {
|
||||||
|
const char *level_str;
|
||||||
|
int offset = 0;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
/* clang-format off */
|
||||||
|
switch (level) {
|
||||||
|
case LOG_LEVEL_DEBUG: level_str = "DEBUG"; break;
|
||||||
|
case LOG_LEVEL_INFO: level_str = "INFO "; break;
|
||||||
|
case LOG_LEVEL_WARN: level_str = "WARN "; break;
|
||||||
|
case LOG_LEVEL_ERROR: level_str = "ERROR"; break;
|
||||||
|
case LOG_LEVEL_FATAL: level_str = "FATAL"; break;
|
||||||
|
case LOG_LEVEL_TRACE: level_str = "TRACE"; break;
|
||||||
|
default: level_str = "NOTSET"; break;
|
||||||
|
}
|
||||||
|
/// @note: 定义 __LOG_NO_COLOR__ 会取消颜色输出
|
||||||
|
#ifndef __LOG_NO_COLOR__
|
||||||
|
const char *color_code;
|
||||||
|
switch (level) {
|
||||||
|
case LOG_LEVEL_DEBUG: color_code = ANSI_FG_CYAN; break;
|
||||||
|
case LOG_LEVEL_INFO: color_code = ANSI_FG_GREEN; break;
|
||||||
|
case LOG_LEVEL_TRACE: color_code = ANSI_FG_BLUE; break;
|
||||||
|
case LOG_LEVEL_WARN: color_code = ANSI_FG_YELLOW; break;
|
||||||
|
case LOG_LEVEL_ERROR: color_code = ANSI_FG_RED; break;
|
||||||
|
case LOG_LEVEL_FATAL: color_code = ANSI_FG_RED ANSI_UNDERLINED; break;
|
||||||
|
default: color_code = ANSI_NONE;
|
||||||
|
}
|
||||||
|
/* clang-format on */
|
||||||
|
offset = log_snprintf(module->buf, sizeof(module->buf),
|
||||||
|
ANSI_BOLD "%s[%s] %s - %s:%d in %s()" ANSI_NONE " ", color_code,
|
||||||
|
level_str, module->name, file, line, func);
|
||||||
|
#else
|
||||||
|
offset = log_snprintf(module->buf, sizeof(module->buf), "[%s] %s - %s:%d in %s() ", level_str,
|
||||||
|
module->name, file, line, func);
|
||||||
|
#endif
|
||||||
|
/* 然后写入用户消息(如果有) */
|
||||||
|
if (fmt && fmt[0]) {
|
||||||
|
log_vsnprintf(module->buf + offset, sizeof(module->buf) - offset, fmt, args);
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
log_puts(module->buf);
|
||||||
|
// for clangd warning
|
||||||
|
// clang-analyzer-deadcode.DeadStores
|
||||||
|
(void)color_code;
|
||||||
|
(void)level_str;
|
||||||
|
if (level & LOG_LEVEL_FATAL) {
|
||||||
|
log_abort();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger_t __default_logger_root = {
|
||||||
|
.name = "root",
|
||||||
|
.level = LOG_LEVEL_ALL,
|
||||||
|
.handler = log_default_handler,
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_logger(logger_t *logger, const char *name) {
|
||||||
|
logger->name = name;
|
||||||
|
logger->handler = log_default_handler;
|
||||||
|
log_set_level(logger, LOG_LEVEL_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_set_level(logger_t *logger, int level) {
|
||||||
|
if (logger)
|
||||||
|
logger->level = level;
|
||||||
|
else
|
||||||
|
__default_logger_root.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_set_handler(logger_t *logger, log_handler handler) {
|
||||||
|
if (logger)
|
||||||
|
logger->handler = handler;
|
||||||
|
else
|
||||||
|
__default_logger_root.handler = handler;
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
/**
|
||||||
|
* @file log.h
|
||||||
|
* @brief 日志系统核心模块(支持多级日志、断言和异常处理)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SCC_LOG_IMPL_H__
|
||||||
|
#define __SCC_LOG_IMPL_H__
|
||||||
|
|
||||||
|
#include "color.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#ifdef __SCC_LOG_IMPL_USE_STD_IMPL__
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define log_vsnprintf vsnprintf
|
||||||
|
#define log_puts puts
|
||||||
|
#define log_abort abort
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__ // GCC, Clang
|
||||||
|
#define __scc_log_unreachable() (__builtin_unreachable())
|
||||||
|
#elif defined _MSC_VER // MSVC
|
||||||
|
#define __scc_log_unreachable() (__assume(false))
|
||||||
|
#elif defined __SCC_BUILTIN_UNREACHEABLE__ // The SCC Compiler (my compiler)
|
||||||
|
#define __scc_log_unreachable() (__scc_builtin_unreachable())
|
||||||
|
#else
|
||||||
|
#define __scc_log_unreachable() ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef log_vsnprintf
|
||||||
|
#define log_vsnprintf(...)
|
||||||
|
#warning "log_vsnprintf not defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef log_puts
|
||||||
|
#define log_puts(...)
|
||||||
|
#warning "log_puts not defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef log_abort
|
||||||
|
#define log_abort(...)
|
||||||
|
#warning "log_abort not defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 日志级别枚举
|
||||||
|
*
|
||||||
|
* 定义日志系统的输出级别和组合标志位
|
||||||
|
*/
|
||||||
|
typedef enum log_level {
|
||||||
|
LOG_LEVEL_NOTSET = 0, ///< 未设置级别(继承默认配置)
|
||||||
|
LOG_LEVEL_DEBUG = 1 << 0, ///< 调试信息(开发阶段详细信息)
|
||||||
|
LOG_LEVEL_INFO = 1 << 1, ///< 常规信息(系统运行状态)
|
||||||
|
LOG_LEVEL_WARN = 1 << 2, ///< 警告信息(潜在问题提示)
|
||||||
|
LOG_LEVEL_ERROR = 1 << 3, ///< 错误信息(可恢复的错误)
|
||||||
|
LOG_LEVEL_FATAL = 1 << 4, ///< 致命错误(导致程序终止的严重错误)
|
||||||
|
LOG_LEVEL_TRACE = 1 << 5, ///< 追踪(性能追踪或者栈帧追踪)
|
||||||
|
LOG_LEVEL_ALL = 0xFF, ///< 全级别标志(组合所有日志级别)
|
||||||
|
} log_level_t;
|
||||||
|
|
||||||
|
#ifndef LOGGER_MAX_BUF_SIZE
|
||||||
|
#define LOGGER_MAX_BUF_SIZE 512 ///< 单条日志最大缓冲区尺寸
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct logger logger_t;
|
||||||
|
|
||||||
|
typedef int (*log_handler)(logger_t *module, log_level_t level, const char *file, int line,
|
||||||
|
const char *func, const char *fmt, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 日志器实例结构体
|
||||||
|
*
|
||||||
|
* 每个日志器实例维护独立的配置和缓冲区
|
||||||
|
*/
|
||||||
|
struct logger {
|
||||||
|
const char *name; ///< 日志器名称(用于模块区分)
|
||||||
|
log_level_t level; ///< 当前设置的日志级别
|
||||||
|
union {
|
||||||
|
log_handler handler;
|
||||||
|
void *user_handler;
|
||||||
|
}; ///< 日志处理回调函数
|
||||||
|
void *user_data; ///< 用户自定义数据
|
||||||
|
char buf[LOGGER_MAX_BUF_SIZE]; ///< 格式化缓冲区
|
||||||
|
};
|
||||||
|
|
||||||
|
int log_default_handler(logger_t *module, log_level_t level, const char *file, int line,
|
||||||
|
const char *func, const char *fmt, ...);
|
||||||
|
extern logger_t __default_logger_root;
|
||||||
|
#ifndef LOG_DEFAULT_HANDLER
|
||||||
|
#define LOG_DEFAULT_HANDLER &__default_logger_root
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化日志实例 其余参数设置为默认值
|
||||||
|
* @param[in] logger 日志器实例指针
|
||||||
|
* @param[in] name 日志器名称(nullptr表示获取默认日志器名称)
|
||||||
|
*/
|
||||||
|
void init_logger(logger_t *logger, const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置日志级别
|
||||||
|
* @param[in] logger 目标日志器实例
|
||||||
|
* @param[in] level 要设置的日志级别(可组合多个级别)
|
||||||
|
*/
|
||||||
|
void log_set_level(logger_t *logger, int level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置自定义日志处理器
|
||||||
|
* @param[in] logger 目标日志器实例
|
||||||
|
* @param[in] handler 自定义处理函数(nullptr恢复默认处理)
|
||||||
|
*/
|
||||||
|
void log_set_handler(logger_t *logger, log_handler handler);
|
||||||
|
|
||||||
|
#ifndef LOG_MAX_MAROC_BUF_SIZE
|
||||||
|
#define LOG_MAX_MAROC_BUF_SIZE LOGGER_MAX_BUF_SIZE ///< 宏展开缓冲区尺寸
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SCC_LOG_HANDLE_ARGS(_module_, _level_, ...) \
|
||||||
|
(_module_), (_level_), __FILE__, __LINE__, __func__, ##__VA_ARGS__
|
||||||
|
|
||||||
|
#define SCC_LOG_IMPL(_module_, _level_, _fmt_, ...) \
|
||||||
|
do { \
|
||||||
|
/* TODO check _module_ is nullptr */ \
|
||||||
|
if ((_module_)->handler && ((_module_)->level & (_level_))) \
|
||||||
|
(_module_)->handler(SCC_LOG_HANDLE_ARGS(_module_, _level_, _fmt_, ##__VA_ARGS__)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
/// @name 模块日志宏
|
||||||
|
/// @{
|
||||||
|
#define MLOG_NOTSET(module, ...)SCC_LOG_IMPL(module, LOG_LEVEL_NOTSET, __VA_ARGS__) ///< 未分类日志
|
||||||
|
#define MLOG_DEBUG(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_DEBUG, __VA_ARGS__) ///< 调试日志(需启用DEBUG级别)
|
||||||
|
#define MLOG_INFO(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_INFO, __VA_ARGS__) ///< 信息日志(常规运行日志)
|
||||||
|
#define MLOG_WARN(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_WARN, __VA_ARGS__) ///< 警告日志(潜在问题)
|
||||||
|
#define MLOG_ERROR(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_ERROR, __VA_ARGS__) ///< 错误日志(可恢复错误)
|
||||||
|
#define MLOG_FATAL(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_FATAL, __VA_ARGS__) ///< 致命错误日志(程序终止前)
|
||||||
|
#define MLOG_TRACE(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_TRACE, __VA_ARGS__) ///< 追踪日志(调用栈跟踪)
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @name 快捷日志宏
|
||||||
|
/// @{
|
||||||
|
#define LOG_NOTSET(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_NOTSET, __VA_ARGS__) ///< 未分类日志
|
||||||
|
#define LOG_DEBUG(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_DEBUG, __VA_ARGS__) ///< 调试日志(需启用DEBUG级别)
|
||||||
|
#define LOG_INFO(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_INFO, __VA_ARGS__) ///< 信息日志(常规运行日志)
|
||||||
|
#define LOG_WARN(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_WARN, __VA_ARGS__) ///< 警告日志(潜在问题)
|
||||||
|
#define LOG_ERROR(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_ERROR, __VA_ARGS__) ///< 错误日志(可恢复错误)
|
||||||
|
#define LOG_FATAL(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_FATAL, __VA_ARGS__) ///< 致命错误日志(程序终止前)
|
||||||
|
#define LOG_TRACE(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_TRACE, __VA_ARGS__) ///< 追踪日志(调用栈跟踪)
|
||||||
|
/// @}
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def _Assert
|
||||||
|
* @brief 断言检查内部宏
|
||||||
|
* @param cond 检查条件表达式
|
||||||
|
* @param ... 错误信息参数(格式字符串+参数)
|
||||||
|
*/
|
||||||
|
#define _Assert(cond, ...) \
|
||||||
|
((void)((cond) || (__default_logger_root.handler(SCC_LOG_HANDLE_ARGS( \
|
||||||
|
&__default_logger_root, LOG_LEVEL_FATAL, __VA_ARGS__)), \
|
||||||
|
log_abort(), __scc_log_unreachable(), 0)))
|
||||||
|
|
||||||
|
/// @name 断言工具宏
|
||||||
|
/// @{
|
||||||
|
#define __INNERSCC_LOG_IMPL_STR(str) #str
|
||||||
|
#define _SCC_LOG_IMPL_STR(str) __INNERSCC_LOG_IMPL_STR(str)
|
||||||
|
#define AssertFmt(cond, format, ...) \
|
||||||
|
_Assert(cond, "Assertion Failure: " format, ##__VA_ARGS__) ///< 带格式的断言检查
|
||||||
|
#define PanicFmt(format, ...) _Assert(0, "Panic: " format, ##__VA_ARGS__) ///< 立即触发致命错误
|
||||||
|
#define Assert(cond) AssertFmt(cond, "cond is `" _SCC_LOG_IMPL_STR(cond) "`") ///< 基础断言检查
|
||||||
|
#define Panic(...) PanicFmt(__VA_ARGS__) ///< 触发致命错误(带自定义消息)
|
||||||
|
#define TODO() PanicFmt("TODO please implement me") ///< 标记未实现代码(触发致命错误)
|
||||||
|
#define UNREACHABLE() PanicFmt("UNREACHABLE") ///< 触发致命错误(代码不可达)
|
||||||
|
#define FIXME(str) PanicFmt("FIXME " _SCC_LOG_IMPL_STR(str)) ///< 提醒开发者修改代码(触发致命错误)
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 静态断言(编译时)
|
||||||
|
*
|
||||||
|
* 利用数组大小不能为负的特性
|
||||||
|
* 或使用 _Static_assert (C11)
|
||||||
|
*/
|
||||||
|
#if __STDC_VERSION__ >= 201112L
|
||||||
|
#define StaticAssert _Static_assert
|
||||||
|
#else
|
||||||
|
#define StaticAssert(cond, msg) extern char __static_assertion[(cond) ? 1 : -1]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __SCC_LOG_IMPL_IMPORT_SRC__
|
||||||
|
#include "log.c"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SCC_LOG_IMPL_H__ */
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef __UTILS_H__
|
||||||
|
#define __UTILS_H__
|
||||||
|
|
||||||
|
#define __SCC_LOG_IMPL_USE_STD_IMPL__
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include "core_map.h"
|
||||||
|
#include "core_vec.h"
|
||||||
|
|
||||||
|
#endif /* __UTILS_H__ */
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/* spl_cli.c — SIR VM launcher
|
||||||
|
*
|
||||||
|
* Loads a compiled .sir binary and runs it via the SIR VM.
|
||||||
|
* Built-in syscalls are auto-registered via spl_syscall_register().
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* spl_cli [-d] <file.sir> [entry_point]
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spl_mcode.h"
|
||||||
|
#include "spl_syscall.h"
|
||||||
|
#include "spl_vm.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(int argc, const char **argv) {
|
||||||
|
int debug_mode = 0;
|
||||||
|
int arg_idx = 1;
|
||||||
|
|
||||||
|
if (argc >= 2 && strcmp(argv[1], "-d") == 0) {
|
||||||
|
debug_mode = 1;
|
||||||
|
arg_idx = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_idx >= argc) {
|
||||||
|
fprintf(stderr, "Usage: spl_cli [-d] <file.sir> [entry_point]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *path = argv[arg_idx];
|
||||||
|
const char *entry = argc >= arg_idx + 2 ? argv[arg_idx + 1] : "main";
|
||||||
|
|
||||||
|
spl_prog_t prog;
|
||||||
|
if (spl_prog_load_from_file(path, &prog) != 0) {
|
||||||
|
fprintf(stderr, "spl_cli: cannot load '%s'\n", path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_syscall_register(&prog);
|
||||||
|
|
||||||
|
spl_vm_t vm;
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (debug_mode) spl_vm_set_debug(&vm, 1);
|
||||||
|
if (spl_vm_load_prog(&vm, &prog) != 0) {
|
||||||
|
fprintf(stderr, "vm: prog '%s' not found\n", entry);
|
||||||
|
spl_prog_drop(&prog);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (spl_vm_prepare(&vm, entry, argc, argv, NULL) != 0) {
|
||||||
|
fprintf(stderr, "vm: entry point '%s' not found\n", entry);
|
||||||
|
spl_prog_drop(&prog);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = spl_vm_run_until(&vm, 0);
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
spl_prog_drop(&prog);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "spl_cli: VM error (exit_code=%d)\n", vm.exit_code);
|
||||||
|
return (int)vm.exit_code;
|
||||||
|
}
|
||||||
|
return (int)vm.exit_code;
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/* spl_disasm.c — SIR bytecode disassembler
|
||||||
|
*
|
||||||
|
* Usage: spl_disasm <file.sir>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spl_mcode.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(int argc, const char **argv) {
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Usage: spl_disasm <file.sir>\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_prog_t prog;
|
||||||
|
if (spl_prog_load_from_file(argv[1], &prog) != 0) {
|
||||||
|
fprintf(stderr, "spl_disasm: cannot load '%s'\n", argv[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(";; SPL bytecode dump: %s\n", argv[1]);
|
||||||
|
printf(";; ninsns=%zu nfuncs=%zu nnatives=%zu nstrs=%zu\n\n", vec_size(prog.insns),
|
||||||
|
vec_size(prog.funcs), vec_size(prog.natives), vec_size(prog.strtab));
|
||||||
|
|
||||||
|
if (vec_size(prog.funcs) > 0) {
|
||||||
|
printf(";; --- functions ---\n");
|
||||||
|
for (usize i = 0; i < vec_size(prog.funcs); i++) {
|
||||||
|
spl_func_t *f = &vec_at(prog.funcs, i);
|
||||||
|
printf(" %s nargs=%zu ninsns=%zu addr=%zu\n", f->name ? f->name : "(anon)", f->nargs,
|
||||||
|
f->ninsns, f->address);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vec_size(prog.natives) > 0) {
|
||||||
|
printf(";; --- natives ---\n");
|
||||||
|
for (int i = 0; i < (int)vec_size(prog.natives); i++) {
|
||||||
|
spl_native_t *n = &vec_at(prog.natives, i);
|
||||||
|
printf(" %s\n", n->name ? n->name : "(anon)");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vec_size(prog.strtab) > 0) {
|
||||||
|
printf(";; --- string table ---\n");
|
||||||
|
for (int i = 0; i < (int)vec_size(prog.strtab); i++) {
|
||||||
|
printf(" %d: \"%s\"\n", i, vec_at(prog.strtab, i));
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(";; --- instructions ---\n");
|
||||||
|
for (usize i = 0; i < vec_size(prog.insns); i++) {
|
||||||
|
spl_ins_t *ins = &vec_at(prog.insns, i);
|
||||||
|
printf("%4zd: %s", i, spl_opcode_name((spl_opcode_t)ins->opcode));
|
||||||
|
if (ins->type != SPL_VOID)
|
||||||
|
printf(" %s", spl_type_tag_name((spl_type_t)ins->type));
|
||||||
|
printf(" %zd", ins->imm);
|
||||||
|
|
||||||
|
/* annotate jumps / calls */
|
||||||
|
if (ins->opcode == SPL_JMP || ins->opcode == SPL_BZ || ins->opcode == SPL_BNZ) {
|
||||||
|
long long target = (long long)i + 1 + (long long)ins->imm;
|
||||||
|
printf(" ; -> %zd", target);
|
||||||
|
} else if (ins->opcode == SPL_CALL) {
|
||||||
|
printf(" ; nargs=%zd, from stack", (long long)ins->imm);
|
||||||
|
} else if (ins->opcode == SPL_CALLI) {
|
||||||
|
printf(" ; indirect call");
|
||||||
|
} else if (ins->opcode == SPL_GADDR) {
|
||||||
|
printf(" ; gdata[%zd]", (long long)ins->imm);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_prog_drop(&prog);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
/* spl_mcode.c — SPL VM machine code binary serialization, deserialization, and utilities
|
||||||
|
*
|
||||||
|
* Binary format (all metadata fields are spl_val_t = uint64_t LE):
|
||||||
|
* [HEADER] magic(8) nfuncs(8) ninsns(8) nnatives(8) nstrs(8) ndata(8)
|
||||||
|
* [FUNCS] each: name_len(8) name(padded to 8) idx_of_strtab(8)
|
||||||
|
* nargs(8) ninsns(8) address(8)
|
||||||
|
* [INSTRS] each: opcode(2) type(2) imm(8) = 12 bytes
|
||||||
|
* [NATIVES] each: name_len(8) name(padded to 8) idx_of_strtab(8)
|
||||||
|
* [STRTAB] each: slen(8) str(slen bytes, padded to 8)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spl_mcode.h"
|
||||||
|
|
||||||
|
void spl_prog_init(spl_prog_t *prog) {
|
||||||
|
if (!prog)
|
||||||
|
return;
|
||||||
|
vec_init(prog->insns);
|
||||||
|
vec_init(prog->funcs);
|
||||||
|
vec_init(prog->natives);
|
||||||
|
vec_init(prog->strtab);
|
||||||
|
vec_init(prog->gdata);
|
||||||
|
map_init(prog->symtab, MAP_HASH_STR, MAP_CMP_STR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spl_prog_drop(spl_prog_t *prog) {
|
||||||
|
if (!prog)
|
||||||
|
return;
|
||||||
|
vec_free(prog->insns);
|
||||||
|
/* free each func name */
|
||||||
|
vec_for(prog->funcs, i) { free(vec_at(prog->funcs, i).name); }
|
||||||
|
vec_free(prog->funcs);
|
||||||
|
/* free each native name */
|
||||||
|
vec_for(prog->natives, i) { free(vec_at(prog->natives, i).name); }
|
||||||
|
vec_free(prog->natives);
|
||||||
|
/* free each gdata entry */
|
||||||
|
vec_for(prog->gdata, i) { free(vec_at(prog->gdata, i).data); }
|
||||||
|
vec_free(prog->gdata);
|
||||||
|
/* free each strtab entry */
|
||||||
|
vec_for(prog->strtab, i) { free((void *)vec_at(prog->strtab, i)); }
|
||||||
|
vec_free(prog->strtab);
|
||||||
|
map_free(prog->symtab);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- LE read/write helpers ---- */
|
||||||
|
|
||||||
|
static inline spl_val_t rd64(const unsigned char **p) {
|
||||||
|
spl_val_t v = (spl_val_t)(*p)[0] | ((spl_val_t)(*p)[1] << 8) | ((spl_val_t)(*p)[2] << 16) |
|
||||||
|
((spl_val_t)(*p)[3] << 24) | ((spl_val_t)(*p)[4] << 32) |
|
||||||
|
((spl_val_t)(*p)[5] << 40) | ((spl_val_t)(*p)[6] << 48) |
|
||||||
|
((spl_val_t)(*p)[7] << 56);
|
||||||
|
*p += 8;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void wr64(unsigned char **p, spl_val_t v) {
|
||||||
|
*(*p)++ = (unsigned char)(v);
|
||||||
|
*(*p)++ = (unsigned char)(v >> 8);
|
||||||
|
*(*p)++ = (unsigned char)(v >> 16);
|
||||||
|
*(*p)++ = (unsigned char)(v >> 24);
|
||||||
|
*(*p)++ = (unsigned char)(v >> 32);
|
||||||
|
*(*p)++ = (unsigned char)(v >> 40);
|
||||||
|
*(*p)++ = (unsigned char)(v >> 48);
|
||||||
|
*(*p)++ = (unsigned char)(v >> 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Round n up to next multiple of 8 */
|
||||||
|
#define ALIGN8(n) (((n) + 7) & ~7)
|
||||||
|
|
||||||
|
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||||
|
FILE *f;
|
||||||
|
unsigned char *data;
|
||||||
|
long len;
|
||||||
|
const unsigned char *p;
|
||||||
|
spl_val_t nfuncs, ninsns, nnatives, nstrs, ndata;
|
||||||
|
|
||||||
|
if (!fname || !prog)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
f = fopen(fname, "rb");
|
||||||
|
if (!f)
|
||||||
|
return -1;
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
len = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
if (len < 48) {
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
data = (unsigned char *)malloc((size_t)len);
|
||||||
|
if (!data) {
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fread(data, 1, (size_t)len, f) != (size_t)len) {
|
||||||
|
free(data);
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
p = data;
|
||||||
|
|
||||||
|
/* magic */
|
||||||
|
if (p[0] != 'S' || p[1] != 'P' || p[2] != 'L' || p[3] != 'B' || p[4] != 'I' || p[5] != 'N' ||
|
||||||
|
p[6] != '\0' || p[7] != '\0') {
|
||||||
|
free(data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
p += 8;
|
||||||
|
|
||||||
|
spl_prog_init(prog);
|
||||||
|
|
||||||
|
/* header counts */
|
||||||
|
nfuncs = rd64(&p);
|
||||||
|
ninsns = rd64(&p);
|
||||||
|
nnatives = rd64(&p);
|
||||||
|
nstrs = rd64(&p);
|
||||||
|
ndata = rd64(&p);
|
||||||
|
|
||||||
|
/* ---- function table ---- */
|
||||||
|
for (spl_val_t i = 0; i < nfuncs; i++) {
|
||||||
|
spl_func_t func = {0};
|
||||||
|
spl_val_t nlen = rd64(&p);
|
||||||
|
usize pad = ALIGN8((usize)nlen) - (usize)nlen;
|
||||||
|
|
||||||
|
func.name = (char *)malloc((usize)nlen);
|
||||||
|
if (!func.name) {
|
||||||
|
free(data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(func.name, p, (usize)nlen);
|
||||||
|
p += (usize)nlen + pad;
|
||||||
|
|
||||||
|
func.idx_of_strtab = rd64(&p);
|
||||||
|
func.nargs = rd64(&p);
|
||||||
|
func.ninsns = rd64(&p);
|
||||||
|
func.address = rd64(&p);
|
||||||
|
vec_push(prog->funcs, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- instructions ---- */
|
||||||
|
for (spl_val_t i = 0; i < ninsns; i++) {
|
||||||
|
if ((size_t)(p - data) + 12 > (size_t)len) {
|
||||||
|
free(data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
spl_ins_t ins;
|
||||||
|
ins.opcode = (uint16_t)p[0] | ((uint16_t)p[1] << 8);
|
||||||
|
ins.type = (uint16_t)p[2] | ((uint16_t)p[3] << 8);
|
||||||
|
p += 4;
|
||||||
|
ins.imm = rd64(&p);
|
||||||
|
vec_push(prog->insns, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- native table ---- */
|
||||||
|
for (spl_val_t i = 0; i < nnatives; i++) {
|
||||||
|
spl_native_t nat = {0};
|
||||||
|
spl_val_t nlen = rd64(&p);
|
||||||
|
usize pad = ALIGN8((usize)nlen) - (usize)nlen;
|
||||||
|
|
||||||
|
nat.name = (char *)malloc((usize)nlen);
|
||||||
|
if (!nat.name) {
|
||||||
|
free(data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(nat.name, p, (usize)nlen);
|
||||||
|
p += (usize)nlen + pad;
|
||||||
|
|
||||||
|
nat.idx_of_strtab = rd64(&p);
|
||||||
|
nat.impl_fn = NULL; /* function pointer can't be serialised */
|
||||||
|
vec_push(prog->natives, nat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- string table ---- */
|
||||||
|
for (spl_val_t i = 0; i < nstrs; i++) {
|
||||||
|
spl_val_t slen = rd64(&p);
|
||||||
|
usize pad = ALIGN8((usize)slen) - (usize)slen;
|
||||||
|
char *s = (char *)malloc((usize)slen + 1);
|
||||||
|
if (!s) {
|
||||||
|
free(data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(s, p, (usize)slen);
|
||||||
|
s[(usize)slen] = '\0';
|
||||||
|
p += (usize)slen + pad;
|
||||||
|
vec_push(prog->strtab, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- global data ---- */
|
||||||
|
for (spl_val_t i = 0; i < ndata; i++) {
|
||||||
|
spl_gdata_t entry;
|
||||||
|
entry.size = rd64(&p);
|
||||||
|
usize pad = ALIGN8(entry.size) - entry.size;
|
||||||
|
entry.data = (unsigned char *)malloc(entry.size);
|
||||||
|
if (!entry.data) {
|
||||||
|
free(data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(entry.data, p, entry.size);
|
||||||
|
p += entry.size + pad;
|
||||||
|
vec_push(prog->gdata, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog) {
|
||||||
|
unsigned char *buf, *p;
|
||||||
|
spl_val_t i;
|
||||||
|
size_t total;
|
||||||
|
spl_val_t nfuncs, ninsns, nnatives, nstrs, ndata;
|
||||||
|
usize nlen, pad;
|
||||||
|
|
||||||
|
if (!fname || !prog)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
nfuncs = vec_size(prog->funcs);
|
||||||
|
ninsns = vec_size(prog->insns);
|
||||||
|
nnatives = vec_size(prog->natives);
|
||||||
|
nstrs = vec_size(prog->strtab);
|
||||||
|
ndata = vec_size(prog->gdata);
|
||||||
|
|
||||||
|
/* Calculate total size */
|
||||||
|
total = 8 /* magic */
|
||||||
|
+ 8 + 8 + 8 + 8 + 8; /* 5 counts (nfuncs+ninsns+nnatives+nstrs+ndata) */
|
||||||
|
|
||||||
|
/* funcs */
|
||||||
|
for (i = 0; i < nfuncs; i++) {
|
||||||
|
nlen = strlen(vec_at(prog->funcs, i).name) + 1; /* include null */
|
||||||
|
total +=
|
||||||
|
8 + ALIGN8(nlen) + 8 + 8 + 8 + 8; /* nlen + name(pad) + idx + nargs + ninsns + addr */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* insns */
|
||||||
|
total += ninsns * 12; /* opcode(2) + type(2) + imm(8) */
|
||||||
|
|
||||||
|
/* natives */
|
||||||
|
for (i = 0; i < nnatives; i++) {
|
||||||
|
nlen = strlen(vec_at(prog->natives, i).name) + 1;
|
||||||
|
total += 8 + ALIGN8(nlen) + 8; /* nlen + name(pad) + idx_of_strtab */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strtab */
|
||||||
|
for (i = 0; i < nstrs; i++) {
|
||||||
|
nlen = strlen(vec_at(prog->strtab, i));
|
||||||
|
total += 8 + ALIGN8(nlen); /* slen + str(pad) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gdata */
|
||||||
|
for (i = 0; i < ndata; i++) {
|
||||||
|
usize dsize = vec_at(prog->gdata, i).size;
|
||||||
|
total += 8 + ALIGN8(dsize); /* dsize + data(pad) */
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = (unsigned char *)malloc(total);
|
||||||
|
if (!buf)
|
||||||
|
return -1;
|
||||||
|
p = buf;
|
||||||
|
|
||||||
|
/* magic */
|
||||||
|
memcpy(p, "SPLBIN\0\0", 8);
|
||||||
|
p += 8;
|
||||||
|
|
||||||
|
/* counts */
|
||||||
|
wr64(&p, nfuncs);
|
||||||
|
wr64(&p, ninsns);
|
||||||
|
wr64(&p, nnatives);
|
||||||
|
wr64(&p, nstrs);
|
||||||
|
wr64(&p, ndata);
|
||||||
|
|
||||||
|
/* ---- function table ---- */
|
||||||
|
for (i = 0; i < nfuncs; i++) {
|
||||||
|
const char *name = vec_at(prog->funcs, i).name;
|
||||||
|
nlen = strlen(name) + 1;
|
||||||
|
pad = ALIGN8(nlen) - nlen;
|
||||||
|
wr64(&p, nlen);
|
||||||
|
memcpy(p, name, nlen);
|
||||||
|
p += nlen;
|
||||||
|
memset(p, 0, pad);
|
||||||
|
p += pad;
|
||||||
|
wr64(&p, vec_at(prog->funcs, i).idx_of_strtab);
|
||||||
|
wr64(&p, vec_at(prog->funcs, i).nargs);
|
||||||
|
wr64(&p, vec_at(prog->funcs, i).ninsns);
|
||||||
|
wr64(&p, vec_at(prog->funcs, i).address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- instructions ---- */
|
||||||
|
for (i = 0; i < ninsns; i++) {
|
||||||
|
spl_ins_t *ins = &vec_at(prog->insns, i);
|
||||||
|
*p++ = (unsigned char)(ins->opcode);
|
||||||
|
*p++ = (unsigned char)(ins->opcode >> 8);
|
||||||
|
*p++ = (unsigned char)(ins->type);
|
||||||
|
*p++ = (unsigned char)(ins->type >> 8);
|
||||||
|
wr64(&p, ins->imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- native table ---- */
|
||||||
|
for (i = 0; i < nnatives; i++) {
|
||||||
|
const char *name = vec_at(prog->natives, i).name;
|
||||||
|
nlen = strlen(name) + 1;
|
||||||
|
pad = ALIGN8(nlen) - nlen;
|
||||||
|
wr64(&p, nlen);
|
||||||
|
memcpy(p, name, nlen);
|
||||||
|
p += nlen;
|
||||||
|
memset(p, 0, pad);
|
||||||
|
p += pad;
|
||||||
|
wr64(&p, vec_at(prog->natives, i).idx_of_strtab);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- string table ---- */
|
||||||
|
for (i = 0; i < nstrs; i++) {
|
||||||
|
const char *s = vec_at(prog->strtab, i);
|
||||||
|
nlen = strlen(s);
|
||||||
|
pad = ALIGN8(nlen) - nlen;
|
||||||
|
wr64(&p, nlen);
|
||||||
|
memcpy(p, s, nlen);
|
||||||
|
p += nlen;
|
||||||
|
memset(p, 0, pad);
|
||||||
|
p += pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- global data ---- */
|
||||||
|
for (i = 0; i < ndata; i++) {
|
||||||
|
spl_gdata_t *entry = &vec_at(prog->gdata, i);
|
||||||
|
pad = ALIGN8(entry->size) - entry->size;
|
||||||
|
wr64(&p, entry->size);
|
||||||
|
memcpy(p, entry->data, entry->size);
|
||||||
|
p += entry->size;
|
||||||
|
memset(p, 0, pad);
|
||||||
|
p += pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write file */
|
||||||
|
FILE *f = fopen(fname, "wb");
|
||||||
|
if (!f) {
|
||||||
|
free(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fwrite(buf, 1, total, f);
|
||||||
|
fclose(f);
|
||||||
|
free(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spl_prog_add_func(spl_prog_t *prog, spl_func_t *func) {
|
||||||
|
if (!prog || !func)
|
||||||
|
return 0;
|
||||||
|
vec_push(prog->funcs, *func);
|
||||||
|
return vec_size(prog->funcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int spl_prog_add_native(spl_prog_t *prog, spl_native_t *native) {
|
||||||
|
if (!prog || !native)
|
||||||
|
return 0;
|
||||||
|
vec_push(prog->natives, *native);
|
||||||
|
return vec_size(prog->natives);
|
||||||
|
}
|
||||||
|
|
||||||
|
int spl_prog_add_data(spl_prog_t *prog, void *ptr, usize size) {
|
||||||
|
spl_gdata_t entry;
|
||||||
|
if (!prog)
|
||||||
|
return 0;
|
||||||
|
entry.data = (unsigned char *)malloc(size);
|
||||||
|
if (!entry.data)
|
||||||
|
return 0;
|
||||||
|
memcpy(entry.data, ptr, size);
|
||||||
|
entry.size = size;
|
||||||
|
vec_push(prog->gdata, entry);
|
||||||
|
return vec_size(prog->gdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_func_t *spl_prog_get_func(spl_prog_t *prog, const char *name) {
|
||||||
|
if (!prog || !name)
|
||||||
|
return NULL;
|
||||||
|
vec_for(prog->funcs, i) {
|
||||||
|
const char *match_name = vec_at(prog->funcs, i).name;
|
||||||
|
if (match_name && strcmp(match_name, name) == 0) {
|
||||||
|
return &vec_at(prog->funcs, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_native_t *spl_prog_get_native(spl_prog_t *prog, const char *name) {
|
||||||
|
if (!prog || !name)
|
||||||
|
return NULL;
|
||||||
|
vec_for(prog->natives, i) {
|
||||||
|
const char *match_name = vec_at(prog->natives, i).name;
|
||||||
|
if (match_name && strcmp(match_name, name) == 0) {
|
||||||
|
return &vec_at(prog->natives, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *opcode_name[] = {
|
||||||
|
#define X(opcode, name, argc, pop, push, desc) [opcode] = name,
|
||||||
|
SPL_OPCODES(X)
|
||||||
|
#undef X
|
||||||
|
};
|
||||||
|
const char *spl_opcode_name(spl_opcode_t opcode) { return opcode_name[opcode]; }
|
||||||
|
const char *spl_type_tag_name(spl_type_t type) {
|
||||||
|
switch (type) {
|
||||||
|
case SPL_VOID:
|
||||||
|
return "void";
|
||||||
|
case SPL_BOOL:
|
||||||
|
return "bool";
|
||||||
|
case SPL_I8:
|
||||||
|
return "i8";
|
||||||
|
case SPL_U8:
|
||||||
|
return "u8";
|
||||||
|
case SPL_I16:
|
||||||
|
return "i16";
|
||||||
|
case SPL_U16:
|
||||||
|
return "u16";
|
||||||
|
case SPL_I32:
|
||||||
|
return "i32";
|
||||||
|
case SPL_U32:
|
||||||
|
return "u32";
|
||||||
|
case SPL_I64:
|
||||||
|
return "i64";
|
||||||
|
case SPL_U64:
|
||||||
|
return "u64";
|
||||||
|
case SPL_F32:
|
||||||
|
return "f32";
|
||||||
|
case SPL_F64:
|
||||||
|
return "f64";
|
||||||
|
case SPL_USIZE:
|
||||||
|
return "usize";
|
||||||
|
case SPL_ISIZE:
|
||||||
|
return "isize";
|
||||||
|
case SPL_PTR:
|
||||||
|
return "ptr";
|
||||||
|
default:
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void spl_ins_dump(spl_ins_t *ins, spl_val_t addr) {
|
||||||
|
printf("%4zu: %s", addr, spl_opcode_name((spl_opcode_t)ins->opcode));
|
||||||
|
if (ins->type != SPL_VOID)
|
||||||
|
printf(" %s", spl_type_tag_name((spl_type_t)ins->type));
|
||||||
|
printf(" %zd:%zx", ins->imm, ins->imm);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
/* spl_mcode.h - SPL VM Machine Code: instruction set and binary format
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPL_MCODE_H__
|
||||||
|
#define __SPL_MCODE_H__
|
||||||
|
|
||||||
|
#include "include/core_map.h"
|
||||||
|
#include "include/core_vec.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef uintptr_t usize;
|
||||||
|
typedef intptr_t isize;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SPL_VOID,
|
||||||
|
SPL_BOOL,
|
||||||
|
SPL_I8,
|
||||||
|
SPL_U8,
|
||||||
|
SPL_I16,
|
||||||
|
SPL_U16,
|
||||||
|
SPL_I32,
|
||||||
|
SPL_U32,
|
||||||
|
SPL_I64,
|
||||||
|
SPL_U64,
|
||||||
|
SPL_ISIZE,
|
||||||
|
SPL_USIZE,
|
||||||
|
SPL_F32,
|
||||||
|
SPL_F64,
|
||||||
|
SPL_PTR,
|
||||||
|
SPL_TYPE_COUNT,
|
||||||
|
} spl_type_t;
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
#define SPL_OPCODES(X) \
|
||||||
|
/* opcode, name, argc, pop, push, desc */ \
|
||||||
|
X(SPL_ERROR, "error", 0, 0, 0, "invalid opcode / internal error") \
|
||||||
|
/* 栈操作 */ \
|
||||||
|
X(SPL_PUSH, "push", 1, 0, 1, "push immediate") \
|
||||||
|
X(SPL_DUP, "dup", 0, 0, 1, "duplicate top of stack") \
|
||||||
|
X(SPL_DROP, "drop", 0, 1, 0, "discard top of stack") \
|
||||||
|
X(SPL_SWAP, "swap", 0, 2, 2, "swap top two elements") \
|
||||||
|
X(SPL_PICK, "pick", 1, 0, 1, "push copy of stack[imm] (0=TOS)") \
|
||||||
|
X(SPL_ROT, "rot", 0, 3, 3, "rotate top three (a b c -- b c a)") \
|
||||||
|
/* 算术 */ \
|
||||||
|
X(SPL_ADD, "add", 0, 2, 1, "integer addition") \
|
||||||
|
X(SPL_SUB, "sub", 0, 2, 1, "integer subtraction") \
|
||||||
|
X(SPL_MUL, "mul", 0, 2, 1, "integer multiplication") \
|
||||||
|
X(SPL_DIV_S, "div_s", 0, 2, 1, "signed division") \
|
||||||
|
X(SPL_DIV_U, "div_u", 0, 2, 1, "unsigned division") \
|
||||||
|
X(SPL_REM_S, "rem_s", 0, 2, 1, "signed remainder") \
|
||||||
|
X(SPL_REM_U, "rem_u", 0, 2, 1, "unsigned remainder") \
|
||||||
|
X(SPL_NEG, "neg", 0, 1, 1, "two's complement negation") \
|
||||||
|
/* 位运算 */ \
|
||||||
|
X(SPL_AND, "and", 0, 2, 1, "bitwise AND") \
|
||||||
|
X(SPL_OR, "or", 0, 2, 1, "bitwise OR") \
|
||||||
|
X(SPL_XOR, "xor", 0, 2, 1, "bitwise XOR") \
|
||||||
|
X(SPL_NOT, "not", 0, 1, 1, "bitwise NOT") \
|
||||||
|
X(SPL_SHL, "shl", 0, 2, 1, "left shift") \
|
||||||
|
X(SPL_SHR_U, "shr_u", 0, 2, 1, "logical right shift (zero-fill)") \
|
||||||
|
X(SPL_SHR_S, "shr_s", 0, 2, 1, "arithmetic right shift (sign-fill)") \
|
||||||
|
/* 比较 */ \
|
||||||
|
X(SPL_EQ, "eq", 0, 2, 1, "equal") \
|
||||||
|
X(SPL_NE, "ne", 0, 2, 1, "not equal") \
|
||||||
|
X(SPL_SLT, "slt", 0, 2, 1, "signed less than") \
|
||||||
|
X(SPL_SLE, "sle", 0, 2, 1, "signed less or equal") \
|
||||||
|
X(SPL_ULT, "ult", 0, 2, 1, "unsigned less than") \
|
||||||
|
X(SPL_ULE, "ule", 0, 2, 1, "unsigned less or equal") \
|
||||||
|
X(SPL_SGT, "sgt", 0, 2, 1, "signed greater than") \
|
||||||
|
X(SPL_SGE, "sge", 0, 2, 1, "signed greater or equal") \
|
||||||
|
X(SPL_UGT, "ugt", 0, 2, 1, "unsigned greater than") \
|
||||||
|
X(SPL_UGE, "uge", 0, 2, 1, "unsigned greater or equal") \
|
||||||
|
/* 类型转换 */ \
|
||||||
|
X(SPL_TRUNC, "trunc", 1, 1, 1, "truncate to low imm bits") \
|
||||||
|
X(SPL_SEXT, "sext", 1, 1, 1, "sign-extend from bit imm") \
|
||||||
|
X(SPL_ZEXT, "zext", 1, 1, 1, "zero-extend from bit imm") \
|
||||||
|
/* 控制流 */ \
|
||||||
|
X(SPL_JMP, "jmp", 1, 0, 0, "unconditional jump (offset isize)") \
|
||||||
|
X(SPL_BZ, "bz", 1, 1, 0, "pop; jump if zero (offset isize)") \
|
||||||
|
X(SPL_BNZ, "bnz", 1, 1, 0, "pop; jump if non-zero (offset isize)") \
|
||||||
|
X(SPL_HALT, "halt", 0, 0, 0, "stop execution") \
|
||||||
|
/* 函数调用 */ \
|
||||||
|
X(SPL_CALL, "call", 1, 0, 0, "call function pop function offset, call it") \
|
||||||
|
X(SPL_CALLI, "calli", 0, 1, 0, "indirect call: pop function address, call it") \
|
||||||
|
X(SPL_RET, "ret", 0, 0, 0, "return from function") \
|
||||||
|
/* 栈帧局部变量 */ \
|
||||||
|
X(SPL_ALLOC, "alloc", 1, 0, 0, "allocate imm zero-slots on stack") \
|
||||||
|
X(SPL_LADDR, "laddr", 1, 0, 1, "push address of local at fp + imm bytes") \
|
||||||
|
X(SPL_GADDR, "gaddr", 1, 0, 1, "push address of global at gp + imm") \
|
||||||
|
/* 间接内存访问 */ \
|
||||||
|
X(SPL_LOAD, "load", 0, 1, 1, "load sizeof(type)-bits zero-extended") \
|
||||||
|
X(SPL_STORE, "store", 0, 2, 0, "store low sizeof(type)-bits") \
|
||||||
|
/* 原生接口 */ \
|
||||||
|
X(SPL_NCALL, "ncall", 1, 0, 1, "call native function by index") \
|
||||||
|
X(SPL_NLIB, "nlib", 1, 0, 0, "dlopen library (name idx)") \
|
||||||
|
/* 调试 */ \
|
||||||
|
X(SPL_BK, "breakpoint", 0, 0, 0, "break point when exec will stop run") \
|
||||||
|
X(SPL_DBG, "dbg", 0, 0, 0, "print VM debug info (stack, backtrace, locals)")
|
||||||
|
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
#define X(opcode, name, argc, pop, push, desc) opcode,
|
||||||
|
SPL_OPCODES(X)
|
||||||
|
#undef X
|
||||||
|
} spl_opcode_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Binary format (all metadata fields spl_val_t LE):
|
||||||
|
* magic[8] = "SPLBIN\0\0"
|
||||||
|
* nfuncs, ninsns, nnatives, nstrs, ndata
|
||||||
|
* [func table] each: name_len, name(pad8), idx_of_strtab, nargs, ninsns, address
|
||||||
|
* [insns] each: opcode(2) type(2) imm(8) = 12 bytes
|
||||||
|
* [natives] each: name_len, name(pad8), idx_of_strtab
|
||||||
|
* [strtab] each: slen, str(pad8)
|
||||||
|
* [gdata] each: dsize(8), data(dsize bytes, padded to 8)
|
||||||
|
*
|
||||||
|
* === Calling Convention ===
|
||||||
|
*
|
||||||
|
* Before CALL:
|
||||||
|
* - args pushed left-to-right
|
||||||
|
* - target address pushed last
|
||||||
|
*
|
||||||
|
* CALL (imm = nargs):
|
||||||
|
* 1. pop target address
|
||||||
|
* 2. callstack[cp++] = {saved_fp, saved_ip, nargs}
|
||||||
|
* 3. fp = sp - nargs (fp points to arg0)
|
||||||
|
* 4. ip = target address
|
||||||
|
*
|
||||||
|
* ALLOC k:
|
||||||
|
* sp += k (slots zeroed; local[j] = stacks.data[fp + nargs + j])
|
||||||
|
*
|
||||||
|
* LADDR imm:
|
||||||
|
* push &stacks.data[fp + imm]
|
||||||
|
* (imm < nargs accesses args; imm >= nargs accesses locals)
|
||||||
|
*
|
||||||
|
* GADDR imm:
|
||||||
|
* push prog->gdata[imm] (pointer to global data blob)
|
||||||
|
*
|
||||||
|
* RET:
|
||||||
|
* 1. pop retval if non-void type
|
||||||
|
* 2. sp = fp
|
||||||
|
* 3. pop frame; fp = saved_fp, ip = saved_ip
|
||||||
|
* 4. push retval if non-void type
|
||||||
|
*
|
||||||
|
* CALLI (indirect call):
|
||||||
|
* - stack before: ..., arg0, ..., argN-1, nargs, func_addr
|
||||||
|
* 1. pop func_addr, then pop nargs
|
||||||
|
* 2. same as CALL steps 2-4
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SPL_BINFMT_MAGIC "SPLBIN\0\0"
|
||||||
|
// All SIR stack values are ptr-bit unsigned integers
|
||||||
|
typedef usize spl_val_t;
|
||||||
|
|
||||||
|
typedef struct spl_ins {
|
||||||
|
uint8_t opcode;
|
||||||
|
uint8_t type;
|
||||||
|
spl_val_t imm;
|
||||||
|
} spl_ins_t;
|
||||||
|
typedef VEC(spl_ins_t) spl_ins_vec_t;
|
||||||
|
|
||||||
|
typedef struct spl_func {
|
||||||
|
char *name;
|
||||||
|
spl_val_t idx_of_strtab;
|
||||||
|
spl_val_t nargs;
|
||||||
|
spl_val_t ninsns;
|
||||||
|
spl_val_t address;
|
||||||
|
} spl_func_t;
|
||||||
|
typedef VEC(spl_func_t) spl_func_vec_t;
|
||||||
|
|
||||||
|
/* Native function pointer type */
|
||||||
|
typedef spl_val_t (*spl_fn_t)(int nargs, spl_val_t *args);
|
||||||
|
typedef struct spl_native {
|
||||||
|
char *name;
|
||||||
|
spl_val_t idx_of_strtab;
|
||||||
|
spl_fn_t impl_fn;
|
||||||
|
} spl_native_t;
|
||||||
|
typedef VEC(spl_native_t) spl_native_vec_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned char *data;
|
||||||
|
usize size;
|
||||||
|
} spl_gdata_t;
|
||||||
|
typedef VEC(spl_gdata_t) spl_data_t;
|
||||||
|
typedef VEC(const char *) spl_strtab_t;
|
||||||
|
typedef MAP(const char *, const char *) spl_symtab_t;
|
||||||
|
/* Opaque handle for loaded program */
|
||||||
|
typedef struct spl_prog {
|
||||||
|
spl_ins_vec_t insns;
|
||||||
|
spl_func_vec_t funcs;
|
||||||
|
spl_native_vec_t natives;
|
||||||
|
spl_data_t gdata;
|
||||||
|
spl_strtab_t strtab;
|
||||||
|
spl_symtab_t symtab;
|
||||||
|
} spl_prog_t;
|
||||||
|
|
||||||
|
void spl_prog_init(spl_prog_t *prog);
|
||||||
|
void spl_prog_drop(spl_prog_t *prog);
|
||||||
|
|
||||||
|
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog);
|
||||||
|
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog);
|
||||||
|
|
||||||
|
int spl_prog_add_data(spl_prog_t *prog, void *ptr, usize size);
|
||||||
|
int spl_prog_add_func(spl_prog_t *prog, spl_func_t *func);
|
||||||
|
int spl_prog_add_native(spl_prog_t *prog, spl_native_t *native);
|
||||||
|
|
||||||
|
spl_func_t *spl_prog_get_func(spl_prog_t *prog, const char *name);
|
||||||
|
spl_native_t *spl_prog_get_native(spl_prog_t *prog, const char *name);
|
||||||
|
|
||||||
|
/* Opcode name lookup for debugging/dumping */
|
||||||
|
const char *spl_opcode_name(spl_opcode_t opcode);
|
||||||
|
const char *spl_type_tag_name(spl_type_t type);
|
||||||
|
|
||||||
|
void spl_ins_dump(spl_ins_t *ins, spl_val_t addr);
|
||||||
|
|
||||||
|
#endif /* __SPL_MCODE_H__ */
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
/* spl_syscall.c — built-in syscall implementations + registration
|
||||||
|
*
|
||||||
|
* All syscalls validate nargs, cast spl_val_t args to the expected C types,
|
||||||
|
* execute, and return the result as spl_val_t.
|
||||||
|
*
|
||||||
|
* Simple syscalls use the SYSCALL_N macros; complex ones are written
|
||||||
|
* manually but follow the same template.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spl_syscall.h"
|
||||||
|
#include "include/core_map.h"
|
||||||
|
#include "include/core_vec.h"
|
||||||
|
#include "spl_mcode.h"
|
||||||
|
#include "spl_vm.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* =================================================================
|
||||||
|
* Macro templates
|
||||||
|
*
|
||||||
|
* Each SYSCALL_N macro:
|
||||||
|
* 1. Validates nargs (prints error and returns -1 on mismatch)
|
||||||
|
* 2. Casts args[n] from spl_val_t to the specified C type via
|
||||||
|
* (type)(uintptr_t) — this handles both integer and pointer types
|
||||||
|
* 3. Evaluates the expression and returns the result as spl_val_t
|
||||||
|
* ================================================================= */
|
||||||
|
|
||||||
|
#define CHECK_NARGS(fname, expected) \
|
||||||
|
do { \
|
||||||
|
if (nargs != (expected)) { \
|
||||||
|
fprintf(stderr, fname ": expected " #expected " args, got %d\n", nargs); \
|
||||||
|
return -1; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SYSCALL_0(name, ret_expr) \
|
||||||
|
static spl_val_t name(int nargs, spl_val_t *args) { \
|
||||||
|
CHECK_NARGS(#name, 0); \
|
||||||
|
(void)args; \
|
||||||
|
return (spl_val_t)(uintptr_t)(ret_expr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SYSCALL_1(name, t1, ret_expr) \
|
||||||
|
static spl_val_t name(int nargs, spl_val_t *args) { \
|
||||||
|
CHECK_NARGS(#name, 1); \
|
||||||
|
t1 a1 = (t1)(uintptr_t)args[0]; \
|
||||||
|
return (spl_val_t)(uintptr_t)(ret_expr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SYSCALL_2(name, t1, t2, ret_expr) \
|
||||||
|
static spl_val_t name(int nargs, spl_val_t *args) { \
|
||||||
|
CHECK_NARGS(#name, 2); \
|
||||||
|
t1 a1 = (t1)(uintptr_t)args[0]; \
|
||||||
|
t2 a2 = (t2)(uintptr_t)args[1]; \
|
||||||
|
return (spl_val_t)(uintptr_t)(ret_expr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SYSCALL_3(name, t1, t2, t3, ret_expr) \
|
||||||
|
static spl_val_t name(int nargs, spl_val_t *args) { \
|
||||||
|
CHECK_NARGS(#name, 3); \
|
||||||
|
t1 a1 = (t1)(uintptr_t)args[0]; \
|
||||||
|
t2 a2 = (t2)(uintptr_t)args[1]; \
|
||||||
|
t3 a3 = (t3)(uintptr_t)args[2]; \
|
||||||
|
return (spl_val_t)(uintptr_t)(ret_expr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SYSCALL_4(name, t1, t2, t3, t4, ret_expr) \
|
||||||
|
static spl_val_t name(int nargs, spl_val_t *args) { \
|
||||||
|
CHECK_NARGS(#name, 4); \
|
||||||
|
t1 a1 = (t1)(uintptr_t)args[0]; \
|
||||||
|
t2 a2 = (t2)(uintptr_t)args[1]; \
|
||||||
|
t3 a3 = (t3)(uintptr_t)args[2]; \
|
||||||
|
t4 a4 = (t4)(uintptr_t)args[3]; \
|
||||||
|
return (spl_val_t)(uintptr_t)(ret_expr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =================================================================
|
||||||
|
* OS syscalls
|
||||||
|
* ================================================================= */
|
||||||
|
|
||||||
|
/* vm_exit — terminate process with the given exit code */
|
||||||
|
SYSCALL_1(vm_exit, int, (exit(a1), (spl_val_t)0))
|
||||||
|
|
||||||
|
/* vm_putchar — write a single character to stdout */
|
||||||
|
SYSCALL_1(vm_putchar, int, putchar(a1))
|
||||||
|
|
||||||
|
/* vm_getchar — read a single character from stdin */
|
||||||
|
SYSCALL_0(vm_getchar, getchar())
|
||||||
|
|
||||||
|
/* vm_putint — print an integer to stdout */
|
||||||
|
SYSCALL_1(vm_putint, int, (fprintf(stdout, "%d", a1), (spl_val_t)0))
|
||||||
|
|
||||||
|
/* vm_putstr — print a string to stdout (no trailing newline) */
|
||||||
|
SYSCALL_1(vm_putstr, const char *, (fputs(a1, stdout), (spl_val_t)0))
|
||||||
|
|
||||||
|
/* vm_fopen — open a file, returns FILE* as spl_val_t */
|
||||||
|
SYSCALL_2(vm_fopen, const char *, const char *, (uintptr_t)fopen(a1, a2))
|
||||||
|
|
||||||
|
/* vm_fclose — close a file, returns 0 on success */
|
||||||
|
SYSCALL_1(vm_fclose, FILE *, fclose(a1))
|
||||||
|
|
||||||
|
/* vm_fread — read from file, returns number of items read */
|
||||||
|
SYSCALL_4(vm_fread, void *, size_t, size_t, FILE *, fread(a1, a2, a3, a4))
|
||||||
|
|
||||||
|
/* vm_fwrite — write to file, returns number of items written */
|
||||||
|
SYSCALL_4(vm_fwrite, const void *, size_t, size_t, FILE *, fwrite(a1, a2, a3, a4))
|
||||||
|
|
||||||
|
/* vm_fsize — get file size in bytes */
|
||||||
|
static spl_val_t vm_fsize(int nargs, spl_val_t *args) {
|
||||||
|
CHECK_NARGS("vm_fsize", 1);
|
||||||
|
FILE *f = (FILE *)(uintptr_t)args[0];
|
||||||
|
long cur = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long sz = ftell(f);
|
||||||
|
fseek(f, cur, SEEK_SET);
|
||||||
|
return (spl_val_t)(uintptr_t)sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYSCALL_0(vm_stdin, stdin)
|
||||||
|
SYSCALL_0(vm_stdout, stdout)
|
||||||
|
SYSCALL_0(vm_stderr, stderr)
|
||||||
|
|
||||||
|
/* vm_read_file — read entire file into a malloc'd, null-terminated buffer */
|
||||||
|
static spl_val_t vm_read_file(int nargs, spl_val_t *args) {
|
||||||
|
CHECK_NARGS("vm_read_file", 1);
|
||||||
|
const char *path = (const char *)(uintptr_t)args[0];
|
||||||
|
if (path == nullptr) {
|
||||||
|
fprintf(stderr, "filepath can't be null");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
if (!f) {
|
||||||
|
fprintf(stderr, "filepath %s can't be open", path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long sz = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
char *buf = (char *)malloc((size_t)sz + 1);
|
||||||
|
if (!buf) {
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t nread = fread(buf, 1, (size_t)sz, f);
|
||||||
|
fclose(f);
|
||||||
|
buf[nread] = '\0';
|
||||||
|
return (spl_val_t)(uintptr_t)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vm_alloc — allocate memory (malloc) */
|
||||||
|
SYSCALL_1(vm_alloc, size_t, (uintptr_t)malloc(a1))
|
||||||
|
|
||||||
|
/* vm_free — free memory */
|
||||||
|
SYSCALL_1(vm_free, void *, (free(a1), (spl_val_t)0))
|
||||||
|
|
||||||
|
/* vm_realloc — reallocate memory (realloc) */
|
||||||
|
SYSCALL_2(vm_realloc, void *, size_t, (uintptr_t)realloc(a1, a2))
|
||||||
|
|
||||||
|
/* vm_strlen — get string length */
|
||||||
|
SYSCALL_1(vm_strlen, const char *, strlen(a1))
|
||||||
|
|
||||||
|
/* vm_strcmp — compare two strings */
|
||||||
|
SYSCALL_2(vm_strcmp, const char *, const char *, strcmp(a1, a2))
|
||||||
|
|
||||||
|
/* vm_memcpy — copy memory, returns dst */
|
||||||
|
SYSCALL_3(vm_memcpy, void *, const void *, size_t, (memcpy(a1, a2, a3), (uintptr_t)a1))
|
||||||
|
|
||||||
|
static spl_val_t vm_printf(int nargs, spl_val_t *args) {
|
||||||
|
(void)args;
|
||||||
|
if (nargs <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const char *fmt = (const char *)args[0];
|
||||||
|
usize fmt_len = strlen(fmt);
|
||||||
|
typedef VEC(char) string_builder_t;
|
||||||
|
string_builder_t buffer;
|
||||||
|
vec_init(buffer);
|
||||||
|
if (nargs <= 1) {
|
||||||
|
printf("%s", fmt);
|
||||||
|
return nargs;
|
||||||
|
}
|
||||||
|
int arg_idx = 0;
|
||||||
|
char tmp_buf[32];
|
||||||
|
memset(tmp_buf, 0, sizeof(tmp_buf));
|
||||||
|
for (usize i = 0; i < fmt_len; ++i) {
|
||||||
|
if (fmt[i] != '%') {
|
||||||
|
vec_push(buffer, fmt[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
arg_idx += 1;
|
||||||
|
if (++i >= fmt_len) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (fmt[i]) {
|
||||||
|
case '.': {
|
||||||
|
Assert(i + 2 < fmt_len && arg_idx + 1 <= nargs && fmt[i + 1] == '*' &&
|
||||||
|
fmt[i + 2] == 's');
|
||||||
|
for (usize j = 0; j < args[arg_idx]; ++j) {
|
||||||
|
vec_push(buffer, ((const char *)args[arg_idx + 1])[j]);
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
} break;
|
||||||
|
case 'd':
|
||||||
|
snprintf(tmp_buf, sizeof(tmp_buf), "%zd", args[arg_idx]);
|
||||||
|
for (usize j = 0; j < strlen(tmp_buf); ++j) {
|
||||||
|
vec_push(buffer, tmp_buf[j]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
vec_push(buffer, (char)args[arg_idx]);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
for (usize j = 0; j < strlen((const char *)args[arg_idx]); ++j) {
|
||||||
|
vec_push(buffer, ((const char *)args[arg_idx])[j]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec_push(buffer, '\0');
|
||||||
|
printf("%s", buffer.data);
|
||||||
|
vec_free(buffer);
|
||||||
|
return nargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =================================================================
|
||||||
|
* VM syscalls (for comptime — compile-time code execution)
|
||||||
|
*
|
||||||
|
* These let SPL code create isolated sub-VMs, load compiled .sir
|
||||||
|
* programs into them, push arguments, call functions, and run.
|
||||||
|
* ================================================================= */
|
||||||
|
|
||||||
|
/* vm_new — create a new sub-VM instance, returns spl_vm_t* */
|
||||||
|
static spl_val_t vm_new(int nargs, spl_val_t *args) {
|
||||||
|
CHECK_NARGS("vm_new", 0);
|
||||||
|
(void)args;
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
||||||
|
if (!vm)
|
||||||
|
return 0;
|
||||||
|
spl_vm_init(vm);
|
||||||
|
return (spl_val_t)(uintptr_t)vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vm_drop — destroy a sub-VM and its loaded program */
|
||||||
|
static spl_val_t vm_drop(int nargs, spl_val_t *args) {
|
||||||
|
CHECK_NARGS("vm_drop", 1);
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||||
|
if (!vm)
|
||||||
|
return 0;
|
||||||
|
if (vm->prog) {
|
||||||
|
spl_prog_drop(vm->prog);
|
||||||
|
free(vm->prog);
|
||||||
|
vm->prog = NULL;
|
||||||
|
}
|
||||||
|
spl_vm_drop(vm);
|
||||||
|
free(vm);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vm_load — load a .sir file, register syscalls, return spl_prog_t* */
|
||||||
|
static spl_val_t vm_load(int nargs, spl_val_t *args) {
|
||||||
|
CHECK_NARGS("vm_load", 2);
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||||
|
const char *path = (const char *)(uintptr_t)args[1];
|
||||||
|
if (!vm || !path)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
spl_prog_t *prog = (spl_prog_t *)malloc(sizeof(spl_prog_t));
|
||||||
|
if (!prog)
|
||||||
|
return -1;
|
||||||
|
if (spl_prog_load_from_file(path, prog) != 0) {
|
||||||
|
free(prog);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
spl_syscall_register(prog);
|
||||||
|
if (spl_vm_load_prog(vm, prog) != 0) {
|
||||||
|
spl_prog_drop(prog);
|
||||||
|
free(prog);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return (spl_val_t)(uintptr_t)prog;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vm_push — push a value onto the sub-VM's stack (for passing arguments) */
|
||||||
|
static spl_val_t vm_push(int nargs, spl_val_t *args) {
|
||||||
|
CHECK_NARGS("vm_push", 2);
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||||
|
spl_val_t val = args[1];
|
||||||
|
if (!vm)
|
||||||
|
return -1;
|
||||||
|
if (vm->sp >= vm->config.max_stack_depth) {
|
||||||
|
fprintf(stderr, "vm_push: stack overflow\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vm->stacks.data[vm->sp++] = val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vm_call — call a function by name with pre-pushed arguments
|
||||||
|
*
|
||||||
|
* Args (3): vm, function_name, nargs
|
||||||
|
* The arguments should already be on the sub-VM's stack via vm_push.
|
||||||
|
*
|
||||||
|
* Mirrors the CALL instruction's frame setup:
|
||||||
|
* - Pushes a sentinel frame (saved_ip=-1) if cp==0 so RET halts properly
|
||||||
|
* - Pushes the actual call frame
|
||||||
|
* - Sets fp = sp - nargs (so arg0 = data[fp], arg1 = data[fp+1], ...)
|
||||||
|
* - Sets ip to the function address
|
||||||
|
*/
|
||||||
|
static spl_val_t vm_call(int nargs, spl_val_t *args) {
|
||||||
|
CHECK_NARGS("vm_call", 3);
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||||
|
const char *name = (const char *)(uintptr_t)args[1];
|
||||||
|
spl_val_t call_nargs = args[2];
|
||||||
|
if (!vm || !name)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
spl_func_t *func = spl_prog_get_func(vm->prog, name);
|
||||||
|
if (!func) {
|
||||||
|
fprintf(stderr, "vm_call: function '%s' not found\n", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (vm->cp >= vm->config.max_call_depth - 1) {
|
||||||
|
fprintf(stderr, "vm_call: call stack overflow\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sentinel frame so the entry function's RET halts cleanly */
|
||||||
|
if (vm->cp == 0) {
|
||||||
|
vm->frames.data[vm->cp].saved_fp = 0;
|
||||||
|
vm->frames.data[vm->cp].saved_ip = (uintptr_t)-1;
|
||||||
|
vm->frames.data[vm->cp].nargs = 0;
|
||||||
|
vm->cp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual call frame (same logic as the CALL instruction) */
|
||||||
|
vm->frames.data[vm->cp].saved_fp = vm->fp;
|
||||||
|
vm->frames.data[vm->cp].saved_ip = vm->ip;
|
||||||
|
vm->frames.data[vm->cp].nargs = call_nargs;
|
||||||
|
vm->cp++;
|
||||||
|
vm->fp = vm->sp - call_nargs;
|
||||||
|
vm->ip = (uintptr_t)func->address;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vm_run — run a sub-VM to completion, returns exit_code */
|
||||||
|
SYSCALL_1(vm_run, spl_vm_t *, spl_vm_run_until(a1, 0))
|
||||||
|
|
||||||
|
/* =================================================================
|
||||||
|
* Registration
|
||||||
|
*
|
||||||
|
* Called after loading a .sir file. Matches native declarations in
|
||||||
|
* prog->natives against known syscall names and hooks up impl_fn.
|
||||||
|
* ================================================================= */
|
||||||
|
|
||||||
|
void spl_syscall_register(spl_prog_t *prog) {
|
||||||
|
static spl_native_t table[] = {
|
||||||
|
/* OS operations */
|
||||||
|
{"vm_exit", 0, vm_exit},
|
||||||
|
{"vm_putchar", 0, vm_putchar},
|
||||||
|
{"vm_getchar", 0, vm_getchar},
|
||||||
|
{"vm_putint", 0, vm_putint},
|
||||||
|
{"vm_putstr", 0, vm_putstr},
|
||||||
|
{"vm_fopen", 0, vm_fopen},
|
||||||
|
{"vm_fclose", 0, vm_fclose},
|
||||||
|
{"vm_fread", 0, vm_fread},
|
||||||
|
{"vm_fwrite", 0, vm_fwrite},
|
||||||
|
{"vm_fsize", 0, vm_fsize},
|
||||||
|
{"vm_stdin", 0, vm_stdin},
|
||||||
|
{"vm_stdout", 0, vm_stdout},
|
||||||
|
{"vm_stderr", 0, vm_stderr},
|
||||||
|
{"vm_read_file", 0, vm_read_file},
|
||||||
|
{"vm_alloc", 0, vm_alloc},
|
||||||
|
{"vm_free", 0, vm_free},
|
||||||
|
{"vm_realloc", 0, vm_realloc},
|
||||||
|
{"vm_strlen", 0, vm_strlen},
|
||||||
|
{"vm_strcmp", 0, vm_strcmp},
|
||||||
|
{"vm_memcpy", 0, vm_memcpy},
|
||||||
|
{"vm_printf", 0, vm_printf},
|
||||||
|
/* VM operations (comptime) */
|
||||||
|
{"vm_new", 0, vm_new},
|
||||||
|
{"vm_drop", 0, vm_drop},
|
||||||
|
{"vm_load", 0, vm_load},
|
||||||
|
{"vm_push", 0, vm_push},
|
||||||
|
{"vm_call", 0, vm_call},
|
||||||
|
{"vm_run", 0, vm_run},
|
||||||
|
};
|
||||||
|
int n = (int)(sizeof(table) / sizeof(table[0]));
|
||||||
|
if (!prog)
|
||||||
|
return;
|
||||||
|
vec_for(prog->natives, i) {
|
||||||
|
spl_native_t *nat = &vec_at(prog->natives, i);
|
||||||
|
if (!nat->name || nat->impl_fn)
|
||||||
|
continue;
|
||||||
|
for (int j = 0; j < n; j++) {
|
||||||
|
if (strcmp(nat->name, table[j].name) == 0) {
|
||||||
|
nat->impl_fn = table[j].impl_fn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/* spl_syscall.h — built-in syscall layer for SIR VM
|
||||||
|
*
|
||||||
|
* Provides I/O native functions (putchar, getchar, file ops, etc.)
|
||||||
|
* that compiled SPL programs can call via NCALL.
|
||||||
|
*
|
||||||
|
* spl_syscall_register() must be called AFTER spl_prog_load_from_file()
|
||||||
|
* and BEFORE spl_vm_run_until(). It matches native declarations in the
|
||||||
|
* loaded program against known syscall names and hooks up the C
|
||||||
|
* implementations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPL_SYSCALL_H__
|
||||||
|
#define __SPL_SYSCALL_H__
|
||||||
|
|
||||||
|
#include "spl_mcode.h"
|
||||||
|
|
||||||
|
/* Register all known built-in syscalls into prog->natives[].
|
||||||
|
* Entries whose name matches a known syscall get their impl_fn set;
|
||||||
|
* unmatched entries remain NULL (will error at runtime if called). */
|
||||||
|
void spl_syscall_register(spl_prog_t *prog);
|
||||||
|
|
||||||
|
#endif /* __SPL_SYSCALL_H__ */
|
||||||
+1137
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,68 @@
|
|||||||
|
/* spl_vm.h — SIR interpreter */
|
||||||
|
|
||||||
|
#ifndef __SPL_VM_H__
|
||||||
|
#define __SPL_VM_H__
|
||||||
|
|
||||||
|
#include "include/core_vec.h"
|
||||||
|
#include "spl_mcode.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define SPL_STACK_CANARY ((spl_val_t)0xDEADBEEFCAFEBABEull)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uintptr_t saved_sp;
|
||||||
|
uintptr_t saved_fp;
|
||||||
|
uintptr_t saved_ip;
|
||||||
|
spl_val_t nargs;
|
||||||
|
} spl_callframe_t;
|
||||||
|
|
||||||
|
typedef VEC(spl_val_t) spl_stack_vec_t;
|
||||||
|
typedef VEC(spl_callframe_t) spl_frame_vec_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
spl_stack_vec_t stacks;
|
||||||
|
spl_frame_vec_t frames;
|
||||||
|
uintptr_t gp; // global pointer
|
||||||
|
uintptr_t sp; // stack pointer
|
||||||
|
uintptr_t fp; // frame pointer
|
||||||
|
uintptr_t cp; // call pointer
|
||||||
|
uintptr_t ip; // instr pointer
|
||||||
|
int exit_code;
|
||||||
|
int trace; /* non-zero to print each instruction */
|
||||||
|
int debug; /* non-zero to enable canary checks */
|
||||||
|
int debug_addr; /* non-zero to check for low-address memory access */
|
||||||
|
spl_prog_t *prog;
|
||||||
|
char error_msg[1024];
|
||||||
|
struct {
|
||||||
|
uintptr_t max_stack_depth;
|
||||||
|
uintptr_t max_call_depth;
|
||||||
|
} config;
|
||||||
|
} spl_vm_t;
|
||||||
|
|
||||||
|
/* Initialize VM with default sizes (SPL_DEFAULT_STACK_SIZE /
|
||||||
|
* SPL_DEFAULT_CALL_DEPTH) */
|
||||||
|
void spl_vm_init(spl_vm_t *vm);
|
||||||
|
|
||||||
|
/* Initialize VM with custom sizes (pass 0 to use defaults) */
|
||||||
|
void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth);
|
||||||
|
|
||||||
|
/* Free dynamically allocated memory in VM */
|
||||||
|
void spl_vm_drop(spl_vm_t *vm);
|
||||||
|
|
||||||
|
int spl_vm_load_prog(spl_vm_t *vm, spl_prog_t *prog);
|
||||||
|
int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv, const char **envp);
|
||||||
|
|
||||||
|
/* Enable/disable instruction-level tracing */
|
||||||
|
void spl_vm_set_trace(spl_vm_t *vm, int enabled);
|
||||||
|
|
||||||
|
/* Enable/disable debug mode (stack canary protection) */
|
||||||
|
void spl_vm_set_debug(spl_vm_t *vm, int enabled);
|
||||||
|
|
||||||
|
int spl_vm_run_once(spl_vm_t *vm);
|
||||||
|
int spl_vm_run_until(spl_vm_t *vm, size_t step);
|
||||||
|
|
||||||
|
void spl_vm_dump_instr(spl_vm_t *vm, spl_val_t ip);
|
||||||
|
void spl_vm_stackdump(spl_vm_t *vm, spl_val_t sp);
|
||||||
|
int spl_vm_backtrace(spl_vm_t *vm, spl_val_t fp);
|
||||||
|
|
||||||
|
#endif /* __SPL_VM_H__ */
|
||||||
@@ -0,0 +1,876 @@
|
|||||||
|
/* test_spl_vm.c – unit tests for SPL VM using acutest.h
|
||||||
|
*
|
||||||
|
* Constructs SIR binaries, writes to temp files, loads via
|
||||||
|
* spl_prog_load_from_file, and runs via the spl_vm_t API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "include/acutest.h"
|
||||||
|
#include "spl_mcode.h"
|
||||||
|
#include "spl_vm.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Binary-writing helpers (mirrors spl_mcode.c internal format)
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
/* Write spl_val_t (8 LE bytes), advance pointer */
|
||||||
|
#define LE64(p, v) \
|
||||||
|
do { \
|
||||||
|
unsigned char *_p = (p); \
|
||||||
|
spl_val_t _v = (spl_val_t)(v); \
|
||||||
|
*_p++ = (unsigned char)(_v); \
|
||||||
|
*_p++ = (unsigned char)(_v >> 8); \
|
||||||
|
*_p++ = (unsigned char)(_v >> 16); \
|
||||||
|
*_p++ = (unsigned char)(_v >> 24); \
|
||||||
|
*_p++ = (unsigned char)(_v >> 32); \
|
||||||
|
*_p++ = (unsigned char)(_v >> 40); \
|
||||||
|
*_p++ = (unsigned char)(_v >> 48); \
|
||||||
|
*_p++ = (unsigned char)(_v >> 56); \
|
||||||
|
(p) = _p; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Convenience: build spl_ins_t from raw fields */
|
||||||
|
static spl_ins_t ins(uint16_t op, uint16_t type, spl_val_t imm) {
|
||||||
|
spl_ins_t x;
|
||||||
|
x.opcode = op;
|
||||||
|
x.type = type;
|
||||||
|
x.imm = imm;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build a single-function SIR binary in malloc'd memory.
|
||||||
|
* Returns buffer that must be freed by caller. */
|
||||||
|
static unsigned char *build_binary(const char *fname, spl_val_t nargs, const spl_ins_t *instns,
|
||||||
|
int ninsns, size_t *out_len) {
|
||||||
|
size_t nlen = strlen(fname) + 1;
|
||||||
|
size_t npad = ((nlen + 7) / 8) * 8 - nlen;
|
||||||
|
size_t sz = 8 + 8 + 8 + 8 + 8 + 8 /* magic + 5 counts */
|
||||||
|
+ 8 + nlen + npad + 8 + 8 + 8 + 8 /* func entry */
|
||||||
|
+ (size_t)ninsns * 12; /* instructions */
|
||||||
|
unsigned char *buf = (unsigned char *)malloc(sz);
|
||||||
|
unsigned char *p = buf;
|
||||||
|
|
||||||
|
memcpy(p, "SPLBIN\0\0", 8);
|
||||||
|
p += 8; /* magic */
|
||||||
|
LE64(p, 1); /* nfuncs */
|
||||||
|
LE64(p, (spl_val_t)ninsns); /* ninsns */
|
||||||
|
LE64(p, 0); /* nnatives */
|
||||||
|
LE64(p, 0); /* nstrs */
|
||||||
|
LE64(p, 0); /* ndata */
|
||||||
|
|
||||||
|
/* func entry */
|
||||||
|
LE64(p, (spl_val_t)nlen); /* name_len */
|
||||||
|
memcpy(p, fname, nlen);
|
||||||
|
p += nlen;
|
||||||
|
memset(p, 0, npad);
|
||||||
|
p += npad;
|
||||||
|
LE64(p, 0); /* idx_of_strtab */
|
||||||
|
LE64(p, nargs);
|
||||||
|
LE64(p, (spl_val_t)ninsns);
|
||||||
|
LE64(p, 0); /* address = 0 */
|
||||||
|
|
||||||
|
for (int i = 0; i < ninsns; i++) {
|
||||||
|
*p++ = (unsigned char)(instns[i].opcode);
|
||||||
|
*p++ = (unsigned char)(instns[i].opcode >> 8);
|
||||||
|
*p++ = (unsigned char)(instns[i].type);
|
||||||
|
*p++ = (unsigned char)(instns[i].type >> 8);
|
||||||
|
LE64(p, instns[i].imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_len = sz;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* VM test helpers (file-based prog load)
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static const char *TMPFILE = "test_spl_vm_tmp.bin";
|
||||||
|
|
||||||
|
/* Write binary buffer to temp file */
|
||||||
|
static int write_temp(const unsigned char *bin, size_t len) {
|
||||||
|
FILE *f = fopen(TMPFILE, "wb");
|
||||||
|
if (!f)
|
||||||
|
return -1;
|
||||||
|
fwrite(bin, 1, len, f);
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run a single-function program, return exit code (or -1 on failure). */
|
||||||
|
static int run(const spl_ins_t *instns, int ninsns) {
|
||||||
|
size_t len;
|
||||||
|
unsigned char *bin = build_binary("main", 0, instns, ninsns, &len);
|
||||||
|
spl_prog_t prog;
|
||||||
|
spl_vm_t vm;
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||||
|
if (spl_vm_load_prog(&vm, &prog) == 0) {
|
||||||
|
if (spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||||
|
int ret = spl_vm_run_until(&vm, 0);
|
||||||
|
if (ret == 1) /* halted normally */
|
||||||
|
rc = (int)vm.exit_code;
|
||||||
|
else if (ret == 0) /* still running (shouldn't happen with halt) */
|
||||||
|
rc = -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
remove(TMPFILE);
|
||||||
|
free(bin);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run with native functions registered. */
|
||||||
|
static int run_with_natives(const spl_ins_t *instns, int ninsns, spl_native_t *natives,
|
||||||
|
int nnatives) {
|
||||||
|
size_t len;
|
||||||
|
unsigned char *bin = build_binary("main", 0, instns, ninsns, &len);
|
||||||
|
spl_prog_t prog;
|
||||||
|
spl_vm_t vm;
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||||
|
for (int i = 0; i < nnatives; i++)
|
||||||
|
spl_prog_add_native(&prog, &natives[i]);
|
||||||
|
if (spl_vm_load_prog(&vm, &prog) == 0) {
|
||||||
|
if (spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||||
|
int ret = spl_vm_run_until(&vm, 0);
|
||||||
|
if (ret == 1)
|
||||||
|
rc = (int)vm.exit_code;
|
||||||
|
else if (ret == 0)
|
||||||
|
rc = -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
remove(TMPFILE);
|
||||||
|
free(bin);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Native function for NCALL tests
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static spl_val_t native_add_impl(int nargs, spl_val_t *args) {
|
||||||
|
return (nargs >= 2) ? args[0] + args[1] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Stack tests
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_push_imm(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 42), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 2) == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_dup(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 99), ins(SPL_DUP, SPL_VOID, 0),
|
||||||
|
ins(SPL_ADD, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 198);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_drop(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 1), ins(SPL_PUSH, SPL_I32, 2),
|
||||||
|
ins(SPL_DROP, SPL_VOID, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_swap(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 1), ins(SPL_PUSH, SPL_I32, 2),
|
||||||
|
ins(SPL_SWAP, SPL_VOID, 0), ins(SPL_DROP, SPL_VOID, 0),
|
||||||
|
ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 5) == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_pick(void) {
|
||||||
|
/* push 10, 20, 30, pick 1 -> copy 20, add -> 50 */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||||
|
ins(SPL_PUSH, SPL_I32, 30), ins(SPL_PICK, SPL_VOID, 1),
|
||||||
|
ins(SPL_ADD, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 6) == 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Arithmetic tests
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_add(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 2), ins(SPL_PUSH, SPL_I32, 3), ins(SPL_ADD, SPL_I32, 0),
|
||||||
|
ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_sub(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 3),
|
||||||
|
ins(SPL_SUB, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_mul(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 6), ins(SPL_PUSH, SPL_I32, 7), ins(SPL_MUL, SPL_I32, 0),
|
||||||
|
ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_div(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 100), ins(SPL_PUSH, SPL_I32, 3),
|
||||||
|
ins(SPL_DIV_S, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_rem(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 100), ins(SPL_PUSH, SPL_I32, 3),
|
||||||
|
ins(SPL_REM_S, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_neg(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 42), ins(SPL_NEG, SPL_I32, 0),
|
||||||
|
ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 3) == -42);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_i64_arith(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I64, 42), ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
TEST_CHECK(run(p, 2) == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Bitwise tests
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_and(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xFF00), ins(SPL_PUSH, SPL_U32, 0x0FF0),
|
||||||
|
ins(SPL_AND, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 0x0F00);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_or(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xFF00), ins(SPL_PUSH, SPL_U32, 0x00FF),
|
||||||
|
ins(SPL_OR, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_xor(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xFFFF), ins(SPL_PUSH, SPL_U32, 0x0FF0),
|
||||||
|
ins(SPL_XOR, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 0xF00F);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_not(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xFFFF0000), ins(SPL_NOT, SPL_U32, 0),
|
||||||
|
ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 3) == (unsigned int)0x0000FFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_shl(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 1), ins(SPL_PUSH, SPL_U32, 10),
|
||||||
|
ins(SPL_SHL, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_shr(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 1024), ins(SPL_PUSH, SPL_U32, 10),
|
||||||
|
ins(SPL_SHR_U, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_shr_s(void) {
|
||||||
|
/* arithmetic right shift: -1024 >> 5 sign-extends */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, (spl_val_t)(int32_t)-1024), ins(SPL_PUSH, SPL_U32, 5),
|
||||||
|
ins(SPL_SHR_S, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == -32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Comparison tests
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_eq(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 42), ins(SPL_PUSH, SPL_I32, 42),
|
||||||
|
ins(SPL_EQ, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_neq(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 42), ins(SPL_PUSH, SPL_I32, 99),
|
||||||
|
ins(SPL_NE, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_lt(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||||
|
ins(SPL_SLT, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_gt_signed(void) {
|
||||||
|
/* -1 > 1 should be 0 (false) for signed compare */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 0xFFFFFFFF), ins(SPL_PUSH, SPL_I32, 1),
|
||||||
|
ins(SPL_SGT, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_sle(void) {
|
||||||
|
/* -1 <= 1 -> true (1) for signed */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 0xFFFFFFFF), ins(SPL_PUSH, SPL_I32, 1),
|
||||||
|
ins(SPL_SLE, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_sge(void) {
|
||||||
|
/* -1 >= 1 -> false (0) for signed */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 0xFFFFFFFF), ins(SPL_PUSH, SPL_I32, 1),
|
||||||
|
ins(SPL_SGE, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ult(void) {
|
||||||
|
/* 0xFFFFFFFF < 1 -> false (0) for unsigned */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xFFFFFFFF), ins(SPL_PUSH, SPL_U32, 1),
|
||||||
|
ins(SPL_ULT, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ule(void) {
|
||||||
|
/* 0xFFFFFFFF <= 0xFFFFFFFF -> true (1) */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xFFFFFFFF), ins(SPL_PUSH, SPL_U32, 0xFFFFFFFF),
|
||||||
|
ins(SPL_ULE, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ugt(void) {
|
||||||
|
/* 0xFFFFFFFF > 1 -> true (1) for unsigned */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xFFFFFFFF), ins(SPL_PUSH, SPL_U32, 1),
|
||||||
|
ins(SPL_UGT, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_uge(void) {
|
||||||
|
/* 0xFFFFFFFF >= 1 -> true (1) */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xFFFFFFFF), ins(SPL_PUSH, SPL_U32, 1),
|
||||||
|
ins(SPL_UGE, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Control flow tests (relative offset)
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_jmp(void) {
|
||||||
|
/* push 1, jmp +3 (skip next 2 insns), push 2 (skipped), push 3, add, ret -> 4 */
|
||||||
|
/* jmp at ip=1, after fetch ip=2, target ip=3 (push 3) => offset = 1 */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 1), ins(SPL_JMP, SPL_VOID, 1),
|
||||||
|
ins(SPL_PUSH, SPL_I32, 2), ins(SPL_PUSH, SPL_I32, 3),
|
||||||
|
ins(SPL_ADD, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 6) == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_bz_bnz(void) {
|
||||||
|
/* push 0, bz +2 (skip to ret with 1) -> return 1 */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 0), ins(SPL_BZ, SPL_VOID, 2),
|
||||||
|
ins(SPL_PUSH, SPL_I32, 99), ins(SPL_RET, SPL_I32, 0),
|
||||||
|
ins(SPL_PUSH, SPL_I32, 1), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 6) == 1);
|
||||||
|
|
||||||
|
/* push 1, bnz +2 (skip to ret with 42) -> return 42 */
|
||||||
|
spl_ins_t q[] = {ins(SPL_PUSH, SPL_I32, 1), ins(SPL_BNZ, SPL_VOID, 2),
|
||||||
|
ins(SPL_PUSH, SPL_I32, 99), ins(SPL_RET, SPL_I32, 0),
|
||||||
|
ins(SPL_PUSH, SPL_I32, 42), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(q, 6) == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Memory tests (ALLOC, LADDR, LD, ST)
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_alloc_laddr_st64_ld64(void) {
|
||||||
|
/* alloc 1 local, laddr + st64 42, laddr + ld64 back -> 42 */
|
||||||
|
spl_ins_t p[] = {
|
||||||
|
ins(SPL_ALLOC, SPL_VOID, 1), /* 0: alloc 1 local */
|
||||||
|
ins(SPL_LADDR, SPL_VOID, 0), /* 1: addr of local[0] */
|
||||||
|
ins(SPL_PUSH, SPL_I64, 42), /* 2: value */
|
||||||
|
ins(SPL_STORE, SPL_I64, 0), /* 3: store */
|
||||||
|
ins(SPL_LADDR, SPL_VOID, 0), /* 4: addr of local[0] */
|
||||||
|
ins(SPL_LOAD, SPL_I64, 0), /* 5: load */
|
||||||
|
ins(SPL_RET, SPL_I64, 0) /* 6: ret */
|
||||||
|
};
|
||||||
|
TEST_CHECK(run(p, 7) == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_alloc_zeroed(void) {
|
||||||
|
/* alloc 1 local, laddr + ld64 -> should be 0 (zero-initialized) */
|
||||||
|
spl_ins_t p[] = {ins(SPL_ALLOC, SPL_VOID, 1), ins(SPL_LADDR, SPL_VOID, 0),
|
||||||
|
ins(SPL_LOAD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Indirect memory load/store tests (stack-based, no heap)
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_ld_st64(void) {
|
||||||
|
/* alloc 8 slots, st64 42, ld64 back -> 42 */
|
||||||
|
spl_ins_t p[] = {ins(SPL_ALLOC, SPL_VOID, 8), ins(SPL_LADDR, SPL_VOID, 0),
|
||||||
|
ins(SPL_DUP, SPL_VOID, 0), ins(SPL_PUSH, SPL_I64, 42),
|
||||||
|
ins(SPL_STORE, SPL_I64, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
|
ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
TEST_CHECK(run(p, 7) == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ld_st32(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_ALLOC, SPL_VOID, 8), ins(SPL_LADDR, SPL_VOID, 0),
|
||||||
|
ins(SPL_DUP, SPL_VOID, 0), ins(SPL_PUSH, SPL_I64, 0xAABBCCDD),
|
||||||
|
ins(SPL_STORE, SPL_U32, 0), ins(SPL_LOAD, SPL_U32, 0),
|
||||||
|
ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 7) == (int)0xAABBCCDD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ld_st16(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_ALLOC, SPL_VOID, 8), ins(SPL_LADDR, SPL_VOID, 0),
|
||||||
|
ins(SPL_DUP, SPL_VOID, 0), ins(SPL_PUSH, SPL_I64, 0xBEEF),
|
||||||
|
ins(SPL_STORE, SPL_U16, 0), ins(SPL_LOAD, SPL_U16, 0),
|
||||||
|
ins(SPL_RET, SPL_U16, 0)};
|
||||||
|
TEST_CHECK(run(p, 7) == 0xBEEF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ld_st8(void) {
|
||||||
|
spl_ins_t p[] = {ins(SPL_ALLOC, SPL_VOID, 8), ins(SPL_LADDR, SPL_VOID, 0),
|
||||||
|
ins(SPL_DUP, SPL_VOID, 0), ins(SPL_PUSH, SPL_I64, 0xAB),
|
||||||
|
ins(SPL_STORE, SPL_U8, 0), ins(SPL_LOAD, SPL_U8, 0),
|
||||||
|
ins(SPL_RET, SPL_U8, 0)};
|
||||||
|
TEST_CHECK(run(p, 7) == 0xAB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Function call tests (relative offset CALL)
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_call_add(void) {
|
||||||
|
/*
|
||||||
|
* add(a, b) address 0, 6 insns, 2 args
|
||||||
|
* main() address 6, 5 insns, 0 args
|
||||||
|
*
|
||||||
|
* add (ip=0):
|
||||||
|
* laddr 0 ip=0 arg0 addr
|
||||||
|
* ld64 ip=1 load arg0
|
||||||
|
* laddr 1 ip=2 arg1 addr
|
||||||
|
* ld64 ip=3 load arg1
|
||||||
|
* add i64 ip=4
|
||||||
|
* ret i64 ip=5
|
||||||
|
*
|
||||||
|
* main (ip=6):
|
||||||
|
* push 10 ip=6
|
||||||
|
* push 20 ip=7
|
||||||
|
* push 0 ip=8 target address = add at 0
|
||||||
|
* call 2 ip=9 nargs=2
|
||||||
|
* ret i64 ip=10
|
||||||
|
*/
|
||||||
|
spl_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
|
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
|
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
spl_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||||
|
ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_CALL, SPL_VOID, 2),
|
||||||
|
ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
|
||||||
|
int nadd = (int)(sizeof(add_insns) / sizeof(add_insns[0]));
|
||||||
|
int nmain = (int)(sizeof(main_insns) / sizeof(main_insns[0]));
|
||||||
|
int ninsns_t = nadd + nmain;
|
||||||
|
size_t nlen0 = strlen("add") + 1;
|
||||||
|
size_t npad0 = ((nlen0 + 7) / 8) * 8 - nlen0;
|
||||||
|
size_t nlen1 = strlen("main") + 1;
|
||||||
|
size_t npad1 = ((nlen1 + 7) / 8) * 8 - nlen1;
|
||||||
|
size_t sz = 8 + 8 + 8 + 8 + 8 + 8 /* magic + 5 counts */
|
||||||
|
+ 8 + nlen0 + npad0 + 8 + 8 + 8 + 8 /* func 0 */
|
||||||
|
+ 8 + nlen1 + npad1 + 8 + 8 + 8 + 8 /* func 1 */
|
||||||
|
+ (size_t)ninsns_t * 12;
|
||||||
|
unsigned char *bin = (unsigned char *)malloc(sz);
|
||||||
|
unsigned char *p = bin;
|
||||||
|
|
||||||
|
memcpy(p, "SPLBIN\0\0", 8);
|
||||||
|
p += 8;
|
||||||
|
LE64(p, 2);
|
||||||
|
LE64(p, (spl_val_t)ninsns_t);
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, 0);
|
||||||
|
|
||||||
|
/* func[0]: "add" */
|
||||||
|
LE64(p, (spl_val_t)nlen0);
|
||||||
|
memcpy(p, "add", nlen0);
|
||||||
|
p += nlen0;
|
||||||
|
memset(p, 0, npad0);
|
||||||
|
p += npad0;
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, 2);
|
||||||
|
LE64(p, (spl_val_t)nadd);
|
||||||
|
LE64(p, 0);
|
||||||
|
|
||||||
|
/* func[1]: "main" */
|
||||||
|
LE64(p, (spl_val_t)nlen1);
|
||||||
|
memcpy(p, "main", nlen1);
|
||||||
|
p += nlen1;
|
||||||
|
memset(p, 0, npad1);
|
||||||
|
p += npad1;
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, (spl_val_t)nmain);
|
||||||
|
LE64(p, (spl_val_t)nadd); /* address */
|
||||||
|
|
||||||
|
for (int i = 0; i < nadd; i++) {
|
||||||
|
*p++ = (unsigned char)(add_insns[i].opcode);
|
||||||
|
*p++ = (unsigned char)(add_insns[i].opcode >> 8);
|
||||||
|
*p++ = (unsigned char)(add_insns[i].type);
|
||||||
|
*p++ = (unsigned char)(add_insns[i].type >> 8);
|
||||||
|
LE64(p, add_insns[i].imm);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < nmain; i++) {
|
||||||
|
*p++ = (unsigned char)(main_insns[i].opcode);
|
||||||
|
*p++ = (unsigned char)(main_insns[i].opcode >> 8);
|
||||||
|
*p++ = (unsigned char)(main_insns[i].type);
|
||||||
|
*p++ = (unsigned char)(main_insns[i].type >> 8);
|
||||||
|
LE64(p, main_insns[i].imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_prog_t prog;
|
||||||
|
spl_vm_t vm;
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (write_temp(bin, sz) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||||
|
spl_vm_load_prog(&vm, &prog);
|
||||||
|
if (spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||||
|
int ret = spl_vm_run_until(&vm, 0);
|
||||||
|
if (ret == 1)
|
||||||
|
rc = (int)vm.exit_code;
|
||||||
|
}
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
}
|
||||||
|
remove(TMPFILE);
|
||||||
|
free(bin);
|
||||||
|
TEST_CHECK(rc == 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* CALLI test
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_calli(void) {
|
||||||
|
/*
|
||||||
|
* add (ip=0, 2 args):
|
||||||
|
* laddr 0 ip=0
|
||||||
|
* ld64 ip=1
|
||||||
|
* laddr 1 ip=2
|
||||||
|
* ld64 ip=3
|
||||||
|
* add i64 ip=4
|
||||||
|
* ret i64 ip=5
|
||||||
|
*
|
||||||
|
* main (ip=6, 0 args):
|
||||||
|
* push 10 ip=6
|
||||||
|
* push 20 ip=7
|
||||||
|
* push 2 ip=8 (nargs)
|
||||||
|
* push 0 ip=9 (address of add)
|
||||||
|
* calli ip=10
|
||||||
|
* ret i64 ip=11
|
||||||
|
*/
|
||||||
|
spl_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
|
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
|
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
spl_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||||
|
ins(SPL_PUSH, SPL_VOID, 2), ins(SPL_PUSH, SPL_VOID, 0),
|
||||||
|
ins(SPL_CALLI, SPL_VOID, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
|
||||||
|
int ninsns_t = 6 + 6;
|
||||||
|
size_t nlen0 = strlen("add") + 1;
|
||||||
|
size_t npad0 = ((nlen0 + 7) / 8) * 8 - nlen0;
|
||||||
|
size_t nlen1 = strlen("main") + 1;
|
||||||
|
size_t npad1 = ((nlen1 + 7) / 8) * 8 - nlen1;
|
||||||
|
size_t sz = 8 + 8 + 8 + 8 + 8 + 8 + 8 + nlen0 + npad0 + 8 + 8 + 8 + 8 + 8 + nlen1 + npad1 + 8 +
|
||||||
|
8 + 8 + 8 + (size_t)ninsns_t * 12;
|
||||||
|
unsigned char *bin = (unsigned char *)malloc(sz);
|
||||||
|
unsigned char *p = bin;
|
||||||
|
|
||||||
|
memcpy(p, "SPLBIN\0\0", 8);
|
||||||
|
p += 8;
|
||||||
|
LE64(p, 2);
|
||||||
|
LE64(p, (spl_val_t)ninsns_t);
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, 0);
|
||||||
|
|
||||||
|
/* func[0]: add */
|
||||||
|
LE64(p, (spl_val_t)nlen0);
|
||||||
|
memcpy(p, "add", nlen0);
|
||||||
|
p += nlen0;
|
||||||
|
memset(p, 0, npad0);
|
||||||
|
p += npad0;
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, 2);
|
||||||
|
LE64(p, 6);
|
||||||
|
LE64(p, 0);
|
||||||
|
|
||||||
|
/* func[1]: main */
|
||||||
|
LE64(p, (spl_val_t)nlen1);
|
||||||
|
memcpy(p, "main", nlen1);
|
||||||
|
p += nlen1;
|
||||||
|
memset(p, 0, npad1);
|
||||||
|
p += npad1;
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, 0);
|
||||||
|
LE64(p, 6);
|
||||||
|
LE64(p, 6);
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
*p++ = (unsigned char)(add_insns[i].opcode);
|
||||||
|
*p++ = (unsigned char)(add_insns[i].opcode >> 8);
|
||||||
|
*p++ = (unsigned char)(add_insns[i].type);
|
||||||
|
*p++ = (unsigned char)(add_insns[i].type >> 8);
|
||||||
|
LE64(p, add_insns[i].imm);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
*p++ = (unsigned char)(main_insns[i].opcode);
|
||||||
|
*p++ = (unsigned char)(main_insns[i].opcode >> 8);
|
||||||
|
*p++ = (unsigned char)(main_insns[i].type);
|
||||||
|
*p++ = (unsigned char)(main_insns[i].type >> 8);
|
||||||
|
LE64(p, main_insns[i].imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_prog_t prog;
|
||||||
|
spl_vm_t vm;
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (write_temp(bin, sz) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||||
|
spl_vm_load_prog(&vm, &prog);
|
||||||
|
if (spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||||
|
int ret = spl_vm_run_until(&vm, 0);
|
||||||
|
if (ret == 1)
|
||||||
|
rc = (int)vm.exit_code;
|
||||||
|
}
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
}
|
||||||
|
remove(TMPFILE);
|
||||||
|
free(bin);
|
||||||
|
TEST_CHECK(rc == 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Global data (GADDR) test
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_gaddr_ld32(void) {
|
||||||
|
/* Load program, add global data, GADDR + LD32 to read it back */
|
||||||
|
spl_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_U32, 0),
|
||||||
|
ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
size_t len;
|
||||||
|
unsigned char *bin = build_binary("main", 0, p, 3, &len);
|
||||||
|
spl_prog_t prog;
|
||||||
|
spl_vm_t vm;
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||||
|
uint32_t val = 0xDEADBEEF;
|
||||||
|
spl_prog_add_data(&prog, &val, sizeof(val));
|
||||||
|
|
||||||
|
spl_vm_load_prog(&vm, &prog);
|
||||||
|
if (spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||||
|
int ret = spl_vm_run_until(&vm, 0);
|
||||||
|
if (ret == 1)
|
||||||
|
rc = (int)vm.exit_code;
|
||||||
|
}
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
}
|
||||||
|
remove(TMPFILE);
|
||||||
|
free(bin);
|
||||||
|
TEST_CHECK(rc == (int)0xDEADBEEF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_gaddr_multi(void) {
|
||||||
|
/* Two global data entries: read second one via GADDR 1 */
|
||||||
|
spl_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_U32, 0),
|
||||||
|
ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
size_t len;
|
||||||
|
unsigned char *bin = build_binary("main", 0, p, 3, &len);
|
||||||
|
spl_prog_t prog;
|
||||||
|
spl_vm_t vm;
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||||
|
uint32_t a = 0xAAAAAAAA, b = 0xBBBBBBBB;
|
||||||
|
spl_prog_add_data(&prog, &a, sizeof(a));
|
||||||
|
spl_prog_add_data(&prog, &b, sizeof(b));
|
||||||
|
|
||||||
|
spl_vm_load_prog(&vm, &prog);
|
||||||
|
if (spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||||
|
int ret = spl_vm_run_until(&vm, 0);
|
||||||
|
if (ret == 1)
|
||||||
|
rc = (int)vm.exit_code;
|
||||||
|
}
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
}
|
||||||
|
remove(TMPFILE);
|
||||||
|
free(bin);
|
||||||
|
TEST_CHECK(rc == (int)0xBBBBBBBB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Native call test
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_ncall(void) {
|
||||||
|
/* main() -> i32 { return native_add(30, 12); } */
|
||||||
|
/* NCALL imm = nargs, native index is pushed separately */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 30), ins(SPL_PUSH, SPL_I32, 12),
|
||||||
|
ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_NCALL, SPL_I32, 2),
|
||||||
|
ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
spl_native_t nat[] = {{"native_add", 0, native_add_impl}};
|
||||||
|
TEST_CHECK(run_with_natives(p, 5, nat, 1) == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Type conversion tests
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_trunc(void) {
|
||||||
|
/* push 0xABCD, trunc to 8 bits -> 0xCD */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xABCD), ins(SPL_TRUNC, SPL_U8, 8),
|
||||||
|
ins(SPL_RET, SPL_U8, 0)};
|
||||||
|
TEST_CHECK(run(p, 3) == 0xCD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_sext(void) {
|
||||||
|
/* push 0x80, sext from 8 bits -> 0xFFFFFF80 */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 0x80), ins(SPL_SEXT, SPL_I32, 8),
|
||||||
|
ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 3) == (int)(int8_t)(0x80));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_zext(void) {
|
||||||
|
/* push 0xFFFF, zext from 8 bits -> 0xFF */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 0xFFFF), ins(SPL_ZEXT, SPL_U32, 8),
|
||||||
|
ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 3) == 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Extended arithmetic tests (unsigned div/rem)
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_div_u(void) {
|
||||||
|
/* unsigned: 100 / 3 = 33 */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 100), ins(SPL_PUSH, SPL_U32, 3),
|
||||||
|
ins(SPL_DIV_U, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_rem_u(void) {
|
||||||
|
/* unsigned: 100 % 3 = 1 */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_U32, 100), ins(SPL_PUSH, SPL_U32, 3),
|
||||||
|
ins(SPL_REM_U, SPL_U32, 0), ins(SPL_RET, SPL_U32, 0)};
|
||||||
|
TEST_CHECK(run(p, 4) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Edge case tests
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_many_ops(void) {
|
||||||
|
/* (1+2) * (3+4) = 21 */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 1), ins(SPL_PUSH, SPL_I32, 2), ins(SPL_ADD, SPL_I32, 0),
|
||||||
|
ins(SPL_PUSH, SPL_I32, 3), ins(SPL_PUSH, SPL_I32, 4), ins(SPL_ADD, SPL_I32, 0),
|
||||||
|
ins(SPL_MUL, SPL_I32, 0), ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
TEST_CHECK(run(p, 8) == 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_halt(void) {
|
||||||
|
/* push 77, halt -> exit code 0 (HALT doesn't pop) */
|
||||||
|
spl_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 77), ins(SPL_HALT, SPL_VOID, 0)};
|
||||||
|
TEST_CHECK(run(p, 2) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Bad program tests
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void test_bad_magic(void) {
|
||||||
|
unsigned char bad[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
spl_prog_t prog;
|
||||||
|
if (write_temp(bad, 8) == 0) {
|
||||||
|
TEST_CHECK(spl_prog_load_from_file(TMPFILE, &prog) != 0);
|
||||||
|
remove(TMPFILE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_empty(void) {
|
||||||
|
TEST_CHECK(spl_prog_load_from_file("nonexistent_file_xyz.bin", NULL) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Test list
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
TEST_LIST = {
|
||||||
|
{"push", test_push_imm},
|
||||||
|
{"dup", test_dup},
|
||||||
|
{"drop", test_drop},
|
||||||
|
{"swap", test_swap},
|
||||||
|
{"pick", test_pick},
|
||||||
|
{"add", test_add},
|
||||||
|
{"sub", test_sub},
|
||||||
|
{"mul", test_mul},
|
||||||
|
{"div", test_div},
|
||||||
|
{"rem", test_rem},
|
||||||
|
{"neg", test_neg},
|
||||||
|
{"i64_arith", test_i64_arith},
|
||||||
|
{"and", test_and},
|
||||||
|
{"or", test_or},
|
||||||
|
{"xor", test_xor},
|
||||||
|
{"not", test_not},
|
||||||
|
{"shl", test_shl},
|
||||||
|
{"shr", test_shr},
|
||||||
|
{"shr_s", test_shr_s},
|
||||||
|
{"eq", test_eq},
|
||||||
|
{"neq", test_neq},
|
||||||
|
{"lt", test_lt},
|
||||||
|
{"gt_signed", test_gt_signed},
|
||||||
|
{"sle", test_sle},
|
||||||
|
{"sge", test_sge},
|
||||||
|
{"ult", test_ult},
|
||||||
|
{"ule", test_ule},
|
||||||
|
{"ugt", test_ugt},
|
||||||
|
{"uge", test_uge},
|
||||||
|
{"jmp", test_jmp},
|
||||||
|
{"bz_bnz", test_bz_bnz},
|
||||||
|
{"alloc_laddr_ld_st", test_alloc_laddr_st64_ld64},
|
||||||
|
{"alloc_zeroed", test_alloc_zeroed},
|
||||||
|
{"call_add", test_call_add},
|
||||||
|
{"calli", test_calli},
|
||||||
|
{"gaddr_ld32", test_gaddr_ld32},
|
||||||
|
{"gaddr_multi", test_gaddr_multi},
|
||||||
|
{"ncall", test_ncall},
|
||||||
|
{"div_u", test_div_u},
|
||||||
|
{"rem_u", test_rem_u},
|
||||||
|
{"trunc", test_trunc},
|
||||||
|
{"sext", test_sext},
|
||||||
|
{"zext", test_zext},
|
||||||
|
{"ld_st8", test_ld_st8},
|
||||||
|
{"ld_st16", test_ld_st16},
|
||||||
|
{"ld_st32", test_ld_st32},
|
||||||
|
{"ld_st64", test_ld_st64},
|
||||||
|
{"many_ops", test_many_ops},
|
||||||
|
{"halt", test_halt},
|
||||||
|
{"bad_magic", test_bad_magic},
|
||||||
|
{"empty", test_empty},
|
||||||
|
{NULL, NULL},
|
||||||
|
};
|
||||||
+3426
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,346 @@
|
|||||||
|
#ifndef __SPL_AST_H__
|
||||||
|
#define __SPL_AST_H__
|
||||||
|
|
||||||
|
#include "../stage0/include/utils.h"
|
||||||
|
#include "spl_lexer.h"
|
||||||
|
#include "spl_tok.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SPL_AST_CONTAINER_ITEM,
|
||||||
|
SPL_AST_FN_DECL,
|
||||||
|
SPL_AST_FN_DEFINE,
|
||||||
|
SPL_AST_TYPE_DECL,
|
||||||
|
SPL_AST_VAR_DECL,
|
||||||
|
SPL_AST_CONST_DECL,
|
||||||
|
SPL_AST_MEMBER_DECL,
|
||||||
|
SPL_AST__COMPTIME_STMT, /*不实现*/
|
||||||
|
SPL_AST__DIRECTIVE_BLOCK, /*不实现*/
|
||||||
|
|
||||||
|
SPL_AST_BLOCK_ITEM,
|
||||||
|
SPL_AST_EXPR,
|
||||||
|
SPL_AST_TYPE_EXPR,
|
||||||
|
SPL_AST_ATTR_LIST,
|
||||||
|
} spl_ast_node_kind_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *fname;
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
} spl_ast_loc_t;
|
||||||
|
|
||||||
|
struct spl_ast_node;
|
||||||
|
typedef struct spl_ast_node spl_ast_node_t;
|
||||||
|
typedef usize spl_ast_node_ref_t;
|
||||||
|
typedef VEC(spl_ast_node_ref_t) spl_ast_node_ref_vec_t;
|
||||||
|
struct spl_ast_node {
|
||||||
|
spl_ast_node_kind_t kind;
|
||||||
|
spl_ast_loc_t loc;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t attr_list; /* attr_item */
|
||||||
|
spl_ast_node_ref_t self; /* self */
|
||||||
|
spl_ast_node_ref_vec_t members; /* container_decl 列表 */
|
||||||
|
} container_item;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
const char *ident;
|
||||||
|
spl_ast_node_ref_vec_t expr_list;
|
||||||
|
} attr_item;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t attr_list; /* attr_item */
|
||||||
|
const char *name;
|
||||||
|
spl_ast_node_ref_vec_t param_list; /* param_decl */
|
||||||
|
spl_ast_node_ref_t type_expr;
|
||||||
|
spl_ast_node_ref_vec_t block; /* block_item */
|
||||||
|
} fn_decl;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t attr_list; /* attr_item */
|
||||||
|
const char *name;
|
||||||
|
spl_ast_node_ref_t type_expr;
|
||||||
|
} param_decl;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
const char *name;
|
||||||
|
spl_ast_node_ref_t type_expr;
|
||||||
|
} type_decl;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t attr_list; /* attr_item */
|
||||||
|
const char *name;
|
||||||
|
spl_ast_node_ref_t type_expr;
|
||||||
|
} member_decl;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t attr_list; /* attr_item */
|
||||||
|
const char *name;
|
||||||
|
spl_ast_node_ref_t type_expr;
|
||||||
|
spl_ast_node_ref_t expr;
|
||||||
|
} var_decl;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t attr_list; /* attr_item */
|
||||||
|
spl_ast_node_ref_t type_expr;
|
||||||
|
const char *name;
|
||||||
|
spl_ast_node_ref_t expr;
|
||||||
|
} const_decl;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
enum {
|
||||||
|
SPL_AST_IF_STATEMENT,
|
||||||
|
SPL_AST_IFVAR_STATEMENT,
|
||||||
|
SPL_AST_WHILE_STATEMENT,
|
||||||
|
SPL_AST_LOOP_STATEMENT,
|
||||||
|
SPL_AST_FOR_STATEMENT,
|
||||||
|
SPL_AST_MATCH_STATEMENT,
|
||||||
|
SPL_AST_RET_STATEMENT,
|
||||||
|
SPL_AST_BREAK_STATEMENT,
|
||||||
|
SPL_AST_CONTINUE_STATEMENT,
|
||||||
|
SPL_AST_DEFER_STATEMENT,
|
||||||
|
SPL_AST_VARDECL,
|
||||||
|
SPL_AST_CONSTDECL,
|
||||||
|
SPL_AST_TYPEDECL,
|
||||||
|
SPL_AST_EXPR_STATEMENT,
|
||||||
|
} kind;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_t expr;
|
||||||
|
spl_ast_node_ref_vec_t if_block; /* block_item */
|
||||||
|
spl_ast_node_ref_vec_t else_block; /* block_item */
|
||||||
|
} if_statement;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_t packed_expr;
|
||||||
|
spl_ast_node_ref_vec_t if_block; /* block_item */
|
||||||
|
spl_ast_node_ref_vec_t else_block; /* block_item */
|
||||||
|
} ifvar_statement;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_t expr;
|
||||||
|
spl_ast_node_ref_vec_t while_block; /* block_item */
|
||||||
|
} while_statement;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t loop_block; /* block_item */
|
||||||
|
} loop_statement;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t expr_vec;
|
||||||
|
VEC(char *) ident_vec;
|
||||||
|
spl_ast_node_ref_vec_t block; /* block_item */
|
||||||
|
} for_statement;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_t expr;
|
||||||
|
spl_ast_node_ref_vec_t paced_exprs; /* packed_expr */
|
||||||
|
spl_ast_node_ref_vec_t match_block; /* block_item */
|
||||||
|
} match_statement;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_t expr;
|
||||||
|
} ret_statement;
|
||||||
|
struct {
|
||||||
|
} break_statement;
|
||||||
|
struct {
|
||||||
|
} continue_statement;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t block_or_statement; /* block_item/statement */
|
||||||
|
} defer_statement;
|
||||||
|
spl_ast_node_ref_t var_decl;
|
||||||
|
spl_ast_node_ref_t const_decl;
|
||||||
|
spl_ast_node_ref_t type_decl;
|
||||||
|
spl_ast_node_ref_t expr_statement;
|
||||||
|
};
|
||||||
|
} block_item;
|
||||||
|
struct {
|
||||||
|
const char *ident;
|
||||||
|
const char *bind_ident;
|
||||||
|
spl_ast_node_ref_t expr;
|
||||||
|
} packed_expr;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
enum {
|
||||||
|
SPL_AST_ASSIGN_EXPR,
|
||||||
|
SPL_AST_ASSIGN_ADD_EXPR,
|
||||||
|
SPL_AST_ASSIGN_sUB_EXPR,
|
||||||
|
SPL_AST_ASSIGN_MUL_EXPR,
|
||||||
|
SPL_AST_ASSIGN_DIV_EXPR,
|
||||||
|
SPL_AST_ASSIGN_MOD_EXPR,
|
||||||
|
SPL_AST_ASSIGN_AND_EXPR,
|
||||||
|
SPL_AST_ASSIGN_OR_EXPR,
|
||||||
|
SPL_AST_ASSIGN_XOR_EXPR,
|
||||||
|
SPL_AST_ASSIGN_LSHIFT_EXPR,
|
||||||
|
SPL_AST_ASSIGN_USHIFT_EXPR,
|
||||||
|
|
||||||
|
SPL_AST_BOOLOR_EXPR,
|
||||||
|
SPL_AST_BOOLAND_EXPR,
|
||||||
|
SPL_AST_BITOR_EXPR,
|
||||||
|
SPL_AST_BITXOR_EXPR,
|
||||||
|
SPL_AST_BITAND_EXPR,
|
||||||
|
|
||||||
|
SPL_AST_CMPEQ_EXPR,
|
||||||
|
SPL_AST_CMPNE_EXPR,
|
||||||
|
|
||||||
|
SPL_AST_CMP_LE_EXPR,
|
||||||
|
SPL_AST_CMP_GE_EXPR,
|
||||||
|
SPL_AST_CMP_LT_EXPR,
|
||||||
|
SPL_AST_CMP_GT_EXPR,
|
||||||
|
|
||||||
|
SPL_AST_RANGE_EXPR,
|
||||||
|
|
||||||
|
SPL_AST_LSHIFT_EXPR,
|
||||||
|
SPL_AST_RSHIFT_EXPR,
|
||||||
|
|
||||||
|
SPL_AST_ADD_EXPR,
|
||||||
|
SPL_AST_SUB_EXPR,
|
||||||
|
|
||||||
|
SPL_AST_MUL_EXPR,
|
||||||
|
SPL_AST_DIV_EXPR,
|
||||||
|
SPL_AST_MOD_EXPR,
|
||||||
|
|
||||||
|
SPL_AST_PREFIX_EXPR, /* left ref node */
|
||||||
|
SPL_AST_POSTFIX_EXPR, /* left ref node */
|
||||||
|
SPL_AST_PRIMARY_EXPR, /* left ref node */
|
||||||
|
} op;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_t left;
|
||||||
|
spl_ast_node_ref_t right;
|
||||||
|
} op_expr;
|
||||||
|
} expr;
|
||||||
|
struct {
|
||||||
|
enum {
|
||||||
|
SPL_AST_MINUS_EXPR,
|
||||||
|
SPL_AST_BANG_EXPR,
|
||||||
|
SPL_AST_TILDE_EXPR,
|
||||||
|
SPL_AST_AMPERSAND_EXPR,
|
||||||
|
SPL_AST_ASTERISK_EXPR,
|
||||||
|
} kind;
|
||||||
|
spl_ast_node_ref_t postfix_expr;
|
||||||
|
} prefix_expr;
|
||||||
|
struct {
|
||||||
|
enum {
|
||||||
|
SPL_AST_CALL_EXPR,
|
||||||
|
SPL_AST_FIELD_EXPR,
|
||||||
|
SPL_AST_DEREF_EXPR,
|
||||||
|
SPL_AST_INDEX_EXPR,
|
||||||
|
SPL_AST_SLICE_EXPR,
|
||||||
|
SPL_AST_AS_EXPR,
|
||||||
|
} kind;
|
||||||
|
spl_ast_node_ref_t primary_expr;
|
||||||
|
union {
|
||||||
|
spl_ast_node_ref_vec_t call_expr;
|
||||||
|
const char *field_expr; /* field/method */
|
||||||
|
spl_ast_node_ref_t index_expr;
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_t begin;
|
||||||
|
spl_ast_node_ref_t end;
|
||||||
|
} slice_expr;
|
||||||
|
spl_ast_node_ref_t type_expr;
|
||||||
|
};
|
||||||
|
} postfix_expr;
|
||||||
|
struct {
|
||||||
|
enum {
|
||||||
|
SPL_AST_INTEGER,
|
||||||
|
SPL_AST_FLOAT,
|
||||||
|
SPL_AST_CHAR_LIT,
|
||||||
|
SPL_AST_STRING_LIT,
|
||||||
|
SPL_AST_TRUE,
|
||||||
|
SPL_AST_FALSE,
|
||||||
|
SPL_AST_NULL,
|
||||||
|
SPL_AST_IDENT,
|
||||||
|
SPL_AST_ARGGREGATE_INIT,
|
||||||
|
SPL_AST_EXPR_EXPR,
|
||||||
|
SPL_AST_ARRAY_LIT,
|
||||||
|
SPL_AST_BUILTIN_EXPR,
|
||||||
|
SPL_AST_BLOCK_EXPR,
|
||||||
|
} kind;
|
||||||
|
union {
|
||||||
|
isize integer_expr;
|
||||||
|
double float_expr;
|
||||||
|
char char_lit_expr;
|
||||||
|
const char *string_lit_expr; /* parsed c string */
|
||||||
|
const char *ident;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
const char *name;
|
||||||
|
spl_ast_node_ref_vec_t expr; /* aggregate_init_item */
|
||||||
|
} aggregate_init;
|
||||||
|
|
||||||
|
spl_ast_node_ref_t expr;
|
||||||
|
struct {
|
||||||
|
isize integer;
|
||||||
|
spl_ast_node_ref_t type_expr;
|
||||||
|
spl_ast_node_ref_vec_t expr_list;
|
||||||
|
} array_lit_expr;
|
||||||
|
struct {
|
||||||
|
const char *ident;
|
||||||
|
spl_ast_node_ref_vec_t expr_list;
|
||||||
|
} builtin_expr;
|
||||||
|
spl_ast_node_ref_vec_t block_expr; /* block_item */
|
||||||
|
};
|
||||||
|
} primary_expr;
|
||||||
|
struct {
|
||||||
|
const char *ident;
|
||||||
|
spl_ast_node_ref_t expr;
|
||||||
|
} aggregate_init_item;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t type_prefixs; /* prefix_type */
|
||||||
|
spl_ast_node_ref_vec_t attr_list; /* attr_item */
|
||||||
|
enum {
|
||||||
|
SPL_AST_BASE_TYPE_FN,
|
||||||
|
SPL_AST_BASE_TYPE_PATH,
|
||||||
|
SPL_AST_TYPE_STRUCT,
|
||||||
|
SPL_AST_TYPE_UNION,
|
||||||
|
SPL_AST_TYPE_ENUM,
|
||||||
|
} kind;
|
||||||
|
|
||||||
|
const char *spl_base_type;
|
||||||
|
union {
|
||||||
|
spl_ast_node_ref_vec_t type_path; /* type_atom */
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_vec_t param_list; /* param_decl */
|
||||||
|
spl_ast_node_ref_t type_expr;
|
||||||
|
} fn_type;
|
||||||
|
spl_ast_node_ref_vec_t aggregate_list; /* container_decl */
|
||||||
|
};
|
||||||
|
} type_expr;
|
||||||
|
struct {
|
||||||
|
/* ASTERISK = 1, [] = 2, else = 0*/
|
||||||
|
int pointer;
|
||||||
|
/* if array_size != 0 then pointer == 2 */
|
||||||
|
int array_size;
|
||||||
|
} prefix_type;
|
||||||
|
struct {
|
||||||
|
enum {
|
||||||
|
SPL_AST_TYPE_VOID,
|
||||||
|
SPL_AST_TYPE_BOOL,
|
||||||
|
SPL_AST_TYPE_I8,
|
||||||
|
SPL_AST_TYPE_U8,
|
||||||
|
SPL_AST_TYPE_I16,
|
||||||
|
SPL_AST_TYPE_U16,
|
||||||
|
SPL_AST_TYPE_I32,
|
||||||
|
SPL_AST_TYPE_U32,
|
||||||
|
SPL_AST_TYPE_I64,
|
||||||
|
SPL_AST_TYPE_U64,
|
||||||
|
SPL_AST_TYPE_ISIZE,
|
||||||
|
SPL_AST_TYPE_USIZE,
|
||||||
|
SPL_AST_TYPE__F32, /*不实现*/
|
||||||
|
SPL_AST_TYPE__F64, /*不实现*/
|
||||||
|
SPL_AST_TYPE_PTR,
|
||||||
|
SPL_AST_TYPE_ANY,
|
||||||
|
SPL_AST_TYPE_IDENT,
|
||||||
|
} kind;
|
||||||
|
const char *ident;
|
||||||
|
} type_atom;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef VEC(spl_ast_node_t) spl_ast_node_vec_t;
|
||||||
|
typedef struct {
|
||||||
|
int parsed;
|
||||||
|
spl_tok_vec_t input;
|
||||||
|
spl_ast_node_vec_t buckets;
|
||||||
|
spl_ast_node_ref_t root;
|
||||||
|
} spl_ast_t;
|
||||||
|
|
||||||
|
void spl_ast_init(spl_ast_t *ast, const spl_tok_vec_t *tok_vec /*move*/);
|
||||||
|
void spl_ast_drop(spl_ast_t *ast);
|
||||||
|
|
||||||
|
void spl_ast_prase(spl_ast_t *ast);
|
||||||
|
void spl_ast_valid(spl_ast_t *ast);
|
||||||
|
void spl_ast_dump(spl_ast_t *ast, spl_ast_node_ref_t node);
|
||||||
|
|
||||||
|
#endif /* __SPL_AST_H__ */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#include "spl_ast2ir.h"
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#ifndef __SPL_AST2IR_H__
|
||||||
|
#define __SPL_AST2IR_H__
|
||||||
|
|
||||||
|
#endif /* __SPL_AST2IR_H__ */
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/* spl_dumptree.c 可配置的树形打印模块(只用基本 ASCII)*/
|
||||||
|
|
||||||
|
#include "spl_dumptree.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
const spl_dumptree_style_t spl_dumptree_ascii_style = {
|
||||||
|
"| ",
|
||||||
|
"|-",
|
||||||
|
"`-",
|
||||||
|
" ",
|
||||||
|
};
|
||||||
|
|
||||||
|
void spl_dumptree_print(const spl_dumptree_style_t *st, const char *prefix, int is_last,
|
||||||
|
const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
printf("%s%s ", prefix, is_last ? st->last_branch : st->branch);
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vprintf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void spl_dumptree_child_prefix(const spl_dumptree_style_t *st, const char *prefix, int is_last,
|
||||||
|
char *out, size_t cap) {
|
||||||
|
snprintf(out, cap, "%s%s", prefix, is_last ? st->space : st->vertical);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/* spl_dumptree.h 可配置的树形打印模块(只用基本 ASCII)*/
|
||||||
|
|
||||||
|
#ifndef __SPL_DUMPTREE_H__
|
||||||
|
#define __SPL_DUMPTREE_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* 可配置的缩进字符串 */
|
||||||
|
typedef struct {
|
||||||
|
const char *vertical; /* "| " */
|
||||||
|
const char *branch; /* "|-" */
|
||||||
|
const char *last_branch; /* "`-" */
|
||||||
|
const char *space; /* " " */
|
||||||
|
} spl_dumptree_style_t;
|
||||||
|
|
||||||
|
/* 默认 ASCII 风格 */
|
||||||
|
extern const spl_dumptree_style_t spl_dumptree_ascii_style;
|
||||||
|
|
||||||
|
/* 打印一行节点标签:prefix + 分支符 + label
|
||||||
|
* prefix 已累积的缩进骨架(不含分支符)
|
||||||
|
* is_last 本节点是否为同级最后一个子节点
|
||||||
|
* fmt printf 风格 label */
|
||||||
|
void spl_dumptree_print(const spl_dumptree_style_t *st, const char *prefix, int is_last,
|
||||||
|
const char *fmt, ...);
|
||||||
|
|
||||||
|
/* 生成子节点的缩进骨架:parent_prefix + (parent_is_last ? space : vertical) */
|
||||||
|
void spl_dumptree_child_prefix(const spl_dumptree_style_t *st, const char *prefix, int is_last,
|
||||||
|
char *out, size_t cap);
|
||||||
|
|
||||||
|
#endif /* __SPL_DUMPTREE_H__ */
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#ifndef __SPL_EMIT_H__
|
||||||
|
#define __SPL_EMIT_H__
|
||||||
|
|
||||||
|
#endif /* __SPL_EMIT_H__ */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#include "spl_ir.h"
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
#ifndef __SPL_IR_H__
|
||||||
|
#define __SPL_IR_H__
|
||||||
|
|
||||||
|
#include "../stage0/include/utils.h"
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
#define SPL_IR_FN_TABLE \
|
||||||
|
X(arith.add, V0, SPL_IR_ARITH_ADD) \
|
||||||
|
X(arith.sub, V0, SPL_IR_ARITH_SUB) \
|
||||||
|
X(arith.mul, V0, SPL_IR_ARITH_MUL) \
|
||||||
|
X(arith.div, V0, SPL_IR_ARITH_DIV) \
|
||||||
|
X(arith.rem, V0, SPL_IR_ARITH_REM) \
|
||||||
|
X(arith.neg, V0, SPL_IR_ARITH_NEG) \
|
||||||
|
X(arith.abs, V0, SPL_IR_ARITH_ABS) \
|
||||||
|
X(arith.and, V0, SPL_IR_ARITH_AND) \
|
||||||
|
X(arith.or, V0, SPL_IR_ARITH_OR) \
|
||||||
|
X(arith.xor, V0, SPL_IR_ARITH_XOR) \
|
||||||
|
X(arith.shl, V0, SPL_IR_ARITH_SHL) \
|
||||||
|
X(arith.shr, V0, SPL_IR_ARITH_SHR) \
|
||||||
|
X(arith.not, V0, SPL_IR_ARITH_NOT) \
|
||||||
|
X(cmp.eq, V0, SPL_IR_CMP_EQ) \
|
||||||
|
X(cmp.ne, V0, SPL_IR_CMP_NE) \
|
||||||
|
X(cmp.lt, V0, SPL_IR_CMP_LT) \
|
||||||
|
X(cmp.le, V0, SPL_IR_CMP_LE) \
|
||||||
|
X(cmp.gt, V0, SPL_IR_CMP_GT) \
|
||||||
|
X(cmp.ge, V0, SPL_IR_CMP_GE) \
|
||||||
|
X(cast.trunc, V0, SPL_IR_CAST_TRUNC) \
|
||||||
|
X(cast.zext, V0, SPL_IR_CAST_ZEXT) \
|
||||||
|
X(cast.sext, V0, SPL_IR_CAST_SEXT) \
|
||||||
|
X(cast.fext, V0, SPL_IR_CAST_FEXT) \
|
||||||
|
X(cast.ftrunc, V0, SPL_IR_CAST_FTRUNC) \
|
||||||
|
X(cast.bitcast, V0, SPL_IR_CAST_BITCAST) \
|
||||||
|
X(cast.ptr2int, V0, SPL_IR_CAST_PTR2INT) \
|
||||||
|
X(cast.int2ptr, V0, SPL_IR_CAST_INT2PTR) \
|
||||||
|
X(cast.bool2int, V0, SPL_IR_CAST_BOOL2INT) \
|
||||||
|
X(case.int2float, V0, SPL_IR_CASE_INT2FLOAT) \
|
||||||
|
X(case.float2int, V0, SPL_IR_CASE_FLOAT2INT) \
|
||||||
|
X(mem.alloca, V0, SPL_IR_MEM_ALLOCA) \
|
||||||
|
X(mem.load, V0, SPL_IR_MEM_LOAD) \
|
||||||
|
X(mem.store, V0, SPL_IR_MEM_STORE) \
|
||||||
|
X(mem.offset, V0, SPL_IR_MEM_OFFSET) \
|
||||||
|
X(mem.copy, V0, SPL_IR_MEM_COPY) \
|
||||||
|
X(mem.set, V0, SPL_IR_MEM_SET) \
|
||||||
|
X(mem.fence, V0, SPL_IR_MEM_FENCE) \
|
||||||
|
X(type.const, V0, SPL_IR_TYPE_CONST) \
|
||||||
|
X(type.bitsizeof, V0, SPL_IR_TYPE_BITSIZEOF) \
|
||||||
|
X(type.sizeof, V0, SPL_IR_TYPE_SIZEOF) \
|
||||||
|
X(type.alignof, V0, SPL_IR_TYPE_ALIGNOF) \
|
||||||
|
X(type.offsetof, V0, SPL_IR_TYPE_OFFSETOF) \
|
||||||
|
X(type.field_count, V0, SPL_IR_TYPE_FIELD_COUNT) \
|
||||||
|
X(agg.construct, V0, SPL_IR_AGG_CONSTRUCT) \
|
||||||
|
X(agg.extract, V0, SPL_IR_AGG_EXTRACT) \
|
||||||
|
X(agg.insert, V0, SPL_IR_AGG_INSERT) \
|
||||||
|
X(atomic.load, V0, SPL_IR_ATOMIC_LOAD) \
|
||||||
|
X(atomic.store, V0, SPL_IR_ATOMIC_STORE) \
|
||||||
|
X(atomic.rmw_add, V0, SPL_IR_ATOMIC_RMW_ADD) \
|
||||||
|
X(atomic.rmw_sub, V0, SPL_IR_ATOMIC_RMW_SUB) \
|
||||||
|
X(atomic.rmw_and, V0, SPL_IR_ATOMIC_RMW_AND) \
|
||||||
|
X(atomic.rmw_or, V0, SPL_IR_ATOMIC_RMW_OR) \
|
||||||
|
X(atomic.rmw_xor, V0, SPL_IR_ATOMIC_RMW_XOR) \
|
||||||
|
X(atomic.rmw_xchg, V0, SPL_IR_ATOMIC_RMW_XCHG) \
|
||||||
|
X(atomic.cmpxchg, V0, SPL_IR_ATOMIC_CMPXCHG) \
|
||||||
|
X(control.select, V0, SPL_IR_CONTROL_SELECT) \
|
||||||
|
X(control.br, V0, SPL_IR_CONTROL_BR) \
|
||||||
|
X(control.jmp, V0, SPL_IR_CONTROL_JMP) \
|
||||||
|
X(control.call, V0, SPL_IR_CONTROL_CALL) \
|
||||||
|
X(control.ret, V0, SPL_IR_CONTROL_RET) \
|
||||||
|
X(control.unreachable, V0, SPL_IR_CONTROL_UNREACHABLE) \
|
||||||
|
X(control.trap, V0, SPL_IR_CONTROL_TRAP) \
|
||||||
|
X(dbg.breakpoint, V0, SPL_IR_DBG_BREAKPOINT) \
|
||||||
|
X(dbg.declare, V0, SPL_IR_DBG_DECLARE)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
#ifdef X
|
||||||
|
#undef X
|
||||||
|
#endif
|
||||||
|
#define X(a, b, c) c,
|
||||||
|
SPL_IR_FN_TABLE
|
||||||
|
#undef X
|
||||||
|
} spl_ir_kind_t;
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
spl_ir_kind_t kind;
|
||||||
|
} spl_ir_node_t;
|
||||||
|
typedef VEC(spl_ir_node_t) spl_ir_node_vec_t;
|
||||||
|
|
||||||
|
typedef usize spl_ir_node_ref_t; /* 0 is error */
|
||||||
|
typedef VEC(spl_ir_node_ref_t) spl_ir_node_ref_vec_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
enum {
|
||||||
|
SPL_IR_ATTR_NONE,
|
||||||
|
SPL_IR_ATTR_LINK, /* 不实现 */
|
||||||
|
SPL_IR_ATTR_ABI, /* 只有 C ABI 支持 */
|
||||||
|
SPL_IR_ATTR_SYMBOL, /* 不实现 */
|
||||||
|
SPL_IR_ATTR_NAKED, /* 不实现 */
|
||||||
|
SPL_IR_ATTR_NOINLINE, /* 不实现 */
|
||||||
|
SPL_IR_ATTR_ALWAYSINLINE, /* 不实现 */
|
||||||
|
};
|
||||||
|
} spl_ir_attr_t;
|
||||||
|
typedef VEC(spl_ir_attr_t) spl_ir_attr_vec_t;
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
spl_ir_attr_t attr;
|
||||||
|
spl_ir_node_vec_t nodes;
|
||||||
|
spl_ir_node_ref_vec_t labels;
|
||||||
|
} spl_ir_func_t;
|
||||||
|
typedef usize spl_ir_func_ref_t; /* 0 is error */
|
||||||
|
typedef VEC(spl_ir_func_t) spl_ir_func_vec_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
spl_ir_func_vec_t funcs;
|
||||||
|
} spl_ir_t;
|
||||||
|
|
||||||
|
void spl_ir_init(spl_ir_t *ir);
|
||||||
|
void spl_ir_drop(spl_ir_t *ir);
|
||||||
|
|
||||||
|
spl_ir_node_ref_t spl_ir_alloc_node(spl_ir_t *ir, spl_ir_func_ref_t fn_id);
|
||||||
|
spl_ir_func_ref_t spl_ir_alloc_fn(spl_ir_t *ir);
|
||||||
|
|
||||||
|
spl_ir_node_t *spl_ir_node(spl_ir_t *ir, spl_ir_func_ref_t fn_id, spl_ir_node_ref_t node_id);
|
||||||
|
spl_ir_func_t *spl_ir_func(spl_ir_t *ir, spl_ir_func_ref_t fn_id);
|
||||||
|
|
||||||
|
void spl_ir_dump(spl_ir_t *ir);
|
||||||
|
|
||||||
|
#endif /* __SPL_IR_H__ */
|
||||||
@@ -0,0 +1,534 @@
|
|||||||
|
/* spl_lexer.c — SPL lexical analyzer */
|
||||||
|
|
||||||
|
#include "spl_lexer.h"
|
||||||
|
#include "spl_tok.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Character classification */
|
||||||
|
static int is_ident_start(char c) { return isalpha((unsigned char)c) || c == '_'; }
|
||||||
|
|
||||||
|
static int is_ident_cont(char c) { return isalnum((unsigned char)c) || c == '_'; }
|
||||||
|
|
||||||
|
/* Keyword lookup: if ident is a keyword, return its token type, else TOK_IDENT */
|
||||||
|
static spl_tok_type_t keyword_type(const char *ident, usize len) {
|
||||||
|
#define X(name, enum_name, dummy) \
|
||||||
|
if (len == sizeof(#name) - 1 && memcmp(ident, #name, len) == 0) \
|
||||||
|
return enum_name;
|
||||||
|
KEYWORD_TABLE
|
||||||
|
#undef X
|
||||||
|
return TOK_IDENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Escape sequence decoder — returns the decoded character,
|
||||||
|
* advances *s past the escape sequence, returns 0 on success,
|
||||||
|
* non-zero on error. */
|
||||||
|
int spl_decode_escape(const char **s, char *out) {
|
||||||
|
if (**s != '\\') {
|
||||||
|
*out = **s;
|
||||||
|
(*s)++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
(*s)++; /* skip backslash */
|
||||||
|
switch (**s) {
|
||||||
|
case 'n':
|
||||||
|
*out = '\n';
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
*out = '\t';
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
*out = '\r';
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
*out = '\\';
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
*out = '"';
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
*out = '\'';
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
*out = '\0';
|
||||||
|
break;
|
||||||
|
case 'x': {
|
||||||
|
(*s)++;
|
||||||
|
char hex[3] = {0, 0, 0};
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 2 && isxdigit((unsigned char)**s); i++, (*s)++) {
|
||||||
|
hex[i] = **s;
|
||||||
|
}
|
||||||
|
if (i == 0)
|
||||||
|
return -1;
|
||||||
|
*out = (char)strtol(hex, NULL, 16);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
(*s)++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_tok_vec_t spl_lex(const char *source, const char *fname) {
|
||||||
|
spl_tok_vec_t toks;
|
||||||
|
vec_init(toks);
|
||||||
|
|
||||||
|
usize line = 1;
|
||||||
|
usize col = 1;
|
||||||
|
usize offset = 0;
|
||||||
|
usize len = strlen(source);
|
||||||
|
|
||||||
|
while (offset < len) {
|
||||||
|
const char *start = source + offset;
|
||||||
|
char c = *start;
|
||||||
|
|
||||||
|
/* Skip whitespace (but not newlines — emit TOK_ENDLINE) */
|
||||||
|
if (c == ' ' || c == '\t' || c == '\r') {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Newline */
|
||||||
|
if (c == '\n') {
|
||||||
|
spl_tok_t tok;
|
||||||
|
memset(&tok, 0, sizeof(tok));
|
||||||
|
tok.type = TOK_ENDLINE;
|
||||||
|
tok.lexeme = start;
|
||||||
|
tok.len = 1;
|
||||||
|
tok.fname = fname;
|
||||||
|
tok.offset = offset;
|
||||||
|
tok.line = line;
|
||||||
|
tok.col = col;
|
||||||
|
vec_push(toks, tok);
|
||||||
|
offset++;
|
||||||
|
line++;
|
||||||
|
col = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Line comment */
|
||||||
|
if (c == '/' && offset + 1 < len && source[offset + 1] == '/') {
|
||||||
|
const char *nl = (const char *)memchr(start, '\n', len - offset);
|
||||||
|
usize clen = nl ? (usize)(nl - start) : (len - offset);
|
||||||
|
spl_tok_t tok;
|
||||||
|
memset(&tok, 0, sizeof(tok));
|
||||||
|
tok.type = TOK_LINE_COMMENT;
|
||||||
|
tok.lexeme = start;
|
||||||
|
tok.len = clen;
|
||||||
|
tok.fname = fname;
|
||||||
|
tok.offset = offset;
|
||||||
|
tok.line = line;
|
||||||
|
tok.col = col;
|
||||||
|
vec_push(toks, tok);
|
||||||
|
offset += clen;
|
||||||
|
col += clen;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block comment */
|
||||||
|
if (c == '/' && offset + 1 < len && source[offset + 1] == '*') {
|
||||||
|
offset += 2;
|
||||||
|
col += 2;
|
||||||
|
usize depth = 1;
|
||||||
|
while (offset + 1 < len && depth > 0) {
|
||||||
|
if (source[offset] == '*' && source[offset + 1] == '/') {
|
||||||
|
depth--;
|
||||||
|
offset += 2;
|
||||||
|
col += 2;
|
||||||
|
if (depth == 0)
|
||||||
|
break;
|
||||||
|
} else if (source[offset] == '/' && source[offset + 1] == '*') {
|
||||||
|
depth++;
|
||||||
|
offset += 2;
|
||||||
|
col += 2;
|
||||||
|
} else {
|
||||||
|
if (source[offset] == '\n') {
|
||||||
|
line++;
|
||||||
|
col = 1;
|
||||||
|
} else {
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Char literal: 'x' */
|
||||||
|
if (c == '\'') {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
char buf[16];
|
||||||
|
int bi = 0;
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
if (offset < len) {
|
||||||
|
const char *cp = source + offset;
|
||||||
|
if (spl_decode_escape(&cp, &buf[bi])) {
|
||||||
|
buf[bi] = source[offset];
|
||||||
|
cp = source + offset + 1;
|
||||||
|
}
|
||||||
|
bi++;
|
||||||
|
offset = (usize)(cp - source);
|
||||||
|
col += (usize)(cp - (start + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset < len && source[offset] == '\'') {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_tok_t tok;
|
||||||
|
memset(&tok, 0, sizeof(tok));
|
||||||
|
tok.type = TOK_CHAR_LITERAL;
|
||||||
|
tok.lexeme = start;
|
||||||
|
tok.len = (usize)((source + offset) - start);
|
||||||
|
tok.fname = fname;
|
||||||
|
tok.offset = start - source;
|
||||||
|
tok.line = line;
|
||||||
|
tok.col = col - tok.len;
|
||||||
|
vec_push(toks, tok);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* String literal: "..." */
|
||||||
|
if (c == '"') {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
while (offset < len) {
|
||||||
|
if (source[offset] == '\\') {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
if (offset < len) {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
} else if (source[offset] == '"') {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
break;
|
||||||
|
} else if (source[offset] == '\n') {
|
||||||
|
line++;
|
||||||
|
col = 1;
|
||||||
|
offset++;
|
||||||
|
} else {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_tok_t tok;
|
||||||
|
memset(&tok, 0, sizeof(tok));
|
||||||
|
tok.type = TOK_STRING_LITERAL;
|
||||||
|
tok.lexeme = start;
|
||||||
|
tok.len = (usize)((source + offset) - start);
|
||||||
|
tok.fname = fname;
|
||||||
|
tok.offset = start - source;
|
||||||
|
tok.line = line;
|
||||||
|
tok.col = col - tok.len;
|
||||||
|
vec_push(toks, tok);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Identifiers and keywords */
|
||||||
|
if (is_ident_start(c)) {
|
||||||
|
const char *id_start = start;
|
||||||
|
usize id_len = 0;
|
||||||
|
while (offset < len && is_ident_cont(source[offset])) {
|
||||||
|
offset++;
|
||||||
|
id_len++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_tok_type_t tt = keyword_type(id_start, id_len);
|
||||||
|
spl_tok_t tok;
|
||||||
|
memset(&tok, 0, sizeof(tok));
|
||||||
|
tok.type = tt;
|
||||||
|
tok.lexeme = id_start;
|
||||||
|
tok.len = id_len;
|
||||||
|
tok.fname = fname;
|
||||||
|
tok.offset = id_start - source;
|
||||||
|
tok.line = line;
|
||||||
|
tok.col = col - id_len;
|
||||||
|
vec_push(toks, tok);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Numbers: integers and floats */
|
||||||
|
if (isdigit((unsigned char)c)) {
|
||||||
|
const char *num_start = start;
|
||||||
|
int is_float = 0;
|
||||||
|
|
||||||
|
/* Check for hex/bin/oct prefix */
|
||||||
|
if (c == '0' && offset + 1 < len) {
|
||||||
|
char nc = source[offset + 1];
|
||||||
|
if (nc == 'x' || nc == 'X') {
|
||||||
|
/* Hex literal */
|
||||||
|
offset += 2;
|
||||||
|
col += 2;
|
||||||
|
while (offset < len && isxdigit((unsigned char)source[offset])) {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
goto emit_int;
|
||||||
|
}
|
||||||
|
if (nc == 'b' || nc == 'B') {
|
||||||
|
/* Binary literal */
|
||||||
|
offset += 2;
|
||||||
|
col += 2;
|
||||||
|
while (offset < len && (source[offset] == '0' || source[offset] == '1')) {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
goto emit_int;
|
||||||
|
}
|
||||||
|
if (nc == 'o' || nc == 'O') {
|
||||||
|
/* Octal literal */
|
||||||
|
offset += 2;
|
||||||
|
col += 2;
|
||||||
|
while (offset < len && source[offset] >= '0' && source[offset] <= '7') {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
goto emit_int;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decimal integer or float */
|
||||||
|
while (offset < len && isdigit((unsigned char)source[offset])) {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset < len && source[offset] == '.' && offset + 1 < len &&
|
||||||
|
isdigit((unsigned char)source[offset + 1])) {
|
||||||
|
is_float = 1;
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
while (offset < len && isdigit((unsigned char)source[offset])) {
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_float) {
|
||||||
|
spl_tok_t tok;
|
||||||
|
memset(&tok, 0, sizeof(tok));
|
||||||
|
tok.type = TOK_FLOAT_LITERAL;
|
||||||
|
tok.lexeme = num_start;
|
||||||
|
tok.len = (usize)((source + offset) - num_start);
|
||||||
|
tok.fname = fname;
|
||||||
|
tok.offset = num_start - source;
|
||||||
|
tok.line = line;
|
||||||
|
tok.col = col - tok.len;
|
||||||
|
vec_push(toks, tok);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_int: {
|
||||||
|
spl_tok_t tok;
|
||||||
|
memset(&tok, 0, sizeof(tok));
|
||||||
|
tok.type = TOK_INT_LITERAL;
|
||||||
|
tok.lexeme = num_start;
|
||||||
|
tok.len = (usize)((source + offset) - num_start);
|
||||||
|
tok.fname = fname;
|
||||||
|
tok.offset = num_start - source;
|
||||||
|
tok.line = line;
|
||||||
|
tok.col = col - tok.len;
|
||||||
|
vec_push(toks, tok);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Multi-character operators (longest match) */
|
||||||
|
|
||||||
|
/* Helper: try to match a two-char operator */
|
||||||
|
#define TRY_OP2(c1, c2, tok2, tok1) \
|
||||||
|
if (c == (c1) && offset + 1 < len && source[offset + 1] == (c2)) { \
|
||||||
|
spl_tok_type_t op_type = (tok2); \
|
||||||
|
usize op_len = 2; \
|
||||||
|
spl_tok_t tok; \
|
||||||
|
memset(&tok, 0, sizeof(tok)); \
|
||||||
|
tok.type = op_type; \
|
||||||
|
tok.lexeme = start; \
|
||||||
|
tok.len = op_len; \
|
||||||
|
tok.fname = fname; \
|
||||||
|
tok.offset = offset; \
|
||||||
|
tok.line = line; \
|
||||||
|
tok.col = col; \
|
||||||
|
vec_push(toks, tok); \
|
||||||
|
offset += op_len; \
|
||||||
|
col += op_len; \
|
||||||
|
continue; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TRY_OP3(c1, c2, c3, tok3, tok2, tok1) \
|
||||||
|
if (c == (c1) && offset + 1 < len && source[offset + 1] == (c2)) { \
|
||||||
|
spl_tok_type_t op_type = (tok2); \
|
||||||
|
usize op_len = 2; \
|
||||||
|
if (offset + 2 < len && source[offset + 2] == (c3)) { \
|
||||||
|
op_type = (tok3); \
|
||||||
|
op_len = 3; \
|
||||||
|
} \
|
||||||
|
spl_tok_t tok; \
|
||||||
|
memset(&tok, 0, sizeof(tok)); \
|
||||||
|
tok.type = op_type; \
|
||||||
|
tok.lexeme = start; \
|
||||||
|
tok.len = op_len; \
|
||||||
|
tok.fname = fname; \
|
||||||
|
tok.offset = offset; \
|
||||||
|
tok.line = line; \
|
||||||
|
tok.col = col; \
|
||||||
|
vec_push(toks, tok); \
|
||||||
|
offset += op_len; \
|
||||||
|
col += op_len; \
|
||||||
|
continue; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Three-char operators first */
|
||||||
|
TRY_OP3('<', '<', '=', TOK_ASSIGN_L_SH, TOK_L_SH, TOK_LT)
|
||||||
|
TRY_OP3('>', '>', '=', TOK_ASSIGN_R_SH, TOK_R_SH, TOK_GT)
|
||||||
|
TRY_OP3('.', '.', '.', TOK_ELLIPSIS, TOK_RANGE, TOK_DOT)
|
||||||
|
|
||||||
|
/* Two-char operators */
|
||||||
|
TRY_OP2('=', '=', TOK_EQ, TOK_ASSIGN)
|
||||||
|
TRY_OP2('=', '>', TOK_FAT_ARROW, TOK_ASSIGN)
|
||||||
|
TRY_OP2('!', '=', TOK_NEQ, TOK_NOT)
|
||||||
|
TRY_OP2('<', '=', TOK_LE, TOK_LT)
|
||||||
|
TRY_OP2('>', '=', TOK_GE, TOK_GT)
|
||||||
|
TRY_OP2('&', '&', TOK_AND_AND, TOK_AND)
|
||||||
|
TRY_OP2('|', '|', TOK_OR_OR, TOK_OR)
|
||||||
|
TRY_OP2('+', '=', TOK_ASSIGN_ADD, TOK_ADD)
|
||||||
|
TRY_OP2('-', '=', TOK_ASSIGN_SUB, TOK_SUB)
|
||||||
|
TRY_OP2('*', '=', TOK_ASSIGN_MUL, TOK_MUL)
|
||||||
|
TRY_OP2('/', '=', TOK_ASSIGN_DIV, TOK_DIV)
|
||||||
|
TRY_OP2('%', '=', TOK_ASSIGN_MOD, TOK_MOD)
|
||||||
|
TRY_OP2('&', '=', TOK_ASSIGN_AND, TOK_AND)
|
||||||
|
TRY_OP2('|', '=', TOK_ASSIGN_OR, TOK_OR)
|
||||||
|
TRY_OP2('^', '=', TOK_ASSIGN_XOR, TOK_XOR)
|
||||||
|
TRY_OP2('<', '-', TOK_LEFT_ARRAY, TOK_LT)
|
||||||
|
TRY_OP2('-', '>', TOK_RIGHT_ARRAY, TOK_SUB)
|
||||||
|
TRY_OP2(':', '=', TOK_COLON_ASSIGN, TOK_COLON)
|
||||||
|
|
||||||
|
/* Single-character operators */
|
||||||
|
{
|
||||||
|
spl_tok_type_t tt = TOK_UNKNOWN;
|
||||||
|
switch (c) {
|
||||||
|
case '+':
|
||||||
|
tt = TOK_ADD;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
tt = TOK_SUB;
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
tt = TOK_MUL;
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
tt = TOK_DIV;
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
tt = TOK_MOD;
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
tt = TOK_AND;
|
||||||
|
break;
|
||||||
|
case '|':
|
||||||
|
tt = TOK_OR;
|
||||||
|
break;
|
||||||
|
case '^':
|
||||||
|
tt = TOK_XOR;
|
||||||
|
break;
|
||||||
|
case '~':
|
||||||
|
tt = TOK_BIT_NOT;
|
||||||
|
break;
|
||||||
|
case '!':
|
||||||
|
tt = TOK_NOT;
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
tt = TOK_LT;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
tt = TOK_GT;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
tt = TOK_ASSIGN;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
tt = TOK_DOT;
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
tt = TOK_COMMA;
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
tt = TOK_SEMICOLON;
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
tt = TOK_COLON;
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
tt = TOK_L_PAREN;
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
tt = TOK_R_PAREN;
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
tt = TOK_L_BRACKET;
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
tt = TOK_R_BRACKET;
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
tt = TOK_L_BRACE;
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
tt = TOK_R_BRACE;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
tt = TOK_SHARP;
|
||||||
|
break;
|
||||||
|
case '@':
|
||||||
|
tt = TOK_AT;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
tt = TOK_COND;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tt = TOK_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_tok_t tok;
|
||||||
|
memset(&tok, 0, sizeof(tok));
|
||||||
|
tok.type = tt;
|
||||||
|
tok.lexeme = start;
|
||||||
|
tok.len = 1;
|
||||||
|
tok.fname = fname;
|
||||||
|
tok.offset = offset;
|
||||||
|
tok.line = line;
|
||||||
|
tok.col = col;
|
||||||
|
vec_push(toks, tok);
|
||||||
|
offset++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EOF token */
|
||||||
|
{
|
||||||
|
spl_tok_t tok;
|
||||||
|
memset(&tok, 0, sizeof(tok));
|
||||||
|
tok.type = TOK_EOF;
|
||||||
|
tok.lexeme = source + offset;
|
||||||
|
tok.len = 0;
|
||||||
|
tok.fname = fname;
|
||||||
|
tok.offset = offset;
|
||||||
|
tok.line = line;
|
||||||
|
tok.col = col;
|
||||||
|
vec_push(toks, tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toks;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/* spl_lexer.h — 独立词法分析器 */
|
||||||
|
#ifndef __SPL_LEXER_H__
|
||||||
|
#define __SPL_LEXER_H__
|
||||||
|
|
||||||
|
#include "spl_tok.h"
|
||||||
|
|
||||||
|
/* Lexer entry point */
|
||||||
|
spl_tok_vec_t spl_lex(const char *source, const char *fname);
|
||||||
|
|
||||||
|
/* Decode escape sequence, advance *s past it. Returns 0 on success. */
|
||||||
|
int spl_decode_escape(const char **s, char *out);
|
||||||
|
|
||||||
|
#endif /* __SPL_LEXER_H__ */
|
||||||
+1867
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef __SPL_SEMA_H__
|
||||||
|
#define __SPL_SEMA_H__
|
||||||
|
|
||||||
|
#include "spl_ast.h"
|
||||||
|
#include "spl_type.h"
|
||||||
|
|
||||||
|
typedef usize spl_scope_id_t; /* 0 is error */
|
||||||
|
typedef struct {
|
||||||
|
spl_scope_id_t parent;
|
||||||
|
MAP(const char *, spl_def_id_t) symbols;
|
||||||
|
} spl_scope_node_t;
|
||||||
|
typedef VEC(spl_scope_node_t) spl_scope_node_vec_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
spl_ast_t *ast;
|
||||||
|
spl_type_t type;
|
||||||
|
spl_scope_node_vec_t scopes;
|
||||||
|
spl_scope_id_t root_scope;
|
||||||
|
spl_scope_id_t current_scope;
|
||||||
|
spl_def_id_t root_def;
|
||||||
|
int error_count;
|
||||||
|
} spl_sema_t;
|
||||||
|
|
||||||
|
void spl_sema_init(spl_sema_t *sema);
|
||||||
|
void spl_sema_drop(spl_sema_t *sema);
|
||||||
|
void spl_sema_run(spl_sema_t *sema);
|
||||||
|
void spl_sema_check(spl_sema_t *sema);
|
||||||
|
|
||||||
|
spl_scope_id_t spl_sema_scope_alloc(spl_sema_t *sema);
|
||||||
|
bool spl_sema_scope_insert(spl_sema_t *sema, spl_scope_id_t id, const char *symbol_name,
|
||||||
|
spl_def_id_t symbol_val);
|
||||||
|
typedef VEC(const char *) spl_symbol_path_t;
|
||||||
|
spl_def_id_t spl_sema_scope_find(spl_sema_t *sema, spl_symbol_path_t path);
|
||||||
|
|
||||||
|
#endif /* __SPL_SEMA_H__ */
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/* spl_tok.h — Token type definitions (extracted from spl_lexer.h) */
|
||||||
|
#ifndef __SPL_TOK_H__
|
||||||
|
#define __SPL_TOK_H__
|
||||||
|
|
||||||
|
#include "../stage0/include/utils.h"
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
#define KEYWORD_TABLE \
|
||||||
|
X(as , KW_AS , SPL_V0) \
|
||||||
|
X(bool , KW_BOOL , SPL_V0) \
|
||||||
|
X(break , KW_BREAK , SPL_V0) \
|
||||||
|
X(comptime , KW_COMPTIME , SPL_V0) \
|
||||||
|
X(const , KW_CONST , SPL_V0) \
|
||||||
|
X(continue , KW_CONTINUE , SPL_V0) \
|
||||||
|
X(defer , KW_DEFER , SPL_V0) \
|
||||||
|
X(else , KW_ELSE , SPL_V0) \
|
||||||
|
X(enum , KW_ENUM , SPL_V0) \
|
||||||
|
X(false , KW_FALSE , SPL_V0) \
|
||||||
|
X(fn , KW_FN , SPL_V0) \
|
||||||
|
X(for , KW_FOR , SPL_V0) \
|
||||||
|
X(if , KW_IF , SPL_V0) \
|
||||||
|
X(loop , KW_LOOP , SPL_V0) \
|
||||||
|
X(match , KW_MATCH , SPL_V0) \
|
||||||
|
X(null , KW_NULL , SPL_V0) \
|
||||||
|
X(ret , KW_RET , SPL_V0) \
|
||||||
|
X(struct , KW_STRUCT , SPL_V0) \
|
||||||
|
X(true , KW_TRUE , SPL_V0) \
|
||||||
|
X(type , KW_TYPE , SPL_V0) \
|
||||||
|
X(union , KW_UNION , SPL_V0) \
|
||||||
|
X(var , KW_VAR , SPL_V0) \
|
||||||
|
X(void , KW_VOID , SPL_V0) \
|
||||||
|
X(while , KW_WHILE , SPL_V0) \
|
||||||
|
X(_ , KW_ANY , SPL_V0) \
|
||||||
|
// KEYWORD_TABLE
|
||||||
|
|
||||||
|
#define TOKEN_TABLE \
|
||||||
|
X(unknown , TOK_UNKNOWN , SPL_V0 ) \
|
||||||
|
X(EOF , TOK_EOF , SPL_V0 ) \
|
||||||
|
X(blank , TOK_BLANK , SPL_V0 ) \
|
||||||
|
X(endline , TOK_ENDLINE , SPL_V0 ) \
|
||||||
|
X("#" , TOK_SHARP , SPL_V0 ) \
|
||||||
|
X("@" , TOK_AT , SPL_V0 ) \
|
||||||
|
X("==" , TOK_EQ , SPL_V0 ) \
|
||||||
|
X("=" , TOK_ASSIGN , SPL_V0 ) \
|
||||||
|
X("+=" , TOK_ASSIGN_ADD , SPL_V0 ) \
|
||||||
|
X("+" , TOK_ADD , SPL_V0 ) \
|
||||||
|
X("-=" , TOK_ASSIGN_SUB , SPL_V0 ) \
|
||||||
|
X("->" , TOK_RIGHT_ARRAY , SPL_V0 ) \
|
||||||
|
X("<-" , TOK_LEFT_ARRAY , SPL_V0 ) \
|
||||||
|
X("-" , TOK_SUB , SPL_V0 ) \
|
||||||
|
X("*=" , TOK_ASSIGN_MUL , SPL_V0 ) \
|
||||||
|
X("*" , TOK_MUL , SPL_V0 ) \
|
||||||
|
X("/=" , TOK_ASSIGN_DIV , SPL_V0 ) \
|
||||||
|
X("/" , TOK_DIV , SPL_V0 ) \
|
||||||
|
X("//" , TOK_LINE_COMMENT , SPL_V0 ) \
|
||||||
|
X("/* */" , TOK_BLOCK_COMMENT , SPL_V0 ) \
|
||||||
|
X("%=" , TOK_ASSIGN_MOD , SPL_V0 ) \
|
||||||
|
X("%" , TOK_MOD , SPL_V0 ) \
|
||||||
|
X("&&" , TOK_AND_AND , SPL_V0 ) \
|
||||||
|
X("&=" , TOK_ASSIGN_AND , SPL_V0 ) \
|
||||||
|
X("&" , TOK_AND , SPL_V0 ) \
|
||||||
|
X("||" , TOK_OR_OR , SPL_V0 ) \
|
||||||
|
X("|=" , TOK_ASSIGN_OR , SPL_V0 ) \
|
||||||
|
X("|" , TOK_OR , SPL_V0 ) \
|
||||||
|
X("^=" , TOK_ASSIGN_XOR , SPL_V0 ) \
|
||||||
|
X("^" , TOK_XOR , SPL_V0 ) \
|
||||||
|
X("<<=" , TOK_ASSIGN_L_SH , SPL_V0 ) \
|
||||||
|
X("<<" , TOK_L_SH , SPL_V0 ) \
|
||||||
|
X("<=" , TOK_LE , SPL_V0 ) \
|
||||||
|
X("<" , TOK_LT , SPL_V0 ) \
|
||||||
|
X(">>=" , TOK_ASSIGN_R_SH , SPL_V0 ) \
|
||||||
|
X(">>" , TOK_R_SH , SPL_V0 ) \
|
||||||
|
X(">=" , TOK_GE , SPL_V0 ) \
|
||||||
|
X(">" , TOK_GT , SPL_V0 ) \
|
||||||
|
X("!" , TOK_NOT , SPL_V0 ) \
|
||||||
|
X("!=" , TOK_NEQ , SPL_V0 ) \
|
||||||
|
X("~" , TOK_BIT_NOT , SPL_V0 ) \
|
||||||
|
X("[" , TOK_L_BRACKET , SPL_V0 ) \
|
||||||
|
X("]" , TOK_R_BRACKET , SPL_V0 ) \
|
||||||
|
X("(" , TOK_L_PAREN , SPL_V0 ) \
|
||||||
|
X(")" , TOK_R_PAREN , SPL_V0 ) \
|
||||||
|
X("{" , TOK_L_BRACE , SPL_V0 ) \
|
||||||
|
X("}" , TOK_R_BRACE , SPL_V0 ) \
|
||||||
|
X(";" , TOK_SEMICOLON , SPL_V0 ) \
|
||||||
|
X("," , TOK_COMMA , SPL_V0 ) \
|
||||||
|
X(":" , TOK_COLON , SPL_V0 ) \
|
||||||
|
X(":=" , TOK_COLON_ASSIGN , SPL_V0 ) \
|
||||||
|
X("." , TOK_DOT , SPL_V0 ) \
|
||||||
|
X(".." , TOK_RANGE , SPL_V0 ) \
|
||||||
|
X("..." , TOK_ELLIPSIS , SPL_V0 ) \
|
||||||
|
X("=>" , TOK_FAT_ARROW , SPL_V0 ) \
|
||||||
|
X("?" , TOK_COND , SPL_V0 ) \
|
||||||
|
X(ident , TOK_IDENT , SPL_V0 ) \
|
||||||
|
X(int , TOK_INT_LITERAL , SPL_V0 ) \
|
||||||
|
X(float , TOK_FLOAT_LITERAL , SPL_V0 ) \
|
||||||
|
X(char , TOK_CHAR_LITERAL , SPL_V0 ) \
|
||||||
|
X(string , TOK_STRING_LITERAL , SPL_V0 ) \
|
||||||
|
// TOKEN_TABLE
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
#define X(name, enum_name, dummy) enum_name,
|
||||||
|
KEYWORD_TABLE
|
||||||
|
#undef X
|
||||||
|
#define X(name, enum_name, dummy) enum_name,
|
||||||
|
TOKEN_TABLE
|
||||||
|
#undef X
|
||||||
|
} spl_tok_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
spl_tok_type_t type;
|
||||||
|
const char *lexeme;
|
||||||
|
usize len;
|
||||||
|
const char *fname;
|
||||||
|
usize offset;
|
||||||
|
usize line;
|
||||||
|
usize col;
|
||||||
|
} spl_tok_t;
|
||||||
|
|
||||||
|
typedef VEC(spl_tok_t) spl_tok_vec_t;
|
||||||
|
|
||||||
|
#endif /* __SPL_TOK_H__ */
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
// WRITE BY AI
|
||||||
|
/* spl_type.c SPL 类型系统:type/def 两张完全独立的 arena 表 + 构造器 + dump
|
||||||
|
* type_table 按 spl_type_id_t 存匿名类型;def_table 按 spl_def_id_t 存命名实体。
|
||||||
|
* 两表 id 空间互不相干(0 均保留为 error)。 */
|
||||||
|
#include "spl_type.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static spl_type_id_t type_push(spl_type_t *type) {
|
||||||
|
spl_type_node_t n;
|
||||||
|
memset(&n, 0, sizeof n);
|
||||||
|
n.kind = SPL_TYPE_VOID;
|
||||||
|
vec_push(type->type_table, n);
|
||||||
|
return type->type_table.size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内置整型去重:同 bits/signed 复用同一 id(类型唯一化) */
|
||||||
|
spl_type_id_t spl_type_int(spl_type_t *type, usize bits, int is_signed) {
|
||||||
|
for (usize i = 1; i < type->type_table.size; i++) {
|
||||||
|
spl_type_node_t *n = &type->type_table.data[i];
|
||||||
|
if (n->kind == SPL_TYPE_INT && n->int_type.bits == bits &&
|
||||||
|
n->int_type.is_signed == is_signed)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_INT;
|
||||||
|
type->type_table.data[id].int_type.bits = bits;
|
||||||
|
type->type_table.data[id].int_type.is_signed = is_signed;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内置浮点去重 */
|
||||||
|
spl_type_id_t spl_type_float(spl_type_t *type, usize bits) {
|
||||||
|
for (usize i = 1; i < type->type_table.size; i++) {
|
||||||
|
spl_type_node_t *n = &type->type_table.data[i];
|
||||||
|
if (n->kind == SPL_TYPE_FLOAT && n->float_type.bits == bits)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_FLOAT;
|
||||||
|
type->type_table.data[id].float_type.bits = bits;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_void(spl_type_t *type) {
|
||||||
|
for (usize i = 1; i < type->type_table.size; i++)
|
||||||
|
if (type->type_table.data[i].kind == SPL_TYPE_VOID)
|
||||||
|
return i;
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_VOID;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_bool(spl_type_t *type) {
|
||||||
|
for (usize i = 1; i < type->type_table.size; i++)
|
||||||
|
if (type->type_table.data[i].kind == SPL_TYPE_BOOL)
|
||||||
|
return i;
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_BOOL;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_ptr(spl_type_t *type, spl_type_id_t val) {
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_PTR;
|
||||||
|
type->type_table.data[id].ptr_pointee = val;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_slice(spl_type_t *type, spl_type_id_t val) {
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_SLICE;
|
||||||
|
type->type_table.data[id].slice_element = val;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_range(spl_type_t *type, spl_type_id_t val) {
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_RANGE;
|
||||||
|
type->type_table.data[id].range_element = val;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_array(spl_type_t *type, spl_type_id_t val, usize len) {
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_ARRAY;
|
||||||
|
type->type_table.data[id].array_type.element = val;
|
||||||
|
type->type_table.data[id].array_type.len = len;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 绑定节点/别名引用:type 节点指向被引用的类型 id
|
||||||
|
* (供未来"底层同型但类型系统不认"的 newtype 使用,暂不参与注册) */
|
||||||
|
spl_type_id_t spl_type_tid(spl_type_t *type, spl_type_id_t val) {
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_ID;
|
||||||
|
type->type_table.data[id].type_id = val;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 聚合类型:默认 STRUCT,字段类型列表 */
|
||||||
|
spl_type_id_t spl_type_agg(spl_type_t *type, spl_type_id_vec_t fields) {
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_STRUCT;
|
||||||
|
type->type_table.data[id].agg_field_types = fields;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_enum(spl_type_t *type, spl_type_id_vec_t variants, spl_type_id_t tag) {
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_ENUM;
|
||||||
|
type->type_table.data[id].enum_type.variants = variants;
|
||||||
|
type->type_table.data[id].enum_type.tag_type = tag;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_fn(spl_type_t *type, spl_type_id_vec_t params, spl_type_id_t ret) {
|
||||||
|
spl_type_id_t id = type_push(type);
|
||||||
|
type->type_table.data[id].kind = SPL_TYPE_FN;
|
||||||
|
type->type_table.data[id].fn_type.params = params;
|
||||||
|
type->type_table.data[id].fn_type.ret = ret;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spl_type_init(spl_type_t *type) {
|
||||||
|
vec_init(type->type_table);
|
||||||
|
vec_init(type->def_table);
|
||||||
|
/* 两表 id 0 均保留为 error */
|
||||||
|
type_push(type);
|
||||||
|
spl_type_def_alloc(type);
|
||||||
|
/* 关键字/内置类型(去重后 i8..u64 等各占一个 id;def 由 sema 登记) */
|
||||||
|
spl_type_void(type);
|
||||||
|
spl_type_bool(type);
|
||||||
|
spl_type_int(type, 8, 1);
|
||||||
|
spl_type_int(type, 8, 0);
|
||||||
|
spl_type_int(type, 16, 1);
|
||||||
|
spl_type_int(type, 16, 0);
|
||||||
|
spl_type_int(type, 32, 1);
|
||||||
|
spl_type_int(type, 32, 0);
|
||||||
|
spl_type_int(type, 64, 1);
|
||||||
|
spl_type_int(type, 64, 0);
|
||||||
|
spl_type_float(type, 32);
|
||||||
|
spl_type_float(type, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spl_type_drop(spl_type_t *type) {
|
||||||
|
/* type 节点持有的内嵌 vec */
|
||||||
|
for (usize i = 0; i < type->type_table.size; i++) {
|
||||||
|
spl_type_node_t *n = &type->type_table.data[i];
|
||||||
|
switch (n->kind) {
|
||||||
|
case SPL_TYPE_STRUCT:
|
||||||
|
case SPL_TYPE_UNION:
|
||||||
|
vec_free(n->agg_field_types);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_ENUM:
|
||||||
|
vec_free(n->enum_type.variants);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_FN:
|
||||||
|
vec_free(n->fn_type.params);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* def 节点持有的内嵌 vec */
|
||||||
|
for (usize i = 0; i < type->def_table.size; i++) {
|
||||||
|
spl_def_node_t *d = &type->def_table.data[i];
|
||||||
|
switch (d->kind) {
|
||||||
|
case SPL_DEF_AGG:
|
||||||
|
vec_free(d->agg_def);
|
||||||
|
break;
|
||||||
|
case SPL_DEF_FN_PARAMS:
|
||||||
|
vec_free(d->fn_params_def);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec_free(type->type_table);
|
||||||
|
vec_free(type->def_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_alloc(spl_type_t *type) { return type_push(type); }
|
||||||
|
|
||||||
|
spl_type_node_t *spl_type_node(spl_type_t *type, spl_type_id_t id) {
|
||||||
|
if (!id || id >= type->type_table.size)
|
||||||
|
return NULL;
|
||||||
|
return &type->type_table.data[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_def_id_t spl_type_def_alloc(spl_type_t *type) {
|
||||||
|
spl_def_node_t d;
|
||||||
|
memset(&d, 0, sizeof d);
|
||||||
|
d.kind = SPL_DEF_NONE;
|
||||||
|
vec_push(type->def_table, d);
|
||||||
|
return type->def_table.size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_def_node_t *spl_type_def(spl_type_t *type, spl_def_id_t id) {
|
||||||
|
if (!id || id >= type->def_table.size)
|
||||||
|
return NULL;
|
||||||
|
return &type->def_table.data[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *def_kind_name(spl_def_node_t *d) {
|
||||||
|
switch (d->kind) {
|
||||||
|
case SPL_DEF_NONE:
|
||||||
|
return "none";
|
||||||
|
case SPL_DEF_BUILTIN:
|
||||||
|
return "builtin";
|
||||||
|
case SPL_DEF_VAR:
|
||||||
|
return "var";
|
||||||
|
case SPL_DEF_FN_PARAMS:
|
||||||
|
return "fn_params";
|
||||||
|
case SPL_DEF_AGG:
|
||||||
|
return "agg";
|
||||||
|
case SPL_DEF_DISTINCT:
|
||||||
|
return "distinct";
|
||||||
|
case SPL_DEF_ALIAS:
|
||||||
|
return "alias";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
void spl_type_pure_dump(spl_type_t *type, spl_type_id_t id) {
|
||||||
|
spl_type_node_t *n = spl_type_node(type, id);
|
||||||
|
if (!n) {
|
||||||
|
printf("(err)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (n->kind) {
|
||||||
|
case SPL_TYPE_VOID:
|
||||||
|
printf("void");
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_BOOL:
|
||||||
|
printf("bool");
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_INT:
|
||||||
|
printf("%s%zu", n->int_type.is_signed ? "i" : "u", n->int_type.bits);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_FLOAT:
|
||||||
|
printf("f%zu", n->float_type.bits);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_PTR:
|
||||||
|
printf("*");
|
||||||
|
spl_type_pure_dump(type, n->ptr_pointee);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_SLICE:
|
||||||
|
printf("[]");
|
||||||
|
spl_type_pure_dump(type, n->slice_element);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_RANGE:
|
||||||
|
printf("range[");
|
||||||
|
spl_type_pure_dump(type, n->range_element);
|
||||||
|
printf("]");
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_ARRAY:
|
||||||
|
printf("[%zu]", n->array_type.len);
|
||||||
|
spl_type_pure_dump(type, n->array_type.element);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_STRUCT:
|
||||||
|
printf("struct{%zu fields}", n->agg_field_types.size);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_UNION:
|
||||||
|
printf("union{%zu fields}", n->agg_field_types.size);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_ENUM:
|
||||||
|
printf("enum{%zu variants}", n->enum_type.variants.size);
|
||||||
|
break;
|
||||||
|
case SPL_TYPE_FN: {
|
||||||
|
printf("fn(");
|
||||||
|
for (usize i = 0; i < n->fn_type.params.size; i++) {
|
||||||
|
if (i)
|
||||||
|
printf(",");
|
||||||
|
spl_type_pure_dump(type, n->fn_type.params.data[i]);
|
||||||
|
}
|
||||||
|
printf(") -> ");
|
||||||
|
spl_type_pure_dump(type, n->fn_type.ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SPL_TYPE_ID:
|
||||||
|
printf("id#%zu", n->type_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void spl_type_def_dump(spl_type_t *type, spl_def_id_t id) {
|
||||||
|
spl_def_node_t *d = spl_type_def(type, id);
|
||||||
|
if (!d) {
|
||||||
|
printf("(err)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("kind=%s type_id=%zu", def_kind_name(d), d->type_id);
|
||||||
|
switch (d->kind) {
|
||||||
|
case SPL_DEF_VAR:
|
||||||
|
printf(" var=%s", d->var_def.name ? d->var_def.name : "?");
|
||||||
|
break;
|
||||||
|
case SPL_DEF_ALIAS:
|
||||||
|
case SPL_DEF_DISTINCT:
|
||||||
|
printf(" type=%s", d->type_def.name ? d->type_def.name : "?");
|
||||||
|
break;
|
||||||
|
case SPL_DEF_AGG:
|
||||||
|
printf(" agg{");
|
||||||
|
for (usize i = 0; i < d->agg_def.size; i++) {
|
||||||
|
if (i)
|
||||||
|
printf(",");
|
||||||
|
printf("%s", d->agg_def.data[i].name ? d->agg_def.data[i].name : "?");
|
||||||
|
}
|
||||||
|
printf("}");
|
||||||
|
break;
|
||||||
|
case SPL_DEF_FN_PARAMS:
|
||||||
|
printf(" fn(");
|
||||||
|
for (usize i = 0; i < d->fn_params_def.size; i++) {
|
||||||
|
if (i)
|
||||||
|
printf(",");
|
||||||
|
printf("%s", d->fn_params_def.data[i].name ? d->fn_params_def.data[i].name : "?");
|
||||||
|
}
|
||||||
|
printf(")");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#ifndef __SPL_TYPE_H__
|
||||||
|
#define __SPL_TYPE_H__
|
||||||
|
|
||||||
|
#include "../stage0/include/utils.h"
|
||||||
|
|
||||||
|
typedef usize spl_type_id_t; /* 0 is error */
|
||||||
|
typedef VEC(spl_type_id_t) spl_type_id_vec_t;
|
||||||
|
typedef usize spl_def_id_t; /* 0 is error */
|
||||||
|
typedef VEC(spl_def_id_t) spl_def_id_vec_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
enum {
|
||||||
|
SPL_TYPE_VOID,
|
||||||
|
SPL_TYPE_BOOL,
|
||||||
|
SPL_TYPE_INT,
|
||||||
|
SPL_TYPE_FLOAT,
|
||||||
|
SPL_TYPE_PTR,
|
||||||
|
SPL_TYPE_SLICE,
|
||||||
|
SPL_TYPE_RANGE,
|
||||||
|
SPL_TYPE_ARRAY,
|
||||||
|
SPL_TYPE_STRUCT,
|
||||||
|
SPL_TYPE_UNION,
|
||||||
|
SPL_TYPE_ENUM,
|
||||||
|
SPL_TYPE_FN,
|
||||||
|
SPL_TYPE_ID,
|
||||||
|
} kind;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
usize bits;
|
||||||
|
int is_signed;
|
||||||
|
} int_type;
|
||||||
|
struct {
|
||||||
|
usize bits;
|
||||||
|
} float_type;
|
||||||
|
spl_type_id_t ptr_pointee;
|
||||||
|
spl_type_id_t slice_element;
|
||||||
|
spl_type_id_t range_element;
|
||||||
|
struct {
|
||||||
|
spl_type_id_t element;
|
||||||
|
usize len;
|
||||||
|
} array_type;
|
||||||
|
spl_type_id_vec_t agg_field_types;
|
||||||
|
struct {
|
||||||
|
spl_type_id_vec_t variants;
|
||||||
|
spl_type_id_t tag_type;
|
||||||
|
} enum_type; // ADT
|
||||||
|
struct {
|
||||||
|
spl_type_id_vec_t params;
|
||||||
|
spl_type_id_t ret;
|
||||||
|
} fn_type;
|
||||||
|
spl_type_id_t type_id;
|
||||||
|
};
|
||||||
|
} spl_type_node_t;
|
||||||
|
typedef VEC(spl_type_node_t) spl_type_node_vec_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
spl_def_id_t def_id;
|
||||||
|
spl_type_id_t type_id;
|
||||||
|
usize scope_id;
|
||||||
|
} spl_var_def_t;
|
||||||
|
typedef VEC(spl_var_def_t) spl_var_def_vec_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
enum {
|
||||||
|
SPL_DEF_NONE,
|
||||||
|
SPL_DEF_BUILTIN,
|
||||||
|
SPL_DEF_VAR,
|
||||||
|
SPL_DEF_FN_PARAMS,
|
||||||
|
SPL_DEF_AGG, // include enum variants
|
||||||
|
SPL_DEF_DISTINCT, // newtype
|
||||||
|
SPL_DEF_ALIAS, // sametypes
|
||||||
|
} kind;
|
||||||
|
spl_type_id_t type_id;
|
||||||
|
union {
|
||||||
|
spl_var_def_t var_def;
|
||||||
|
spl_var_def_vec_t agg_def; // include enum variants
|
||||||
|
spl_var_def_vec_t fn_params_def;
|
||||||
|
spl_var_def_t type_def;
|
||||||
|
};
|
||||||
|
int source_loc;
|
||||||
|
enum {
|
||||||
|
SPL_FLAG_NONE,
|
||||||
|
} flag;
|
||||||
|
} spl_def_node_t;
|
||||||
|
typedef VEC(spl_def_node_t) spl_def_node_vec_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
SPL 设计是严格区分类型做到类型和名称无关即
|
||||||
|
type (类型名) = (匿名类型)
|
||||||
|
好处是递归使用可以直接操作类型名的映射的提前分配的匿名类型的id
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
spl_type_node_vec_t type_table;
|
||||||
|
spl_def_node_vec_t def_table;
|
||||||
|
} spl_type_t;
|
||||||
|
|
||||||
|
void spl_type_init(spl_type_t *type);
|
||||||
|
void spl_type_drop(spl_type_t *type);
|
||||||
|
|
||||||
|
void spl_type_def_dump(spl_type_t *type, spl_def_id_t id);
|
||||||
|
void spl_type_pure_dump(spl_type_t *type, spl_type_id_t id);
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_alloc(spl_type_t *type);
|
||||||
|
spl_type_node_t *spl_type_node(spl_type_t *type, spl_type_id_t id);
|
||||||
|
spl_def_id_t spl_type_def_alloc(spl_type_t *type);
|
||||||
|
spl_def_node_t *spl_type_def(spl_type_t *type, spl_def_id_t id);
|
||||||
|
|
||||||
|
spl_type_id_t spl_type_void(spl_type_t *type);
|
||||||
|
spl_type_id_t spl_type_bool(spl_type_t *type);
|
||||||
|
spl_type_id_t spl_type_int(spl_type_t *type, usize bits, int is_signed);
|
||||||
|
spl_type_id_t spl_type_float(spl_type_t *type, usize bits);
|
||||||
|
spl_type_id_t spl_type_ptr(spl_type_t *type, spl_type_id_t val);
|
||||||
|
spl_type_id_t spl_type_slice(spl_type_t *type, spl_type_id_t val);
|
||||||
|
spl_type_id_t spl_type_range(spl_type_t *type, spl_type_id_t val);
|
||||||
|
spl_type_id_t spl_type_array(spl_type_t *type, spl_type_id_t val, usize len);
|
||||||
|
spl_type_id_t spl_type_tid(spl_type_t *type, spl_type_id_t val);
|
||||||
|
spl_type_id_t spl_type_agg(spl_type_t *type, spl_type_id_vec_t fields);
|
||||||
|
spl_type_id_t spl_type_enum(spl_type_t *type, spl_type_id_vec_t variants, spl_type_id_t tag);
|
||||||
|
spl_type_id_t spl_type_fn(spl_type_t *type, spl_type_id_vec_t params, spl_type_id_t ret);
|
||||||
|
|
||||||
|
#endif /* __SPL_TYPE_H__ */
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
/* splc0.c — SPL compiler CLI (stage 1, 引导用)
|
||||||
|
*
|
||||||
|
* splc0 --dump tokens|ast|all <file> dump 前端产物
|
||||||
|
* splc0 <in> <out> 编译 (阶段 B 实现)
|
||||||
|
*/
|
||||||
|
#define __SCC_LOG_IMPL_IMPORT_SRC__
|
||||||
|
#include "../stage0/include/utils.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "spl_ast.h"
|
||||||
|
#include "spl_lexer.h"
|
||||||
|
#include "spl_sema.h"
|
||||||
|
#include "spl_tok.h"
|
||||||
|
|
||||||
|
static char *read_file(const char *path, long *out_len) {
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
if (!f) {
|
||||||
|
perror("fopen");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long len = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
char *buf = malloc((size_t)len + 1);
|
||||||
|
if (!buf) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fread(buf, 1, (size_t)len, f);
|
||||||
|
fclose(f);
|
||||||
|
buf[len] = '\0';
|
||||||
|
*out_len = len;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *const tok_type_names[] = {
|
||||||
|
#define X(name, enum_name, dummy) #enum_name,
|
||||||
|
KEYWORD_TABLE
|
||||||
|
#undef X
|
||||||
|
#define X(name, enum_name, dummy) #enum_name,
|
||||||
|
TOKEN_TABLE
|
||||||
|
#undef X
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dump_tokens(const char *src, const char *fname) {
|
||||||
|
spl_tok_vec_t toks = spl_lex(src, fname);
|
||||||
|
printf("tokens got (%zu)\n", toks.size);
|
||||||
|
for (usize i = 0; i < toks.size; i++) {
|
||||||
|
const spl_tok_t *t = &toks.data[i];
|
||||||
|
printf("[%s] %.*s (%zu:%zu)\n", tok_type_names[t->type], (int)t->len, t->lexeme, t->line,
|
||||||
|
t->col);
|
||||||
|
}
|
||||||
|
vec_free(toks);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_ast(const char *src, const char *fname) {
|
||||||
|
spl_tok_vec_t toks = spl_lex(src, fname);
|
||||||
|
spl_ast_t ast;
|
||||||
|
spl_ast_init(&ast, &toks);
|
||||||
|
spl_ast_prase(&ast);
|
||||||
|
spl_ast_valid(&ast);
|
||||||
|
spl_ast_dump(&ast, ast.root);
|
||||||
|
spl_ast_drop(&ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_sema(const char *src, const char *fname) {
|
||||||
|
spl_tok_vec_t toks = spl_lex(src, fname);
|
||||||
|
spl_ast_t ast;
|
||||||
|
spl_ast_init(&ast, &toks);
|
||||||
|
spl_ast_prase(&ast);
|
||||||
|
spl_ast_valid(&ast);
|
||||||
|
spl_sema_t sema;
|
||||||
|
spl_sema_init(&sema);
|
||||||
|
sema.ast = *
|
||||||
|
spl_sema_run(&sema);
|
||||||
|
spl_sema_check(&sema);
|
||||||
|
printf("Sema root_scope=%zu scopes=%zu errors=%d\n", sema.root_scope, sema.scopes.size,
|
||||||
|
sema.error_count);
|
||||||
|
for (usize i = 0; i < sema.scopes.size; i++) {
|
||||||
|
printf("scope[%zu] parent=%zu\n", i, sema.scopes.data[i].parent);
|
||||||
|
map_for(sema.scopes.data[i].symbols, mi) {
|
||||||
|
printf(" %s -> def#%zu\n", sema.scopes.data[i].symbols.data[mi].key,
|
||||||
|
sema.scopes.data[i].symbols.data[mi].val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("TypeTable:\n");
|
||||||
|
for (usize i = 0; i < sema.type.type_table.size; i++) {
|
||||||
|
printf(" id#%zu type=", i);
|
||||||
|
spl_type_pure_dump(&sema.type, i);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf("DefTable:\n");
|
||||||
|
for (usize i = 0; i < sema.type.def_table.size; i++) {
|
||||||
|
printf(" def#%zu ", i);
|
||||||
|
spl_type_def_dump(&sema.type, i);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
spl_sema_drop(&sema);
|
||||||
|
spl_ast_drop(&ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_dump(const char *flags, const char *path) {
|
||||||
|
long len;
|
||||||
|
char *src = read_file(path, &len);
|
||||||
|
if (!src)
|
||||||
|
return 1;
|
||||||
|
int do_tokens = strstr(flags, "tokens") != NULL || strcmp(flags, "all") == 0;
|
||||||
|
int do_ast = strstr(flags, "ast") != NULL || strcmp(flags, "all") == 0;
|
||||||
|
int do_sema = strstr(flags, "sema") != NULL || strcmp(flags, "all") == 0;
|
||||||
|
if (do_tokens)
|
||||||
|
dump_tokens(src, path);
|
||||||
|
if (do_ast)
|
||||||
|
dump_ast(src, path);
|
||||||
|
if (do_sema)
|
||||||
|
dump_sema(src, path);
|
||||||
|
free(src);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc < 2) {
|
||||||
|
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
|
||||||
|
LOG_INFO("splc0 <in> <out> compile (.spl -> .sir, 阶段 B)\n");
|
||||||
|
LOG_INFO("splc0 --dump <flags> <file> dump: tokens,ast,sema,all\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int argi = 1;
|
||||||
|
if (argi >= argc) {
|
||||||
|
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(argv[argi], "--dump") == 0) {
|
||||||
|
if (argc < argi + 3) {
|
||||||
|
LOG_INFO("splc0: --dump need <flags> <file>\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return cmd_dump(argv[argi + 1], argv[argi + 2]);
|
||||||
|
}
|
||||||
|
LOG_FATAL("splc0: compile todo\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/* spc_vm.c launcher for stage 1 */
|
||||||
|
|
||||||
|
#include "../stage0/spl_mcode.h"
|
||||||
|
#include "../stage0/spl_syscall.h"
|
||||||
|
#include "../stage0/spl_vm.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(int argc, const char **argv) {
|
||||||
|
int argi = 1;
|
||||||
|
|
||||||
|
/* Parse flags: --entry <name>, --trace, -d */
|
||||||
|
const char *entry = "main";
|
||||||
|
int trace = 0;
|
||||||
|
int debug_addr = 0;
|
||||||
|
for (int i = argi; i < argc; i += 1) {
|
||||||
|
if (strcmp(argv[i], "--entry") == 0 && i + 1 < argc) {
|
||||||
|
entry = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "--trace") == 0) {
|
||||||
|
trace = 1;
|
||||||
|
} else if (strcmp(argv[i], "-d") == 0) {
|
||||||
|
debug_addr = 1;
|
||||||
|
} else if (argv[i][0] != '-') {
|
||||||
|
argi = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argi >= argc) {
|
||||||
|
fprintf(stderr, "Usage: spc_vm [-d] [--trace] <file.sir> [args...]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *path = argv[argi++];
|
||||||
|
const char **spl_argv = argv + (argi - 1);
|
||||||
|
int spl_argc = (int)(argc - (argi - 1));
|
||||||
|
|
||||||
|
spl_prog_t prog;
|
||||||
|
if (spl_prog_load_from_file(path, &prog) != 0) {
|
||||||
|
fprintf(stderr, "spc_vm: cannot load '%s'\n", path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_syscall_register(&prog);
|
||||||
|
|
||||||
|
spl_vm_t vm;
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (spl_vm_load_prog(&vm, &prog) != 0) {
|
||||||
|
fprintf(stderr, "vm: prog '%s' not found\n", entry);
|
||||||
|
spl_prog_drop(&prog);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (spl_vm_prepare(&vm, entry, spl_argc, spl_argv, NULL) != 0) {
|
||||||
|
fprintf(stderr, "vm: entry point '%s' not found\n", entry);
|
||||||
|
spl_prog_drop(&prog);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
spl_vm_set_trace(&vm, trace);
|
||||||
|
if (debug_addr)
|
||||||
|
spl_vm_set_debug(&vm, 1);
|
||||||
|
|
||||||
|
int ret = spl_vm_run_until(&vm, 0);
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
spl_prog_drop(&prog);
|
||||||
|
|
||||||
|
return (int)vm.exit_code;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/* ===== 模块1:核心基础 =====
|
||||||
|
* test00_basic — 最小程序 / 返回码
|
||||||
|
* 难度:1/5
|
||||||
|
* 验证点:函数定义、整型返回、ret 语句
|
||||||
|
*/
|
||||||
|
fn main() i32 {
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/* ===== 模块1:核心基础 =====
|
||||||
|
* test01_literals — 全部字面量形式
|
||||||
|
* 难度:1/5
|
||||||
|
* 验证点:十进制/十六进制/二进制/八进制整数、
|
||||||
|
* 字符字面量、字符串字面量、布尔字面量、null
|
||||||
|
*/
|
||||||
|
fn main() i32 {
|
||||||
|
/* 整数:十进制 */
|
||||||
|
var a: i32 = 42;
|
||||||
|
if a != 42 { ret 1; }
|
||||||
|
|
||||||
|
/* 整数:十六进制 */
|
||||||
|
var b: i32 = 0xFF;
|
||||||
|
if b != 255 { ret 2; }
|
||||||
|
|
||||||
|
/* 整数:二进制 */
|
||||||
|
var c: i32 = 0b1010;
|
||||||
|
if c != 10 { ret 3; }
|
||||||
|
|
||||||
|
/* 整数:八进制 */
|
||||||
|
var d: i32 = 0o77;
|
||||||
|
if d != 63 { ret 4; }
|
||||||
|
|
||||||
|
/* 字符字面量 */
|
||||||
|
var ch: i32 = 'A';
|
||||||
|
if ch != 65 { ret 5; }
|
||||||
|
|
||||||
|
/* 字符转义 */
|
||||||
|
var nl: i32 = '\n';
|
||||||
|
if nl != 10 { ret 6; }
|
||||||
|
|
||||||
|
/* 布尔字面量 */
|
||||||
|
var t: bool = true;
|
||||||
|
if !t { ret 7; }
|
||||||
|
var f: bool = false;
|
||||||
|
if f { ret 8; }
|
||||||
|
|
||||||
|
/* null 指针 */
|
||||||
|
var np: *i32 = null;
|
||||||
|
if np != null { ret 9; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/* ===== 模块1:核心基础 =====
|
||||||
|
* test02_variables — 变量与常量声明
|
||||||
|
* 难度:1/5
|
||||||
|
* 验证点:var 声明 + 类型注解、var 声明 + 初始化、
|
||||||
|
* const 声明、var := 短声明、const := 短声明
|
||||||
|
*/
|
||||||
|
fn main() i32 {
|
||||||
|
/* var 带类型注解与初始化 */
|
||||||
|
var a: i32 = 10;
|
||||||
|
if a != 10 { ret 1; }
|
||||||
|
|
||||||
|
/* var 先声明后赋值 */
|
||||||
|
var b: i32;
|
||||||
|
b = 20;
|
||||||
|
if b != 20 { ret 2; }
|
||||||
|
|
||||||
|
/* const 声明 */
|
||||||
|
const c: i32 = 30;
|
||||||
|
if c != 30 { ret 3; }
|
||||||
|
|
||||||
|
/* var := 短声明(类型推断) */
|
||||||
|
var d := 40;
|
||||||
|
if d != 40 { ret 4; }
|
||||||
|
|
||||||
|
/* const := 短声明 */
|
||||||
|
const e := 50;
|
||||||
|
if e != 50 { ret 5; }
|
||||||
|
|
||||||
|
/* 多类型变量 */
|
||||||
|
var i8v: i8 = 127;
|
||||||
|
if i8v != 127 { ret 6; }
|
||||||
|
|
||||||
|
var u8v: u8 = 255;
|
||||||
|
if u8v != 255 { ret 7; }
|
||||||
|
|
||||||
|
var i16v: i16 = 32767;
|
||||||
|
if i16v != 32767 { ret 8; }
|
||||||
|
|
||||||
|
var u16v: u16 = 65535;
|
||||||
|
if u16v != 65535 { ret 9; }
|
||||||
|
|
||||||
|
var u32v: u32 = 4294967295;
|
||||||
|
if u32v != 4294967295 { ret 10; }
|
||||||
|
|
||||||
|
/* 指针类型变量 */
|
||||||
|
var x: i32 = 100;
|
||||||
|
var p: *i32 = &x;
|
||||||
|
if p == null { ret 11; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/* ===== 模块2:表达式 =====
|
||||||
|
* test03_arithmetic — 算术运算
|
||||||
|
* 难度:1/5
|
||||||
|
* 验证点:加、减、乘、除、取模、负数、混合运算优先级
|
||||||
|
*/
|
||||||
|
fn main() i32 {
|
||||||
|
/* 加法 */
|
||||||
|
var a := 12 + 34;
|
||||||
|
if a != 46 { ret 1; }
|
||||||
|
|
||||||
|
/* 减法 */
|
||||||
|
var b := 100 - 23;
|
||||||
|
if b != 77 { ret 2; }
|
||||||
|
|
||||||
|
/* 乘法 */
|
||||||
|
var c := 7 * 8;
|
||||||
|
if c != 56 { ret 3; }
|
||||||
|
|
||||||
|
/* 除法 */
|
||||||
|
var d := 100 / 3;
|
||||||
|
if d != 33 { ret 4; }
|
||||||
|
|
||||||
|
/* 取模 */
|
||||||
|
var e := 100 % 3;
|
||||||
|
if e != 1 { ret 5; }
|
||||||
|
|
||||||
|
/* 负数 */
|
||||||
|
var f := -5;
|
||||||
|
if f != -5 { ret 6; }
|
||||||
|
|
||||||
|
/* 负数运算 */
|
||||||
|
var g := -10 + 3;
|
||||||
|
if g != -7 { ret 7; }
|
||||||
|
|
||||||
|
/* 运算优先级:乘优先于加 */
|
||||||
|
var h := 2 + 3 * 4;
|
||||||
|
if h != 14 { ret 8; }
|
||||||
|
|
||||||
|
/* 括号改变优先级 */
|
||||||
|
var i := (2 + 3) * 4;
|
||||||
|
if i != 20 { ret 9; }
|
||||||
|
|
||||||
|
/* 连续运算 */
|
||||||
|
var j := 1 + 2 + 3 + 4 + 5;
|
||||||
|
if j != 15 { ret 10; }
|
||||||
|
|
||||||
|
/* 混合运算 */
|
||||||
|
var k := 10 * 2 - 8 / 4;
|
||||||
|
if k != 18 { ret 11; }
|
||||||
|
|
||||||
|
/* 负数取模 */
|
||||||
|
var l := -7 % 3;
|
||||||
|
if l != -1 { ret 12; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/* ===== 模块2:表达式 =====
|
||||||
|
* test04_operators — 比较/逻辑/位运算 + 短路求值
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:== != < <= > >=、&& || !、& | ^ ~ << >>、&&/|| 短路
|
||||||
|
*/
|
||||||
|
fn main() i32 {
|
||||||
|
/* ---- 比较运算 ---- */
|
||||||
|
if (1 < 2) {} else { ret 1; }
|
||||||
|
if (3 > 1) {} else { ret 2; }
|
||||||
|
if (2 == 2) {} else { ret 3; }
|
||||||
|
if (2 != 3) {} else { ret 4; }
|
||||||
|
if (2 <= 2) {} else { ret 5; }
|
||||||
|
if (2 <= 3) {} else { ret 6; }
|
||||||
|
if (3 >= 3) {} else { ret 7; }
|
||||||
|
if (3 >= 2) {} else { ret 8; }
|
||||||
|
|
||||||
|
/* 比较结果为 0/1 */
|
||||||
|
var eq := (42 == 42);
|
||||||
|
if !eq { ret 9; }
|
||||||
|
var ne := (42 == 43);
|
||||||
|
if ne { ret 10; }
|
||||||
|
var lt := (5 < 10);
|
||||||
|
if !lt { ret 11; }
|
||||||
|
|
||||||
|
/* ---- 逻辑运算 ---- */
|
||||||
|
if (true) {} else { ret 12; }
|
||||||
|
if (!false) {} else { ret 13; }
|
||||||
|
if (true && true) {} else { ret 14; }
|
||||||
|
if (false || true) {} else { ret 15; }
|
||||||
|
|
||||||
|
/* 逻辑非 */
|
||||||
|
var not_t := !true;
|
||||||
|
if not_t { ret 16; }
|
||||||
|
var not_f := !false;
|
||||||
|
if !not_f { ret 17; }
|
||||||
|
|
||||||
|
// /* ---- 短路求值 ---- */
|
||||||
|
// var short1 := 0;
|
||||||
|
// var short2 := 0;
|
||||||
|
// if (false && { short1 = 1; true; }) {} else {}
|
||||||
|
// if short1 != 0 { ret 18; } /* 右侧未执行 */
|
||||||
|
|
||||||
|
/* ---- 位运算 ---- */
|
||||||
|
var band := 0xFF & 0x0F;
|
||||||
|
if band != 0x0F { ret 19; }
|
||||||
|
|
||||||
|
var bor := 0xF0 | 0x0F;
|
||||||
|
if bor != 0xFF { ret 20; }
|
||||||
|
|
||||||
|
var bxor := 0xFF ^ 0x0F;
|
||||||
|
if bxor != 0xF0 { ret 21; }
|
||||||
|
|
||||||
|
var bnot := ~0xFF;
|
||||||
|
/* ~0xFF 在 32 位下 = 0xFFFFFF00,即 -256 */
|
||||||
|
if bnot != -256 { ret 22; }
|
||||||
|
|
||||||
|
/* 左移 */
|
||||||
|
var shl := 1 << 3;
|
||||||
|
if shl != 8 { ret 23; }
|
||||||
|
|
||||||
|
/* 右移 */
|
||||||
|
var shr := 16 >> 2;
|
||||||
|
if shr != 4 { ret 24; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/* ===== 模块3:控制流 =====
|
||||||
|
* test05_control — 分支与循环
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:if/else、while、loop、break、continue
|
||||||
|
*/
|
||||||
|
fn main() i32 {
|
||||||
|
/* ---- if/else ---- */
|
||||||
|
var x: i32 = 10;
|
||||||
|
if x > 5 {
|
||||||
|
x = 0;
|
||||||
|
} else {
|
||||||
|
x = 1;
|
||||||
|
}
|
||||||
|
if x != 0 { ret 1; }
|
||||||
|
|
||||||
|
/* else 分支 */
|
||||||
|
var y: i32 = 1;
|
||||||
|
if y > 5 {
|
||||||
|
y = 10;
|
||||||
|
} else {
|
||||||
|
y = 20;
|
||||||
|
}
|
||||||
|
if y != 20 { ret 2; }
|
||||||
|
|
||||||
|
/* if 不带 else */
|
||||||
|
var z: i32 = 0;
|
||||||
|
if z == 0 {
|
||||||
|
z = 99;
|
||||||
|
}
|
||||||
|
if z != 99 { ret 3; }
|
||||||
|
|
||||||
|
/* ---- while ---- */
|
||||||
|
var i: i32 = 0;
|
||||||
|
while i < 5 {
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if i != 5 { ret 4; }
|
||||||
|
|
||||||
|
/* while 条件为 false */
|
||||||
|
var j: i32 = 0;
|
||||||
|
while j > 0 {
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
if j != 0 { ret 5; }
|
||||||
|
|
||||||
|
/* ---- loop / break ---- */
|
||||||
|
var k: i32 = 0;
|
||||||
|
loop {
|
||||||
|
k = k + 1;
|
||||||
|
if k >= 3 { break; }
|
||||||
|
}
|
||||||
|
if k != 3 { ret 6; }
|
||||||
|
|
||||||
|
/* ---- continue ---- */
|
||||||
|
var n: i32 = 0;
|
||||||
|
var m: i32 = 0;
|
||||||
|
while n < 5 {
|
||||||
|
n = n + 1;
|
||||||
|
if n == 3 { continue; }
|
||||||
|
m = m + 1;
|
||||||
|
}
|
||||||
|
if n != 5 { ret 7; }
|
||||||
|
if m != 4 { ret 8; }
|
||||||
|
|
||||||
|
/* ---- 嵌套控制流 ---- */
|
||||||
|
var total: i32 = 0;
|
||||||
|
var p: i32 = 0;
|
||||||
|
while p < 3 {
|
||||||
|
var q: i32 = 0;
|
||||||
|
while q < 4 {
|
||||||
|
total = total + 1;
|
||||||
|
q = q + 1;
|
||||||
|
}
|
||||||
|
p = p + 1;
|
||||||
|
}
|
||||||
|
if total != 12 { ret 9; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/* ===== 模块3:控制流 =====
|
||||||
|
* test06_for — for 区间循环
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:for begin..end as i、for slice as val、for slice,0.. as val,idx
|
||||||
|
*/
|
||||||
|
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
/* for 数值区间:for 0..N as i */
|
||||||
|
var sum: i32 = 0;
|
||||||
|
for 0..5 as i {
|
||||||
|
sum = sum + i;
|
||||||
|
}
|
||||||
|
/* 0+1+2+3+4 = 10 */
|
||||||
|
if sum != 10 { ret 1; }
|
||||||
|
|
||||||
|
/* for 数值区间:非 0 起始 */
|
||||||
|
var sum2: i32 = 0;
|
||||||
|
for 3..7 as i {
|
||||||
|
sum2 = sum2 + i;
|
||||||
|
}
|
||||||
|
/* 3+4+5+6 = 18 */
|
||||||
|
if sum2 != 18 { ret 2; }
|
||||||
|
|
||||||
|
/* for 遍历切片 */
|
||||||
|
var arr: [4]i32 = [4]i32{10, 20, 30, 40};
|
||||||
|
var sl: []i32 = arr[0..];
|
||||||
|
var total: i32 = 0;
|
||||||
|
for sl as val {
|
||||||
|
total = total + val;
|
||||||
|
}
|
||||||
|
/* 10+20+30+40 = 100 */
|
||||||
|
if total != 100 { ret 3; }
|
||||||
|
|
||||||
|
/* for 遍历切片 + 索引 */
|
||||||
|
var arr2: [3]i32 = [3]i32{100, 200, 300};
|
||||||
|
var sl2: []i32 = arr2[0..];
|
||||||
|
var sum_val: i32 = 0;
|
||||||
|
var sum_idx: i32 = 0;
|
||||||
|
for sl2, 0.. as val, idx {
|
||||||
|
sum_val = sum_val + val;
|
||||||
|
sum_idx = sum_idx + idx;
|
||||||
|
}
|
||||||
|
if sum_val != 600 { ret 4; }
|
||||||
|
if sum_idx != 3 { ret 5; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/* ===== 模块4:指针 =====
|
||||||
|
* test07_pointers — 指针操作
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:& 取地址、* 解引用、null 空指针、指针自动解引用(->)、.* 后缀解引用
|
||||||
|
*/
|
||||||
|
fn inc(ptr: *i32) void {
|
||||||
|
*ptr = *ptr + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
/* ---- & 取地址和 * 解引用 ---- */
|
||||||
|
var x: i32 = 42;
|
||||||
|
var p: *i32 = &x;
|
||||||
|
var v := p.*;
|
||||||
|
if v != 42 { ret 1; }
|
||||||
|
|
||||||
|
/* 通过指针修改 */
|
||||||
|
p.* = 99;
|
||||||
|
if x != 99 { ret 2; }
|
||||||
|
|
||||||
|
/* 指针作为函数参数 */
|
||||||
|
var y: i32 = 5;
|
||||||
|
inc(&y);
|
||||||
|
if y != 6 { ret 3; }
|
||||||
|
|
||||||
|
/* ---- null 空指针 ---- */
|
||||||
|
var np: *i32 = null;
|
||||||
|
if np != null { ret 4; }
|
||||||
|
if np == null {} else { ret 5; }
|
||||||
|
|
||||||
|
/* ---- 指针自动解引用(ptr.field 相当于 ptr->field) ---- */
|
||||||
|
type Point = struct {
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pt: Point;
|
||||||
|
pt.a = 10;
|
||||||
|
pt.b = 20;
|
||||||
|
var pp: *Point = &pt;
|
||||||
|
if pp.a != 10 { ret 6; }
|
||||||
|
if pp.b != 20 { ret 7; }
|
||||||
|
|
||||||
|
/* 通过指针修改字段 */
|
||||||
|
pp.a = 30;
|
||||||
|
if pt.a != 30 { ret 8; }
|
||||||
|
|
||||||
|
/* ---- 指向数组元素的指针 ---- */
|
||||||
|
var buf: [4]i32 = [4]i32{1, 2, 3, 4};
|
||||||
|
// @dbg(); /* 数组初始化后的栈状态 */
|
||||||
|
var elem_ptr: *i32 = &buf[0];
|
||||||
|
// @dbg(); /* &buf[0] 之后:栈上应有指针值 */
|
||||||
|
var loaded := elem_ptr.*;
|
||||||
|
// @dbg(); /* 解引用后:loaded 值 */
|
||||||
|
if loaded != 1 { ret 9; }
|
||||||
|
|
||||||
|
elem_ptr = &buf[1];
|
||||||
|
// @dbg(); /* &buf[1] 之后 */
|
||||||
|
var loaded2 := elem_ptr.*;
|
||||||
|
// @dbg(); /* 解引用后:loaded2 值 */
|
||||||
|
if loaded2 != 2 { ret 10; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/* ===== 模块5:复合类型 =====
|
||||||
|
* test08_arrays — 数组
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:数组字面量 [N]T{...}、数组索引、数组元素修改
|
||||||
|
*/
|
||||||
|
fn main() i32 {
|
||||||
|
/* 数组字面量 */
|
||||||
|
var arr: [3]i32 = [3]i32{1, 2, 3};
|
||||||
|
if arr[0] != 1 { ret 1; }
|
||||||
|
if arr[1] != 2 { ret 2; }
|
||||||
|
if arr[2] != 3 { ret 3; }
|
||||||
|
|
||||||
|
/* 数组元素修改 */
|
||||||
|
arr[1] = 99;
|
||||||
|
if arr[1] != 99 { ret 4; }
|
||||||
|
|
||||||
|
/* 数组求和 */
|
||||||
|
var nums: [5]i32 = [5]i32{10, 20, 30, 40, 50};
|
||||||
|
var sum: i32 = 0;
|
||||||
|
sum = sum + nums[0];
|
||||||
|
sum = sum + nums[1];
|
||||||
|
sum = sum + nums[2];
|
||||||
|
sum = sum + nums[3];
|
||||||
|
sum = sum + nums[4];
|
||||||
|
if sum != 150 { ret 5; }
|
||||||
|
|
||||||
|
/* 数组通过循环访问(while) */
|
||||||
|
var vals: [4]i32 = [4]i32{2, 4, 6, 8};
|
||||||
|
var i: i32 = 0;
|
||||||
|
var s: i32 = 0;
|
||||||
|
while i < 4 {
|
||||||
|
s = s + vals[i];
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if s != 20 { ret 6; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/* ===== 模块5:复合类型 =====
|
||||||
|
* test09_slices — 切片
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:arr[begin..end] 创建切片、arr[begin..] 到末尾、
|
||||||
|
* 切片索引访问、slice.len、slice.ptr
|
||||||
|
*/
|
||||||
|
fn main() i32 {
|
||||||
|
/* 从数组创建切片 */
|
||||||
|
var arr: [5]i32 = [5]i32{10, 20, 30, 40, 50};
|
||||||
|
var slice: []i32 = arr[1..4];
|
||||||
|
|
||||||
|
/* 切片长度 */
|
||||||
|
if slice.len != 3 { ret 1; }
|
||||||
|
|
||||||
|
/* 切片元素访问 */
|
||||||
|
if slice[0] != 20 { ret 2; }
|
||||||
|
if slice[1] != 30 { ret 3; }
|
||||||
|
if slice[2] != 40 { ret 4; }
|
||||||
|
|
||||||
|
/* 省略结束值:arr[begin..] */
|
||||||
|
var full: []i32 = arr[0..];
|
||||||
|
if full.len != 5 { ret 5; }
|
||||||
|
if full[0] != 10 { ret 6; }
|
||||||
|
if full[4] != 50 { ret 7; }
|
||||||
|
|
||||||
|
/* 从开头到中间 */
|
||||||
|
var head: []i32 = arr[0..3];
|
||||||
|
if head.len != 3 { ret 8; }
|
||||||
|
if head[0] != 10 { ret 9; }
|
||||||
|
|
||||||
|
/* 切片遍历(配合 for)*/
|
||||||
|
var nums: [3]i32 = [3]i32{100, 200, 300};
|
||||||
|
var sl: []i32 = nums[0..];
|
||||||
|
var total: i32 = 0;
|
||||||
|
var i: i32 = 0;
|
||||||
|
while i < sl.len {
|
||||||
|
total = total + sl[i];
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if total != 600 { ret 10; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
/* ===== 模块5:复合类型 =====
|
||||||
|
* test10_struct — 结构体
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:struct 定义、字段访问、内联 type、嵌套结构体
|
||||||
|
*/
|
||||||
|
type Pair = struct {
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Three = struct {
|
||||||
|
a: i64,
|
||||||
|
b: i64,
|
||||||
|
c: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tag = enum {
|
||||||
|
Unknown,
|
||||||
|
BlockComment,
|
||||||
|
LineComment,
|
||||||
|
Ident,
|
||||||
|
Number,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token = struct {
|
||||||
|
loc_start: i32,
|
||||||
|
loc_end: i32,
|
||||||
|
tag: Tag,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_three(a: i64, b: i64, c: i64) Three {
|
||||||
|
ret Three { .a = a, .b = b, .c = c };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum_three(t: Three) i64 {
|
||||||
|
ret t.a + t.b + t.c;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_token(s: i32, e: i32, t: Tag) Token {
|
||||||
|
ret Token { .loc_start = s, .loc_end = e, .tag = t };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_ret_enum_struct() i32 {
|
||||||
|
var tok := make_token(10, 20, Tag.BlockComment);
|
||||||
|
if tok.loc_start != 10 { ret 1; }
|
||||||
|
if tok.loc_end != 20 { ret 2; }
|
||||||
|
if tok.tag != Tag.BlockComment { ret 3; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_nested_ret_enum() i32 {
|
||||||
|
var tok := make_token(1, 5, Tag.Ident);
|
||||||
|
if tok.tag != Tag.Ident { ret 1; }
|
||||||
|
var tok2 := make_token(6, 9, Tag.Number);
|
||||||
|
if tok2.tag != Tag.Number { ret 2; }
|
||||||
|
if tok.tag != Tag.Ident { ret 3; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_copy_token() i32 {
|
||||||
|
var tok := make_token(0, 100, Tag.LineComment);
|
||||||
|
var c := tok;
|
||||||
|
if c.loc_start != 0 { ret 1; }
|
||||||
|
if c.tag != Tag.LineComment { ret 2; }
|
||||||
|
c.tag = Tag.Ident;
|
||||||
|
if tok.tag != Tag.LineComment { ret 3; }
|
||||||
|
if c.tag != Tag.Ident { ret 4; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_pass_token(t: Token) i32 {
|
||||||
|
if t.tag != Tag.Number { ret 1; }
|
||||||
|
if t.loc_start != 42 { ret 2; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_param_pass_token() i32 {
|
||||||
|
var tok := make_token(42, 99, Tag.Number);
|
||||||
|
var r := test_pass_token(tok);
|
||||||
|
if r != 0 { ret r; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_get_tag(tok: Token) Tag {
|
||||||
|
ret tok.tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_chain_func_call() i32 {
|
||||||
|
var tok := make_token(1, 2, Tag.BlockComment);
|
||||||
|
var tg := test_get_tag(tok);
|
||||||
|
if tg != Tag.BlockComment { ret 1; }
|
||||||
|
var tok2 := make_token(3, 4, Tag.LineComment);
|
||||||
|
var tg2 := test_get_tag(tok2);
|
||||||
|
if tg2 != Tag.LineComment { ret 2; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_ret_multi_slot_struct() i32 {
|
||||||
|
var t := make_three(10, 20, 30);
|
||||||
|
if t.a != 10 { ret 1; }
|
||||||
|
if t.b != 20 { ret 2; }
|
||||||
|
if t.c != 30 { ret 3; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_pass_struct_by_value() i32 {
|
||||||
|
var t := make_three(10, 20, 30);
|
||||||
|
var sum := sum_three(t);
|
||||||
|
if sum != 60 { ret 1; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_copy_struct() i32 {
|
||||||
|
var t := make_three(10, 20, 30);
|
||||||
|
var t2: Three;
|
||||||
|
t2 = t;
|
||||||
|
if t2.a != 10 { ret 1; }
|
||||||
|
t2.a = 99;
|
||||||
|
if t.a != 10 { ret 2; }
|
||||||
|
if t2.a != 99 { ret 3; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_multi_calls() i32 {
|
||||||
|
var ta := make_three(5, 10, 15);
|
||||||
|
var tb := make_three(20, 25, 30);
|
||||||
|
if ta.a + ta.b + ta.c != 30 { ret 1; }
|
||||||
|
if tb.a + tb.b + tb.c != 75 { ret 2; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_literal_arg() i32 {
|
||||||
|
var sum := sum_three(Three { .a = 7, .b = 8, .c = 9 });
|
||||||
|
if sum != 24 { ret 1; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
/* struct 字段访问 */
|
||||||
|
var p: Pair;
|
||||||
|
p.a = 1;
|
||||||
|
p.b = 2;
|
||||||
|
if p.a != 1 { ret 1; }
|
||||||
|
if p.b != 2 { ret 2; }
|
||||||
|
|
||||||
|
/* 同一类型复用 */
|
||||||
|
var p2: Pair;
|
||||||
|
p2.a = 10;
|
||||||
|
p2.b = 20;
|
||||||
|
if p2.a + p2.b != 30 { ret 3; }
|
||||||
|
|
||||||
|
/* 不影响其他实例 */
|
||||||
|
if p.a != 1 { ret 4; }
|
||||||
|
|
||||||
|
/* 内联 type 定义(在函数内) */
|
||||||
|
type Triple = struct {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
var t: Triple;
|
||||||
|
t.x = 5;
|
||||||
|
t.y = 10;
|
||||||
|
t.z = 15;
|
||||||
|
if t.x + t.y + t.z != 30 { ret 5; }
|
||||||
|
|
||||||
|
/* 结构体字段运算 */
|
||||||
|
var calc: Pair;
|
||||||
|
calc.a = 7;
|
||||||
|
calc.b = 3;
|
||||||
|
var r := calc.a * calc.b + calc.a - calc.b;
|
||||||
|
if r != 25 { ret 6; }
|
||||||
|
|
||||||
|
/* 结构体嵌套 */
|
||||||
|
type Outer = struct {
|
||||||
|
inner: Inner,
|
||||||
|
extra: i32,
|
||||||
|
type Inner = struct {
|
||||||
|
val: i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var o: Outer;
|
||||||
|
o.inner.val = 42;
|
||||||
|
o.extra = 58;
|
||||||
|
if o.inner.val != 42 { ret 7; }
|
||||||
|
if o.extra != 58 { ret 8; }
|
||||||
|
|
||||||
|
/* 多 slot 结构体返回值测试 */
|
||||||
|
var r9 := test_ret_multi_slot_struct();
|
||||||
|
if r9 != 0 { ret 100; }
|
||||||
|
var r10 := test_pass_struct_by_value();
|
||||||
|
if r10 != 0 { ret 101; }
|
||||||
|
var r11 := test_copy_struct();
|
||||||
|
if r11 != 0 { ret 102; }
|
||||||
|
var r12 := test_multi_calls();
|
||||||
|
if r12 != 0 { ret 103; }
|
||||||
|
var r13 := test_literal_arg();
|
||||||
|
if r13 != 0 { ret 104; }
|
||||||
|
|
||||||
|
/* 含 enum 字段的结构体返回值测试 */
|
||||||
|
var r14 := test_ret_enum_struct();
|
||||||
|
if r14 != 0 { ret 110; }
|
||||||
|
var r15 := test_nested_ret_enum();
|
||||||
|
if r15 != 0 { ret 111; }
|
||||||
|
var r16 := test_copy_token();
|
||||||
|
if r16 != 0 { ret 112; }
|
||||||
|
var r17 := test_param_pass_token();
|
||||||
|
if r17 != 0 { ret 113; }
|
||||||
|
var r18 := test_chain_func_call();
|
||||||
|
if r18 != 0 { ret 114; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/* ===== 模块5:复合类型 =====
|
||||||
|
* test11_enum — 枚举
|
||||||
|
* 难度:3/5
|
||||||
|
* 验证点:enum 定义、简单枚举值、带数据枚举变体
|
||||||
|
*/
|
||||||
|
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||||
|
|
||||||
|
type Color = enum {
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Expr = enum {
|
||||||
|
val: i32,
|
||||||
|
tag: Tag,
|
||||||
|
|
||||||
|
type Tag = enum {
|
||||||
|
TagA,
|
||||||
|
TagB,
|
||||||
|
TagC,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
vm_printf("enum values: %d %d %d\n", Color.Red, Color.Green, Color.Blue);
|
||||||
|
vm_printf("enum values: %d %d %d\n", Expr.Tag.TagA, Expr.Tag.TagB, Expr.Tag.TagC);
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/* ===== 模块6:函数 =====
|
||||||
|
* test12_functions — 函数调用
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:函数定义与调用、参数传递、返回值、多个参数
|
||||||
|
*/
|
||||||
|
fn add(a: i32, b: i32) i32 {
|
||||||
|
ret a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub(a: i32, b: i32) i32 {
|
||||||
|
ret a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul(a: i32, b: i32) i32 {
|
||||||
|
ret a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn identity(x: i32) i32 {
|
||||||
|
ret x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addr(x: *i32) *i32 {
|
||||||
|
ret x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
/* 函数调用 */
|
||||||
|
var r1 := add(3, 4);
|
||||||
|
if r1 != 7 { ret 1; }
|
||||||
|
|
||||||
|
var r2 := sub(10, 3);
|
||||||
|
if r2 != 7 { ret 2; }
|
||||||
|
|
||||||
|
var r3 := mul(6, 7);
|
||||||
|
if r3 != 42 { ret 3; }
|
||||||
|
|
||||||
|
/* 函数嵌套调用 */
|
||||||
|
var r4 := add(mul(2, 3), sub(10, 4));
|
||||||
|
if r4 != 12 { ret 4; }
|
||||||
|
|
||||||
|
/* 函数返回值的传递 */
|
||||||
|
var r5 := identity(99);
|
||||||
|
if r5 != 99 { ret 5; }
|
||||||
|
|
||||||
|
/* 多个参数 */
|
||||||
|
var r6 := add(add(1, 2), add(3, 4));
|
||||||
|
if r6 != 10 { ret 6; }
|
||||||
|
|
||||||
|
/* 返回地址 */
|
||||||
|
var r7 := addr(&r6);
|
||||||
|
if r7 != &r6 { ret 7; }
|
||||||
|
if r7.* != 10 { ret 8; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/* ===== 模块6:函数 =====
|
||||||
|
* test13_extern — 外部 VM 函数与字符串
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:@extern(vm) 声明、vm_printf 调用、
|
||||||
|
* 字符串字面量、字符串参数传递
|
||||||
|
*/
|
||||||
|
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||||
|
@extern(vm) fn vm_strlen(s: *u8) i32;
|
||||||
|
@extern(vm) fn vm_strcmp(a: *u8, b: *u8) i32;
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
/* vm_printf 输出测试 */
|
||||||
|
vm_printf("hello spl\n");
|
||||||
|
vm_printf("%d %d %d\n", 1, 2, 3);
|
||||||
|
vm_printf("chars: %c%c\n", 'A', 'B');
|
||||||
|
|
||||||
|
/* 字符串字面量用于 extern */
|
||||||
|
var len := vm_strlen("hello");
|
||||||
|
if len != 5 { ret 1; }
|
||||||
|
|
||||||
|
/* 字符串比较 */
|
||||||
|
var cmp := vm_strcmp("abc", "abc");
|
||||||
|
if cmp != 0 { ret 2; }
|
||||||
|
|
||||||
|
var cmp2 := vm_strcmp("abc", "def");
|
||||||
|
if cmp2 == 0 { ret 3; }
|
||||||
|
|
||||||
|
/* 字符串作为格式化参数 */
|
||||||
|
vm_printf("string test: %s %s\n", "foo", "bar");
|
||||||
|
|
||||||
|
/* vm_printf 返回 void(不关心返回值)*/
|
||||||
|
vm_printf("all extern tests done\n");
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/* ===== 模块7:高级特性 =====
|
||||||
|
* test14_assignment — 复合赋值
|
||||||
|
* 难度:2/5
|
||||||
|
* 验证点:+= -= *= /= %= &= |= ^= <<= >>=
|
||||||
|
*/
|
||||||
|
fn main() i32 {
|
||||||
|
var x: i32 = 10;
|
||||||
|
|
||||||
|
/* += */
|
||||||
|
x += 5;
|
||||||
|
if x != 15 { ret 1; }
|
||||||
|
|
||||||
|
/* -= */
|
||||||
|
x -= 3;
|
||||||
|
if x != 12 { ret 2; }
|
||||||
|
|
||||||
|
/* *= */
|
||||||
|
x *= 2;
|
||||||
|
if x != 24 { ret 3; }
|
||||||
|
|
||||||
|
/* /= */
|
||||||
|
x /= 4;
|
||||||
|
if x != 6 { ret 4; }
|
||||||
|
|
||||||
|
/* %= */
|
||||||
|
var y: i32 = 17;
|
||||||
|
y %= 5;
|
||||||
|
if y != 2 { ret 5; }
|
||||||
|
|
||||||
|
/* &= */
|
||||||
|
var a: i32 = 0xFF;
|
||||||
|
a &= 0x0F;
|
||||||
|
if a != 0x0F { ret 6; }
|
||||||
|
|
||||||
|
/* |= */
|
||||||
|
var b: i32 = 0xF0;
|
||||||
|
b |= 0x0F;
|
||||||
|
if b != 0xFF { ret 7; }
|
||||||
|
|
||||||
|
/* ^= */
|
||||||
|
var c: i32 = 0xFF;
|
||||||
|
c ^= 0xF0;
|
||||||
|
if c != 0x0F { ret 8; }
|
||||||
|
|
||||||
|
/* <<= */
|
||||||
|
var d: i32 = 1;
|
||||||
|
d <<= 4;
|
||||||
|
if d != 16 { ret 9; }
|
||||||
|
|
||||||
|
/* >>= */
|
||||||
|
var e: i32 = 64;
|
||||||
|
e >>= 3;
|
||||||
|
if e != 8 { ret 10; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/* ===== 模块7:高级特性 =====
|
||||||
|
* test15_defer — 延迟执行
|
||||||
|
* 难度:3/5
|
||||||
|
* 验证点:defer 语句、defer 块、多个 defer(逆序执行)、
|
||||||
|
* 函数中 defer、块作用域 defer
|
||||||
|
*/
|
||||||
|
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||||
|
|
||||||
|
fn with_cleanup() void {
|
||||||
|
defer vm_printf(" inner defer\n");
|
||||||
|
vm_printf(" inside with_cleanup\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
vm_printf("--- defer test ---\n");
|
||||||
|
|
||||||
|
/* defer 语句 */
|
||||||
|
defer vm_printf("defer1\n");
|
||||||
|
vm_printf("after defer1\n");
|
||||||
|
|
||||||
|
/* defer 块 */
|
||||||
|
defer {
|
||||||
|
vm_printf("defer block\n");
|
||||||
|
}
|
||||||
|
vm_printf("after defer block\n");
|
||||||
|
|
||||||
|
/* 函数内 defer */
|
||||||
|
with_cleanup();
|
||||||
|
vm_printf("after with_cleanup\n");
|
||||||
|
|
||||||
|
/* 多个 defer 应逆序执行出作用域应该立刻执行,包括循环作用域 */
|
||||||
|
{
|
||||||
|
defer vm_printf("block defer last\n");
|
||||||
|
defer vm_printf("block defer middle\n");
|
||||||
|
defer vm_printf("block defer first (should print third)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 多个 defer 应逆序执行 */
|
||||||
|
defer vm_printf("defer last\n");
|
||||||
|
defer vm_printf("defer middle\n");
|
||||||
|
defer vm_printf("defer first (should print third)\n");
|
||||||
|
|
||||||
|
vm_printf("--- defer test end ---\n");
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/* ===== 模块7:高级特性 =====
|
||||||
|
* test16_methods — 类型关联方法
|
||||||
|
* 难度:3/5
|
||||||
|
* 验证点:struct 方法、enum 方法、self 参数自动填充、方法调用
|
||||||
|
*/
|
||||||
|
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||||
|
|
||||||
|
type Point = struct {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
|
||||||
|
fn init(x: i32, y: i32) Point {
|
||||||
|
ret Point { .x = x, .y = y };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump(self: *Point) void {
|
||||||
|
vm_printf("Point(%d, %d)\n", self.x, self.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Expr = enum {
|
||||||
|
Int: i32,
|
||||||
|
Add: struct { left: *Expr, right: *Expr },
|
||||||
|
|
||||||
|
fn eval(self: *Expr) i32 {
|
||||||
|
match self {
|
||||||
|
.Int[val] => { ret val; },
|
||||||
|
.Add[sub] => { ret eval(sub.left) + eval(sub.right); }
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
/* struct 方法调用 */
|
||||||
|
var p: Point = Point.init(3, 4);
|
||||||
|
p.dump(&p);
|
||||||
|
|
||||||
|
/* enum 方法 + match */
|
||||||
|
var expr_l := Expr { .Int = 3 };
|
||||||
|
var expr_r := Expr { .Int = 4 };
|
||||||
|
var expr := Expr { .Add = .{ .left = expr_l, .right = expr_r } };
|
||||||
|
var result := expr.eval(&expr);
|
||||||
|
vm_printf("eval result: %d\n", result);
|
||||||
|
if result != 7 { ret 1; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/* ===== 模块8:综合 =====
|
||||||
|
* test17_integration — 综合测试
|
||||||
|
* 难度:3/5
|
||||||
|
* 验证点:多特性组合——指针 + 结构体 + 函数 + 循环 + 数组 + 切片
|
||||||
|
*/
|
||||||
|
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||||
|
|
||||||
|
type Point = struct {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 计算点积 */
|
||||||
|
fn dot(a: *Point, b: *Point) i32 {
|
||||||
|
ret a.x * b.x + a.y * b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 递归斐波那契 */
|
||||||
|
fn fib(n: i32) i32 {
|
||||||
|
if n <= 1 { ret n; }
|
||||||
|
ret fib(n - 1) + fib(n - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
/* 综合:指针 + 结构体 + 函数 */
|
||||||
|
var p1: Point;
|
||||||
|
p1.x = 3;
|
||||||
|
p1.y = 4;
|
||||||
|
|
||||||
|
var p2: Point;
|
||||||
|
p2.x = 5;
|
||||||
|
p2.y = 6;
|
||||||
|
|
||||||
|
var d := dot(&p1, &p2);
|
||||||
|
/* 3*5 + 4*6 = 15 + 24 = 39 */
|
||||||
|
if d != 39 { ret 1; }
|
||||||
|
|
||||||
|
/* 综合:数组 + while 循环 */
|
||||||
|
var arr: [5]i32 = [5]i32{1, 2, 3, 4, 5};
|
||||||
|
var sum: i32 = 0;
|
||||||
|
var i: i32 = 0;
|
||||||
|
while i < 5 {
|
||||||
|
sum = sum + arr[i];
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if sum != 15 { ret 2; }
|
||||||
|
|
||||||
|
/* 综合:切片 + for */
|
||||||
|
var sl: []i32 = arr[1..4];
|
||||||
|
var sl_sum: i32 = 0;
|
||||||
|
for sl as val {
|
||||||
|
sl_sum = sl_sum + val;
|
||||||
|
}
|
||||||
|
if sl_sum != 9 { ret 3; } /* 2+3+4 */
|
||||||
|
|
||||||
|
/* 综合:递归 */
|
||||||
|
var f := fib(10);
|
||||||
|
if f != 55 { ret 4; }
|
||||||
|
|
||||||
|
/* 综合:loop/break/continue */
|
||||||
|
var n: i32 = 0;
|
||||||
|
var count: i32 = 0;
|
||||||
|
loop {
|
||||||
|
n = n + 1;
|
||||||
|
if n == 2 { continue; }
|
||||||
|
count = count + 1;
|
||||||
|
if n >= 5 { break; }
|
||||||
|
}
|
||||||
|
/* 执行 n=1,3,4,5 共 4 次 (n=2 被 continue) */
|
||||||
|
if count != 4 { ret 5; }
|
||||||
|
|
||||||
|
/* 综合:指针修改结构体 */
|
||||||
|
var pp: *Point = &p1;
|
||||||
|
pp.x = 10;
|
||||||
|
pp.y = 20;
|
||||||
|
if p1.x != 10 { ret 6; }
|
||||||
|
if p1.y != 20 { ret 7; }
|
||||||
|
|
||||||
|
/* 综合:嵌套控制流 */
|
||||||
|
var mat: [3]i32 = [3]i32{1, 2, 3};
|
||||||
|
var outer_sum: i32 = 0;
|
||||||
|
var j: i32 = 0;
|
||||||
|
while j < 3 {
|
||||||
|
var k: i32 = 0;
|
||||||
|
while k < 3 {
|
||||||
|
if mat[j] > mat[k] {
|
||||||
|
outer_sum = outer_sum + 1;
|
||||||
|
}
|
||||||
|
k = k + 1;
|
||||||
|
}
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
if outer_sum != 3 { ret 8; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,488 @@
|
|||||||
|
/* ===== 模块4:match 语句 =====
|
||||||
|
* test18_match — match 枚举匹配 + 整数匹配(类似 switch)
|
||||||
|
* 难度:3/5
|
||||||
|
* 验证点:枚举匹配、带数据绑定、结构体字段绑定、枚举指针、
|
||||||
|
* 整数自变量匹配、默认分支 _
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ---- 简单枚举(无数据) ---- */
|
||||||
|
type Color = enum {
|
||||||
|
Red;
|
||||||
|
Green;
|
||||||
|
Blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 带 i32 数据的枚举 ---- */
|
||||||
|
type Optional = enum {
|
||||||
|
Some: i32;
|
||||||
|
None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 带结构体数据的枚举 ---- */
|
||||||
|
type Point = struct {
|
||||||
|
var x: i32;
|
||||||
|
var y: i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Shape = enum {
|
||||||
|
Circle: i32;
|
||||||
|
Rect: Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 多变体枚举 ---- */
|
||||||
|
type ActionResult = enum {
|
||||||
|
Success: i32;
|
||||||
|
NotFound;
|
||||||
|
Timeout: i32;
|
||||||
|
Error: *u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 1: 无数据枚举匹配
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_color_match() i32 {
|
||||||
|
var c: Color = Color { .Red };
|
||||||
|
var val: i32 = 0;
|
||||||
|
|
||||||
|
match c {
|
||||||
|
.Red => { val = 1; },
|
||||||
|
.Green => { val = 2; },
|
||||||
|
.Blue => { val = 3; }
|
||||||
|
}
|
||||||
|
if val != 1 { ret 1; }
|
||||||
|
|
||||||
|
c = Color { .Green };
|
||||||
|
match c {
|
||||||
|
.Red => { val = 0; },
|
||||||
|
.Green => { val = 2; },
|
||||||
|
.Blue => { val = 0; }
|
||||||
|
}
|
||||||
|
if val != 2 { ret 2; }
|
||||||
|
|
||||||
|
c = Color { .Blue };
|
||||||
|
match c {
|
||||||
|
.Red => { val = 0; },
|
||||||
|
.Green => { val = 0; },
|
||||||
|
.Blue => { val = 3; }
|
||||||
|
}
|
||||||
|
if val != 3 { ret 3; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 2: 带 i32 数据枚举匹配
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_optional_match() i32 {
|
||||||
|
var o: Optional = Optional { .Some = 42 };
|
||||||
|
|
||||||
|
match o {
|
||||||
|
.Some[val] => {
|
||||||
|
if val != 42 { ret 1; }
|
||||||
|
},
|
||||||
|
.None => {
|
||||||
|
ret 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
o = Optional { .None };
|
||||||
|
var is_none: i32 = 0;
|
||||||
|
match o {
|
||||||
|
.Some[val] => {},
|
||||||
|
.None => { is_none = 1; }
|
||||||
|
}
|
||||||
|
if is_none != 1 { ret 3; }
|
||||||
|
|
||||||
|
/* 多次提取不同值 */
|
||||||
|
o = Optional { .Some = 99 };
|
||||||
|
match o {
|
||||||
|
.Some[val] => {
|
||||||
|
if val != 99 { ret 4; }
|
||||||
|
},
|
||||||
|
.None => { ret 5; }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 3: 带结构体数据枚举匹配(多字段绑定)
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_shape_match() i32 {
|
||||||
|
/* Circle: 单数据 */
|
||||||
|
var s: Shape = Shape { .Circle = 10 };
|
||||||
|
match s {
|
||||||
|
.Circle[r] => {
|
||||||
|
if r != 10 { ret 1; }
|
||||||
|
},
|
||||||
|
.Rect[p] => {
|
||||||
|
ret 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rect: 结构体数据,绑定为 (x, y) 对应 Point 的字段 */
|
||||||
|
s = Shape { .Rect = Point { .x = 3, .y = 4 } };
|
||||||
|
match s {
|
||||||
|
.Circle[r] => { ret 3; },
|
||||||
|
.Rect[p] => {
|
||||||
|
if p.x != 3 { ret 4; }
|
||||||
|
if p.y != 4 { ret 5; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 4: 多数据变体枚举匹配
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_action_result_match() i32 {
|
||||||
|
var r: ActionResult = ActionResult { .Success = 200 };
|
||||||
|
match r {
|
||||||
|
.Success[code] => {
|
||||||
|
if code != 200 { ret 1; }
|
||||||
|
},
|
||||||
|
.NotFound => {
|
||||||
|
ret 2;
|
||||||
|
},
|
||||||
|
.Timeout[ms] => {
|
||||||
|
ret 3;
|
||||||
|
},
|
||||||
|
.Error[msg] => {
|
||||||
|
ret 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = ActionResult { .NotFound };
|
||||||
|
var found: i32 = 1;
|
||||||
|
match r {
|
||||||
|
.Success[code] => { found = 0; },
|
||||||
|
.NotFound => { },
|
||||||
|
.Timeout[ms] => { found = 0; },
|
||||||
|
.Error[msg] => { found = 0; }
|
||||||
|
}
|
||||||
|
if found != 1 { ret 5; }
|
||||||
|
|
||||||
|
r = ActionResult { .Timeout = 5000 };
|
||||||
|
match r {
|
||||||
|
.Success[code] => { ret 6; },
|
||||||
|
.NotFound => { ret 7; },
|
||||||
|
.Timeout[ms] => {
|
||||||
|
if ms != 5000 { ret 8; }
|
||||||
|
},
|
||||||
|
.Error[msg] => { ret 9; }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 5: 枚举指针匹配
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_ptr_match() i32 {
|
||||||
|
var c: Color = Color { .Green };
|
||||||
|
var p: *Color = &c;
|
||||||
|
|
||||||
|
match p {
|
||||||
|
.Red => { ret 1; },
|
||||||
|
.Green => { },
|
||||||
|
.Blue => { ret 2; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改后通过指针匹配 */
|
||||||
|
c = Color { .Blue };
|
||||||
|
match p {
|
||||||
|
.Red => { ret 3; },
|
||||||
|
.Green => { ret 4; },
|
||||||
|
.Blue => { }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 6: match 作为函数返回值
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn classify_color(c: Color) i32 {
|
||||||
|
match c {
|
||||||
|
.Red => { ret 1; },
|
||||||
|
.Green => { ret 2; },
|
||||||
|
.Blue => { ret 3; }
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_match_in_func() i32 {
|
||||||
|
var r: i32;
|
||||||
|
|
||||||
|
r = classify_color(Color { .Red });
|
||||||
|
if r != 1 { ret 1; }
|
||||||
|
|
||||||
|
r = classify_color(Color { .Green });
|
||||||
|
if r != 2 { ret 2; }
|
||||||
|
|
||||||
|
r = classify_color(Color { .Blue });
|
||||||
|
if r != 3 { ret 3; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 7: match 嵌套在循环中
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_match_in_loop() i32 {
|
||||||
|
var i: i32 = 0;
|
||||||
|
var sum: i32 = 0;
|
||||||
|
|
||||||
|
while i < 3 {
|
||||||
|
var o: Optional;
|
||||||
|
if i == 0 {
|
||||||
|
o = Optional { .Some = 10 };
|
||||||
|
} else if i == 1 {
|
||||||
|
o = Optional { .Some = 20 };
|
||||||
|
} else {
|
||||||
|
o = Optional { .Some = 30 };
|
||||||
|
}
|
||||||
|
|
||||||
|
match o {
|
||||||
|
.Some[val] => {
|
||||||
|
sum = sum + val;
|
||||||
|
},
|
||||||
|
.None => { }
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if sum != 60 { ret 1; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 8: 整数 match(类似 switch)
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_int_match() i32 {
|
||||||
|
var x: i32 = 2;
|
||||||
|
var result: i32 = 0;
|
||||||
|
|
||||||
|
match x {
|
||||||
|
1 => { result = 10; },
|
||||||
|
2 => { result = 20; },
|
||||||
|
3 => { result = 30; }
|
||||||
|
}
|
||||||
|
if result != 20 { ret 1; }
|
||||||
|
|
||||||
|
/* 匹配第一个值 */
|
||||||
|
x = 1;
|
||||||
|
match x {
|
||||||
|
1 => { result = 100; },
|
||||||
|
2 => { result = 200; },
|
||||||
|
3 => { result = 300; }
|
||||||
|
}
|
||||||
|
if result != 100 { ret 2; }
|
||||||
|
|
||||||
|
/* 匹配最后一个值 */
|
||||||
|
x = 3;
|
||||||
|
match x {
|
||||||
|
1 => { result = 1000; },
|
||||||
|
2 => { result = 2000; },
|
||||||
|
3 => { result = 3000; }
|
||||||
|
}
|
||||||
|
if result != 3000 { ret 3; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 9: 整数 match 带默认分支 _
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_int_match_default() i32 {
|
||||||
|
var x: i32 = 99;
|
||||||
|
var result: i32 = 0;
|
||||||
|
|
||||||
|
match x {
|
||||||
|
1 => { result = 1; },
|
||||||
|
2 => { result = 2; },
|
||||||
|
_ => { result = 99; }
|
||||||
|
}
|
||||||
|
if result != 99 { ret 1; }
|
||||||
|
|
||||||
|
/* 默认分支未触发 */
|
||||||
|
x = 1;
|
||||||
|
match x {
|
||||||
|
1 => { result = 1; },
|
||||||
|
2 => { result = 2; },
|
||||||
|
_ => { result = 99; }
|
||||||
|
}
|
||||||
|
if result != 1 { ret 2; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 10: 整数 match 多个值跳转到相同逻辑
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_int_match_multi() i32 {
|
||||||
|
var x: i32 = 2;
|
||||||
|
var result: i32 = 0;
|
||||||
|
|
||||||
|
/* 每个分支独立 */
|
||||||
|
match x {
|
||||||
|
0 => { result = 0; },
|
||||||
|
1 => { result = 1; },
|
||||||
|
2 => { result = 2; },
|
||||||
|
3 => { result = 3; }
|
||||||
|
}
|
||||||
|
if result != 2 { ret 1; }
|
||||||
|
|
||||||
|
/* 负数和零 */
|
||||||
|
x = -1;
|
||||||
|
match x {
|
||||||
|
-1 => { result = -1; },
|
||||||
|
0 => { result = 0; },
|
||||||
|
1 => { result = 1; }
|
||||||
|
}
|
||||||
|
if result != -1 { ret 2; }
|
||||||
|
|
||||||
|
x = 0;
|
||||||
|
match x {
|
||||||
|
-1 => { result = -1; },
|
||||||
|
0 => { result = 0; },
|
||||||
|
1 => { result = 1; }
|
||||||
|
}
|
||||||
|
if result != 0 { ret 3; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 11a: match 多值 fallthrough: 1, 2, 3 => body
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_int_match_fallthrough() i32 {
|
||||||
|
var x: i32;
|
||||||
|
var r: i32;
|
||||||
|
|
||||||
|
/* 三个值映射到同一个 body */
|
||||||
|
x = 1; r = 0;
|
||||||
|
match x {
|
||||||
|
1, 2, 3 => { r = 10; },
|
||||||
|
4, 5 => { r = 20; }
|
||||||
|
}
|
||||||
|
if r != 10 { ret 1; }
|
||||||
|
|
||||||
|
x = 3;
|
||||||
|
match x {
|
||||||
|
1, 2, 3 => { r = 10; },
|
||||||
|
4, 5 => { r = 20; }
|
||||||
|
}
|
||||||
|
if r != 10 { ret 2; }
|
||||||
|
|
||||||
|
x = 5;
|
||||||
|
match x {
|
||||||
|
1, 2, 3 => { r = 10; },
|
||||||
|
4, 5 => { r = 20; }
|
||||||
|
}
|
||||||
|
if r != 20 { ret 3; }
|
||||||
|
|
||||||
|
/* 单一值(非fallthrough)仍然正常 */
|
||||||
|
x = 7; r = 0;
|
||||||
|
match x {
|
||||||
|
1 => { r = 1; },
|
||||||
|
7 => { r = 7; }
|
||||||
|
}
|
||||||
|
if r != 7 { ret 4; }
|
||||||
|
|
||||||
|
/* 多个负数值 */
|
||||||
|
x = -2; r = 0;
|
||||||
|
match x {
|
||||||
|
-3, -2, -1 => { r = 100; },
|
||||||
|
0, 1 => { r = 200; }
|
||||||
|
}
|
||||||
|
if r != 100 { ret 5; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 11b: match with body 中修改变量
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_match_with_side_effects() i32 {
|
||||||
|
var x: i32 = 3;
|
||||||
|
var acc: i32 = 0;
|
||||||
|
|
||||||
|
match x {
|
||||||
|
1 => { acc = acc + 1; },
|
||||||
|
2 => { acc = acc + 2; },
|
||||||
|
3 => { acc = acc + 3; },
|
||||||
|
4 => { acc = acc + 4; }
|
||||||
|
}
|
||||||
|
if acc != 3 { ret 1; }
|
||||||
|
|
||||||
|
/* 再次 match 同一个变量 */
|
||||||
|
match x {
|
||||||
|
1 => { acc = acc + 1; },
|
||||||
|
2 => { acc = acc + 2; },
|
||||||
|
3 => { acc = acc + 3; },
|
||||||
|
4 => { acc = acc + 4; }
|
||||||
|
}
|
||||||
|
if acc != 6 { ret 2; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 主函数
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
var r: i32;
|
||||||
|
|
||||||
|
r = test_color_match();
|
||||||
|
if r != 0 { ret r; }
|
||||||
|
|
||||||
|
r = test_optional_match();
|
||||||
|
if r != 0 { ret r + 10; }
|
||||||
|
|
||||||
|
r = test_shape_match();
|
||||||
|
if r != 0 { ret r + 20; }
|
||||||
|
|
||||||
|
r = test_action_result_match();
|
||||||
|
if r != 0 { ret r + 30; }
|
||||||
|
|
||||||
|
r = test_ptr_match();
|
||||||
|
if r != 0 { ret r + 40; }
|
||||||
|
|
||||||
|
r = test_match_in_func();
|
||||||
|
if r != 0 { ret r + 50; }
|
||||||
|
|
||||||
|
r = test_match_in_loop();
|
||||||
|
if r != 0 { ret r + 60; }
|
||||||
|
|
||||||
|
r = test_int_match();
|
||||||
|
if r != 0 { ret r + 70; }
|
||||||
|
|
||||||
|
r = test_int_match_default();
|
||||||
|
if r != 0 { ret r + 80; }
|
||||||
|
|
||||||
|
r = test_int_match_multi();
|
||||||
|
if r != 0 { ret r + 90; }
|
||||||
|
|
||||||
|
r = test_match_with_side_effects();
|
||||||
|
if r != 0 { ret r + 100; }
|
||||||
|
|
||||||
|
r = test_int_match_fallthrough();
|
||||||
|
if r != 0 { ret r + 110; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
/* ===== 进阶数组与切片 =====
|
||||||
|
* test19_hardarray — 多维数组、多维切片、切片构造、字符串测试
|
||||||
|
* 难度:4/5
|
||||||
|
* 验证点:多维数组索引与修改、多维切片、切片字段读写、扁平指针访问、字符串切片
|
||||||
|
*/
|
||||||
|
fn test_string() i32 {
|
||||||
|
/* ============================
|
||||||
|
* String test: str is []u8
|
||||||
|
* ============================ */
|
||||||
|
var data: *u8 = "hello";
|
||||||
|
var s: []u8 = .{ .ptr = data, .len = 5 };
|
||||||
|
|
||||||
|
if s.len != 5 { ret 100; }
|
||||||
|
if s[0] != 104 { ret 101; } /* 'h' */
|
||||||
|
if s[1] != 101 { ret 102; } /* 'e' */
|
||||||
|
if s[4] != 111 { ret 103; } /* 'o' */
|
||||||
|
|
||||||
|
/* Slice the string slice */
|
||||||
|
var sub: []u8 = s[1..4];
|
||||||
|
if sub.len != 3 { ret 104; }
|
||||||
|
if sub[0] != 101 { ret 105; } /* 'e' */
|
||||||
|
if sub[2] != 108 { ret 106; } /* 'l' */
|
||||||
|
|
||||||
|
/* Modify through slice → original changes */
|
||||||
|
s[0] = 72; /* 'H' */
|
||||||
|
if data[0] != 72 { ret 107; }
|
||||||
|
|
||||||
|
/* Full slice */
|
||||||
|
var full: []u8 = s[0..];
|
||||||
|
if full.len != 5 { ret 108; }
|
||||||
|
if full[0] != 72 { ret 109; }
|
||||||
|
|
||||||
|
/* Construct slice from pointer via field assignment */
|
||||||
|
var s2: []u8;
|
||||||
|
s2.ptr = data;
|
||||||
|
s2.len = 3;
|
||||||
|
if s2.len != 3 { ret 110; }
|
||||||
|
if s2[0] != 72 { ret 111; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
/* ============================
|
||||||
|
* Part 1: 多维数组(逐元素初始化)
|
||||||
|
* ============================ */
|
||||||
|
var matrix: [2][3]i32;
|
||||||
|
matrix[0][0] = 1; matrix[0][1] = 2; matrix[0][2] = 3;
|
||||||
|
matrix[1][0] = 4; matrix[1][1] = 5; matrix[1][2] = 6;
|
||||||
|
|
||||||
|
if matrix[0][0] != 1 { ret 1; }
|
||||||
|
if matrix[0][1] != 2 { ret 2; }
|
||||||
|
if matrix[0][2] != 3 { ret 3; }
|
||||||
|
if matrix[1][0] != 4 { ret 4; }
|
||||||
|
if matrix[1][1] != 5 { ret 5; }
|
||||||
|
if matrix[1][2] != 6 { ret 6; }
|
||||||
|
|
||||||
|
/* 元素修改 */
|
||||||
|
matrix[0][0] = 10;
|
||||||
|
matrix[1][2] = 60;
|
||||||
|
if matrix[0][0] != 10 { ret 7; }
|
||||||
|
if matrix[1][2] != 60 { ret 8; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 2: 嵌套循环遍历多维数组
|
||||||
|
* ============================ */
|
||||||
|
var big: [3][4]i32;
|
||||||
|
big[0][0] = 1; big[0][1] = 2; big[0][2] = 3; big[0][3] = 4;
|
||||||
|
big[1][0] = 5; big[1][1] = 6; big[1][2] = 7; big[1][3] = 8;
|
||||||
|
big[2][0] = 9; big[2][1] = 10; big[2][2] = 11; big[2][3] = 12;
|
||||||
|
|
||||||
|
var total: i32 = 0;
|
||||||
|
var i: i32 = 0;
|
||||||
|
while i < 3 {
|
||||||
|
var j: i32 = 0;
|
||||||
|
while j < 4 {
|
||||||
|
total = total + big[i][j];
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if total != 78 { ret 9; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 3: &取地址 + 扁平指针访问
|
||||||
|
* ============================ */
|
||||||
|
var flat: *i32 = &matrix[0][0];
|
||||||
|
if flat[0] != 10 { ret 10; }
|
||||||
|
if flat[1] != 2 { ret 11; }
|
||||||
|
if flat[2] != 3 { ret 12; }
|
||||||
|
if flat[3] != 4 { ret 13; }
|
||||||
|
if flat[4] != 5 { ret 14; }
|
||||||
|
if flat[5] != 60 { ret 15; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 4: 多维数组切片
|
||||||
|
* ============================ */
|
||||||
|
var row: []i32 = matrix[0][0..3];
|
||||||
|
if row.len != 3 { ret 16; }
|
||||||
|
if row[0] != 10 { ret 17; }
|
||||||
|
if row[1] != 2 { ret 18; }
|
||||||
|
if row[2] != 3 { ret 19; }
|
||||||
|
|
||||||
|
/* 切取第二行 */
|
||||||
|
var row2: []i32 = matrix[1][0..];
|
||||||
|
if row2.len != 3 { ret 20; }
|
||||||
|
if row2[0] != 4 { ret 21; }
|
||||||
|
if row2[2] != 60 { ret 22; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 5: 切片的切片
|
||||||
|
* ============================ */
|
||||||
|
var sub: []i32 = row[1..3];
|
||||||
|
if sub.len != 2 { ret 23; }
|
||||||
|
if sub[0] != 2 { ret 24; }
|
||||||
|
if sub[1] != 3 { ret 25; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 6: 切片字段读写
|
||||||
|
* ============================ */
|
||||||
|
var arr: [4]i32;
|
||||||
|
arr[0] = 100; arr[1] = 200; arr[2] = 300; arr[3] = 400;
|
||||||
|
var custom: []i32;
|
||||||
|
custom.ptr = &arr[1];
|
||||||
|
custom.len = 2;
|
||||||
|
|
||||||
|
if custom.len != 2 { ret 26; }
|
||||||
|
if custom[0] != 200 { ret 27; }
|
||||||
|
if custom[1] != 300 { ret 28; }
|
||||||
|
|
||||||
|
/* 修改切片长度 */
|
||||||
|
custom.len = 3;
|
||||||
|
if custom.len != 3 { ret 29; }
|
||||||
|
if custom[2] != 400 { ret 30; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 7: 空范围 / 全切片
|
||||||
|
* ============================ */
|
||||||
|
var full: []i32 = arr[0..];
|
||||||
|
if full.len != 4 { ret 31; }
|
||||||
|
if full[0] != 100 { ret 32; }
|
||||||
|
if full[3] != 400 { ret 33; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 8: 从指针构造切片
|
||||||
|
* ============================ */
|
||||||
|
var data: [5]i32;
|
||||||
|
data[0] = 10; data[1] = 20; data[2] = 30; data[3] = 40; data[4] = 50;
|
||||||
|
var p: *i32 = &data[2];
|
||||||
|
var from_ptr: []i32 = .{ .ptr = p, .len = 2 };
|
||||||
|
var from_ptr2: []i32;
|
||||||
|
from_ptr2.ptr = p;
|
||||||
|
from_ptr2.len = 2;
|
||||||
|
|
||||||
|
if from_ptr.len != 2 { ret 34; }
|
||||||
|
if from_ptr[0] != 30 { ret 35; }
|
||||||
|
if from_ptr[1] != 40 { ret 36; }
|
||||||
|
|
||||||
|
if from_ptr2.len != 2 { ret 37; }
|
||||||
|
if from_ptr2[0] != 30 { ret 38; }
|
||||||
|
if from_ptr2[1] != 40 { ret 39; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 9: 切片元素修改反映到原始数组
|
||||||
|
* ============================ */
|
||||||
|
row2[0] = 99;
|
||||||
|
if matrix[1][0] != 99 { ret 40; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 10: 1D 数组字面量仍然正常
|
||||||
|
* ============================ */
|
||||||
|
var literal: [3]i32 = [3]i32{10, 20, 30};
|
||||||
|
if literal[0] != 10 { ret 41; }
|
||||||
|
if literal[1] != 20 { ret 42; }
|
||||||
|
if literal[2] != 30 { ret 43; }
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
* Part 11: 字符串切片测试
|
||||||
|
* ============================ */
|
||||||
|
var r: i32 = test_string();
|
||||||
|
if r != 0 { ret r; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,320 @@
|
|||||||
|
/* ===== 复杂类型嵌套综合测试 =====
|
||||||
|
* test20_complex — 结构体嵌套、切片、数组、方法、类型别名、枚举等
|
||||||
|
* 难度:5/5
|
||||||
|
*/
|
||||||
|
/* ---- 基础结构体 ---- */
|
||||||
|
type Point = struct {
|
||||||
|
var x: i32;
|
||||||
|
var y: i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 嵌套结构体 ---- */
|
||||||
|
type Rect = struct {
|
||||||
|
var min: Point;
|
||||||
|
var max: Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 含切片字段的结构体 (核心 bug 测试) ---- */
|
||||||
|
type Buffer = struct {
|
||||||
|
var data: []u8;
|
||||||
|
var len: usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 含数组字段的结构体 ---- */
|
||||||
|
type MatrixRow = struct {
|
||||||
|
var items: [4]i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 含指针字段的结构体 ---- */
|
||||||
|
type Node = struct {
|
||||||
|
var ptr: *i32;
|
||||||
|
var val: i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 多层级嵌套:结构体里的结构体里的切片 ---- */
|
||||||
|
type Bundle = struct {
|
||||||
|
var name: *u8;
|
||||||
|
var buf: Buffer;
|
||||||
|
var row: MatrixRow;
|
||||||
|
var pt: Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 枚举含数据 ---- */
|
||||||
|
type Status = enum {
|
||||||
|
Active: i32;
|
||||||
|
Inactive;
|
||||||
|
Pending: Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 含方法的结构体 (方法定义在结构体内部) ---- */
|
||||||
|
type Counter = struct {
|
||||||
|
var val: i32;
|
||||||
|
|
||||||
|
fn inc(self: *Counter) i32 {
|
||||||
|
self.val = self.val + 1;
|
||||||
|
ret self.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(self: *Counter, n: i32) i32 {
|
||||||
|
self.val = self.val + n;
|
||||||
|
ret self.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(self: *Counter) {
|
||||||
|
self.val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(self: *Counter) i32 {
|
||||||
|
ret self.val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 1: 切片在结构体内部初始化 (修复的核心 bug)
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_slice_in_struct() i32 {
|
||||||
|
var raw: [4]u8;
|
||||||
|
raw[0] = 65; raw[1] = 66; raw[2] = 67; raw[3] = 68;
|
||||||
|
|
||||||
|
/* Bug fix: { .ptr = ..., .len = ... } inside struct literal */
|
||||||
|
var b: Buffer = Buffer { .data = .{ .ptr = &raw[0], .len = 4 }, .len = 4 };
|
||||||
|
|
||||||
|
if b.len != 4 { ret 1; }
|
||||||
|
if b.data[0] != 65 { ret 2; }
|
||||||
|
if b.data[1] != 66 { ret 3; }
|
||||||
|
if b.data[3] != 68 { ret 4; }
|
||||||
|
|
||||||
|
/* Modify through slice — verify reflection */
|
||||||
|
b.data[0] = 90;
|
||||||
|
if raw[0] != 90 { ret 5; }
|
||||||
|
|
||||||
|
/* Initialize with shorter slice */
|
||||||
|
var b2: Buffer = Buffer { .data = .{ .ptr = &raw[2], .len = 2 }, .len = 2 };
|
||||||
|
if b2.len != 2 { ret 6; }
|
||||||
|
if b2.data[0] != 67 { ret 7; }
|
||||||
|
|
||||||
|
/* Slice field assignment via field access */
|
||||||
|
b2.data.ptr = &raw[0];
|
||||||
|
b2.data.len = 4;
|
||||||
|
if b2.data[0] != 90 { ret 8; }
|
||||||
|
if b2.data.len != 4 { ret 9; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 2: 结构体含数组字段
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_struct_with_array() i32 {
|
||||||
|
var mr: MatrixRow = MatrixRow { .items = [4]i32{10, 20, 30, 40} };
|
||||||
|
|
||||||
|
if mr.items[0] != 10 { ret 1; }
|
||||||
|
if mr.items[1] != 20 { ret 2; }
|
||||||
|
if mr.items[2] != 30 { ret 3; }
|
||||||
|
if mr.items[3] != 40 { ret 4; }
|
||||||
|
|
||||||
|
/* 修改数组元素 */
|
||||||
|
mr.items[2] = 99;
|
||||||
|
if mr.items[2] != 99 { ret 5; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 3: 嵌套结构体初始化
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_nested_struct() i32 {
|
||||||
|
var p: Point = Point { .x = 5, .y = 10 };
|
||||||
|
if p.x != 5 { ret 1; }
|
||||||
|
if p.y != 10 { ret 2; }
|
||||||
|
|
||||||
|
/* 嵌套结构体字面量 */
|
||||||
|
var r: Rect = Rect {
|
||||||
|
.min = Point { .x = 1, .y = 2 },
|
||||||
|
.max = Point { .x = 3, .y = 4 }
|
||||||
|
};
|
||||||
|
if r.min.x != 1 { ret 3; }
|
||||||
|
if r.min.y != 2 { ret 4; }
|
||||||
|
if r.max.x != 3 { ret 5; }
|
||||||
|
if r.max.y != 4 { ret 6; }
|
||||||
|
|
||||||
|
/* 修改嵌套字段 */
|
||||||
|
r.min.x = 100;
|
||||||
|
if r.min.x != 100 { ret 7; }
|
||||||
|
if r.min.y != 2 { ret 8; } /* unchanged */
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 4: 结构体成员方法 (实例方法调用)
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_struct_method() i32 {
|
||||||
|
var c: Counter = Counter { .val = 0 };
|
||||||
|
|
||||||
|
/* 实例方法调用 c.inc(&c) */
|
||||||
|
var r1: i32 = c.inc(&c);
|
||||||
|
if r1 != 1 { ret 1; }
|
||||||
|
if c.val != 1 { ret 2; }
|
||||||
|
|
||||||
|
/* 带参数方法调用 c.add(&c, n) */
|
||||||
|
var r2: i32 = c.add(&c, 5);
|
||||||
|
if r2 != 6 { ret 3; }
|
||||||
|
if c.val != 6 { ret 4; }
|
||||||
|
|
||||||
|
/* 连续调用 (显式 self) */
|
||||||
|
c.reset(&c);
|
||||||
|
if c.val != 0 { ret 5; }
|
||||||
|
|
||||||
|
c.add(&c, 10);
|
||||||
|
c.inc(&c);
|
||||||
|
var r3: i32 = c.get(&c);
|
||||||
|
if r3 != 11 { ret 6; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 5: 结构体含指针字段
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_ptr_in_struct() i32 {
|
||||||
|
var v: i32 = 42;
|
||||||
|
var n: Node = Node { .ptr = &v, .val = 99 };
|
||||||
|
|
||||||
|
if n.val != 99 { ret 1; }
|
||||||
|
if n.ptr[0] != 42 { ret 2; }
|
||||||
|
|
||||||
|
/* 通过指针修改 */
|
||||||
|
v = 100;
|
||||||
|
if n.ptr[0] != 100 { ret 3; }
|
||||||
|
|
||||||
|
/* 通过指针在结构体内修改 */
|
||||||
|
n.ptr[0] = 200;
|
||||||
|
if v != 200 { ret 4; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 6: 多层级复杂嵌套
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_complex_nesting() i32 {
|
||||||
|
var str_data: [5]u8;
|
||||||
|
str_data[0] = 72; str_data[1] = 101;
|
||||||
|
str_data[2] = 108; str_data[3] = 108; str_data[4] = 111;
|
||||||
|
|
||||||
|
var bundle: Bundle = Bundle {
|
||||||
|
.name = &str_data[0],
|
||||||
|
.buf = Buffer { .data = .{ .ptr = &str_data[1], .len = 3 }, .len = 3 },
|
||||||
|
.row = MatrixRow { .items = [4]i32{1, 2, 3, 4} },
|
||||||
|
.pt = Point { .x = -5, .y = 15 }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Verify name */
|
||||||
|
if bundle.name[0] != 72 { ret 1; }
|
||||||
|
if bundle.name[4] != 111 { ret 2; }
|
||||||
|
|
||||||
|
/* Verify nested slice */
|
||||||
|
if bundle.buf.len != 3 { ret 3; }
|
||||||
|
if bundle.buf.data[0] != 101 { ret 4; }
|
||||||
|
if bundle.buf.data[2] != 108 { ret 5; }
|
||||||
|
|
||||||
|
/* Verify array field */
|
||||||
|
if bundle.row.items[0] != 1 { ret 6; }
|
||||||
|
if bundle.row.items[3] != 4 { ret 7; }
|
||||||
|
|
||||||
|
/* Verify nested struct field */
|
||||||
|
if bundle.pt.x != -5 { ret 8; }
|
||||||
|
if bundle.pt.y != 15 { ret 9; }
|
||||||
|
|
||||||
|
/* Modify nested slice */
|
||||||
|
bundle.buf.data[1] = 87; /* 'W' */
|
||||||
|
if str_data[2] != 87 { ret 10; }
|
||||||
|
|
||||||
|
/* Modify nested array */
|
||||||
|
bundle.row.items[2] = 33;
|
||||||
|
if bundle.row.items[2] != 33 { ret 11; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 测试 7: 枚举含结构体数据
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn test_enum_complex() i32 {
|
||||||
|
var s: Status = Status { .Active = 42 };
|
||||||
|
|
||||||
|
/* Verify active variant */
|
||||||
|
match s {
|
||||||
|
.Active[val] => {
|
||||||
|
if val != 42 { ret 1; }
|
||||||
|
},
|
||||||
|
.Inactive => {
|
||||||
|
ret 2;
|
||||||
|
},
|
||||||
|
.Pending[p] => {
|
||||||
|
ret 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test Inactive variant */
|
||||||
|
var s2: Status = Status { .Inactive };
|
||||||
|
var is_inactive: i32 = 0;
|
||||||
|
match s2 {
|
||||||
|
.Active[val] => {},
|
||||||
|
.Inactive => { is_inactive = 1; },
|
||||||
|
.Pending[p] => {}
|
||||||
|
}
|
||||||
|
if is_inactive != 1 { ret 4; }
|
||||||
|
|
||||||
|
/* Test Pending variant with struct data */
|
||||||
|
var s3: Status = Status { .Pending = Point { .x = 7, .y = 8 } };
|
||||||
|
match s3 {
|
||||||
|
.Active[val] => { ret 5; },
|
||||||
|
.Inactive => { ret 6; },
|
||||||
|
.Pending[p] => {
|
||||||
|
if p.x != 7 { ret 7; }
|
||||||
|
if p.y != 8 { ret 8; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 主函数
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
fn main() i32 {
|
||||||
|
var r: i32;
|
||||||
|
|
||||||
|
r = test_slice_in_struct();
|
||||||
|
if r != 0 { ret r; }
|
||||||
|
|
||||||
|
r = test_struct_with_array();
|
||||||
|
if r != 0 { ret r + 100; }
|
||||||
|
|
||||||
|
r = test_nested_struct();
|
||||||
|
if r != 0 { ret r + 200; }
|
||||||
|
|
||||||
|
r = test_struct_method();
|
||||||
|
if r != 0 { ret r + 300; }
|
||||||
|
|
||||||
|
r = test_ptr_in_struct();
|
||||||
|
if r != 0 { ret r + 400; }
|
||||||
|
|
||||||
|
r = test_complex_nesting();
|
||||||
|
if r != 0 { ret r + 500; }
|
||||||
|
|
||||||
|
r = test_enum_complex();
|
||||||
|
if r != 0 { ret r + 600; }
|
||||||
|
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user