feat: reactivityl; vdom; rhei; styles; fs; image element

This commit is contained in:
Faynot
2026-06-02 12:12:20 +03:00
commit f956b750e3
16 changed files with 7713 additions and 0 deletions

280
src/interpreter/reader.rs Normal file
View File

@@ -0,0 +1,280 @@
use super::opcodes::*;
use super::types::InterpError;
pub struct Reader<'a> {
pub data: &'a [u8],
pub pos: usize,
}
impl<'a> Reader<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
pub fn remaining(&self) -> usize {
self.data.len() - self.pos
}
pub fn read_byte(&mut self) -> Result<u8, InterpError> {
self.require(1)?;
let b = self.data[self.pos];
self.pos += 1;
Ok(b)
}
pub fn read_u32(&mut self) -> Result<u32, InterpError> {
self.require(4)?;
let v = u32::from_le_bytes(self.data[self.pos..self.pos + 4].try_into().unwrap());
self.pos += 4;
Ok(v)
}
pub fn read_i64(&mut self) -> Result<i64, InterpError> {
self.require(8)?;
let v = i64::from_le_bytes(self.data[self.pos..self.pos + 8].try_into().unwrap());
self.pos += 8;
Ok(v)
}
pub fn read_f64(&mut self) -> Result<f64, InterpError> {
self.require(8)?;
let v = f64::from_le_bytes(self.data[self.pos..self.pos + 8].try_into().unwrap());
self.pos += 8;
Ok(v)
}
pub fn read_string(&mut self) -> Result<String, InterpError> {
let len = self.read_u32()? as usize;
self.require(len)?;
let s = std::str::from_utf8(&self.data[self.pos..self.pos + len])
.map_err(|_| InterpError::InvalidUtf8)?
.to_string();
self.pos += len;
Ok(s)
}
pub fn read_value_as_string(&mut self, type_op: u8) -> Result<Option<String>, InterpError> {
let s = match type_op {
OP_PROP_STR | OP_PROP_COLOR | OP_PROP_FSPATH | OP_PROP_RHEI => {
Some(self.read_string()?)
}
OP_PROP_IDENT => {
Some(self.read_string()?)
}
OP_PROP_VAR => {
Some(format!("${}", self.read_string()?))
}
OP_PROP_INT => {
Some(self.read_i64()?.to_string())
}
OP_PROP_FLOAT => {
Some(self.read_f64()?.to_string())
}
OP_PROP_BOOL => {
Some((self.read_byte()? != 0).to_string())
}
OP_PROP_NULL => None,
OP_PROP_ARRAY => {
let items = self.read_array_as_strings()?;
Some(items.join(","))
}
OP_PROP_UNIT => {
let num = self.read_f64()?;
let unit = self.read_string()?;
if num.fract() == 0.0 {
Some(format!("{}{}", num as i64, unit))
} else {
Some(format!("{}{}", num, unit))
}
}
OP_PROP_CALL => {
let name = self.read_string()?;
let arg_count = self.read_u32()? as usize;
let mut args = Vec::with_capacity(arg_count);
for _ in 0..arg_count {
let op = self.read_byte()?;
let val = self.read_value_as_string(op)?
.unwrap_or_default();
args.push(val);
}
Some(format!("{}({})", name, args.join(",")))
}
_ => None,
};
Ok(s)
}
/// Read `OP_PROP_ARRAY` (opcode already consumed) and return elements as strings.
pub fn read_array_as_strings(&mut self) -> Result<Vec<String>, InterpError> {
let count = self.read_u32()? as usize;
let mut items = Vec::with_capacity(count);
for _ in 0..count {
let elem_op = self.read_byte()?;
if let Some(s) = self.read_value_as_string(elem_op)? {
items.push(s);
}
}
Ok(items)
}
pub fn skip_value(&mut self, type_op: u8) -> Result<(), InterpError> {
match type_op {
OP_PROP_STR
| OP_PROP_COLOR
| OP_PROP_FSPATH
| OP_PROP_VAR
| OP_PROP_RHEI
| OP_PROP_IDENT => {
self.read_string()?;
}
OP_PROP_INT => { self.read_i64()?; }
OP_PROP_FLOAT => { self.read_f64()?; }
OP_PROP_BOOL => { self.read_byte()?; }
OP_PROP_NULL => {}
OP_PROP_ARRAY => {
let count = self.read_u32()?;
for _ in 0..count {
let elem_op = self.read_byte()?;
self.skip_value(elem_op)?;
}
}
OP_PROP_UNIT => {
self.read_f64()?; // number
self.read_string()?; // unit suffix
}
OP_PROP_CALL => {
self.read_string()?; // function name
let arg_count = self.read_u32()?;
for _ in 0..arg_count {
let op = self.read_byte()?;
self.skip_value(op)?;
}
}
_ => {}
}
Ok(())
}
/// Skip a full opcode + its payload without interpreting it.
pub fn skip_opcode(&mut self, op: u8) -> Result<(), InterpError> {
match op {
OP_VERSION => { self.read_i64()?; }
OP_STYLE | OP_RHEI_BLK => { self.read_string()?; }
OP_PROP_STR
| OP_PROP_COLOR
| OP_PROP_FSPATH
| OP_PROP_VAR
| OP_PROP_RHEI
| OP_PROP_INT
| OP_PROP_FLOAT
| OP_PROP_BOOL
| OP_PROP_NULL
| OP_PROP_ARRAY
| OP_PROP_CALL
| OP_PROP_UNIT
| OP_PROP_IDENT => {
self.read_string()?; // key
self.skip_value(op)?; // value
}
OP_GLOBAL | OP_LET => {
self.read_string()?;
let vop = self.read_byte()?;
self.skip_value(vop)?;
}
OP_SINGLETON => {
self.read_string()?;
let count = self.read_u32()?;
for _ in 0..count {
self.read_string()?;
let vop = self.read_byte()?;
self.skip_value(vop)?;
}
}
OP_COMPONENT => {
self.read_string()?;
let params = self.read_u32()?;
for _ in 0..params {
self.read_string()?;
self.read_string()?;
}
self.skip_block()?;
}
OP_IF => {
let vop = self.read_byte()?;
self.skip_value(vop)?;
self.skip_block()?;
if self.read_byte()? == 1 { self.skip_block()?; }
}
OP_EACH => {
self.read_string()?;
let vop = self.read_byte()?;
self.skip_value(vop)?;
self.skip_block()?;
}
OP_ON => {
self.read_string()?;
let args = self.read_u32()?;
for _ in 0..args {
self.read_string()?;
let vop = self.read_byte()?;
self.skip_value(vop)?;
}
self.skip_block()?;
}
OP_ELEM_PUSH => { self.read_string()?; }
OP_CONTENT => {
let vop = self.read_byte()?;
self.skip_value(vop)?;
}
OP_STYLE_RULE => {
self.read_string()?; // selector
let count = self.read_u32()?;
for _ in 0..count {
self.read_string()?; // property key
let vop = self.read_byte()?;
self.skip_value(vop)?; // property value
}
}
OP_STYLE_ANIM => {
self.read_string()?; // animation name
let frame_count = self.read_u32()?;
for _ in 0..frame_count {
self.read_string()?; // step ("from", "to", "50%", …)
let prop_count = self.read_u32()?;
for _ in 0..prop_count {
self.read_string()?;
let vop = self.read_byte()?;
self.skip_value(vop)?;
}
}
}
_ => {}
}
Ok(())
}
pub fn skip_block(&mut self) -> Result<(), InterpError> {
loop {
let op = self.read_byte()?;
if op == OP_END_BLOCK { return Ok(()); }
self.skip_opcode(op)?;
}
}
#[inline]
fn require(&self, n: usize) -> Result<(), InterpError> {
if self.remaining() < n {
Err(InterpError::UnexpectedEof)
} else {
Ok(())
}
}
}