diff --git a/src/ast.rs b/src/ast.rs index aa23afe..c48d564 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -7,91 +7,91 @@ pub enum NodeId { } #[derive(Debug, Clone, PartialEq)] -pub enum Value { - String(String), +pub enum Value<'a> { + String(&'a str), Int(i64), Float(f64), Bool(bool), - Color(String), - FsPath(String), - Variable(String), - Rhei(String), - Null, - Array(Vec), - // glts - Call(String, Vec), // rgba(30, 30, 46, 0.88) - Unit(f64, String), // 8px, 120ms - Ident(String), + Color(&'a str), + FsPath(&'a str), + Variable(&'a str), + Rhei(&'a str), + Null, + Array(Vec>), + Call(&'a str, Vec>), + Unit(f64, &'a str), + Ident(&'a str), } #[derive(Debug, Clone)] -pub enum Directive { +pub enum Directive<'a> { Version(i64), - Style(String), + Style(&'a str), Global { - name: String, - value: Value, + name: &'a str, + value: Value<'a>, }, Singleton { - name: String, + name: &'a str, prop_span: (u32, u32), }, Component { - name: String, - params: Vec<(String, String)>, + name: &'a str, + params: Vec<(&'a str, &'a str)>, child_span: (u32, u32), }, Let { - name: String, - value: Value, + name: &'a str, + value: Value<'a>, }, If { - condition: Value, + condition: Value<'a>, child_span: (u32, u32), else_span: Option<(u32, u32)>, }, Each { - item: String, - collection: Value, + item: &'a str, + collection: Value<'a>, child_span: (u32, u32), }, On { - event: String, - args: Vec<(String, Value)>, + event: &'a str, + args: Vec<(&'a str, Value<'a>)>, child_span: (u32, u32), }, - RheiBlock(String), + RheiBlock(&'a str), StyleRule { - selector: String, + selector: &'a str, prop_span: (u32, u32), }, StyleAnim { - name: String, + name: &'a str, frames: Vec<(String, (u32, u32))>, }, } #[derive(Debug, Default)] -pub struct ModuleSoA { - pub elem_types: Vec, +pub struct ModuleSoA<'a> { + pub elem_types: Vec<&'a str>, pub elem_prop_spans: Vec<(u32, u32)>, pub elem_child_spans: Vec<(u32, u32)>, - pub elem_content: Vec>, + pub elem_content: Vec>>, - pub prop_keys: Vec, - pub prop_values: Vec, + pub prop_keys: Vec<&'a str>, + pub prop_values: Vec>, - pub directives: Vec, + pub directives: Vec>, pub hierarchy: Vec, } -impl ModuleSoA { +impl<'a> ModuleSoA<'a> { pub fn new() -> Self { Self::default() } - pub fn push_element(&mut self, typ: String) -> u32 { + #[inline] + pub fn push_element(&mut self, typ: &'a str) -> u32 { let id = self.elem_types.len() as u32; self.elem_types.push(typ); self.elem_prop_spans.push((0, 0)); @@ -100,7 +100,8 @@ impl ModuleSoA { id } - pub fn push_directive(&mut self, dir: Directive) -> u32 { + #[inline] + pub fn push_directive(&mut self, dir: Directive<'a>) -> u32 { let id = self.directives.len() as u32; self.directives.push(dir); id diff --git a/src/compiler.rs b/src/compiler.rs index 7740d29..8a1622c 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,13 +1,13 @@ use crate::ast::*; use crate::opcodes::*; -pub struct Compiler<'a> { - module: &'a ModuleSoA, +pub struct Compiler<'a, 'b> { + module: &'b ModuleSoA<'a>, buf: Vec, } -impl<'a> Compiler<'a> { - pub fn new(module: &'a ModuleSoA) -> Self { +impl<'a, 'b> Compiler<'a, 'b> { + pub fn new(module: &'b ModuleSoA<'a>) -> Self { Self { module, buf: Vec::with_capacity(module.hierarchy.len() * 32 + std::mem::size_of_val(&MAGIC_HEADER)), @@ -38,17 +38,17 @@ impl<'a> Compiler<'a> { self.buf.push(OP_END_BLOCK); } -fn compile_element(&mut self, id: u32) { + fn compile_element(&mut self, id: u32) { let idx = id as usize; self.buf.push(OP_ELEM_PUSH); - self.write_string(&self.module.elem_types[idx]); + self.write_string(self.module.elem_types[idx]); let (p_start, p_len) = self.module.elem_prop_spans[idx]; let p_start = p_start as usize; let p_end = p_start + p_len as usize; for i in p_start..p_end { - self.compile_property(&self.module.prop_keys[i], &self.module.prop_values[i]); + self.compile_property(self.module.prop_keys[i], &self.module.prop_values[i]); } if let Some(content) = &self.module.elem_content[idx] { @@ -90,7 +90,7 @@ fn compile_element(&mut self, id: u32) { let start = prop_span.0 as usize; let end = start + prop_span.1 as usize; for i in start..end { - self.write_string(&self.module.prop_keys[i]); + self.write_string(self.module.prop_keys[i]); self.compile_value(&self.module.prop_values[i]); } } @@ -148,7 +148,7 @@ fn compile_element(&mut self, id: u32) { let start = prop_span.0 as usize; let end = start + prop_span.1 as usize; for i in start..end { - self.write_string(&self.module.prop_keys[i]); + self.write_string(self.module.prop_keys[i]); self.compile_value(&self.module.prop_values[i]); } } @@ -162,7 +162,7 @@ fn compile_element(&mut self, id: u32) { let start = span.0 as usize; let end = start + span.1 as usize; for i in start..end { - self.write_string(&self.module.prop_keys[i]); + self.write_string(self.module.prop_keys[i]); self.compile_value(&self.module.prop_values[i]); } } @@ -183,7 +183,6 @@ fn compile_element(&mut self, id: u32) { Value::Rhei(_) => OP_PROP_RHEI, Value::Null => OP_PROP_NULL, Value::Array(_) => OP_PROP_ARRAY, - // glts Value::Call(_, _) => OP_PROP_CALL, Value::Unit(_, _) => OP_PROP_UNIT, Value::Ident(_) => OP_PROP_IDENT, @@ -210,7 +209,6 @@ fn compile_element(&mut self, id: u32) { Value::Rhei(_) => OP_PROP_RHEI, Value::Null => OP_PROP_NULL, Value::Array(_) => OP_PROP_ARRAY, - // glts Value::Call(_, _) => OP_PROP_CALL, Value::Unit(_, _) => OP_PROP_UNIT, Value::Ident(_) => OP_PROP_IDENT, @@ -222,7 +220,7 @@ fn compile_element(&mut self, id: u32) { #[inline] fn compile_value_data(&mut self, val: &Value) { match val { - Value::String(s) | Value::Color(s) | Value::FsPath(s) | Value::Variable(s) | Value::Rhei(s) => { + Value::String(s) | Value::Color(s) | Value::FsPath(s) | Value::Variable(s) | Value::Rhei(s) | Value::Ident(s) => { self.write_string(s); } Value::Int(i) => self.write_i64(*i), @@ -235,7 +233,6 @@ fn compile_element(&mut self, id: u32) { self.compile_value(v); } } - Value::Ident(s) => self.write_string(s), Value::Unit(num, unit) => { self.write_f64(*num); self.write_string(unit); diff --git a/src/lib.rs b/src/lib.rs index c0d2522..fb89ccb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,7 @@ pub use parser::Parser; pub use style_parser::StyleParser; pub fn compile_project(gltm_src: &str, glts_src: &str) -> Result, String> { - let mut module = ModuleSoA::new(); - + let mut module = ModuleSoA::new(); let mut style_parser = StyleParser::new(glts_src, &mut module); style_parser.parse_all()?; diff --git a/src/parser.rs b/src/parser.rs index 6c1389e..1f22fe0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,16 +3,15 @@ use crate::ast::*; pub struct Parser<'a> { src: &'a [u8], pos: usize, - pub module: ModuleSoA, + pub module: ModuleSoA<'a>, } impl<'a> Parser<'a> { - pub fn new(src: &'a str) -> Self { Self::with_module(src, ModuleSoA::new()) } - pub fn with_module(src: &'a str, module: ModuleSoA) -> Self { + pub fn with_module(src: &'a str, module: ModuleSoA<'a>) -> Self { Self { src: src.as_bytes(), pos: 0, @@ -30,9 +29,9 @@ impl<'a> Parser<'a> { self.pos += 1; } - #[inline] + #[inline(always)] fn consume_if(&mut self, expected: u8) -> bool { - if self.peek() == Some(expected) { + if self.src.get(self.pos) == Some(&expected) { self.pos += 1; true } else { @@ -40,6 +39,7 @@ impl<'a> Parser<'a> { } } + #[inline] fn skip_whitespace(&mut self) { let len = self.src.len(); while self.pos < len { @@ -57,27 +57,29 @@ impl<'a> Parser<'a> { } } - fn parse_ident(&mut self) -> String { + #[inline] + fn parse_ident(&mut self) -> &'a str { let start = self.pos; let len = self.src.len(); while self.pos < len { let c = self.src[self.pos]; - if c.is_ascii_alphanumeric() || matches!(c, b'-' | b'_' | b'.') { + if c.is_ascii_alphanumeric() || c == b'-' || c == b'_' || c == b'.' { self.pos += 1; } else { break; } } - unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]).to_string() } + unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]) } } - fn parse_rhei_expr(&mut self) -> Result { + #[inline] + fn parse_rhei_expr(&mut self) -> Result<&'a str, String> { self.skip_whitespace(); if self.consume_if(b'{') { let start = self.pos; let mut depth = 1; let len = self.src.len(); - + while depth > 0 && self.pos < len { match self.src[self.pos] { b'{' => depth += 1, @@ -89,9 +91,9 @@ impl<'a> Parser<'a> { if depth > 0 { return Err("Unexpected EOF in Rhei block".into()); } - + let expr = unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos - 1]) }; - Ok(expr.trim().to_string()) + Ok(expr.trim()) } else { let start = self.pos; let mut parens = 0; @@ -107,7 +109,7 @@ impl<'a> Parser<'a> { self.pos += 1; continue; } - + match c { b'\\' => escape = true, b'"' => in_str = !in_str, @@ -124,14 +126,14 @@ impl<'a> Parser<'a> { self.pos += 1; } let expr = unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]) }; - Ok(expr.trim().to_string()) + Ok(expr.trim()) } } - fn parse_value(&mut self) -> Result { + fn parse_value(&mut self) -> Result, String> { self.skip_whitespace(); let c = self.peek().ok_or("Expected value, found EOF")?; - + match c { b'"' => { self.advance(); @@ -143,7 +145,7 @@ impl<'a> Parser<'a> { if self.pos >= len { return Err("Unexpected EOF inside string literal".into()); } - let val = std::str::from_utf8(&self.src[start..self.pos]).unwrap().to_string(); + let val = unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]) }; self.advance(); Ok(Value::String(val)) } @@ -160,8 +162,11 @@ impl<'a> Parser<'a> { Ok(Value::Array(items)) } b'#' => { + let start = self.pos; self.advance(); - Ok(Value::Color(format!("#{}", self.parse_ident()))) + let _ = self.parse_ident(); + let color_str = unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]) }; + Ok(Value::Color(color_str)) } b'$' => { self.advance(); @@ -183,7 +188,7 @@ impl<'a> Parser<'a> { } _ => { let s = self.parse_ident(); - match s.as_str() { + match s { "true" => Ok(Value::Bool(true)), "false" => Ok(Value::Bool(false)), "null" => Ok(Value::Null), @@ -198,7 +203,7 @@ impl<'a> Parser<'a> { } self.pos += 1; } - let path = unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]) }.to_string(); + let path = unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]) }; Ok(Value::FsPath(path)) } _ => Err(format!("Unknown value token: {}", s)), @@ -213,21 +218,21 @@ impl<'a> Parser<'a> { loop { self.skip_whitespace(); if self.consume_if(b')') { break; } - + let key = self.parse_ident(); if key.is_empty() { return Err(format!("Expected property identifier, found {:?}", self.peek().map(|b| b as char))); } - + self.skip_whitespace(); if !self.consume_if(b'=') { return Err(format!("Expected '=' after property key '{}', found {:?}", key, self.peek().map(|b| b as char))); } - + let val = self.parse_value()?; self.module.prop_keys.push(key); self.module.prop_values.push(val); - + self.skip_whitespace(); self.consume_if(b','); } @@ -257,7 +262,7 @@ impl<'a> Parser<'a> { fn parse_directive(&mut self) -> Result { self.advance(); let name = self.parse_ident(); - let dir = match name.as_str() { + let dir = match name { "version" => { self.skip_whitespace(); Directive::Version(self.parse_ident().parse().map_err(|_| "Invalid version")?) @@ -269,17 +274,17 @@ impl<'a> Parser<'a> { "global" => { self.skip_whitespace(); if !self.consume_if(b'$') { return Err("Expected '$' for variable name in @global".into()); } - let name = self.parse_ident(); + let var_name = self.parse_ident(); self.skip_whitespace(); - if !self.consume_if(b'=') { return Err(format!("Expected '=' after global variable name '${}'", name)); } - Directive::Global { name, value: self.parse_value()? } + if !self.consume_if(b'=') { return Err(format!("Expected '=' after global variable name '${}'", var_name)); } + Directive::Global { name: var_name, value: self.parse_value()? } } "singleton" => { self.skip_whitespace(); - let name = self.parse_ident(); + let s_name = self.parse_ident(); self.skip_whitespace(); let start_idx = self.module.prop_keys.len() as u32; - if !self.consume_if(b'{') { return Err(format!("Expected '{{' for singleton '{}'", name)); } + if !self.consume_if(b'{') { return Err(format!("Expected '{{' for singleton '{}'", s_name)); } loop { self.skip_whitespace(); if self.consume_if(b'}') { break; } @@ -296,55 +301,55 @@ impl<'a> Parser<'a> { self.consume_if(b','); } let len = (self.module.prop_keys.len() as u32) - start_idx; - Directive::Singleton { name, prop_span: (start_idx, len) } + Directive::Singleton { name: s_name, prop_span: (start_idx, len) } } "component" => { self.skip_whitespace(); - let name = self.parse_ident(); + let comp_name = self.parse_ident(); let mut params = Vec::new(); self.skip_whitespace(); if self.consume_if(b'(') { loop { self.skip_whitespace(); if self.consume_if(b')') { break; } - + let pname = self.parse_ident(); if pname.is_empty() { return Err("Expected parameter name in @component".into()); } - + self.skip_whitespace(); if !self.consume_if(b':') { return Err(format!("Expected ':' after parameter '{}'", pname)); } self.skip_whitespace(); let ptype = self.parse_ident(); if ptype.is_empty() { return Err(format!("Expected type after ':' for parameter '{}'", pname)); } params.push((pname, ptype)); - + self.skip_whitespace(); self.consume_if(b','); } } - Directive::Component { name, params, child_span: self.parse_block()? } + Directive::Component { name: comp_name, params, child_span: self.parse_block()? } } "let" => { self.skip_whitespace(); if !self.consume_if(b'$') { return Err("Expected '$' for variable name in @let".into()); } - let name = self.parse_ident(); + let var_name = self.parse_ident(); self.skip_whitespace(); - if !self.consume_if(b'=') { return Err(format!("Expected '=' after variable name '${}'", name)); } - Directive::Let { name, value: self.parse_value()? } + if !self.consume_if(b'=') { return Err(format!("Expected '=' after variable name '${}'", var_name)); } + Directive::Let { name: var_name, value: self.parse_value()? } } "if" => { self.skip_whitespace(); let condition = self.parse_value()?; let child_span = self.parse_block()?; let mut else_span = None; - + self.skip_whitespace(); let backup = self.pos; if self.consume_if(b'@') { if self.parse_ident() == "else" { else_span = Some(self.parse_block()?); } else { - self.pos = backup; // rollback + self.pos = backup; } } Directive::If { condition, child_span, else_span } @@ -383,36 +388,36 @@ impl<'a> Parser<'a> { } fn parse_element(&mut self) -> Result { - let name = self.parse_ident(); - let el_id = self.module.push_element(name); + let name = self.parse_ident(); + let el_id = self.module.push_element(name); - self.skip_whitespace(); - if self.peek() == Some(b'(') { - self.module.elem_prop_spans[el_id as usize] = self.parse_properties()?; - } - - self.skip_whitespace(); - match self.peek() { - Some(b'{') => { - self.module.elem_child_spans[el_id as usize] = self.parse_block()?; + self.skip_whitespace(); + if self.peek() == Some(b'(') { + self.module.elem_prop_spans[el_id as usize] = self.parse_properties()?; } - Some(ch) if matches!(ch, b'"' | b'#' | b'$' | b'!' | b'-' | b'[' | b'0'..=b'9') => { - self.module.elem_content[el_id as usize] = Some(self.parse_value()?); - } - Some(b'a'..=b'z') => { - let backup = self.pos; - let ident = self.parse_ident(); - self.pos = backup; - if matches!(ident.as_str(), "true" | "false" | "null" | "fs") { + self.skip_whitespace(); + match self.peek() { + Some(b'{') => { + self.module.elem_child_spans[el_id as usize] = self.parse_block()?; + } + Some(ch) if matches!(ch, b'"' | b'#' | b'$' | b'!' | b'-' | b'[' | b'0'..=b'9') => { self.module.elem_content[el_id as usize] = Some(self.parse_value()?); } - } - _ => {} - } + Some(b'a'..=b'z') => { + let backup = self.pos; + let ident = self.parse_ident(); + self.pos = backup; - Ok(NodeId::Element(el_id)) -} + if matches!(ident, "true" | "false" | "null" | "fs") { + self.module.elem_content[el_id as usize] = Some(self.parse_value()?); + } + } + _ => {} + } + + Ok(NodeId::Element(el_id)) + } pub fn parse_node(&mut self) -> Result { self.skip_whitespace(); @@ -430,7 +435,7 @@ impl<'a> Parser<'a> { b'A'..=b'Z' => self.parse_element(), ch if matches!(ch, b'"' | b'#' | b'$' | b'-' | b'[' | b'0'..=b'9') => { let val = self.parse_value()?; - let el_id = self.module.push_element("#text".to_string()); + let el_id = self.module.push_element("#text"); self.module.elem_content[el_id as usize] = Some(val); Ok(NodeId::Element(el_id)) } diff --git a/src/style_parser.rs b/src/style_parser.rs index 2d4c3fb..0dde134 100644 --- a/src/style_parser.rs +++ b/src/style_parser.rs @@ -1,16 +1,16 @@ use crate::ast::*; use std::collections::HashMap; -pub struct StyleParser<'a> { +pub struct StyleParser<'a, 'm> { src: &'a [u8], pos: usize, - pub module: &'a mut ModuleSoA, - variables: HashMap, - mixins: HashMap>, + pub module: &'m mut ModuleSoA<'a>, + variables: HashMap>, + mixins: HashMap)>>, } -impl<'a> StyleParser<'a> { - pub fn new(src: &'a str, module: &'a mut ModuleSoA) -> Self { +impl<'a, 'm> StyleParser<'a, 'm> { + pub fn new(src: &'a str, module: &'m mut ModuleSoA<'a>) -> Self { Self { src: src.as_bytes(), pos: 0, @@ -20,14 +20,17 @@ impl<'a> StyleParser<'a> { } } + #[inline(always)] fn peek(&self) -> Option { self.src.get(self.pos).copied() } + #[inline(always)] fn advance(&mut self) { self.pos += 1; } + #[inline(always)] fn consume_if(&mut self, expected: u8) -> bool { if self.peek() == Some(expected) { self.pos += 1; @@ -37,6 +40,7 @@ impl<'a> StyleParser<'a> { } } + #[inline] fn skip_whitespace(&mut self) { let len = self.src.len(); while self.pos < len { @@ -54,44 +58,46 @@ impl<'a> StyleParser<'a> { } } - fn parse_ident(&mut self) -> String { + #[inline] + fn parse_ident(&mut self) -> &'a str { let start = self.pos; while self.pos < self.src.len() { let c = self.src[self.pos]; - if c.is_ascii_alphanumeric() || matches!(c, b'-' | b'_') { + if c.is_ascii_alphanumeric() || c == b'-' || c == b'_' { self.pos += 1; } else { break; } } - unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]).to_string() } + unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]) } } - fn parse_selector(&mut self) -> String { + #[inline] + fn parse_selector(&mut self) -> &'a str { let start = self.pos; while self.pos < self.src.len() && self.src[self.pos] != b'{' { self.pos += 1; } - unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]).trim().to_string() } + unsafe { std::str::from_utf8_unchecked(&self.src[start..self.pos]) }.trim() } - fn parse_single_value(&self, s: &str) -> Value { + fn parse_single_value(&self, s: &'a str) -> Value<'a> { let s = s.trim_end_matches(','); - - if let Some(stripped) = s.strip_prefix('#') { - return Value::Color(format!("#{}", stripped)); + + if let Some(_stripped) = s.strip_prefix('#') { + return Value::Color(s); } if let Some(var_name) = s.strip_prefix('$') { if let Some(val) = self.variables.get(var_name) { return val.clone(); } - return Value::Variable(var_name.to_string()); + return Value::Variable(var_name); } if s.contains('(') && s.ends_with(')') { let parts: Vec<&str> = s.splitn(2, '(').collect(); - let name = parts[0].trim().to_string(); + let name = parts[0].trim(); let args_str = parts[1].trim_end_matches(')'); let args = args_str.split(',') .filter(|a| !a.trim().is_empty()) @@ -109,24 +115,27 @@ impl<'a> StyleParser<'a> { if num_end > 0 && num_end < s.len() { if let Ok(num) = s[..num_end].parse::() { - return Value::Unit(num, s[num_end..].to_string()); + return Value::Unit(num, &s[num_end..]); } } if let Ok(i) = s.parse::() { return Value::Int(i); } if let Ok(f) = s.parse::() { return Value::Float(f); } - if s == "true" { return Value::Bool(true); } - if s == "false" { return Value::Bool(false); } - if s == "null" { return Value::Null; } - - Value::Ident(s.to_string()) + + match s { + "true" => Value::Bool(true), + "false" => Value::Bool(false), + "null" => Value::Null, + _ => Value::Ident(s), + } } - fn parse_value_line(&mut self) -> Value { + fn parse_value_line(&mut self) -> Value<'a> { let start = self.pos; let mut parens = 0; + let len = self.src.len(); - while self.pos < self.src.len() { + while self.pos < len { let c = self.src[self.pos]; if c == b'(' { parens += 1; } else if c == b')' { parens -= 1; } @@ -140,31 +149,43 @@ impl<'a> StyleParser<'a> { if self.peek() == Some(b';') { self.advance(); } let mut tokens = Vec::new(); - let mut current = String::new(); + let mut token_start = None; let mut p = 0; - for c in line.chars() { + + let bytes = line.as_bytes(); + for (i, &c) in bytes.iter().enumerate() { match c { - '(' => { p += 1; current.push(c); } - ')' => { p -= 1; current.push(c); } - ' ' | '\t' if p == 0 => { - if !current.is_empty() { - tokens.push(current.clone()); - current.clear(); + b'(' => { + p += 1; + if token_start.is_none() { token_start = Some(i); } + } + b')' => { + if p > 0 { p -= 1; } + if token_start.is_none() { token_start = Some(i); } + } + b' ' | b'\t' if p == 0 => { + if let Some(s_idx) = token_start { + tokens.push(&line[s_idx..i]); + token_start = None; } } - _ => current.push(c), + _ => { + if token_start.is_none() { token_start = Some(i); } + } } } - if !current.is_empty() { tokens.push(current); } + if let Some(s_idx) = token_start { + tokens.push(&line[s_idx..]); + } if tokens.len() == 1 { - self.parse_single_value(&tokens[0]) + self.parse_single_value(tokens[0]) } else { - Value::Array(tokens.into_iter().map(|t| self.parse_single_value(&t)).collect()) + Value::Array(tokens.into_iter().map(|t| self.parse_single_value(t)).collect()) } } - fn parse_properties(&mut self, target: &mut Vec<(String, Value)>) -> Result<(), String> { + fn parse_properties(&mut self, target: &mut Vec<(&'a str, Value<'a>)>) -> Result<(), String> { self.consume_if(b'{'); loop { self.skip_whitespace(); @@ -176,7 +197,7 @@ impl<'a> StyleParser<'a> { if ident == "use" { self.skip_whitespace(); let mixin_name = self.parse_ident(); - if let Some(props) = self.mixins.get(&mixin_name) { + if let Some(props) = self.mixins.get(mixin_name) { target.extend(props.clone()); } else { return Err(format!("Unknown mixin: {}", mixin_name)); @@ -202,7 +223,7 @@ impl<'a> StyleParser<'a> { match self.peek() { Some(b'$') => { self.advance(); - let name = self.parse_ident(); + let name = self.parse_ident().to_string(); self.skip_whitespace(); self.consume_if(b'='); self.skip_whitespace(); @@ -213,10 +234,10 @@ impl<'a> StyleParser<'a> { Some(b'@') => { self.advance(); let name = self.parse_ident(); - match name.as_str() { + match name { "mixin" => { self.skip_whitespace(); - let mixin_name = self.parse_ident(); + let mixin_name = self.parse_ident().to_string(); self.skip_whitespace(); let mut props = Vec::new(); self.parse_properties(&mut props)?; @@ -234,7 +255,7 @@ impl<'a> StyleParser<'a> { self.skip_whitespace(); if self.consume_if(b'}') { break; } - let step = self.parse_selector(); + let step = self.parse_selector().to_string(); let mut props = Vec::new(); self.parse_properties(&mut props)?; @@ -267,8 +288,7 @@ impl<'a> StyleParser<'a> { } let len = self.module.hierarchy.len() as u32 - start_idx; - // Condition evaluates at runtime via Rhei - let condition = Value::Rhei(cond_str.to_string()); + let condition = Value::Rhei(cond_str); let id = self.module.push_directive(Directive::If { condition, child_span: (start_idx, len),