fix: parse & compile, add null & array support

This commit is contained in:
Faynot
2026-05-18 18:30:04 +03:00
parent 4d91abc9ce
commit e01490774d
4 changed files with 102 additions and 46 deletions

View File

@@ -16,6 +16,8 @@ pub enum Value {
FsPath(String),
Variable(String),
Rhei(String),
Null,
Array(Vec<Value>),
}
#[derive(Debug, Clone)]

View File

@@ -123,10 +123,10 @@ impl<'a> Compiler<'a> {
self.compile_value(condition);
self.compile_block(*child_span);
if let Some(es) = else_span {
self.buf.push(1); // Has else
self.buf.push(1);
self.compile_block(*es);
} else {
self.buf.push(0); // No else
self.buf.push(0);
}
}
Directive::Each {
@@ -202,6 +202,18 @@ impl<'a> Compiler<'a> {
self.write_string(key);
self.write_string(r);
}
Value::Null => {
self.buf.push(OP_PROP_NULL);
self.write_string(key);
}
Value::Array(arr) => {
self.buf.push(OP_PROP_ARRAY);
self.write_string(key);
self.write_u32(arr.len() as u32);
for v in arr {
self.compile_value(v);
}
}
}
}
@@ -239,6 +251,16 @@ impl<'a> Compiler<'a> {
self.buf.push(OP_PROP_RHEI);
self.write_string(r);
}
Value::Null => {
self.buf.push(OP_PROP_NULL);
}
Value::Array(arr) => {
self.buf.push(OP_PROP_ARRAY);
self.write_u32(arr.len() as u32);
for v in arr {
self.compile_value(v);
}
}
}
}

View File

@@ -11,6 +11,8 @@ pub const OP_IF: u8 = 0x07;
pub const OP_EACH: u8 = 0x08;
pub const OP_ON: u8 = 0x09;
pub const OP_RHEI_BLK: u8 = 0x0A;
pub const OP_PROP_NULL: u8 = 0x28;
pub const OP_PROP_ARRAY: u8 = 0x29;
// elements managment
pub const OP_ELEM_PUSH: u8 = 0x10;

View File

@@ -66,7 +66,7 @@ impl<'a> Parser<'a> {
.to_string()
}
fn parse_rhei_expr(&mut self) -> Result<String, String> {
fn parse_rhei_expr(&mut self) -> Result<String, String> {
self.skip_whitespace();
if self.consume_if(b'{') {
let start = self.pos;
@@ -74,21 +74,16 @@ impl<'a> Parser<'a> {
while depth > 0 {
let c = self.peek().ok_or("Unexpected EOF in Rhei block")?;
self.advance();
if c == b'{' {
depth += 1;
}
if c == b'}' {
depth -= 1;
}
if c == b'{' { depth += 1; }
if c == b'}' { depth -= 1; }
}
let expr = std::str::from_utf8(&self.src[start..self.pos - 1])
.unwrap()
.trim()
.to_string();
.unwrap().trim().to_string();
Ok(expr)
} else {
let start = self.pos;
let mut parens = 0;
let mut brackets = 0;
let mut in_str = false;
let mut escape = false;
while let Some(c) = self.peek() {
@@ -102,29 +97,27 @@ impl<'a> Parser<'a> {
self.advance();
continue;
}
if c == b'"' {
in_str = !in_str;
}
if c == b'"' { in_str = !in_str; }
if !in_str {
if c == b'(' {
parens += 1;
}
if c == b'(' { parens += 1; }
if c == b')' {
if parens == 0 {
break;
}
if parens == 0 { break; }
parens -= 1;
}
if parens == 0 && (c == b',' || c == b'\n' || c == b'}' || c == b'{') {
if c == b'[' { brackets += 1; }
if c == b']' {
if brackets > 0 { brackets -= 1; }
}
if parens == 0 && brackets == 0 {
if c == b',' || c == b'\n' || c == b'}' || c == b'{' {
break;
}
}
}
self.advance();
}
let expr = std::str::from_utf8(&self.src[start..self.pos])
.unwrap()
.trim()
.to_string();
.unwrap().trim().to_string();
Ok(expr)
}
}
@@ -142,12 +135,23 @@ impl<'a> Parser<'a> {
}
self.advance();
}
let val = std::str::from_utf8(&self.src[start..self.pos])
.unwrap()
.to_string();
let val = std::str::from_utf8(&self.src[start..self.pos]).unwrap().to_string();
self.advance();
Ok(Value::String(val))
}
b'[' => {
self.advance();
let mut items = Vec::new();
loop {
self.skip_whitespace();
if self.consume_if(b']') { break; }
let val = self.parse_value()?;
items.push(val);
self.skip_whitespace();
self.consume_if(b',');
}
Ok(Value::Array(items))
}
b'#' => {
self.advance();
let mut color = String::from("#");
@@ -161,9 +165,7 @@ impl<'a> Parser<'a> {
b'!' => {
self.advance();
let ident = self.parse_ident();
if ident != "rhei" {
return Err("Expected !rhei".into());
}
if ident != "rhei" { return Err("Expected !rhei".into()); }
self.consume_if(b':');
Ok(Value::Rhei(self.parse_rhei_expr()?))
}
@@ -180,6 +182,7 @@ impl<'a> Parser<'a> {
match s.as_str() {
"true" => Ok(Value::Bool(true)),
"false" => Ok(Value::Bool(false)),
"null" => Ok(Value::Null),
"fs" => {
self.consume_if(b':');
let start = self.pos;
@@ -189,9 +192,7 @@ impl<'a> Parser<'a> {
}
self.advance();
}
let path = std::str::from_utf8(&self.src[start..self.pos])
.unwrap()
.to_string();
let path = std::str::from_utf8(&self.src[start..self.pos]).unwrap().to_string();
Ok(Value::FsPath(path))
}
_ => Err(format!("Unknown value token: {}", s)),
@@ -320,7 +321,16 @@ impl<'a> Parser<'a> {
if self.consume_if(b')') {
break;
}
let pname = self.parse_ident();
if pname.is_empty() {
return Err(format!(
"Expected parameter name in @component, found {:?}",
self.peek().map(|b| b as char)
));
}
self.skip_whitespace();
self.consume_if(b':');
self.skip_whitespace();
@@ -349,8 +359,12 @@ impl<'a> Parser<'a> {
value: val,
}
}
"if" => {
let condition = self.parse_value()?;
"if" => {
self.skip_whitespace();
let expr_str = self.parse_rhei_expr()?;
let condition = Value::Rhei(expr_str);
let child_span = self.parse_block()?;
let mut else_span = None;
self.skip_whitespace();
@@ -418,7 +432,7 @@ impl<'a> Parser<'a> {
Ok(NodeId::Directive(self.module.push_directive(dir)))
}
fn parse_element(&mut self) -> Result<NodeId, String> {
fn parse_element(&mut self) -> Result<NodeId, String> {
let name = self.parse_ident();
let el_id = self.module.push_element(name);
@@ -433,10 +447,12 @@ impl<'a> Parser<'a> {
if p == Some(b'{') {
let span = self.parse_block()?;
self.module.elem_child_spans[el_id as usize] = span;
} else if p == Some(b'"') || p == Some(b'$') {
} else if let Some(ch) = p {
if ch == b'"' || ch == b'#' || ch == b'$' || ch == b'!' || ch == b'-' || ch.is_ascii_digit() || ch == b'[' || ch.is_ascii_lowercase() {
let val = self.parse_value()?;
self.module.elem_content[el_id as usize] = Some(val);
}
}
Ok(NodeId::Element(el_id))
}
@@ -450,9 +466,7 @@ impl<'a> Parser<'a> {
b'!' => {
self.advance();
let i = self.parse_ident();
if i != "rhei" {
return Err("Expected rhei".into());
}
if i != "rhei" { return Err("Expected rhei".into()); }
self.consume_if(b':');
let expr = self.parse_rhei_expr()?;
Ok(NodeId::Directive(
@@ -460,13 +474,29 @@ impl<'a> Parser<'a> {
))
}
b'A'..=b'Z' => self.parse_element(),
b'"' | b'$' => {
b'"' | b'#' | b'$' | b'-' | b'0'..=b'9' | b'[' => {
let val = self.parse_value()?;
let el_id = self.module.push_element("#text".to_string());
self.module.elem_content[el_id as usize] = Some(val);
Ok(NodeId::Element(el_id))
}
_ => Err(format!("Unexpected token: {}", c as char)),
ch if ch.is_ascii_lowercase() => {
let val = self.parse_value()?;
let el_id = self.module.push_element("#text".to_string());
self.module.elem_content[el_id as usize] = Some(val);
Ok(NodeId::Element(el_id))
}
_ => {
let extract_start = self.pos.saturating_sub(10);
let extract_end = std::cmp::min(self.src.len(), self.pos + 20);
let context = std::str::from_utf8(&self.src[extract_start..extract_end])
.unwrap_or("");
Err(format!(
"Unexpected token: '{}' at position {}. Context around: \"...{}...\"",
c as char, self.pos, context.trim()
))
}
}
}