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), FsPath(String),
Variable(String), Variable(String),
Rhei(String), Rhei(String),
Null,
Array(Vec<Value>),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@@ -123,10 +123,10 @@ impl<'a> Compiler<'a> {
self.compile_value(condition); self.compile_value(condition);
self.compile_block(*child_span); self.compile_block(*child_span);
if let Some(es) = else_span { if let Some(es) = else_span {
self.buf.push(1); // Has else self.buf.push(1);
self.compile_block(*es); self.compile_block(*es);
} else { } else {
self.buf.push(0); // No else self.buf.push(0);
} }
} }
Directive::Each { Directive::Each {
@@ -202,6 +202,18 @@ impl<'a> Compiler<'a> {
self.write_string(key); self.write_string(key);
self.write_string(r); 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.buf.push(OP_PROP_RHEI);
self.write_string(r); 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_EACH: u8 = 0x08;
pub const OP_ON: u8 = 0x09; pub const OP_ON: u8 = 0x09;
pub const OP_RHEI_BLK: u8 = 0x0A; pub const OP_RHEI_BLK: u8 = 0x0A;
pub const OP_PROP_NULL: u8 = 0x28;
pub const OP_PROP_ARRAY: u8 = 0x29;
// elements managment // elements managment
pub const OP_ELEM_PUSH: u8 = 0x10; pub const OP_ELEM_PUSH: u8 = 0x10;

View File

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