restore(lang): full lang tree from ff3ef452 (306 files) — compiler, vm, shared, runner, c-abi, etc.\n\n- Restores lang/ directory (files≈306, dirs≈64) as per historical branch with selfhost sources\n- Keeps our recent parser index changes in compiler/* (merged clean by checkout)\n- Unblocks selfhost development and documentation references

This commit is contained in:
nyash-codex
2025-10-31 20:45:46 +09:00
parent dbc285f2b1
commit e5f697eb22
244 changed files with 16915 additions and 47 deletions

View File

@ -0,0 +1,69 @@
// GcBox — Policy wrapper (Hakorune) around host GC externs
// Phase 20.8: docs/導線優先既定OFF。Rustはデータ平面走査/バリア/セーフポイント)、
// Hakorune はポリシー平面(しきい値/スケジューリング/ログ)を担当するよ。
// 参考: docs/development/architecture/gc/policy-vs-data-plane.md
static box GcBox {
// Return JSON string with counters {safepoints, barrier_reads, barrier_writes}
stats() {
return call("env.gc.stats/0")
}
// Return total roots count (host handles + modules), besteffort integer
roots_snapshot() {
return call("env.gc.roots_snapshot/0")
}
// Request collection (noop until host supports it)
collect() {
// Host may ignore; keep FailFast if extern missing
call("env.gc.collect/0")
}
// Optional lifecycle hooks (noop unless host implements)
start() { call("env.gc.start/0"); }
stop() { call("env.gc.stop/0"); }
// Example (dev): a tiny cadence policy, OFF by default.
// Enable with: HAKO_GC_POLICY_TICK=1 (docs only; call manually from scripts)
policy_tick() {
if (call("env.local.get/1", "HAKO_GC_POLICY_TICK") == "1") {
// Read metrics and print a compact line for observation
local s = me.stats()
call("env.console.log/1", "[GcBox] stats=" + s)
// Example threshold (no-op unless host implements collect):
// me.collect()
}
}
// Example (dev): run simple cadence based on env thresholds (strings; best-effort)
// HAKO_GC_POLICY_LOG=1 → print stats each tick
// HAKO_GC_POLICY_FORCE=1 → call collect() each tick (may be no-op; guard expected)
// HAKO_GC_POLICY_EVERY_N=K → best-effort modulus trigger (tick_count % K == 0)
tick_with_policy(tick_count) {
local log = call("env.local.get/1", "HAKO_GC_POLICY_LOG")
local every = call("env.local.get/1", "HAKO_GC_POLICY_EVERY_N")
local force = call("env.local.get/1", "HAKO_GC_POLICY_FORCE")
if (log == "1") {
call("env.console.log/1", "[GcBox] stats=" + me.stats())
}
if (force == "1") {
// Gate: host may not implement collect() yet
collect()
return 0
}
if (every != "") {
// crude parse: treat non-empty string as integer if equals to tick_count string
// consumers should pass small positive K
local k = every
// Only attempt modulus when both are simple digits
// (language JSON helpers or numeric ops may be used when available)
if (k == "1") {
collect()
return 0
}
}
return 0
}
}

View File

@ -0,0 +1,62 @@
// arc_box.hako — ArcBox (policy plane)
// Responsibility: simple reference counting semantics in Hakorune space.
// Storage: uses env.local.get/set with key prefix "arc:" to keep per-pointer counts.
// Data plane: when available, host-side env.arc.* can replace these, but this MVP
// remains pure to avoid native coupling. Optional free-on-zero via env.mem.free/1
// guarded by HAKO_ARC_FREE_ON_ZERO=1 (NYASH_ alias honored via runner env mirroring).
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
static box ArcBox {
_key(ptr) { return "ARC_" + ("" + ptr) }
_debug_on() {
local d = call("env.local.get/1", "HAKO_DEBUG_ARC")
if d == null || d == "" { d = call("env.local.get/1", "NYASH_DEBUG_ARC") }
return d == "1"
}
_log(msg) { if me._debug_on() { call("env.console.log/1", "[ArcBox] " + msg) } }
// Read current count (>=0) or -1 if unset/unknown)
_get_count(ptr) { return call("env.arc.count/1", ptr) }
// Create a new ARC handle with count=1. Fail if already exists.
arc_birth(ptr) {
local c = me._get_count(ptr)
if c >= 0 { call("env.console.error/1", "[arc/already_exists]") return -1 }
call("env.arc.birth/1", ptr)
me._log("birth ptr=" + ("" + ptr) + " -> 1")
return 1
}
// Increment; Fail if unknown.
arc_retain(ptr) {
local c = call("env.arc.retain/1", ptr)
if c < 0 { call("env.console.error/1", "[arc/unknown]") return -1 }
me._log("retain ptr=" + ("" + ptr) + " -> " + StringHelpers.int_to_str(c))
return c
}
// Decrement; Fail on unknown or underflow. When reaches 0, optionally free via env.mem.free/1
arc_release(ptr) {
local n = call("env.arc.release/1", ptr)
if n < 0 { return -1 }
me._log("release ptr=" + ("" + ptr) + " -> " + StringHelpers.int_to_str(n))
if n == 0 {
local fz = call("env.local.get/1", "HAKO_ARC_FREE_ON_ZERO")
if fz == null || fz == "" { fz = call("env.local.get/1", "NYASH_ARC_FREE_ON_ZERO") }
if fz == "1" {
// Best-effort free; ignore missing handler
call("env.mem.free/1", ptr)
}
}
return n
}
// Return current count or -1 if unknown
arc_count(ptr) { return me._get_count(ptr) }
// Sugar: clone = retain + return ptr (for chaining)
arc_clone(ptr) { local r = me.arc_retain(ptr) if r < 0 { return -1 } return ptr }
}

View File

@ -0,0 +1,67 @@
// refcell_box.hako — RefCellBox (policy plane)
// Responsibility: simple borrow/borrow_mut semantics with conflict checks.
// Storage: uses env.local.get/set with key prefix "ref:" per-pointer state.
// State encoding: "0" = idle, ">0" = shared borrows count, "-1" = mutable borrow active.
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
static box RefCellBox {
_key(ptr) { return "ref:" + ("" + ptr) }
_debug_on() {
local d = call("env.local.get/1", "HAKO_DEBUG_REFCELL")
if d == null || d == "" { d = call("env.local.get/1", "NYASH_DEBUG_REFCELL") }
return d == "1"
}
_log(msg) { if me._debug_on() { call("env.console.log/1", "[RefCellBox] " + msg) } }
_get(ptr) {
local s = call("env.local.get/1", me._key(ptr))
if s == null || s == "" { return 0 }
return StringHelpers.to_i64(s)
}
_set(ptr, n) { call("env.local.set/2", me._key(ptr), StringHelpers.int_to_str(n)) }
// Initialize state to 0 (idempotent)
init(ptr) { me._set(ptr, 0) return 0 }
// Shared borrow: allow when state >= 0; increments shared counter.
try_borrow(ptr) {
local st = me._get(ptr)
if st < 0 { call("env.console.error/1", "[refcell/conflict_shared]") return -1 }
me._set(ptr, st + 1)
me._log("borrow ptr=" + ("" + ptr) + " -> " + StringHelpers.int_to_str(st + 1))
return 1
}
// Mutable borrow: allow only when state == 0.
try_borrow_mut(ptr) {
local st = me._get(ptr)
if st != 0 { call("env.console.error/1", "[refcell/conflict_mut]") return -1 }
me._set(ptr, -1)
me._log("borrow_mut ptr=" + ("" + ptr) + " -> -1")
return 1
}
// Release one shared borrow; Fail if not in shared state
release_shared(ptr) {
local st = me._get(ptr)
if st <= 0 { call("env.console.error/1", "[refcell/release_shared_invalid]") return -1 }
me._set(ptr, st - 1)
me._log("release_shared ptr=" + ("" + ptr) + " -> " + StringHelpers.int_to_str(st - 1))
return st - 1
}
// Release mutable borrow; Fail if not in mut state
release_mut(ptr) {
local st = me._get(ptr)
if st != -1 { call("env.console.error/1", "[refcell/release_mut_invalid]") return -1 }
me._set(ptr, 0)
me._log("release_mut ptr=" + ("" + ptr) + " -> 0")
return 0
}
// Observe current state (debug aid): returns -1 (mut), 0 (idle), N>0 (shared count)
state(ptr) { return me._get(ptr) }
}

View File

@ -0,0 +1,8 @@
[module]
name = "selfhost.meta"
version = "1.0.0"
[exports]
UsingResolver = "using_resolver.hako"
UsingDecision = "using_decision.hako"
JsonShapeToMap = "json_shape_parser.hako"

View File

@ -0,0 +1,153 @@
// JsonShapeToMap — minimal, optin JSON→Map builder for Using shape (skeleton)
// Note: This is a very small adapter intended for controlled inputs produced by
// UsingResolver.shape/1. It does not implement a general JSON parser.
using "lang/src/shared/json/json_utils.hako" as JsonUtilsBox
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
static box JsonShapeToMap {
_empty(){ return map({ using_paths: new ArrayBox(), modules: new ArrayBox(), aliases: map({}), packages: map({}) }) }
_parse_array_of_strings(arr_json){
local out = new ArrayBox()
if !arr_json || arr_json.size() < 2 { return out }
local parts = JsonUtilsBox.split_top_level(arr_json)
local i = 0
loop(i < parts.size()){
local v = StringHelpers.trim(parts.get(i))
if v.size() >= 2 && v.substring(0,1) == "\"" && v.substring(v.size()-1, v.size()) == "\"" {
out.push(JsonUtilsBox.unescape_string(v.substring(1, v.size()-1)))
}
i = i + 1
}
return out
}
_split_object_pairs(obj_json){
// Like split_top_level for arrays, but for object key:value pairs inside {...}
local out = new ArrayBox()
if !obj_json || obj_json.size() < 2 { return out }
local n = obj_json.size()
local i = 1
local start = 1
local depth = 0
local in_string = 0
loop(i < n - 1){
local ch = obj_json.substring(i, i + 1)
if in_string == 1 {
if ch == "\\" { i = i + 2 } else { if ch == "\"" { in_string = 0 } i = i + 1 }
} else {
if ch == "\"" { in_string = 1 i = i + 1 }
else {
if ch == "{" || ch == "[" { depth = depth + 1 }
else if ch == "}" || ch == "]" { depth = depth - 1 }
else if ch == "," && depth == 0 {
out.push(obj_json.substring(start, i))
start = i + 1
}
i = i + 1
}
}
}
if start < n - 1 { out.push(obj_json.substring(start, n - 1)) }
return out
}
_read_key_from_pair(pair_json){
// pair_json: '"key" : value'
local s = StringHelpers.trim(pair_json)
if s.size() < 3 || s.substring(0,1) != "\"" { return null }
local end = JsonUtilsBox.skip_string(s, 0)
local raw = s.substring(0, end)
// raw like "\"key\"@pos" from read_string; we used skip_string, so raw lacks marker
// Remove quotes
local key = JsonUtilsBox.unescape_string(raw.substring(1, raw.size()-1))
return key
}
_read_value_from_pair(pair_json){
// Return raw JSON value string
local s = StringHelpers.trim(pair_json)
// find ':'
local idx = StringHelpers.index_of(s, 0, ":")
if idx < 0 { return null }
idx = StringHelpers.skip_ws(s, idx + 1)
local v = JsonUtilsBox.read_value(s, idx)
// v includes '@pos' marker — strip it
local at = StringHelpers.last_index_of(v, "@")
if at >= 0 { return v.substring(0, at) } else { return v }
}
_parse_modules(mods_json){
local out = new ArrayBox()
if !mods_json || mods_json.size() < 2 { return out }
local parts = JsonUtilsBox.split_top_level(mods_json)
local i = 0
loop(i < parts.size()){
local obj = StringHelpers.trim(parts.get(i))
if obj.size() >= 2 && obj.substring(0,1) == "{" {
local ns = JsonUtilsBox.extract_string_value(obj, "ns", "")
local path = JsonUtilsBox.extract_string_value(obj, "path", "")
out.push(map({ ns: ns, path: path }))
}
i = i + 1
}
return out
}
_parse_aliases(obj_json){
local out = map({})
if !obj_json || obj_json.size() < 2 { return out }
local pairs = me._split_object_pairs(obj_json)
local i = 0
loop(i < pairs.size()){
local p = pairs.get(i)
local key = me._read_key_from_pair(p)
local val_raw = me._read_value_from_pair(p)
if key != null && val_raw != null {
local v = StringHelpers.trim(val_raw)
if v.size() >= 2 && v.substring(0,1) == "\"" && v.substring(v.size()-1, v.size()) == "\"" {
out.set(key, JsonUtilsBox.unescape_string(v.substring(1, v.size()-1)))
}
}
i = i + 1
}
return out
}
_parse_packages(obj_json){
local out = map({})
if !obj_json || obj_json.size() < 2 { return out }
local pairs = me._split_object_pairs(obj_json)
local i = 0
loop(i < pairs.size()){
local p = pairs.get(i)
local key = me._read_key_from_pair(p)
local val_raw = me._read_value_from_pair(p)
if key != null && val_raw != null {
local kind = JsonUtilsBox.extract_string_value(val_raw, "kind", "")
local path = JsonUtilsBox.extract_string_value(val_raw, "path", "")
local main = JsonUtilsBox.extract_string_value(val_raw, "main", "")
out.set(key, map({ kind: kind, path: path, main: main }))
}
i = i + 1
}
return out
}
// parse(json) -> Map with keys: using_paths(Array), modules(Array of Map), aliases(Map), packages(Map)
parse(json){
if !json { return me._empty() }
// Extract each segment
local using_arr = JsonUtilsBox.extract_value(json, "using_paths")
local mods_arr = JsonUtilsBox.extract_value(json, "modules")
local aliases_obj = JsonUtilsBox.extract_value(json, "aliases")
local packages_obj = JsonUtilsBox.extract_value(json, "packages")
local using_paths = me._parse_array_of_strings(using_arr)
local modules = me._parse_modules(mods_arr)
local aliases = me._parse_aliases(aliases_obj)
local packages = me._parse_packages(packages_obj)
return map({ using_paths: using_paths, modules: modules, aliases: aliases, packages: packages })
}
}

View File

@ -0,0 +1,13 @@
// UsingDecision — convenience meta box to decide OK/FAIL based on UsingResolver shape
using "selfhost.meta.UsingResolver"
static box UsingDecision {
decide(token){
local r = UsingResolver.resolve(token)
if !r { return "FAIL" }
local a = r.get("using_paths"); local m = r.get("modules"); local al = r.get("aliases"); local pk = r.get("packages"); local po = r.get("policy")
if !a || !m || !al || !pk || !po { return "FAIL" }
return "OK"
}
}

View File

@ -0,0 +1,31 @@
// using_resolver.hako — Meta Using Resolver (lang-side, minimal)
// Public name (SSOT): UsingResolver.resolve/1
static box UsingResolver {
// Minimal shape (Phase20.10): return an empty resolver result.
// Shape: { using_paths: [], modules: [], aliases: {}, packages: {}, policy: {} }
// Behavior-invariant: no I/O, no host-slot dependence.
resolve(_token){
return map({
using_paths: new ArrayBox(),
modules: new ArrayBox(),
aliases: map({}),
packages: map({}),
policy: map({})
});
}
// stats/1 — Return minimal JSON counts (lang-side observability; behavior-invariant)
// Shape: {"modules":N,"paths":P,"aliases":A}
stats(_token){
// Minimal, side-effect free, no Map/Array construction
return "{\"modules\":0,\"paths\":0,\"aliases\":0}";
}
// shape/1 — Return minimal resolver shape as JSON (behavior-invariant)
// Shape JSON (keys only; values are empty):
// {"using_paths":[], "modules":[], "aliases":{}, "packages":{}}
shape(_token){
return "{\"using_paths\":[],\"modules\":[],\"aliases\":{},\"packages\":{}}";
}
}