macro(ast-json): add Loop/Break/Continue to AST JSON v0; add loop_normalize_macro (MVP identity) and loopform smoke; docs update
This commit is contained in:
13
apps/macros/examples/loop_normalize_macro.nyash
Normal file
13
apps/macros/examples/loop_normalize_macro.nyash
Normal file
@ -0,0 +1,13 @@
|
||||
// loop_normalize_macro.nyash
|
||||
// MVP: identity expansion with (json, ctx) signature.
|
||||
// Next steps: normalize `loop(cond){ body }` into carrier-based LoopForm.
|
||||
|
||||
static box MacroBoxSpec {
|
||||
static function name() { return "LoopNormalize" }
|
||||
|
||||
static function expand(json, ctx) {
|
||||
// For MVP, return input unchanged.
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,13 @@ Top-level
|
||||
- Nested nodes referenced inline; no IDs.
|
||||
- Span is omitted in v0 (unknown). Future versions may include `span` with file/line/col.
|
||||
|
||||
Kinds (subset for Phase 2)
|
||||
Kinds (subset for Phase 2+)
|
||||
- Program: { kind: "Program", statements: [Node] }
|
||||
- Loop: { kind: "Loop", condition: Node, body: [Node] }
|
||||
- Print: { kind: "Print", expression: Node }
|
||||
- Return: { kind: "Return", value: Node|null }
|
||||
- Break: { kind: "Break" }
|
||||
- Continue: { kind: "Continue" }
|
||||
- Assignment: { kind: "Assignment", target: Node, value: Node }
|
||||
- If: { kind: "If", condition: Node, then: [Node], else: [Node]|null }
|
||||
- FunctionDeclaration: { kind: "FunctionDeclaration", name: string, params: [string], body: [Node], static: bool, override: bool }
|
||||
|
||||
@ -7,6 +7,11 @@ pub fn ast_to_json(ast: &ASTNode) -> Value {
|
||||
"kind": "Program",
|
||||
"statements": statements.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>()
|
||||
}),
|
||||
ASTNode::Loop { condition, body, .. } => json!({
|
||||
"kind": "Loop",
|
||||
"condition": ast_to_json(&condition),
|
||||
"body": body.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>()
|
||||
}),
|
||||
ASTNode::Print { expression, .. } => json!({
|
||||
"kind": "Print",
|
||||
"expression": ast_to_json(&expression),
|
||||
@ -15,6 +20,8 @@ pub fn ast_to_json(ast: &ASTNode) -> Value {
|
||||
"kind": "Return",
|
||||
"value": value.as_ref().map(|v| ast_to_json(v)),
|
||||
}),
|
||||
ASTNode::Break { .. } => json!({"kind":"Break"}),
|
||||
ASTNode::Continue { .. } => json!({"kind":"Continue"}),
|
||||
ASTNode::Assignment { target, value, .. } => json!({
|
||||
"kind": "Assignment",
|
||||
"target": ast_to_json(&target),
|
||||
@ -77,8 +84,15 @@ pub fn json_to_ast(v: &Value) -> Option<ASTNode> {
|
||||
let stmts = v.get("statements")?.as_array()?.iter().filter_map(json_to_ast).collect::<Vec<_>>();
|
||||
ASTNode::Program { statements: stmts, span: Span::unknown() }
|
||||
}
|
||||
"Loop" => ASTNode::Loop {
|
||||
condition: Box::new(json_to_ast(v.get("condition")?)?),
|
||||
body: v.get("body")?.as_array()?.iter().filter_map(json_to_ast).collect::<Vec<_>>(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
"Print" => ASTNode::Print { expression: Box::new(json_to_ast(v.get("expression")?)?), span: Span::unknown() },
|
||||
"Return" => ASTNode::Return { value: v.get("value").and_then(json_to_ast).map(Box::new), span: Span::unknown() },
|
||||
"Break" => ASTNode::Break { span: Span::unknown() },
|
||||
"Continue" => ASTNode::Continue { span: Span::unknown() },
|
||||
"Assignment" => ASTNode::Assignment { target: Box::new(json_to_ast(v.get("target")?)?), value: Box::new(json_to_ast(v.get("value")?)?), span: Span::unknown() },
|
||||
"If" => ASTNode::If { condition: Box::new(json_to_ast(v.get("condition")?)?), then_body: v.get("then")?.as_array()?.iter().filter_map(json_to_ast).collect::<Vec<_>>(), else_body: v.get("else").and_then(|a| a.as_array().map(|arr| arr.iter().filter_map(json_to_ast).collect::<Vec<_>>())), span: Span::unknown() },
|
||||
"FunctionDeclaration" => ASTNode::FunctionDeclaration {
|
||||
|
||||
19
tools/test/smoke/macro/loopform_identity_smoke.sh
Normal file
19
tools/test/smoke/macro/loopform_identity_smoke.sh
Normal file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "$0")"/../../../.. && pwd)
|
||||
bin="$root/target/release/nyash"
|
||||
src="apps/tests/loop_min_while.nyash"
|
||||
|
||||
if [ ! -x "$bin" ]; then
|
||||
echo "nyash binary not found at $bin; build first (cargo build --release)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export NYASH_MACRO_ENABLE=1
|
||||
export NYASH_MACRO_PATHS="apps/macros/examples/loop_normalize_macro.nyash"
|
||||
|
||||
# Prefer PyVM run to align with macro pipeline
|
||||
"$bin" --backend vm "$src" >/dev/null
|
||||
echo "[OK] loopform identity smoke passed"
|
||||
|
||||
Reference in New Issue
Block a user