refactor(json_v1_bridge): split parsing helpers

This commit is contained in:
2025-12-28 03:13:01 +09:00
parent 6f485beb1e
commit a0da22628e
3 changed files with 219 additions and 201 deletions

View File

@ -0,0 +1,172 @@
use crate::mir::{ConstValue, EffectMask};
use serde_json::Value;
pub(super) fn parse_effects_from(node: &Value) -> EffectMask {
if let Some(arr) = node.get("effects").and_then(Value::as_array) {
let mut m = EffectMask::PURE;
for e in arr {
if let Some(s) = e.as_str() {
match s {
"write" | "mut" | "WriteHeap" => {
m = m.union(EffectMask::WRITE);
}
"read" | "ReadHeap" => {
m = m.union(EffectMask::READ);
}
"io" | "IO" | "ffi" | "FFI" | "debug" => {
m = m.union(EffectMask::IO);
}
"control" | "Control" => {
m = m.union(EffectMask::CONTROL);
}
_ => {}
}
}
}
return m;
}
EffectMask::PURE
}
#[allow(dead_code)]
pub(super) fn parse_const_value(value_obj: &Value) -> Result<ConstValue, String> {
// Accept both shapes:
// 1) { "type": "i64", "value": 123 }
// 2) { "type": {"kind":"handle","box_type":"StringBox"}, "value": "str" }
// 3) Minimal fallback: when "type" is omitted, assume integer/string directly
let (type_desc, raw_val) = if let Some(t) = value_obj.get("type") {
(
Some(t.clone()),
value_obj
.get("value")
.cloned()
.ok_or_else(|| "const value missing 'value' field".to_string())?,
)
} else {
(None, value_obj.clone())
};
// String type descriptor
if let Some(Value::String(s)) = type_desc.as_ref() {
match s.as_str() {
// Integer
"i64" | "int" => {
let val = raw_val
.as_i64()
.ok_or_else(|| "const value expected integer".to_string())?;
return Ok(ConstValue::Integer(val));
}
// Float
"f64" | "float" => {
let val = raw_val
.as_f64()
.ok_or_else(|| "const value expected float".to_string())?;
return Ok(ConstValue::Float(val));
}
// Bool (allow explicit bool schema even if current emitter uses i64)
"i1" | "bool" => {
let b = match raw_val {
Value::Bool(v) => v,
Value::Number(n) => n.as_i64().unwrap_or(0) != 0,
Value::String(ref s) => s == "true" || s == "1",
_ => false,
};
return Ok(ConstValue::Bool(b));
}
// String explicit
"string" | "String" => {
let s = raw_val
.as_str()
.ok_or_else(|| "const value expected string".to_string())?;
return Ok(ConstValue::String(s.to_string()));
}
// Void/Null
"void" => {
return Ok(ConstValue::Void);
}
other => {
return Err(format!(
"unsupported const type '{}' in Gate-C v1 bridge",
other
));
}
}
}
// Object descriptor (e.g., handle/StringBox)
if let Some(Value::Object(map)) = type_desc.as_ref() {
if let Some(Value::String(kind)) = map.get("kind") {
if kind == "handle" {
if let Some(Value::String(box_type)) = map.get("box_type") {
match box_type.as_str() {
// StringBox handle is serialized with raw string payload
"StringBox" => {
let s = raw_val.as_str().ok_or_else(|| {
"StringBox const expects string value".to_string()
})?;
return Ok(ConstValue::String(s.to_string()));
}
// Other handle kinds are not yet supported in the bridge
other => {
return Err(format!(
"unsupported const handle type '{}' in Gate-C v1 bridge",
other
));
}
}
}
}
}
return Err("unsupported const type object in Gate-C v1 bridge".to_string());
}
// No explicit type: heuristics
match raw_val {
Value::Number(n) => Ok(ConstValue::Integer(
n.as_i64().ok_or_else(|| "integer expected".to_string())?,
)),
Value::Bool(b) => Ok(ConstValue::Bool(b)),
Value::String(s) => Ok(ConstValue::String(s)),
_ => Err("const value has unsupported type descriptor".to_string()),
}
}
pub(super) fn require_u64(node: &Value, key: &str, context: &str) -> Result<u64, String> {
node.get(key)
.and_then(Value::as_u64)
.ok_or_else(|| format!("{} missing field '{}'", context, key))
}
pub(super) fn parse_binop(op: &str) -> Result<crate::mir::types::BinaryOp, String> {
use crate::mir::types::BinaryOp;
let bop = match op {
"+" => BinaryOp::Add,
"-" => BinaryOp::Sub,
"*" => BinaryOp::Mul,
"/" => BinaryOp::Div,
"%" => BinaryOp::Mod,
"&" | "bitand" => BinaryOp::BitAnd,
"|" | "bitor" => BinaryOp::BitOr,
"^" | "bitxor" => BinaryOp::BitXor,
"shl" => BinaryOp::Shl,
"shr" => BinaryOp::Shr,
"and" => BinaryOp::And,
"or" => BinaryOp::Or,
other => return Err(format!("unsupported binop '{}'", other)),
};
Ok(bop)
}
pub(super) fn parse_compare(op: &str) -> Result<crate::mir::types::CompareOp, String> {
use crate::mir::types::CompareOp;
let cop = match op {
"==" => CompareOp::Eq,
"!=" => CompareOp::Ne,
"<" => CompareOp::Lt,
"<=" => CompareOp::Le,
">" => CompareOp::Gt,
">=" => CompareOp::Ge,
other => return Err(format!("unsupported compare op '{}'", other)),
};
Ok(cop)
}

View File

@ -0,0 +1,4 @@
mod helpers;
mod parse;
pub use parse::try_parse_v1_to_module;

View File

@ -1,36 +1,11 @@
use super::mir_json::common as mirjson_common;
use super::helpers::{parse_binop, parse_compare, parse_effects_from, require_u64};
use crate::mir::{
function::{FunctionSignature, MirFunction, MirModule},
BasicBlock, BasicBlockId, ConstValue, EffectMask, MirInstruction, MirType, ValueId,
BasicBlock, BasicBlockId, EffectMask, MirInstruction, MirType, ValueId,
};
use serde_json::Value;
fn parse_effects_from(node: &Value) -> EffectMask {
if let Some(arr) = node.get("effects").and_then(Value::as_array) {
let mut m = EffectMask::PURE;
for e in arr {
if let Some(s) = e.as_str() {
match s {
"write" | "mut" | "WriteHeap" => {
m = m.union(EffectMask::WRITE);
}
"read" | "ReadHeap" => {
m = m.union(EffectMask::READ);
}
"io" | "IO" | "ffi" | "FFI" | "debug" => {
m = m.union(EffectMask::IO);
}
"control" | "Control" => {
m = m.union(EffectMask::CONTROL);
}
_ => {}
}
}
}
return m;
}
EffectMask::PURE
}
use super::super::mir_json::common as mirjson_common;
/// Try to parse MIR JSON v1 schema into a MIR module.
/// Returns Ok(None) when the input is not v1 (schema_version missing).
@ -360,8 +335,9 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
"nyash.builtin.print" => "nyash.builtin.print".to_string(),
"nyash.console.log" => "nyash.console.log".to_string(),
// Accept env.console.* as nyash.console.log (numeric only)
"env.console.log" | "env.console.warn"
| "env.console.error" => "nyash.console.log".to_string(),
"env.console.log" | "env.console.warn" | "env.console.error" => {
"nyash.console.log".to_string()
}
other => {
return Err(format!(
"unsupported Global callee '{}' in mir_call (Gate-C v1 bridge)",
@ -385,10 +361,12 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
let bt = callee_obj
.get("box_type")
.and_then(Value::as_str)
.ok_or_else(|| format!(
"mir_call callee Constructor missing box_type in function '{}'",
func_name
))?;
.ok_or_else(|| {
format!(
"mir_call callee Constructor missing box_type in function '{}'",
func_name
)
})?;
// dst required for Constructor
let dst = dst_opt.ok_or_else(|| {
format!(
@ -410,9 +388,9 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
.and_then(Value::as_str)
.ok_or_else(|| {
format!(
"mir_call callee Method missing method in function '{}'",
func_name
)
"mir_call callee Method missing method in function '{}'",
func_name
)
})?
.to_string();
let recv_id = callee_obj
@ -420,9 +398,9 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
.and_then(Value::as_u64)
.ok_or_else(|| {
format!(
"mir_call callee Method missing receiver in function '{}'",
func_name
)
"mir_call callee Method missing receiver in function '{}'",
func_name
)
})? as u32;
let box_name = callee_obj
.get("box_name")
@ -468,10 +446,12 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
callee_obj.get("params").and_then(Value::as_array)
{
for p in arr {
let s = p.as_str().ok_or_else(|| format!(
"mir_call Closure params must be strings in function '{}'",
func_name
))?;
let s = p.as_str().ok_or_else(|| {
format!(
"mir_call Closure params must be strings in function '{}'",
func_name
)
})?;
params.push(s.to_string());
}
}
@ -481,12 +461,16 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
callee_obj.get("captures").and_then(Value::as_array)
{
for e in arr {
let pair = e.as_array().ok_or_else(|| format!(
"mir_call Closure capture entry must be array in function '{}'",
func_name
))?;
let pair = e.as_array().ok_or_else(|| {
format!(
"mir_call Closure capture entry must be array in function '{}'",
func_name
)
})?;
if pair.len() != 2 {
return Err("mir_call Closure capture entry must have 2 elements".into());
return Err(
"mir_call Closure capture entry must have 2 elements".into(),
);
}
let name = pair[0].as_str().ok_or_else(|| {
"mir_call Closure capture[0] must be string"
@ -495,8 +479,7 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
let id = pair[1].as_u64().ok_or_else(|| {
"mir_call Closure capture[1] must be integer"
.to_string()
})?
as u32;
})? as u32;
captures.push((name.to_string(), ValueId::new(id)));
}
}
@ -521,19 +504,21 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
.and_then(Value::as_u64)
.ok_or_else(|| {
format!(
"mir_call callee Closure missing func in function '{}'",
func_name
)
"mir_call callee Closure missing func in function '{}'",
func_name
)
})? as u32;
// Captures array (if present) are appended to argv for minimal parity
if let Some(caps) =
callee_obj.get("captures").and_then(Value::as_array)
{
for c in caps {
let id = c.as_u64().ok_or_else(|| format!(
"mir_call Closure capture must be integer in function '{}'",
func_name
))? as u32;
let id = c.as_u64().ok_or_else(|| {
format!(
"mir_call Closure capture must be integer in function '{}'",
func_name
)
})? as u32;
argv.push(ValueId::new(id));
}
}
@ -622,146 +607,3 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
Ok(Some(module))
}
#[allow(dead_code)]
fn parse_const_value(value_obj: &Value) -> Result<ConstValue, String> {
// Accept both shapes:
// 1) { "type": "i64", "value": 123 }
// 2) { "type": {"kind":"handle","box_type":"StringBox"}, "value": "str" }
// 3) Minimal fallback: when "type" is omitted, assume integer/string directly
let (type_desc, raw_val) = if let Some(t) = value_obj.get("type") {
(
Some(t.clone()),
value_obj
.get("value")
.cloned()
.ok_or_else(|| "const value missing 'value' field".to_string())?,
)
} else {
(None, value_obj.clone())
};
// String type descriptor
if let Some(Value::String(s)) = type_desc.as_ref() {
match s.as_str() {
// Integer
"i64" | "int" => {
let val = raw_val
.as_i64()
.ok_or_else(|| "const value expected integer".to_string())?;
return Ok(ConstValue::Integer(val));
}
// Float
"f64" | "float" => {
let val = raw_val
.as_f64()
.ok_or_else(|| "const value expected float".to_string())?;
return Ok(ConstValue::Float(val));
}
// Bool (allow explicit bool schema even if current emitter uses i64)
"i1" | "bool" => {
let b = match raw_val {
Value::Bool(v) => v,
Value::Number(n) => n.as_i64().unwrap_or(0) != 0,
Value::String(ref s) => s == "true" || s == "1",
_ => false,
};
return Ok(ConstValue::Bool(b));
}
// String explicit
"string" | "String" => {
let s = raw_val
.as_str()
.ok_or_else(|| "const value expected string".to_string())?;
return Ok(ConstValue::String(s.to_string()));
}
// Void/Null
"void" => {
return Ok(ConstValue::Void);
}
other => {
return Err(format!(
"unsupported const type '{}' in Gate-C v1 bridge",
other
));
}
}
}
// Object descriptor (e.g., handle/StringBox)
if let Some(Value::Object(map)) = type_desc.as_ref() {
if let Some(Value::String(kind)) = map.get("kind") {
if kind == "handle" {
if let Some(Value::String(box_type)) = map.get("box_type") {
match box_type.as_str() {
// StringBox handle is serialized with raw string payload
"StringBox" => {
let s = raw_val.as_str().ok_or_else(|| {
"StringBox const expects string value".to_string()
})?;
return Ok(ConstValue::String(s.to_string()));
}
// Other handle kinds are not yet supported in the bridge
other => {
return Err(format!(
"unsupported const handle type '{}' in Gate-C v1 bridge",
other
));
}
}
}
}
}
return Err("unsupported const type object in Gate-C v1 bridge".to_string());
}
// No explicit type: heuristics
match raw_val {
Value::Number(n) => Ok(ConstValue::Integer(
n.as_i64().ok_or_else(|| "integer expected".to_string())?,
)),
Value::Bool(b) => Ok(ConstValue::Bool(b)),
Value::String(s) => Ok(ConstValue::String(s)),
_ => Err("const value has unsupported type descriptor".to_string()),
}
}
fn require_u64(node: &Value, key: &str, context: &str) -> Result<u64, String> {
node.get(key)
.and_then(Value::as_u64)
.ok_or_else(|| format!("{} missing field '{}'", context, key))
}
fn parse_binop(op: &str) -> Result<crate::mir::types::BinaryOp, String> {
use crate::mir::types::BinaryOp;
let bop = match op {
"+" => BinaryOp::Add,
"-" => BinaryOp::Sub,
"*" => BinaryOp::Mul,
"/" => BinaryOp::Div,
"%" => BinaryOp::Mod,
"&" | "bitand" => BinaryOp::BitAnd,
"|" | "bitor" => BinaryOp::BitOr,
"^" | "bitxor" => BinaryOp::BitXor,
"shl" => BinaryOp::Shl,
"shr" => BinaryOp::Shr,
"and" => BinaryOp::And,
"or" => BinaryOp::Or,
other => return Err(format!("unsupported binop '{}'", other)),
};
Ok(bop)
}
fn parse_compare(op: &str) -> Result<crate::mir::types::CompareOp, String> {
use crate::mir::types::CompareOp;
let cop = match op {
"==" => CompareOp::Eq,
"!=" => CompareOp::Ne,
"<" => CompareOp::Lt,
"<=" => CompareOp::Le,
">" => CompareOp::Gt,
">=" => CompareOp::Ge,
other => return Err(format!("unsupported compare op '{}'", other)),
};
Ok(cop)
}