fix: parse & compile, add null & array support
This commit is contained in:
@@ -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)]
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
118
src/parser.rs
118
src/parser.rs
@@ -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()
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user