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,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
}
}