feat: reactivityl; vdom; rhei; styles; fs; image element
This commit is contained in:
280
src/interpreter/reader.rs
Normal file
280
src/interpreter/reader.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user