Phase 285W-Syntax-0: Migrate weak reference syntax from function call to unary operator for consistency and clarity. **Changes**: - Parser: Add UnaryOperator::Weak variant and parse_unary() handling - MIR: Lower UnaryOp::Weak to emit_weak_new() (reuses existing path) - AST: Add Weak to UnaryOperator enum + Display/JSON support - Tests: Migrate 8 files from `weak(x)` to `weak x` syntax - 7 .hako test files updated - 1 smoke test shell script updated - Cleanup: Remove obsolete weak(x) parser/MIR special cases - Docs: Update Phase 285 README **Syntax Change**: - Old: `local w = weak(x)` (function call) - New: `local w = weak x` (unary operator) **Validation**: All migrated phase285* smoke tests pass (4/4 relevant) **SSOT**: docs/reference/language/lifecycle.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
628 lines
23 KiB
Rust
628 lines
23 KiB
Rust
use nyash_rust::ast::{ASTNode, BinaryOperator, LiteralValue, Span, UnaryOperator};
|
||
use serde_json::{json, Value};
|
||
|
||
pub fn ast_to_json(ast: &ASTNode) -> Value {
|
||
match ast.clone() {
|
||
ASTNode::Program { statements, .. } => json!({
|
||
"kind": "Program",
|
||
"statements": statements.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>()
|
||
}),
|
||
// Phase 54: Loop with JoinIR-compatible fields
|
||
ASTNode::Loop {
|
||
condition, body, ..
|
||
} => json!({
|
||
"kind": "Loop",
|
||
"type": "Loop", // JoinIR Frontend expects "type"
|
||
"condition": ast_to_json(&condition),
|
||
"cond": ast_to_json(&condition), // JoinIR expects "cond"
|
||
"body": body.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>()
|
||
}),
|
||
// Phase 54: Print with JoinIR-compatible fields
|
||
ASTNode::Print { expression, .. } => json!({
|
||
"kind": "Print",
|
||
"type": "Print", // JoinIR Frontend expects "type"
|
||
"expression": ast_to_json(&expression),
|
||
"expr": ast_to_json(&expression), // JoinIR expects "expr"
|
||
}),
|
||
// Phase 54: Return with JoinIR-compatible fields
|
||
ASTNode::Return { value, .. } => json!({
|
||
"kind": "Return",
|
||
"type": "Return", // JoinIR Frontend expects "type"
|
||
"value": value.as_ref().map(|v| ast_to_json(v)),
|
||
}),
|
||
// Phase 56: Break with JoinIR-compatible type field
|
||
ASTNode::Break { .. } => json!({
|
||
"kind": "Break",
|
||
"type": "Break" // JoinIR Frontend expects "type"
|
||
}),
|
||
// Phase 56: Continue with JoinIR-compatible type field
|
||
ASTNode::Continue { .. } => json!({
|
||
"kind": "Continue",
|
||
"type": "Continue" // JoinIR Frontend expects "type"
|
||
}),
|
||
// Phase 54: Assignment with JoinIR-compatible fields
|
||
ASTNode::Assignment { target, value, .. } => {
|
||
// Extract variable name if target is a simple Variable
|
||
let target_str = match target.as_ref() {
|
||
ASTNode::Variable { name, .. } => name.clone(),
|
||
_ => "complex".to_string(), // FieldAccess or other complex target
|
||
};
|
||
json!({
|
||
"kind": "Assignment",
|
||
"type": "Assignment", // JoinIR Frontend expects "type"
|
||
"target": target_str, // JoinIR expects string variable name
|
||
"lhs": ast_to_json(&target), // Keep full AST for complex cases
|
||
"value": ast_to_json(&value),
|
||
"expr": ast_to_json(&value), // JoinIR expects "expr"
|
||
})
|
||
}
|
||
// Phase 54: Local with JoinIR-compatible fields
|
||
ASTNode::Local {
|
||
variables,
|
||
initial_values,
|
||
..
|
||
} => {
|
||
// For single-variable declarations, add "name" and "expr" for JoinIR compatibility
|
||
let (name, expr) = if variables.len() == 1 {
|
||
let n = variables[0].clone();
|
||
let e = initial_values
|
||
.get(0)
|
||
.and_then(|opt| opt.as_ref())
|
||
.map(|v| ast_to_json(v));
|
||
(Some(n), e)
|
||
} else {
|
||
(None, None)
|
||
};
|
||
|
||
let inits: Vec<_> = initial_values
|
||
.into_iter()
|
||
.map(|opt| opt.map(|v| ast_to_json(&v)))
|
||
.collect();
|
||
|
||
json!({
|
||
"kind": "Local",
|
||
"type": "Local", // JoinIR Frontend expects "type"
|
||
"name": name, // Single variable name for JoinIR (null if multiple)
|
||
"expr": expr, // Single variable init for JoinIR (null if multiple)
|
||
"variables": variables,
|
||
"inits": inits
|
||
})
|
||
}
|
||
// Phase 54: If with JoinIR-compatible fields
|
||
ASTNode::If {
|
||
condition,
|
||
then_body,
|
||
else_body,
|
||
..
|
||
} => json!({
|
||
"kind": "If",
|
||
"type": "If", // JoinIR Frontend expects "type"
|
||
"condition": ast_to_json(&condition),
|
||
"cond": ast_to_json(&condition), // JoinIR expects "cond"
|
||
"then": then_body.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>(),
|
||
"else": else_body.map(|v| v.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>()),
|
||
}),
|
||
ASTNode::TryCatch {
|
||
try_body,
|
||
catch_clauses,
|
||
finally_body,
|
||
..
|
||
} => json!({
|
||
"kind": "TryCatch",
|
||
"try": try_body.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>(),
|
||
"catch": catch_clauses.into_iter().map(|cc| json!({
|
||
"type": cc.exception_type,
|
||
"var": cc.variable_name,
|
||
"body": cc.body.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>()
|
||
})).collect::<Vec<_>>(),
|
||
"cleanup": finally_body.map(|v| v.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>())
|
||
}),
|
||
ASTNode::FunctionDeclaration {
|
||
name,
|
||
params,
|
||
body,
|
||
is_static,
|
||
is_override,
|
||
..
|
||
} => json!({
|
||
"kind": "FunctionDeclaration",
|
||
"name": name,
|
||
"params": params,
|
||
"body": body.into_iter().map(|s| ast_to_json(&s)).collect::<Vec<_>>(),
|
||
"static": is_static,
|
||
"override": is_override,
|
||
}),
|
||
// Phase 52: Variable → Var ノード(JoinIR Frontend 互換)
|
||
ASTNode::Variable { name, .. } => json!({
|
||
"kind": "Variable",
|
||
"type": "Var", // JoinIR Frontend expects "type": "Var"
|
||
"name": name
|
||
}),
|
||
// Phase 52: Literal → Int/Bool/String ノード(JoinIR Frontend 互換)
|
||
ASTNode::Literal { value, .. } => literal_to_joinir_json(&value),
|
||
// Phase 52: BinaryOp → Binary/Compare ノード(JoinIR Frontend 互換)
|
||
ASTNode::BinaryOp {
|
||
operator,
|
||
left,
|
||
right,
|
||
..
|
||
} => {
|
||
let op_str = bin_to_str(&operator);
|
||
// JoinIR Frontend distinguishes between Binary (arithmetic) and Compare
|
||
let type_str = if is_compare_op(&operator) {
|
||
"Compare"
|
||
} else {
|
||
"Binary"
|
||
};
|
||
json!({
|
||
"kind": "BinaryOp",
|
||
"type": type_str,
|
||
"op": op_str,
|
||
// JoinIR Frontend expects "lhs"/"rhs" not "left"/"right"
|
||
"lhs": ast_to_json(&left),
|
||
"rhs": ast_to_json(&right),
|
||
// Also keep "left"/"right" for backward compatibility
|
||
"left": ast_to_json(&left),
|
||
"right": ast_to_json(&right),
|
||
})
|
||
}
|
||
// Phase 56: UnaryOp → Unary ノード(JoinIR Frontend 互換)
|
||
ASTNode::UnaryOp {
|
||
operator, operand, ..
|
||
} => json!({
|
||
"kind": "UnaryOp",
|
||
"type": "Unary", // Phase 56: JoinIR Frontend expects "type" field
|
||
"op": un_to_str(&operator),
|
||
"operand": ast_to_json(&operand),
|
||
}),
|
||
// Phase 52: MethodCall → Method ノード(JoinIR Frontend 互換)
|
||
ASTNode::MethodCall {
|
||
object,
|
||
method,
|
||
arguments,
|
||
..
|
||
} => json!({
|
||
"kind": "MethodCall",
|
||
"type": "Method", // JoinIR Frontend expects "type": "Method"
|
||
// JoinIR Frontend expects "receiver" not "object"
|
||
"receiver": ast_to_json(&object),
|
||
"object": ast_to_json(&object), // Keep for backward compatibility
|
||
"method": method,
|
||
// JoinIR Frontend expects "args" not "arguments"
|
||
"args": arguments.iter().map(|a| ast_to_json(a)).collect::<Vec<_>>(),
|
||
"arguments": arguments.into_iter().map(|a| ast_to_json(&a)).collect::<Vec<_>>() // Keep for backward compatibility
|
||
}),
|
||
// Phase 56: FunctionCall with JoinIR-compatible type field
|
||
ASTNode::FunctionCall {
|
||
name, arguments, ..
|
||
} => json!({
|
||
"kind": "FunctionCall",
|
||
"type": "Call", // JoinIR Frontend expects "type": "Call"
|
||
"name": name,
|
||
"func": name.clone(), // JoinIR expects "func" for function name
|
||
"args": arguments.iter().map(|a| ast_to_json(a)).collect::<Vec<_>>(), // JoinIR expects "args"
|
||
"arguments": arguments.into_iter().map(|a| ast_to_json(&a)).collect::<Vec<_>>() // Keep for backward compatibility
|
||
}),
|
||
// Phase 56: ArrayLiteral with JoinIR-compatible type field
|
||
ASTNode::ArrayLiteral { elements, .. } => json!({
|
||
"kind": "Array",
|
||
"type": "Array", // JoinIR Frontend expects "type"
|
||
"elements": elements.into_iter().map(|e| ast_to_json(&e)).collect::<Vec<_>>()
|
||
}),
|
||
// Phase 56: MapLiteral with JoinIR-compatible type field
|
||
ASTNode::MapLiteral { entries, .. } => json!({
|
||
"kind": "Map",
|
||
"type": "Map", // JoinIR Frontend expects "type"
|
||
"entries": entries.into_iter().map(|(k,v)| json!({"k":k,"v":ast_to_json(&v)})).collect::<Vec<_>>()
|
||
}),
|
||
ASTNode::MatchExpr {
|
||
scrutinee,
|
||
arms,
|
||
else_expr,
|
||
..
|
||
} => json!({
|
||
"kind":"MatchExpr",
|
||
"scrutinee": ast_to_json(&scrutinee),
|
||
"arms": arms.into_iter().map(|(lit, body)| json!({
|
||
"literal": {
|
||
"kind": "Literal",
|
||
"value": lit_to_json(&lit)
|
||
},
|
||
"body": ast_to_json(&body)
|
||
})).collect::<Vec<_>>(),
|
||
"else": ast_to_json(&else_expr),
|
||
}),
|
||
// Phase 52: FieldAccess → Field ノード(JoinIR Frontend 互換)
|
||
ASTNode::FieldAccess { object, field, .. } => json!({
|
||
"kind": "FieldAccess",
|
||
"type": "Field", // JoinIR Frontend expects "type": "Field"
|
||
"object": ast_to_json(&object),
|
||
"field": field
|
||
}),
|
||
// Phase 52: Me → Var("me") ノード(JoinIR Frontend 互換)
|
||
ASTNode::Me { .. } => json!({
|
||
"kind": "Me",
|
||
"type": "Var", // JoinIR Frontend expects "type": "Var"
|
||
"name": "me"
|
||
}),
|
||
// Phase 52: New → NewBox ノード(JoinIR Frontend 互換)
|
||
ASTNode::New {
|
||
class, arguments, ..
|
||
} => json!({
|
||
"kind": "New",
|
||
"type": "NewBox", // JoinIR Frontend expects "type": "NewBox"
|
||
"box_name": class,
|
||
"args": arguments.into_iter().map(|a| ast_to_json(&a)).collect::<Vec<_>>()
|
||
}),
|
||
other => json!({"kind":"Unsupported","debug": format!("{:?}", other)}),
|
||
}
|
||
}
|
||
|
||
pub fn json_to_ast(v: &Value) -> Option<ASTNode> {
|
||
let k = v.get("kind")?.as_str()?;
|
||
Some(match k {
|
||
"Program" => {
|
||
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(),
|
||
},
|
||
"Local" => {
|
||
let vars = v
|
||
.get("variables")?
|
||
.as_array()?
|
||
.iter()
|
||
.filter_map(|s| s.as_str().map(|x| x.to_string()))
|
||
.collect();
|
||
let inits = v
|
||
.get("inits")?
|
||
.as_array()?
|
||
.iter()
|
||
.map(|initv| {
|
||
if initv.is_null() {
|
||
None
|
||
} else {
|
||
json_to_ast(initv).map(Box::new)
|
||
}
|
||
})
|
||
.collect();
|
||
ASTNode::Local {
|
||
variables: vars,
|
||
initial_values: inits,
|
||
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 {
|
||
name: v.get("name")?.as_str()?.to_string(),
|
||
params: v
|
||
.get("params")?
|
||
.as_array()?
|
||
.iter()
|
||
.filter_map(|s| s.as_str().map(|x| x.to_string()))
|
||
.collect(),
|
||
body: v
|
||
.get("body")?
|
||
.as_array()?
|
||
.iter()
|
||
.filter_map(json_to_ast)
|
||
.collect(),
|
||
is_static: v.get("static").and_then(|b| b.as_bool()).unwrap_or(false),
|
||
is_override: v.get("override").and_then(|b| b.as_bool()).unwrap_or(false),
|
||
span: Span::unknown(),
|
||
},
|
||
"Variable" => ASTNode::Variable {
|
||
name: v.get("name")?.as_str()?.to_string(),
|
||
span: Span::unknown(),
|
||
},
|
||
"Literal" => ASTNode::Literal {
|
||
value: json_to_lit(v.get("value")?)?,
|
||
span: Span::unknown(),
|
||
},
|
||
"BinaryOp" => ASTNode::BinaryOp {
|
||
operator: str_to_bin(v.get("op")?.as_str()?)?,
|
||
left: Box::new(json_to_ast(v.get("left")?)?),
|
||
right: Box::new(json_to_ast(v.get("right")?)?),
|
||
span: Span::unknown(),
|
||
},
|
||
"UnaryOp" => ASTNode::UnaryOp {
|
||
operator: str_to_un(v.get("op")?.as_str()?)?,
|
||
operand: Box::new(json_to_ast(v.get("operand")?)?),
|
||
span: Span::unknown(),
|
||
},
|
||
"MethodCall" => ASTNode::MethodCall {
|
||
object: Box::new(json_to_ast(v.get("object")?)?),
|
||
method: v.get("method")?.as_str()?.to_string(),
|
||
arguments: v
|
||
.get("arguments")?
|
||
.as_array()?
|
||
.iter()
|
||
.filter_map(json_to_ast)
|
||
.collect(),
|
||
span: Span::unknown(),
|
||
},
|
||
"FunctionCall" => ASTNode::FunctionCall {
|
||
name: v.get("name")?.as_str()?.to_string(),
|
||
arguments: v
|
||
.get("arguments")?
|
||
.as_array()?
|
||
.iter()
|
||
.filter_map(json_to_ast)
|
||
.collect(),
|
||
span: Span::unknown(),
|
||
},
|
||
"Array" => ASTNode::ArrayLiteral {
|
||
elements: v
|
||
.get("elements")?
|
||
.as_array()?
|
||
.iter()
|
||
.filter_map(json_to_ast)
|
||
.collect(),
|
||
span: Span::unknown(),
|
||
},
|
||
"Map" => ASTNode::MapLiteral {
|
||
entries: v
|
||
.get("entries")?
|
||
.as_array()?
|
||
.iter()
|
||
.filter_map(|e| {
|
||
Some((e.get("k")?.as_str()?.to_string(), json_to_ast(e.get("v")?)?))
|
||
})
|
||
.collect(),
|
||
span: Span::unknown(),
|
||
},
|
||
"MatchExpr" => {
|
||
let scr = json_to_ast(v.get("scrutinee")?)?;
|
||
let arms_json = v.get("arms")?.as_array()?.iter();
|
||
let mut arms = Vec::new();
|
||
for arm_v in arms_json {
|
||
let lit_val = arm_v.get("literal")?.get("value")?;
|
||
let lit = json_to_lit(lit_val)?;
|
||
let body = json_to_ast(arm_v.get("body")?)?;
|
||
arms.push((lit, body));
|
||
}
|
||
let else_expr = json_to_ast(v.get("else")?)?;
|
||
ASTNode::MatchExpr {
|
||
scrutinee: Box::new(scr),
|
||
arms,
|
||
else_expr: Box::new(else_expr),
|
||
span: Span::unknown(),
|
||
}
|
||
}
|
||
"TryCatch" => {
|
||
let try_b = v
|
||
.get("try")?
|
||
.as_array()?
|
||
.iter()
|
||
.filter_map(json_to_ast)
|
||
.collect::<Vec<_>>();
|
||
let mut catches = Vec::new();
|
||
if let Some(arr) = v.get("catch").and_then(|x| x.as_array()) {
|
||
for c in arr.iter() {
|
||
let exc_t = match c.get("type") {
|
||
Some(t) if !t.is_null() => t.as_str().map(|s| s.to_string()),
|
||
_ => None,
|
||
};
|
||
let var = match c.get("var") {
|
||
Some(vv) if !vv.is_null() => vv.as_str().map(|s| s.to_string()),
|
||
_ => None,
|
||
};
|
||
let body = c
|
||
.get("body")?
|
||
.as_array()?
|
||
.iter()
|
||
.filter_map(json_to_ast)
|
||
.collect::<Vec<_>>();
|
||
catches.push(nyash_rust::ast::CatchClause {
|
||
exception_type: exc_t,
|
||
variable_name: var,
|
||
body,
|
||
span: Span::unknown(),
|
||
});
|
||
}
|
||
}
|
||
let cleanup = v.get("cleanup").and_then(|cl| {
|
||
cl.as_array()
|
||
.map(|arr| arr.iter().filter_map(json_to_ast).collect::<Vec<_>>())
|
||
});
|
||
ASTNode::TryCatch {
|
||
try_body: try_b,
|
||
catch_clauses: catches,
|
||
finally_body: cleanup,
|
||
span: Span::unknown(),
|
||
}
|
||
}
|
||
_ => return None,
|
||
})
|
||
}
|
||
|
||
fn lit_to_json(v: &LiteralValue) -> Value {
|
||
match v {
|
||
LiteralValue::String(s) => json!({"type":"string","value":s}),
|
||
LiteralValue::Integer(i) => json!({"type":"int","value":i}),
|
||
LiteralValue::Float(f) => json!({"type":"float","value":f}),
|
||
LiteralValue::Bool(b) => json!({"type":"bool","value":b}),
|
||
LiteralValue::Null => json!({"type":"null"}),
|
||
LiteralValue::Void => json!({"type":"void"}),
|
||
}
|
||
}
|
||
|
||
fn json_to_lit(v: &Value) -> Option<LiteralValue> {
|
||
let t = v.get("type")?.as_str()?;
|
||
Some(match t {
|
||
"string" => LiteralValue::String(v.get("value")?.as_str()?.to_string()),
|
||
"int" => LiteralValue::Integer(v.get("value")?.as_i64()?),
|
||
"float" => LiteralValue::Float(v.get("value")?.as_f64()?),
|
||
"bool" => LiteralValue::Bool(v.get("value")?.as_bool()?),
|
||
"null" => LiteralValue::Null,
|
||
"void" => LiteralValue::Void,
|
||
_ => return None,
|
||
})
|
||
}
|
||
|
||
fn bin_to_str(op: &BinaryOperator) -> &'static str {
|
||
match op {
|
||
BinaryOperator::Add => "+",
|
||
BinaryOperator::Subtract => "-",
|
||
BinaryOperator::Multiply => "*",
|
||
BinaryOperator::Divide => "/",
|
||
BinaryOperator::Modulo => "%",
|
||
BinaryOperator::BitAnd => "&",
|
||
BinaryOperator::BitOr => "|",
|
||
BinaryOperator::BitXor => "^",
|
||
BinaryOperator::Shl => "<<",
|
||
BinaryOperator::Shr => ">>",
|
||
BinaryOperator::Equal => "==",
|
||
BinaryOperator::NotEqual => "!=",
|
||
BinaryOperator::Less => "<",
|
||
BinaryOperator::Greater => ">",
|
||
BinaryOperator::LessEqual => "<=",
|
||
BinaryOperator::GreaterEqual => ">=",
|
||
BinaryOperator::And => "&&",
|
||
BinaryOperator::Or => "||",
|
||
}
|
||
}
|
||
|
||
fn str_to_bin(s: &str) -> Option<BinaryOperator> {
|
||
Some(match s {
|
||
"+" => BinaryOperator::Add,
|
||
"-" => BinaryOperator::Subtract,
|
||
"*" => BinaryOperator::Multiply,
|
||
"/" => BinaryOperator::Divide,
|
||
"%" => BinaryOperator::Modulo,
|
||
"&" => BinaryOperator::BitAnd,
|
||
"|" => BinaryOperator::BitOr,
|
||
"^" => BinaryOperator::BitXor,
|
||
"<<" => BinaryOperator::Shl,
|
||
">>" => BinaryOperator::Shr,
|
||
"==" => BinaryOperator::Equal,
|
||
"!=" => BinaryOperator::NotEqual,
|
||
"<" => BinaryOperator::Less,
|
||
">" => BinaryOperator::Greater,
|
||
"<=" => BinaryOperator::LessEqual,
|
||
">=" => BinaryOperator::GreaterEqual,
|
||
"&&" => BinaryOperator::And,
|
||
"||" => BinaryOperator::Or,
|
||
_ => return None,
|
||
})
|
||
}
|
||
|
||
fn un_to_str(op: &UnaryOperator) -> &'static str {
|
||
match op {
|
||
UnaryOperator::Minus => "-",
|
||
UnaryOperator::Not => "not",
|
||
UnaryOperator::BitNot => "~",
|
||
UnaryOperator::Weak => "weak", // Phase 285W-Syntax-0
|
||
}
|
||
}
|
||
|
||
fn str_to_un(s: &str) -> Option<UnaryOperator> {
|
||
Some(match s {
|
||
"-" => UnaryOperator::Minus,
|
||
"not" => UnaryOperator::Not,
|
||
"~" => UnaryOperator::BitNot,
|
||
"weak" => UnaryOperator::Weak, // Phase 285W-Syntax-0
|
||
_ => return None,
|
||
})
|
||
}
|
||
|
||
/// Phase 52: Check if a binary operator is a comparison operator
|
||
fn is_compare_op(op: &BinaryOperator) -> bool {
|
||
matches!(
|
||
op,
|
||
BinaryOperator::Equal
|
||
| BinaryOperator::NotEqual
|
||
| BinaryOperator::Less
|
||
| BinaryOperator::Greater
|
||
| BinaryOperator::LessEqual
|
||
| BinaryOperator::GreaterEqual
|
||
)
|
||
}
|
||
|
||
/// Phase 52: Convert a literal value to JoinIR-compatible JSON format
|
||
///
|
||
/// JoinIR Frontend expects:
|
||
/// - Integer: {"type": "Int", "value": <number>}
|
||
/// - Boolean: {"type": "Bool", "value": <bool>}
|
||
/// - String: {"type": "String", "value": <string>}
|
||
fn literal_to_joinir_json(v: &LiteralValue) -> Value {
|
||
match v {
|
||
LiteralValue::Integer(i) => json!({
|
||
"kind": "Literal",
|
||
"type": "Int", // JoinIR Frontend expects "type": "Int"
|
||
"value": i
|
||
}),
|
||
LiteralValue::Bool(b) => json!({
|
||
"kind": "Literal",
|
||
"type": "Bool", // JoinIR Frontend expects "type": "Bool"
|
||
"value": b
|
||
}),
|
||
LiteralValue::String(s) => json!({
|
||
"kind": "Literal",
|
||
"type": "String",
|
||
"value": s
|
||
}),
|
||
LiteralValue::Float(f) => json!({
|
||
"kind": "Literal",
|
||
"type": "Float",
|
||
"value": f
|
||
}),
|
||
LiteralValue::Null => json!({
|
||
"kind": "Literal",
|
||
"type": "Null"
|
||
}),
|
||
LiteralValue::Void => json!({
|
||
"kind": "Literal",
|
||
"type": "Void"
|
||
}),
|
||
}
|
||
}
|