phase15: update CLAUDE.md and sync with current progress

- Update phase indicator to Phase 15 (Self-Hosting)
- Update documentation links to Phase 15 resources
- Reflect completion of R1-R5 tasks and ongoing work
- Fix CURRENT_TASK.md location to root directory

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Tomoaki
2025-09-05 13:29:17 +09:00
parent e05a385524
commit a2b89fae7e
29 changed files with 1163 additions and 2562 deletions

View File

@ -0,0 +1 @@
return 1 + 2 * 3

View File

@ -0,0 +1 @@
return 42

View File

@ -0,0 +1,14 @@
# Ny Parser (v0) — Minimal Nyash-made Parser
- Scope: integers, + - * /, parentheses, and a single `return` statement.
- Output: JSON IR v0 as documented in CURRENT_TASK.md (Program/Return/Int/Binary).
Usage (Unix)
- echo "return 1+2*3" | ./tools/ny_parser_run.sh
Usage (Windows PowerShell)
- Get-Content .\apps\ny-mir-samples\arithmetic.nyash | .\tools\ny_parser_run.ps1
Notes
- This is a minimal educational parser to bootstrap the self-host loop.
- Errors print a JSON envelope: {"version":0,"kind":"Error",...}.

View File

@ -0,0 +1,29 @@
// Entry: read stdin, parse with ParserV0, print JSON IR or error JSON
include("./apps/ny-parser-nyash/parser_minimal.nyash")
static box Main {
main(args) {
local console = new ConsoleBox()
// Read all stdin
local buf = ""
loop(true) {
local line = console.readLine()
if line == null { break }
buf = buf + line + "\n"
}
if buf == "" { buf = "return 0\n" }
local ir = ParserV0.parse_program(buf)
// If already an Error envelope, print as-is
local s = ir.as_any().toString()
if s.indexOf("\"kind\":\"Error\"") >= 0 {
console.log(s)
return 1
}
// Expect MapBox with Program; toJson available on MapBox
local json = ir.toJson()
console.log(json)
return 0
}
}

View File

@ -0,0 +1,88 @@
// Minimal recursive-descent parser for Ny v0 producing JSON IR v0 (MapBox)
include("./apps/ny-parser-nyash/tokenizer.nyash")
static box ParserV0 {
init { tokens, pos }
parse_program(input) {
me.tokens = Tokenizer.tokenize(input)
// Error passthrough
if me.tokens.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 {
return me.tokens
}
me.pos = 0
local stmt = me.parse_stmt()
if stmt.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 { return stmt }
local body = new ArrayBox(); body.push(stmt)
local prog = new MapBox(); prog.set("version", 0); prog.set("kind", "Program"); prog.set("body", body)
return prog
}
parse_stmt() {
local tok = me.peek()
if tok.get("type") == "RETURN" {
me.next()
local expr = me.parse_expr()
if expr.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 { return expr }
local ret = new MapBox(); ret.set("type", "Return"); ret.set("expr", expr)
return ret
}
return me.err("Expected 'return'")
}
parse_expr() {
local left = me.parse_term()
loop(true) {
local t = me.peek(); local ty = t.get("type")
if ty == "+" || ty == "-" {
me.next(); local right = me.parse_term()
left = me.bin(ty, left, right)
} else { break }
}
return left
}
parse_term() {
local left = me.parse_factor()
loop(true) {
local t = me.peek(); local ty = t.get("type")
if ty == "*" || ty == "/" {
me.next(); local right = me.parse_factor()
left = me.bin(ty, left, right)
} else { break }
}
return left
}
parse_factor() {
local t = me.peek(); local ty = t.get("type")
if ty == "INT" {
me.next();
local node = new MapBox(); node.set("type", "Int"); node.set("value", t.get("value"))
return node
}
if ty == "(" {
me.next();
local e = me.parse_expr()
local r = me.peek(); if r.get("type") != ")" { return me.err(") expected") } else { me.next() }
return e
}
return me.err("factor expected")
}
// helpers
peek() { return me.tokens.get(me.pos) }
next() { me.pos = me.pos + 1; return me.tokens.get(me.pos-1) }
bin(op, lhs, rhs) {
local m = new MapBox(); m.set("type", "Binary"); m.set("op", op); m.set("lhs", lhs); m.set("rhs", rhs); return m
}
err(msg) {
local err = new MapBox(); err.set("version", 0); err.set("kind", "Error")
local e = new MapBox(); e.set("message", msg)
local sp = new MapBox(); sp.set("start", me.pos); sp.set("end", me.pos)
e.set("span", sp); err.set("error", e)
return err
}
}

View File

@ -0,0 +1,55 @@
// Minimal tokenizer for Ny v0 (ints, + - * /, ( ), return)
static box Tokenizer {
tokenize(input) {
local tokens = new ArrayBox()
local i = 0
local n = input.length()
// helper: skip whitespace
fn skip_ws() {
loop(i < n) {
local ch = input.substring(i, i+1)
if ch == " " || ch == "\t" || ch == "\r" || ch == "\n" { i = i + 1 } else { return }
}
}
// main loop
loop(i < n) {
skip_ws()
if i >= n { break }
local ch = input.substring(i, i+1)
if ch == "+" || ch == "-" || ch == "*" || ch == "/" || ch == "(" || ch == ")" {
local tok = new MapBox(); tok.set("type", ch)
tokens.push(tok); i = i + 1; continue
}
// keyword: return
if i + 6 <= n {
local kw = input.substring(i, i+6)
if kw == "return" {
local t = new MapBox(); t.set("type", "RETURN")
tokens.push(t); i = i + 6; continue
}
}
// integer literal
if ch >= "0" && ch <= "9" {
local j = i
loop(j < n) {
local cj = input.substring(j, j+1)
if cj >= "0" && cj <= "9" { j = j + 1 } else { break }
}
local num_str = input.substring(i, j)
local tnum = new MapBox(); tnum.set("type", "INT"); tnum.set("value", num_str)
tokens.push(tnum); i = j; continue
}
// unknown
local err = new MapBox(); err.set("version", 0); err.set("kind", "Error")
local e = new MapBox(); e.set("message", "Unknown token");
local sp = new MapBox(); sp.set("start", i); sp.set("end", i+1)
e.set("span", sp); err.set("error", e)
return err
}
// EOF
local eof = new MapBox(); eof.set("type", "EOF"); tokens.push(eof)
return tokens
}
}

63
apps/std/ny-config.nyash Normal file
View File

@ -0,0 +1,63 @@
// ny-config.nyash - Load nyash.toml and expose minimal helpers
static box NyConfig {
// Read nyash.toml (or given path) and return JSON string via TOMLBox.toJson()
load_toml(path) {
local p = path
if p == null || p == "" { p = "nyash.toml" }
local f = new FileBox()
// Open read-only if supported; fallback to default if mode not required
// Many plugins accept open(path, mode). Use "r" here.
f.open(p, "r")
local content = f.read()
f.close()
local t = new TOMLBox()
// parse(content) returns Result.Ok(bool) in some variants; call and ignore return here
t.parse(content)
local json = t.toJson()
return json
}
// Return counts for env/tasks/box_types/plugins (approx by '=' occurrences per table)
counts() {
// Parse nyash.toml
local f2 = new FileBox()
f2.open("nyash.toml", "r")
local content2 = f2.read()
f2.close()
local t = new TOMLBox()
t.parse(content2)
local out = new MapBox()
out.setS("env", me.count_keys_in_string(t.get("env")))
out.setS("tasks", me.count_keys_in_string(t.get("tasks")))
out.setS("box_types", me.count_keys_in_string(t.get("box_types")))
out.setS("plugins", me.count_keys_in_string(t.get("plugins")))
return out
}
// helper: count '=' in a string
count_keys_in_string(s) {
local i = 0
local n = s.length()
local c = 0
loop(i < n) {
local ch = s.substring(i, i+1)
if ch == "=" { c = c + 1 }
i = i + 1
}
return c
}
// Convert JSON back to TOML-like display if needed (placeholder: returns empty to force parse to no-op)
json_to_toml_hint(js) { return "" }
}
static box Main {
main(args) {
local console = new ConsoleBox()
local json = NyConfig.load_toml(null)
local c = NyConfig.counts(json)
console.println("ny-config: env=" + c.getS("env").toString() + ", tasks=" + c.getS("tasks").toString() + ", plugins=" + c.getS("plugins").toString() + ", box_types=" + c.getS("box_types").toString())
return 0
}
}