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:
69
lang/src/runtime/gc/gc_box.hako
Normal file
69
lang/src/runtime/gc/gc_box.hako
Normal 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), best‑effort integer
|
||||
roots_snapshot() {
|
||||
return call("env.gc.roots_snapshot/0")
|
||||
}
|
||||
|
||||
// Request collection (no‑op until host supports it)
|
||||
collect() {
|
||||
// Host may ignore; keep Fail‑Fast if extern missing
|
||||
call("env.gc.collect/0")
|
||||
}
|
||||
|
||||
// Optional lifecycle hooks (no‑op 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
|
||||
}
|
||||
}
|
||||
62
lang/src/runtime/memory/arc_box.hako
Normal file
62
lang/src/runtime/memory/arc_box.hako
Normal 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 }
|
||||
}
|
||||
67
lang/src/runtime/memory/refcell_box.hako
Normal file
67
lang/src/runtime/memory/refcell_box.hako
Normal 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) }
|
||||
}
|
||||
|
||||
8
lang/src/runtime/meta/hako_module.toml
Normal file
8
lang/src/runtime/meta/hako_module.toml
Normal 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"
|
||||
153
lang/src/runtime/meta/json_shape_parser.hako
Normal file
153
lang/src/runtime/meta/json_shape_parser.hako
Normal file
@ -0,0 +1,153 @@
|
||||
// JsonShapeToMap — minimal, opt‑in 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 })
|
||||
}
|
||||
}
|
||||
13
lang/src/runtime/meta/using_decision.hako
Normal file
13
lang/src/runtime/meta/using_decision.hako
Normal 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"
|
||||
}
|
||||
}
|
||||
|
||||
31
lang/src/runtime/meta/using_resolver.hako
Normal file
31
lang/src/runtime/meta/using_resolver.hako
Normal 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 (Phase‑20.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\":{}}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user