hv1: early-exit at main (no plugin init); tokenizer: Stage-3 single-quote + full escapes (\/ \b \f \' \r fix); builder: route BinOp via SSOT emit_binop_to_dst; hv1 verify canary route (builder→Core); docs: phase-20.39 updates
This commit is contained in:
@ -15,7 +15,7 @@
|
||||
use super::NyashRunner;
|
||||
use std::io::Write;
|
||||
|
||||
pub(crate) fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 {
|
||||
pub fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 {
|
||||
// Optional: direct Core Dispatcher via child nyash (boxed)
|
||||
// Toggle: HAKO_CORE_DIRECT=1 (alias: NYASH_CORE_DIRECT)
|
||||
let core_direct = std::env::var("HAKO_CORE_DIRECT").ok().as_deref() == Some("1")
|
||||
@ -39,6 +39,26 @@ pub(crate) fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 {
|
||||
}
|
||||
let mut payload = json.to_string();
|
||||
|
||||
// Fast-path: accept MIR(JSON v0) directly when it looks like a module (functions/blocks)
|
||||
if payload.contains("\"functions\"") && payload.contains("\"blocks\"") {
|
||||
match super::mir_json_v0::parse_mir_v0_to_module(&payload) {
|
||||
Ok(module) => {
|
||||
super::json_v0_bridge::maybe_dump_mir(&module);
|
||||
crate::runner::child_env::pre_run_reset_oob_if_strict();
|
||||
let rc = runner.execute_mir_module_quiet_exit(&module);
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
|
||||
eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)");
|
||||
return 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ MIR JSON v0 parse error: {}", e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always try the v1 bridge first (Stage‑B Program JSON → MIR module).
|
||||
// This is no‑op when input is already MIR(JSON v0) with functions/blocks.
|
||||
if let Ok(j) = crate::runner::modes::common_util::core_bridge::canonicalize_module_json(&payload) {
|
||||
|
||||
@ -9,6 +9,9 @@ use std::{fs, process};
|
||||
|
||||
/// Thin file dispatcher: select backend and delegate to mode executors
|
||||
pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
// Note: hv1 direct route is now handled at main.rs entry point (before NyashRunner creation).
|
||||
// This function is only called after plugins and runner initialization have already occurred.
|
||||
|
||||
// Selfhost pipeline (Ny -> JSON v0)
|
||||
// Default: ON. Backward‑compat envs:
|
||||
// - NYASH_USE_NY_COMPILER={1|true|on} to force ON
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use super::ast::{ProgramV0, StmtV0};
|
||||
use super::ast::{ProgramV0, StmtV0, ExprV0};
|
||||
use crate::mir::{
|
||||
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule,
|
||||
MirPrinter, MirType, ValueId,
|
||||
MirPrinter, MirType, ValueId, BinaryOp,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
@ -28,6 +28,8 @@ pub(super) struct LoopContext {
|
||||
thread_local! {
|
||||
static EXIT_SNAPSHOT_STACK: RefCell<Vec<Vec<(BasicBlockId, HashMap<String, ValueId>)>>> = RefCell::new(Vec::new());
|
||||
static CONT_SNAPSHOT_STACK: RefCell<Vec<Vec<(BasicBlockId, HashMap<String, ValueId>)>>> = RefCell::new(Vec::new());
|
||||
// Optional increment hint for current loop frame: (var_name, step)
|
||||
static INCR_HINT_STACK: RefCell<Vec<Option<(String, i64)>>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
pub(super) fn push_loop_snapshot_frames() {
|
||||
@ -59,6 +61,35 @@ fn record_continue_snapshot(cur_bb: BasicBlockId, vars: &HashMap<String, ValueId
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn detect_and_push_increment_hint(body: &[StmtV0]) {
|
||||
let mut hint: Option<(String, i64)> = None;
|
||||
for stmt in body.iter().rev() {
|
||||
if let StmtV0::Local { name, expr } = stmt.clone() {
|
||||
if let ExprV0::Binary { op, lhs, rhs } = expr {
|
||||
if let ExprV0::Var { name: vname } = *lhs {
|
||||
if vname == name {
|
||||
if let ExprV0::Int { value } = *rhs {
|
||||
if let Some(v) = value.as_i64() {
|
||||
let s = match op.as_str() { "+" => v, "-" => -v, _ => 0 };
|
||||
if s != 0 { hint = Some((name.clone(), s)); break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
INCR_HINT_STACK.with(|s| s.borrow_mut().push(hint));
|
||||
}
|
||||
|
||||
pub(super) fn pop_increment_hint() -> Option<(String, i64)> {
|
||||
INCR_HINT_STACK.with(|s| s.borrow_mut().pop().unwrap_or(None))
|
||||
}
|
||||
|
||||
fn peek_increment_hint() -> Option<(String, i64)> {
|
||||
INCR_HINT_STACK.with(|s| s.borrow().last().cloned().unwrap_or(None))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct BridgeEnv {
|
||||
pub(super) throw_enabled: bool,
|
||||
@ -172,7 +203,13 @@ pub(super) fn lower_stmt_with_vars(
|
||||
}
|
||||
StmtV0::Continue => {
|
||||
if let Some(ctx) = loop_stack.last().copied() {
|
||||
// snapshot variables at continue
|
||||
// Optional: apply increment hint before continue (so header sees updated var)
|
||||
if let Some((ref var_name, step)) = peek_increment_hint() {
|
||||
let _ = crate::mir::ssot::loop_common::apply_increment_before_continue(
|
||||
f, cur_bb, vars, var_name, step,
|
||||
);
|
||||
}
|
||||
// snapshot variables at continue (after increment)
|
||||
record_continue_snapshot(cur_bb, vars);
|
||||
lower_continue_stmt(f, cur_bb, ctx.cond_bb);
|
||||
}
|
||||
|
||||
@ -152,22 +152,11 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
|
||||
ExprV0::Binary { op, lhs, rhs } => {
|
||||
let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
|
||||
let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?;
|
||||
let bop = match op.as_str() {
|
||||
"+" => BinaryOp::Add,
|
||||
"-" => BinaryOp::Sub,
|
||||
"*" => BinaryOp::Mul,
|
||||
"/" => BinaryOp::Div,
|
||||
_ => return Err("unsupported op".into()),
|
||||
let bop = match crate::mir::ssot::binop_lower::parse_binop_str(op) {
|
||||
Some(b) => b,
|
||||
None => return Err("unsupported op".into()),
|
||||
};
|
||||
let dst = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur_after_r) {
|
||||
bb.add_instruction(MirInstruction::BinOp {
|
||||
dst,
|
||||
op: bop,
|
||||
lhs: l,
|
||||
rhs: r,
|
||||
});
|
||||
}
|
||||
let dst = crate::mir::ssot::binop_lower::emit_binop_func(f, cur_after_r, bop, l, r);
|
||||
Ok((dst, cur_after_r))
|
||||
}
|
||||
ExprV0::Extern {
|
||||
|
||||
@ -126,8 +126,11 @@ pub(super) fn lower_loop_stmt(
|
||||
// open snapshot frames for nested break/continue
|
||||
super::push_loop_snapshot_frames();
|
||||
loop_stack.push(LoopContext { cond_bb, exit_bb });
|
||||
// Detect simple increment hint for this loop body
|
||||
super::detect_and_push_increment_hint(body);
|
||||
let bend_res = lower_stmt_list_with_vars(ops.f, body_bb, body, &mut body_vars, loop_stack, env);
|
||||
loop_stack.pop();
|
||||
let _ = super::pop_increment_hint();
|
||||
let bend = bend_res?;
|
||||
// collect snapshots for this loop level
|
||||
let continue_snaps = super::pop_continue_snapshots();
|
||||
|
||||
@ -85,6 +85,15 @@ pub fn parse_mir_v0_to_module(json: &str) -> Result<MirModule, String> {
|
||||
block_ref.add_instruction(MirInstruction::Copy { dst: ValueId::new(dst), src: ValueId::new(src) });
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"binop" => {
|
||||
let dst = require_u64(inst, "dst", "binop dst")? as u32;
|
||||
let lhs = require_u64(inst, "lhs", "binop lhs")? as u32;
|
||||
let rhs = require_u64(inst, "rhs", "binop rhs")? as u32;
|
||||
let operation = inst.get("operation").and_then(Value::as_str).ok_or_else(|| "binop missing operation".to_string())?;
|
||||
let bop = parse_binop(operation)?;
|
||||
block_ref.add_instruction(MirInstruction::BinOp { dst: ValueId::new(dst), op: bop, lhs: ValueId::new(lhs), rhs: ValueId::new(rhs) });
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"compare" => {
|
||||
let dst = require_u64(inst, "dst", "compare dst")? as u32;
|
||||
let lhs = require_u64(inst, "lhs", "compare lhs")? as u32;
|
||||
@ -160,3 +169,15 @@ fn parse_compare(op: &str) -> Result<crate::mir::types::CompareOp, String> {
|
||||
s => return Err(format!("unsupported compare op '{}'", s)),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_binop(op: &str) -> Result<crate::mir::types::BinaryOp, String> {
|
||||
use crate::mir::types::BinaryOp;
|
||||
Ok(match op {
|
||||
"+" => BinaryOp::Add,
|
||||
"-" => BinaryOp::Sub,
|
||||
"*" => BinaryOp::Mul,
|
||||
"/" => BinaryOp::Div,
|
||||
"%" => BinaryOp::Mod,
|
||||
s => return Err(format!("unsupported binary op '{}'", s)),
|
||||
})
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ mod mir_json_v0;
|
||||
pub mod mir_json_emit;
|
||||
pub mod modes;
|
||||
mod pipe_io;
|
||||
mod core_executor;
|
||||
pub mod core_executor;
|
||||
mod pipeline;
|
||||
mod jit_direct;
|
||||
mod selfhost;
|
||||
|
||||
@ -15,32 +15,8 @@ use std::{fs, process};
|
||||
impl NyashRunner {
|
||||
/// Execute VM mode (split)
|
||||
pub(crate) fn execute_vm_mode(&self, filename: &str) {
|
||||
// Fast-path: hv1 verify direct (bypass NyashParser)
|
||||
// If NYASH_VERIFY_JSON is present and hv1 route is requested, parse JSON v1 → MIR and run Core interpreter.
|
||||
// This avoids generating/compiling Hako inline drivers and stabilizes -c/inline verify flows.
|
||||
let want_hv1_direct = {
|
||||
let has_json = std::env::var("NYASH_VERIFY_JSON").is_ok();
|
||||
let route = std::env::var("HAKO_ROUTE_HAKOVM").ok().as_deref() == Some("1")
|
||||
|| std::env::var("HAKO_VERIFY_PRIMARY").ok().as_deref() == Some("hakovm");
|
||||
has_json && route
|
||||
};
|
||||
if want_hv1_direct {
|
||||
if let Ok(j) = std::env::var("NYASH_VERIFY_JSON") {
|
||||
// Try v1 schema first, then v0 for compatibility
|
||||
if let Ok(Some(module)) = crate::runner::json_v1_bridge::try_parse_v1_to_module(&j) {
|
||||
let rc = self.execute_mir_module_quiet_exit(&module);
|
||||
println!("{}", rc);
|
||||
std::process::exit(rc);
|
||||
}
|
||||
if let Ok(module) = crate::runner::mir_json_v0::parse_mir_v0_to_module(&j) {
|
||||
let rc = self.execute_mir_module_quiet_exit(&module);
|
||||
println!("{}", rc);
|
||||
std::process::exit(rc);
|
||||
}
|
||||
eprintln!("❌ hv1-direct: invalid JSON for MIR (v1/v0)");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
// Note: hv1 direct route is now handled at main.rs entry point (before plugin initialization).
|
||||
// This function is only called after plugin initialization has already occurred.
|
||||
|
||||
// Quiet mode for child pipelines (e.g., selfhost compiler JSON emit)
|
||||
let quiet_pipe = std::env::var("NYASH_JSON_ONLY").ok().as_deref() == Some("1");
|
||||
|
||||
@ -15,6 +15,9 @@ impl NyashRunner {
|
||||
/// - Respects using preprocessing done earlier in the pipeline
|
||||
/// - Relies on global plugin host initialized by runner
|
||||
pub(crate) fn execute_vm_fallback_interpreter(&self, filename: &str) {
|
||||
// Note: hv1 direct route is now handled at main.rs entry point (before plugin initialization).
|
||||
// This function is only called after plugin initialization has already occurred.
|
||||
|
||||
// Read source
|
||||
let code = match fs::read_to_string(filename) {
|
||||
Ok(s) => s,
|
||||
|
||||
Reference in New Issue
Block a user