refactor(json_v1_bridge): split parsing helpers
This commit is contained in:
172
src/runner/json_v1_bridge/helpers.rs
Normal file
172
src/runner/json_v1_bridge/helpers.rs
Normal 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)
|
||||
}
|
||||
4
src/runner/json_v1_bridge/mod.rs
Normal file
4
src/runner/json_v1_bridge/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
mod helpers;
|
||||
mod parse;
|
||||
|
||||
pub use parse::try_parse_v1_to_module;
|
||||
@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user