echo-1 / src /value.rs
lupodevelop's picture
echo-1 Stage 0 explainer: diffusion vs autoregressive, execution-verified
3afc977 verified
Raw
History Blame Contribute Delete
3.43 kB
//! Typed value interchange for program I/O.
//!
//! Inputs and outputs of generated Lua functions are restricted to a small,
//! JSON-serializable, deterministic value space. Anything outside it (functions,
//! userdata, non-sequence tables, NaN/Inf) is treated as non-observable and
//! causes the sample to be filtered out upstream.
use mlua::{Lua, Value as LuaValue};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "k", content = "v")]
pub enum LValue {
Nil,
Bool(bool),
Int(i64),
Num(f64),
Str(String),
List(Vec<LValue>),
}
impl LValue {
/// Convert a Rust-side value into a Lua value inside `lua`.
pub fn to_lua(&self, lua: &Lua) -> mlua::Result<LuaValue> {
Ok(match self {
LValue::Nil => LuaValue::Nil,
LValue::Bool(b) => LuaValue::Boolean(*b),
LValue::Int(i) => LuaValue::Integer(*i),
LValue::Num(n) => LuaValue::Number(*n),
LValue::Str(s) => LuaValue::String(lua.create_string(s)?),
LValue::List(items) => {
let t = lua.create_table()?;
for (idx, item) in items.iter().enumerate() {
t.set(idx as i64 + 1, item.to_lua(lua)?)?;
}
LuaValue::Table(t)
}
})
}
/// Best-effort conversion from a Lua value. Returns None when the value is
/// outside the observable space (function/userdata/thread, non-sequence
/// table, or a non-finite number).
pub fn from_lua(v: &LuaValue) -> Option<LValue> {
match v {
LuaValue::Nil => Some(LValue::Nil),
LuaValue::Boolean(b) => Some(LValue::Bool(*b)),
LuaValue::Integer(i) => Some(LValue::Int(*i)),
LuaValue::Number(n) => {
if n.is_finite() {
Some(LValue::Num(*n))
} else {
None
}
}
LuaValue::String(s) => {
let st = s.to_str().ok()?;
// Cap length so a runaway string.rep is treated as non-observable
// rather than ballooning the dataset.
if st.len() > 4096 {
return None;
}
Some(LValue::Str(st.to_owned()))
}
LuaValue::Table(t) => {
// Only accept array-like (sequence) tables: keys 1..=n, no holes,
// no extra keys. Bounded length to avoid pathological outputs.
let len = t.raw_len();
if len > 256 {
return None;
}
let mut items = Vec::with_capacity(len);
for i in 1..=len {
let elem: LuaValue = t.raw_get(i as i64).ok()?;
items.push(LValue::from_lua(&elem)?);
}
// Reject tables with non-sequence keys (e.g. string keys, holes).
let mut count = 0usize;
for pair in t.clone().pairs::<LuaValue, LuaValue>() {
let (_k, _v) = pair.ok()?;
count += 1;
}
if count != len {
return None;
}
Some(LValue::List(items))
}
_ => None, // function, userdata, thread, lightuserdata, error
}
}
}