fix: parse & compile, add null & array support
This commit is contained in:
@@ -16,6 +16,8 @@ pub enum Value {
|
||||
FsPath(String),
|
||||
Variable(String),
|
||||
Rhei(String),
|
||||
Null,
|
||||
Array(Vec<Value>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
112
src/parser.rs
112
src/parser.rs
@@ -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()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user