From 062048ef6e3e060bcf841ea2ec92e026f09d8da0 Mon Sep 17 00:00:00 2001 From: eichlan Date: Tue, 23 Jun 2026 15:31:51 -0700 Subject: Roorganized into workspace and packages. This is to accomidate a new proc_macro package, which can't have anything else in it. --- src/context.rs | 289 --------------------- src/error.rs | 59 ----- src/lexer.rs | 368 -------------------------- src/lib.rs | 544 --------------------------------------- src/parser.rs | 785 -------------------------------------------------------- src/position.rs | 96 ------- 6 files changed, 2141 deletions(-) delete mode 100644 src/context.rs delete mode 100644 src/error.rs delete mode 100644 src/lexer.rs delete mode 100644 src/lib.rs delete mode 100644 src/parser.rs delete mode 100644 src/position.rs (limited to 'src') diff --git a/src/context.rs b/src/context.rs deleted file mode 100644 index 6f8e04f..0000000 --- a/src/context.rs +++ /dev/null @@ -1,289 +0,0 @@ -use std::collections::HashMap; -use std::fmt; - -use crate::error::CrimResult; - -use crate::{Identifier,IdentifierValue}; - -pub type Context = HashMap; - -pub trait MappedStructure<'a> { - fn get_value(&'a self, id: &str) -> Option>; -} - -impl<'a> MappedStructure<'a> for Context { - fn get_value(&'a self, id: &str) -> Option> { - self.get(id).map(|v|Into::>::into(v)) - } -} - -pub trait MappedList<'a> { - fn get_index(&'a self, id: usize) -> Option>; - fn for_each(&'a self, func: &dyn Fn(&MappedValue<'a>, &mut String) -> CrimResult<()>) -> CrimResult; -} - -impl<'a, T: MappedStructure<'a>> MappedList<'a> for Vec { - fn get_index(&'a self, id: usize) -> Option> { - if let Some(x) = self.get(id) { - Some(MappedValue::<'a>::Struct(x)) - } else { - None - } - } - - fn for_each(&'a self, func: &dyn Fn(&MappedValue<'a>, &mut String) -> CrimResult<()>) -> CrimResult { - let mut buf = String::new(); - for i in self { - func( &MappedValue::Struct(i), &mut buf )?; - } - Ok(buf) - } -} - -impl<'a> MappedList<'a> for Vec { - fn get_index(&'a self, id: usize) -> Option> { - if let Some(x) = self.get(id) { - Some(x.into()) - } else { - None - } - } - - fn for_each(&'a self, func: &dyn Fn(&MappedValue<'a>, &mut String) -> CrimResult<()>) -> CrimResult { - let mut buf = String::new(); - for i in self { - func( &Into::>::into( i ), &mut buf )?; - } - Ok(buf) - } -} - -impl<'a> MappedList<'a> for Vec> { - fn get_index(&'a self, id: usize) -> Option> { - if let Some(x) = self.get(id) { - Some(*x) - } else { - None - } - } - - fn for_each(&'a self, func: &dyn Fn(&MappedValue<'a>, &mut String) -> CrimResult<()>) -> CrimResult { - let mut buf = String::new(); - for i in self { - func( i, &mut buf )?; - } - Ok(buf) - } -} - -pub type MappedHash<'a> = HashMap>; - -impl<'a> MappedStructure<'a> for MappedHash<'a> { - fn get_value(&'a self, id: &str) -> Option> { - self.get(id).copied() - } -} - -#[derive(Copy,Clone)] -pub enum MappedValue<'a> { - Struct(&'a dyn MappedStructure<'a>), - List(&'a dyn MappedList<'a>), - - Str(&'a str), - String(&'a String), - Int8(i8), - Int16(i16), - Int32(i32), - Int64(i64), - UInt8(u8), - UInt16(u16), - UInt32(u32), - UInt64(u64), - Float32(f32), - Float64(f64), - Bool(bool), -} - -impl<'a> fmt::Debug for MappedValue<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - MappedValue::<'a>::Struct(_) => write!(f, "MappedValue"), - MappedValue::<'a>::List(_) => write!(f, "MappedList"), - MappedValue::<'a>::Str(v) => write!(f, "{:?}", v), - MappedValue::<'a>::String(v) => write!(f, "{:?}", v), - MappedValue::<'a>::Int8(v) => write!(f, "{:?}", v), - MappedValue::<'a>::Int16(v) => write!(f, "{:?}", v), - MappedValue::<'a>::Int32(v) => write!(f, "{:?}", v), - MappedValue::<'a>::Int64(v) => write!(f, "{:?}", v), - MappedValue::<'a>::UInt8(v) => write!(f, "{:?}", v), - MappedValue::<'a>::UInt16(v) => write!(f, "{:?}", v), - MappedValue::<'a>::UInt32(v) => write!(f, "{:?}", v), - MappedValue::<'a>::UInt64(v) => write!(f, "{:?}", v), - MappedValue::<'a>::Float32(v) => write!(f, "{:?}", v), - MappedValue::<'a>::Float64(v) => write!(f, "{:?}", v), - MappedValue::<'a>::Bool(v) => write!(f, "{:?}", v), - } - } - -} - -pub struct StatefulContext<'a> { - root: &'a dyn MappedStructure<'a>, - local: &'a dyn MappedStructure<'a>, -} - -impl<'a> StatefulContext<'a> { - pub fn root( root: &'a dyn MappedStructure<'a> ) -> Self { - Self { - root, local: root, - } - } - - pub fn local(&self, local: &'a dyn MappedStructure<'a> ) -> Self { - Self { - root: self.root, - local - } - } - - pub fn full( root: &'a dyn MappedStructure<'a>, local: &'a dyn MappedStructure<'a> ) -> Self { - Self { - root, local - } - } - - pub fn get_value(&self, id: &[IdentifierValue]) -> Option> { - let mut value : Option = - if id[0] == IdentifierValue::Root { - Some(MappedValue::Struct(self.root)) - } else { - Some(MappedValue::Struct(self.local)) - }; - for idpart in id { - match idpart { - IdentifierValue::Root => { - continue; - } - IdentifierValue::Name(name) => { - if let Some(MappedValue::Struct(s)) = value && - let Some(nv) = s.get_value( &name ) { - value.replace( nv ); - } else { - return None; - } - } - IdentifierValue::Index(_) => { - return None; - } - } - } - value - } -} - -impl<'a> From<&'a Value> for MappedValue<'a> { - fn from(value: &'a Value) -> Self { - match value { - Value::Dictionary(ctx) => MappedValue::Struct(ctx), - Value::List(list) => MappedValue::List(list), - Value::String(s) => MappedValue::String(s), - Value::Int(i) => MappedValue::Int64(*i), - Value::Float(f) => MappedValue::Float64(*f), - Value::Bool(b) => MappedValue::Bool(*b), - Value::Identifier(_identifier) => panic!("Identifier!?"), - } - } -} - -#[derive(Debug,Clone)] -pub enum Value { - Dictionary(Context), - List(Vec), - String(String), - Int(i64), - Float(f64), - Bool(bool), - Identifier(Identifier), -} - -impl From<&str> for Value { - fn from(val: &str ) -> Value { - Value::String(val.into()) - } -} - -impl From for Value { - fn from(val: String) -> Value { - Value::String(val) - } -} - -impl From for Value { - fn from(val: i64) -> Value { - Value::Int(val) - } -} - -impl From for Value { - fn from(val: f64) -> Value { - Value::Float(val) - } -} - -impl From for Value { - fn from(val: bool) -> Value { - Value::Bool(val) - } -} - -impl From> for Value { - fn from(val: Vec) -> Value { - Value::List(val) - } -} - -impl From<&[Value]> for Value { - fn from(val: &[Value]) -> Value { - Value::List(Vec::from(val)) - } -} - -impl From for Value { - fn from(val: Context) -> Value { - Value::Dictionary(val) - } -} - -impl From<[(String,Value); N]> for Value { - fn from(val: [(String,Value); N]) -> Value { - Value::Dictionary(HashMap::from(val)) - } -} - -/* -impl MappedStructure for Value { - fn get_value(&self, id: &Identifier) -> Option { - if id.len() == 0 { - return Some(self); - } - match Value { - Value::Dictionary(dict) => { - } - } - let mut cur_val : Option<&Value> = Some(&self); - - for idx in 0..id.len() { - if let IdentifierValue::Name(s) = &id[idx] && - let Some(Value::Dictionary(d)) = &cur_val { - cur_val.replace( if let Some(v) = d.get(s) { - v - } else { - return None; - }); - } - } - - return cur_val.cloned(); - } -} -*/ diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index bbb5d6f..0000000 --- a/src/error.rs +++ /dev/null @@ -1,59 +0,0 @@ - -use core::error::Error; -use std::fmt; - -use crate::position::*; - -#[derive(Debug)] -pub struct CrimError { - position: Position, - what: String, -} - -impl CrimError { - pub fn parse( position: Position, what: String ) -> Self { - Self { - position, - what, - } - } - - pub fn other(what: String) -> Self { - Self { - position: Position::none(), - what, - } - } - - pub fn broken( position: Position, what: &str ) -> Self { - Self { - position, - what: format!("Broken parser? {}", what), - } - } - - pub fn eos( context: &str ) -> Self { - Self { - position: Position::none(), - what: format!("Premature end of stream while looking for {}", context), - } - } - - pub fn what(&self) -> &str { - &self.what - } - - pub fn start(&self) -> &Position { - &self.position - } -} - -impl fmt::Display for CrimError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Error parsing input: {}", "yeah") - } -} - -impl Error for CrimError { } - -pub type CrimResult = Result; diff --git a/src/lexer.rs b/src/lexer.rs deleted file mode 100644 index d4d39e9..0000000 --- a/src/lexer.rs +++ /dev/null @@ -1,368 +0,0 @@ -use std::iter::Iterator; -//use core::error::Error; -use std::str::CharIndices; -use std::fmt; - -use crate::Position; - -#[derive(Debug,Copy,Clone,PartialEq,Eq)] -pub enum ErrorType { - UnexpectedChar(char), -} - -#[derive(Debug,Copy,Clone,PartialEq,Eq)] -pub enum SymbolType<'a> { - StartFlat, - StartPoint, - EndFlat, - EndPoint, - - View, - Output, - Show, - Loop, - If, - ElIf, - Else, - - Sharp, - Equals, - Period, - Token(&'a str), - Literal(&'a str), - Text(&'a str), - Error{ what: ErrorType }, -} - -#[derive(Copy,Clone)] -pub struct Symbol<'a> { - symbol: SymbolType<'a>, - start: Position, - end: Position, -} - -impl<'a> fmt::Debug for Symbol<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?} @ {:?}-{:?}", self.symbol, self.start, self.end ) - } -} - -impl<'a> Symbol<'a> { - pub fn new( symbol: SymbolType<'a>, start: Position, end: Position ) -> Self { - Self { - symbol, start, end, - } - } - - pub fn check_type bool>(&self, f: T ) -> bool { - f( &self.symbol ) - } - - pub fn symbol(&self) -> &SymbolType<'a> { - &self.symbol - } - - pub fn start(&self) -> &Position { - &self.start - } - - /* - pub fn end(&self) -> &Position { - &self.end - } - */ -} - -#[derive(Debug)] -enum Mode { - Text, - InTag, -} - -pub struct Lexer<'a> { - data: &'a str, - chars: CharIndices<'a>, - cur: [Option<(usize, char)>;2], - icur: usize, - mode: Mode, - pos: Position, -} - -fn is_valid_token_char( c: char, first: bool ) -> bool { - if c.is_whitespace() { - return false; - } - if first { - match c { - 'a'..'z' | 'A'..'Z' | '_' => true, - _ => false - } - } else { - match c { - 'a'..'z' | 'A'..'Z' | '0'..'9' | '_' => true, - _ => false - } - } -} - -impl<'a> Lexer<'a> { - pub fn new( data: &'a str ) -> Lexer<'a> { - let mut chars = data.char_indices(); - let cur = chars.next(); - let cur2 = chars.next(); - //println!(" - cur: {:?}, peek: {:?}", cur, cur2 ); - Lexer { - data, - chars, - cur: [cur, cur2], - icur: 0, - mode: Mode::Text, - pos: Position::new(1,1), - } - } - - fn next(&mut self) -> Option { - self.cur[self.icur] = self.chars.next(); - self.icur = (self.icur+1)%2; - //println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() ); - if let Some((_,chr)) = self.cur[self.icur] { - if chr == '\n' { - self.pos.column = 1; - self.pos.line += 1; - } else { - self.pos.column += 1; - } - Some(chr) - } else { - None - } - } - - fn cur(&self) -> Option { - if let Some((_, chr)) = self.cur[self.icur] { - Some(chr) - } else { - None - } - } - - fn cur_index(&self) -> usize { - if let Some((idx, _)) = self.cur[self.icur] { - idx - } else { - self.chars.offset() - } - } - - fn peek(&self) -> Option { - if let Some((_,chr)) = self.cur[(self.icur+1)%2] { - Some(chr) - } else { - None - } - } - - fn peek_index(&self) -> usize { - if let Some((idx, _)) = self.cur[(self.icur+1)%2] { - idx - } else { - self.chars.offset() - } - } - - fn error( &self, what: ErrorType ) -> Option> { - Some(Symbol::new( SymbolType::Error { - what: what - }, self.pos, self.pos )) - } - - fn skip_ws( &mut self ) { - while self.cur().is_some_and(|x|x.is_whitespace()) { - self.next(); - } - } - - fn next_symbol(&mut self) -> Option> { - // If we hit the end then we're already done. - if self.cur().is_none() { - return None; - } - - match self.mode { - Mode::Text => { - if let Some(sym) = self.parse_start_tag() { - Some(sym) - } else { - self.parse_text() - } - } - Mode::InTag => { - if let Some(sym) = self.parse_end_tag() { - Some(sym) - } else { - self.parse_token() - } - } - } - } - - fn parse_text(&mut self) -> Option> { - let start = self.cur_index(); - let start_pos = self.pos.clone(); - while self.next().is_some() && !self.is_start_tag() { } - let end = self.cur_index(); - let end_pos = self.pos.clone(); - let s = &self.data[start..end]; - //println!(" text: >>>{}<<<", s); - if start == end { - None - } else { - Some(Symbol::new( SymbolType::Text(s), start_pos, end_pos )) - } - } - - fn parse_token(&mut self) -> Option> { - self.skip_ws(); - match self.cur() { - Some('"') => { - return self.parse_literal_str(); - } - Some('#') => { - self.next(); - return Some(Symbol::new(SymbolType::Sharp, self.pos, self.pos)); - } - Some('=') => { - self.next(); - return Some(Symbol::new(SymbolType::Equals, self.pos, self.pos)); - } - Some('.') => { - self.next(); - return Some(Symbol::new(SymbolType::Period, self.pos, self.pos)); - } - _ => {} - } - let start = self.cur_index(); - let start_pos = self.pos.clone(); - - let mut first = true; - while self.next().is_some_and(|ch| is_valid_token_char(ch, first) ) && - !self.is_end_tag() { first = false; } - let end = self.cur_index(); - let end_pos = self.pos.clone(); - let s = &self.data[start..end]; - if start == end { - None - } else { - Some(Symbol::new( match s { - "view" => SymbolType::View, - "show" => SymbolType::Show, - "output" => SymbolType::Output, - "loop" => SymbolType::Loop, - "if" => SymbolType::If, - "elif" => SymbolType::ElIf, - "else" => SymbolType::Else, - _ => SymbolType::Token(s) - }, start_pos, end_pos )) - } - } - - fn parse_literal_str(&mut self) -> Option> { - if let Some(chr) = self.cur() && chr != '"' { - return self.error( ErrorType::UnexpectedChar(chr) ); - } - let start = self.peek_index(); - let start_pos = self.pos.clone(); - while self.next().is_some_and(|chr| chr != '"') { } - let end = self.cur_index(); - let end_pos = self.pos.clone(); - self.next(); - let s = &self.data[start..end]; - Some(Symbol::new(SymbolType::Literal(s), start_pos, end_pos )) - } - - fn is_start_tag(&mut self) -> bool { - if let Some(cur) = self.cur() && (cur == '[' || cur == '<') && - let Some(peek) = self.peek() && peek == '|' { - true - } else { - false - } - } - - fn parse_start_tag(&mut self) -> Option> { - let start_pos = self.pos.clone(); - match self.cur() { - Some('[') => { - if let Some(p) = self.peek() && p == '|' { - self.next(); - let end_pos = self.pos.clone(); - self.next(); - self.mode = Mode::InTag; - Some(Symbol::new( SymbolType::StartFlat, start_pos, end_pos ) ) - } else { - None - } - } - Some('<') => { - if let Some(p) = self.peek() && p == '|' { - self.next(); - let end_pos = self.pos.clone(); - self.next(); - self.mode = Mode::InTag; - Some(Symbol::new(SymbolType::StartPoint, start_pos, end_pos ) ) - } else { - None - } - } - _ => { - None - } - } - } - - fn is_end_tag(&mut self) -> bool { - if let Some(cur) = self.cur() && cur == '|' && - let Some(peek) = self.peek() && (peek == '>' || peek == ']') { - true - } else { - false - } - } - - fn parse_end_tag(&mut self) -> Option> { - self.skip_ws(); - let start_pos = self.pos.clone(); - if let Some(chr) = self.cur() && chr == '|' { - match self.peek() { - Some(']') => { - self.next(); - let end_pos = self.pos.clone(); - self.next(); - self.mode = Mode::Text; - Some(Symbol::new( SymbolType::EndFlat, start_pos, end_pos)) - } - Some('>') => { - self.next(); - let end_pos = self.pos.clone(); - self.next(); - self.mode = Mode::Text; - Some(Symbol::new( SymbolType::EndPoint, start_pos, end_pos)) - } - _ => { - None - } - } - } else { - None - } - } -} - -impl<'a> Iterator for Lexer<'a> { - type Item = Symbol<'a>; - - fn next(&mut self) -> Option { - self.next_symbol() - } -} - diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index e8f9e9f..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,544 +0,0 @@ -use std::path::{PathBuf,Path}; -use std::collections::HashMap; -use core::error::Error; -use std::time::SystemTime; - -mod lexer; -mod parser; -mod position; -mod error; -pub mod context; - -pub use error::{CrimError,CrimResult}; -pub use position::*; -pub use context::{Context,Value,MappedStructure,StatefulContext,MappedValue,MappedList,MappedHash}; - -#[derive(Debug)] -pub struct Crimtag { - views: HashMap, - sources: Vec, -} - -#[derive(PartialEq,Eq,Debug,Clone)] -pub enum ViewSource { - File { - path: PathBuf, - loaded: SystemTime, - }, - Static, - External { - key: String, - }, -} - -#[derive(Debug)] -struct View { - name: String, - source: usize, - outputs: Vec, -// theme: Option - layout: Option, -} - -impl View { - fn process(&self, input: &dyn for<'a> MappedStructure<'a>) -> CrimResult { - let mut out_vars = Context::new(); - for output in &self.outputs { - let mut buf = String::new(); - let sc = StatefulContext::root( input ); - exec( &sc, &output.code, &mut buf )?; - out_vars.insert( output.name.clone(), Value::String(buf) ); - } - Ok(out_vars) - } -} - -#[derive(Debug)] -struct Output { - name: String, - code: Vec, -} - -type Properties = HashMap; - -#[derive(Debug)] -enum Token { - Loop(Identifier, Vec), - Show(Identifier,Properties), - If(Identifier, Vec, Vec), - Text(String), -} - -#[derive(Debug,Clone,PartialEq)] -pub enum IdentifierValue { - Root, - Name(String), - Index(usize), -} - -pub type Identifier = Vec; - -impl Crimtag { - pub fn new() -> Self { - Self { - views: HashMap::new(), - sources: vec![ViewSource::Static], - } - } - - fn id_source(&mut self, src: &ViewSource ) -> usize { - for idx in 0..self.sources.len() { - if self.sources[idx] == *src { - return idx; - } - } - - // Nothing found, add a new one - let idx = self.sources.len(); - self.sources.push( src.clone() ); - idx - } - - pub fn load_file(&mut self, path: &Path) -> Result<(),Box::> { - let time = if let Ok(meta) = path.metadata() { - if let Ok(t) = meta.modified() { - t - } else if let Ok(t) = meta.created() { - t - } else { - SystemTime::now() - } - } else { - SystemTime::now() - }; - let source_id = self.id_source( - &ViewSource::File { - path: path.to_path_buf(), - loaded: time, - } - ); - let p = parser::Parser::new(); - self.register_views( - p.parse( - &String::from_utf8( std::fs::read( path )? )?, - source_id - )?, - )?; - Ok(()) - } - - pub fn load_static(&mut self, data: &str) -> CrimResult<()> { - let source_id = self.id_source( &ViewSource::Static ); - let p = parser::Parser::new(); - self.register_views( p.parse( &data, source_id )? ) - } - - pub fn load_external(&mut self, key: &str, data: &str) -> CrimResult<()> { - let source_id = self.id_source( - &ViewSource::External{ key: key.to_string()} - ); - - let p = parser::Parser::new(); - self.register_views( - p.parse( &data, source_id )?, - ) - } - - fn register_views(&mut self, views: Vec) -> CrimResult<()> { - for view in views { - self.views.insert( view.name.clone(), view ); - } - Ok(()) - } - - pub fn get_view_source(&self, view: &str) -> Option { - if let Some(view) = self.views.get(view) { - if view.source < self.sources.len() { - Some(self.sources[view.source].clone()) - } else { - None - } - } else { - None - } - } - - pub fn render_partial(&self, view: &str, input: &dyn for<'a> MappedStructure<'a> ) -> CrimResult { - if let Some(view) = self.views.get(view) { - return view.process( input ) - } else { - return Err(CrimError::other("No such view found".into())); - } - } - - pub fn render(&self, view: &str, input: &dyn for<'a> MappedStructure<'a> ) -> CrimResult { - if let Some(view) = self.views.get( view ) { - //println!("::Token tree::\n{:?}", view ); - let mut output = view.process( input )?; - if let Some(l) = &view.layout { - self.render( l, &output ) - } else { - if let Some(content) = output.remove("content") && - let Value::String(s) = content { - - Ok(s) - } else { - Err(CrimError::other("No content found in root layout.".into())) - } - } - - } else { - Err(CrimError::other("No such view found".into())) - } - } -} - -fn exec<'a>(input: &StatefulContext<'a>, tokens: &Vec, buf: &mut String) -> CrimResult<()> { - for token in tokens { - match token { - Token::Loop(ident,code) => { - //println!("!!! Loop over: {:?} {:?}", ident, input.get_value( &ident )); - if let Some(value) = input.get_value( &ident ) && - let MappedValue::List(list) = value { - let output = - &list.for_each(&|rec: &MappedValue, buf: &mut String| { - if let MappedValue::Struct(d) = rec { - exec( &input.local(*d), code, buf )?; - } - Ok(()) - })?; - //println!("!!! Output from loop: {}", output ); - buf.push_str( &output ); - } - } - Token::If(ident,code,other) => { - if let Some(value) = input.get_value( &ident ) && - let MappedValue::Bool(b) = value && b { - exec(input, code, buf)? - } else { - exec(input, other, buf)? - } - } - Token::Show(ident,p) => { - let format = if let Some(s) = p.get("format") { - s - } else { - &"".to_string() - }; - if let Some(value) = input.get_value( &ident ) { - match value { - MappedValue::Struct(_) => { - buf.push_str("Struct"); - } - MappedValue::List(_) => { - buf.push_str("List"); - } - MappedValue::Bool(b) => { - if b { - buf.push_str("true"); - } else { - buf.push_str("false"); - } - } - MappedValue::Str(s) => { - buf.push_str(s); - } - MappedValue::String(s) => { - buf.push_str(s.as_str()); - } - MappedValue::Int8(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::Int16(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::Int32(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::Int64(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::UInt8(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::UInt16(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::UInt32(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::UInt64(i) => { - buf.push_str(&i.to_string()); - } - MappedValue::Float32(f) => { - if format == "" { - buf.push_str(&f.to_string()); - } else { - buf.push_str(&f.to_string()); - } - } - MappedValue::Float64(f) => { - if format == "" { - buf.push_str(&f.to_string()); - } else { - buf.push_str(&f.to_string()); - } - } - } - } - } - Token::Text(s) => { - buf.push_str( s ); - } - //_ => {} - } - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::lexer::*; - - #[test] - fn lexing() { - let data = r#"Leading comment: [|view "basic" something="yeup"|>Hello there <|view|] Trailing text"#; - let ll = lexer::Lexer::new( &data ); - for sym in ll { - if let SymbolType::Error{what} = sym.symbol() { - println!("Error {}:{}: {:?}", sym.start().line, sym.start().column, what ); - break; - } else { - println!("Symbol: {:?}", sym ); - } - } - } - - #[test] - fn parsing() { - let mut ct = Crimtag::new(); - if let Err(e) = ct.load_static(r#"Here is a sample -[|view "index" theme="standard"|>Hi<|view|] -That was fun! now a placeholder: [|view "placeholder"|] and now one with an implicit [|view "simple"|>Body -> [|show content|] <- and end<|view|] aoeu - -[|view "person" layout="simple"|>Welcome [|show person.last_name|], [|show person.first_name|]! It's lovely to see you again.<|view|] - -Now with explicit outputs: -[|view "complex"|> - [|output "content"|> - Here's a [|show name|]. - <|output|] - [|output "sidebar"|> - What's up world? - <|output|] - [|output "footer"|> - Same, I guess! - <|output|] -<|view|]"#) { - println!("Error: {:?}", e ); - } else { - println!("It finished"); - if let Ok(v) = ct.render( - "person", - &Context::from([ - ("hi".to_string(),Value::String("hi".to_string())), - ("person".to_string(),Value::Dictionary(HashMap::from([ - ("first_name".to_string(), Value::String("Bob".to_string())), - ("last_name".to_string(), Value::String("Smith".to_string())), - ]))), - ]) - ) { - println!("View result: {:?}", v ); - } else { - println!("Error?"); - } - } - } - - #[test] - fn loops() -> Result<(),CrimError> { - let mut ct = Crimtag::new(); - ct.load_static(r#"Looping code: -[|view "index"|>We will enumerate people here:[|loop people|> - - [|show last_name|], [|show first_name|][|if show_title |>: [|show title|] <|else|> **title redacted** <|if|] ::>[|loop tags|> [|show tag|]<|loop|] <:: <|loop|] -<|view|] -"#)?; - - let ctx = Context::from([ - ("people".to_string(), vec![ - [ - ("first_name".into(), "Joe".into()), - ("last_name".into(), "Smith".into()), - ("show_title".into(), true.into()), - ("title".into(), "CEO".into()), - ("tags".into(), vec![ - Context::from([("tag".into(), "jerk".into()),]).into(), - Context::from([("tag".into(), "ugly".into()),]).into(), - ].into()), - ].into(), - [ - ("first_name".into(), "Chris".into()), - ("last_name".into(), "Perkens".into()), - ("show_title".into(), false.into()), - ("title".into(), "Baconeer".into()), - ("tags".into(), vec![ - Context::from([("tag".into(), "jerk".into()),]).into(), - Context::from([("tag".into(), "ugly".into()),]).into(), - ].into()), - ].into(), - [ - ("first_name".into(), "Will".into()), - ("last_name".into(), "Power".into()), - ("show_title".into(), false.into()), - ("title".into(), "CFO".into()), - ("tags".into(), vec![ - Context::from([("tag".into(), "jerk".into()),]).into(), - Context::from([("tag".into(), "ugly".into()),]).into(), - ].into()), - ].into(), - [ - ("first_name".into(), "Justin".into()), - ("last_name".into(), "Time".into()), - ("show_title".into(), true.into()), - ("title".into(), "CIO".into()), - ("tags".into(), vec![ - Context::from([("tag".into(), "jerk".into()),]).into(), - Context::from([("tag".into(), "ugly".into()),]).into(), - ].into()), - ].into(), - ].into()), - ]); - - println!("View result: {}", ct.render("index", &ctx)? ); - - Ok(()) - } - - #[test] - fn conditional() -> Result<(),CrimError> { - let mut ct = Crimtag::new(); - ct.load_static(r#"Hi there -[|view "index"|> - Color: [|if is_red|> red <|elif is_blue |> blue <|else|> green <|if|] -<|view|]"#)?; - - let ctx = Context::from([ - ("is_red".into(), false.into()), - ("is_blue".into(), false.into()), - ("color".into(), "purple".into()), - ]); - - println!("View result: {}", ct.render("index", &ctx)? ); - - Ok(()) - } - - struct Item { - pub id: i32, - pub title: String, - pub body: String, - } - - struct Person { - pub id: i32, - pub username: String, - pub name: String, - } - - struct Page { - pub user: Person, - pub total_items: i32, - pub total_pages: i32, - pub cur_page: i32, - pub items: Vec, - } - - impl Page { - fn new() -> Page { - Page { - user: Person { - id: 443, - username: "eichlan".into(), - name: "Mike".into(), - }, - total_items: 4000, - total_pages: 400, - cur_page: 35, - items: vec![ - Item{ - id: 123, - title: "hi".into(), - body: "body".into(), - }, - Item{ - id: 124, - title: "bye".into(), - body: "wooo".into(), - }, - Item{ - id: 125, - title: "ciao".into(), - body: "whatever".into(), - } - ], - } - } - } - - impl<'a> MappedStructure<'a> for Page { - fn get_value(&'a self, id: &str) -> Option> { - match id { - "user" => Some(MappedValue::Struct(&self.user)), - "total_items" => Some(MappedValue::Int32(self.total_items)), - "total_pages" => Some(MappedValue::Int32(self.total_pages)), - "cur_page" => Some(MappedValue::Int32(self.cur_page)), - "items" => Some(MappedValue::<'a>::List(&self.items)), - _ => None, - } - } - } - - impl<'a> MappedStructure<'a> for Person { - fn get_value(&'a self, id: &str) -> Option> { - match id { - "id" => Some(MappedValue::Int32(self.id)), - "username" => Some(MappedValue::String(&self.username)), - "name" => Some(MappedValue::String(&self.name)), - _ => None, - } - } - } - - impl<'a> MappedStructure<'a> for Item { - fn get_value(&'a self, id: &str) -> Option> { - match id { - "id" => Some(MappedValue::Int32(self.id)), - "title" => Some(MappedValue::String(&self.title)), - "body" => Some(MappedValue::String(&self.body)), - _ => None, - } - } - } - - #[test] - fn custom() -> Result<(), CrimError> { - let page = Page::new(); - - println!("{:?}", - page.get_value(&"total_pages") - ); - - if let Some(MappedValue::List(l)) = page.get_value(&"items") { - l.for_each(&|x: &MappedValue, buf: &mut String| { - if let MappedValue::Struct(s) = x { - println!(" - {:?}", s.get_value(&"title")); - buf.push_str(&format!("{:?}",s.get_value(&"title"))); - } - Ok(()) - }).expect("loop"); - } - Ok(()) - } -} diff --git a/src/parser.rs b/src/parser.rs deleted file mode 100644 index 4f7ff6a..0000000 --- a/src/parser.rs +++ /dev/null @@ -1,785 +0,0 @@ -use crate::lexer::*; -use crate::*; - -struct Context<'a> { - cur: [Option>;2], - icur: usize, - ll: Lexer<'a>, - source: usize, -} - -impl<'a> Context<'a> { - pub fn new( mut ll: Lexer<'a>, source: usize ) -> Self { - let cur = [ll.next(), ll.next()]; - //println!(" - cur: {:?}, peek: {:?}", cur[0], cur[1] ); - Self { - cur, - icur: 0, - ll, - source, - } - } - - pub fn next(&mut self) -> Option> { - self.cur[self.icur] = self.ll.next(); - self.icur = (self.icur+1)%2; - //println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() ); - self.cur[self.icur] - } - - pub fn cur(&self) -> Option> { - self.cur[self.icur] - } - - pub fn peek(&self) -> Option> { - self.cur[(self.icur+1)%2] - } - - /* - pub fn check_unwrapped bool>(&self, context: &str, f: T) -> CrimResult { - if self.cur().is_none() || self.peek().is_none() { - Err(CrimError::eos( context )) - } else { - Ok(f( self.cur().unwrap().symbol(), self.peek().unwrap().symbol())) - } - } - */ - - pub fn source(&self) -> usize { - self.source - } -} - -trait SymbolHelper { - fn is bool>(&self, context: &str, f: T ) -> CrimResult; - #[allow(dead_code)] - fn is_valid_tag_name(&self) -> CrimResult; -} - -impl<'a> SymbolHelper for Option> { - fn is bool>(&self, context: &str, f: T ) -> CrimResult { - if let Some(st) = self { - Ok(f( &st.symbol() )) - } else { - Err(CrimError::eos( context )) - } - } - - fn is_valid_tag_name(&self) -> CrimResult { - if let Some(st) = self { - Ok(match st.symbol() { - SymbolType::Token(_) | - SymbolType::View | - SymbolType::Output | - SymbolType::Loop | - SymbolType::If | - SymbolType::ElIf | - SymbolType::Else | - SymbolType::Show => true, - _ => false - }) - } else { - Err(CrimError::parse( - Position::none(), - "Unexpeceted end of stream.".to_string() - )) - } - } -} - -pub struct Parser { -} - -/** - * input: input complete_tag - * | input text - * | - * ; - * - * tag: unary_tag - * | multinary_open_tag - * | multinary_mid_tag - * | multinary_close_tag - * ; - * - * unary_tag: '[|' tag_guts '|]' - * ; - * - * | '[|' tag_guts '|>' - * | '<|' tag_guts '|>' - * | '<|' tag_guts '|]' - * ; - * - * tag_pair: open_tag tag_body close_tag - * ; - * - * tag_body: tag_body tag - * | tag_body tag_pair - * | tag_body text - * | - * ; - * - * open_tag: '[|' tag_guts '|>' - * ; - * - * close_tag: '<|' token '|]' - * ; - * - * tag_guts: 'view' literal props - * | 'section' literal - * | 'output' literal - * ; - * - * literal: '"' [^"]* '"' - * ; - * - * props: props token '=' literal - * | - * ; - * - * disambiguate (tm): - * - * input: input tag - * | input text - * | - * ; - * - * tag: open_tag_base unary_tag - * | open_tag_base binary_tag - * ; - * - * open_tag_base: '[|' tag_guts - * ; - * - * unary_tag: '|]' - * ; - * - * binary_tag: '|>' tag_body close_tag - * ; - * - * tag_body: tag_body tag - * | tag_body text - * | - * ; - * - * close_tag: '<|' token '|]' - * ; - * - * tag_guts: 'view' literal props - * | 'section' literal - * | 'output' literal - * ; - * - * literal: '"' [^"]* '"' - * ; - * - * props: props token '=' literal - * | - * ; - */ -impl Parser { - pub fn new() -> Self { - Self { - } - } - - fn lex_error(&self, error: &Symbol) -> CrimResult { - if let SymbolType::Error{what} = error.symbol() { - Err(CrimError::parse( *error.start(), - format!("What: {:?}", *what) - )) - } else { - Err(CrimError::parse( Position::none(), "Not an error?".into())) - } - } - - pub fn parse(&self, src: &str, source: usize ) -> CrimResult> { - let ll = Lexer::new( src ); - let mut ctx = Context::new( ll, source ); - - // Parse the root of the file, the input context - self.p_input( &mut ctx ) - } - - fn p_input(&self, ctx: &mut Context ) -> CrimResult> { - let mut tags = Vec::new(); - loop { - if ctx.cur().is_none() { - break; - } - match ctx.cur().unwrap().symbol() { - SymbolType::Text(_) => { /* Skip top level text */ } - SymbolType::StartFlat => { - let tb = self.parse_tag( ctx )?; - let tb = self.parse_tag_set( ctx, tb )?; - tags.push( tb ); - } - SymbolType::Error{..} => { - self.lex_error( &ctx.cur().unwrap() )?; - } - _ => { - return Err(CrimError::parse( - *ctx.cur().unwrap().start(), - format!("Unexpected symbol {:?} found looking for tags.", ctx.cur().unwrap().symbol() ) - )); - } - } - ctx.next(); - } - - let mut views = Vec::new(); - for tag in tags { - let start_pos = tag.start_pos(); - let name = tag.name().clone(); - if tag.is_name(&SymbolType::View) && - let BuildResult::View(view) = tag.build( ctx )? { - views.push( view ); - } else { - return Err(CrimError::parse( - start_pos, - format!("Expected view at root, found {:?}", name ) - )); - } - } - Ok(views) - } - - fn parse_tag<'a>(&self, ctx: &mut Context<'a>) -> CrimResult> { - let start_sym = ctx.cur().unwrap(); - let mut tb = TagBuilder::new(&start_sym); - - if ctx.next().is_some() { - match ctx.cur().unwrap().symbol() { - SymbolType::View | SymbolType::Show | SymbolType::Loop | - SymbolType::If | SymbolType::ElIf | SymbolType::Else | - SymbolType::Output => { - let name_sym = ctx.cur().unwrap(); - ctx.next(); - tb.set_name( name_sym ); - } - _ => { - return Err(CrimError::parse( - *ctx.cur().unwrap().start(), - "Unexpected symbol".to_string() - )); - } - } - } else { - return Err(CrimError::eos("tag type")); - } - - if tb.can_have_params() || tb.can_have_expr() { - self.parse_tag_params( ctx, &mut tb )?; - } - if tb.can_have_props() { - self.parse_tag_props( ctx, &mut tb )?; - } - - if let Some(end_sym) = ctx.cur() { - match end_sym.symbol() { - SymbolType::EndPoint | SymbolType::EndFlat => { - tb.set_end( &end_sym )?; - } - _ => { - return Err(CrimError::parse( - *end_sym.start(), - format!("Unexpected symbol {:?} looking for end of tag.", end_sym.symbol()) - )); - } - } - } - - ctx.next(); - - Ok(tb) - } - - fn parse_tag_set<'a>(&self, ctx: &mut Context<'a>, mut base: TagBuilder<'a>) -> CrimResult> { - if base.is_unary() { - return Ok(base); - } - - loop { - if ctx.cur().is_none() { - return Err(CrimError::eos(format!("close tag for {:?}", base.name()).as_str())); - } - match ctx.cur().unwrap().symbol() { - SymbolType::Text(s) => { - base.add_child(Entry::Token(Token::Text(s.to_string()))); - ctx.next(); - } - SymbolType::StartFlat | SymbolType::StartPoint => { - let tb = self.parse_tag( ctx )?; - - match tb.tag_type() { - TagType::MultinaryOpen => { - base.add_child(Entry::TagBuilder(self.parse_tag_set( ctx, tb )?)); - } - TagType::MultinaryMid => { - base.add_chain(Entry::TagBuilder(self.parse_tag_set( ctx, tb )?)); - return Ok(base); - } - TagType::MultinaryClose => { - if (tb.is_name(&SymbolType::If) && - (base.is_name(&SymbolType::ElIf) || - base.is_name(&SymbolType::Else))) || - tb.is_name(base.name().unwrap().symbol()) { - return Ok(base); - } else { - return Err(CrimError::parse( - *tb.name().unwrap().start(), - format!("Found {:?} looking for close of {:?}", tb.name().unwrap().symbol(), base.name().unwrap().symbol()) - )); - } - } - TagType::Unary => { - base.add_child(Entry::TagBuilder(tb)); - } - TagType::Unknown => { - return Err(CrimError::broken(base.start_pos(), "Found unknown tag when looking for something else.")); - } - } - } - SymbolType::Error{..} => { - return self.lex_error( &ctx.cur().unwrap() ); - } - _ => { - return Err(CrimError::parse( - *ctx.cur().unwrap().start(), - format!("Unexpected token: {:?}", ctx.cur().unwrap().symbol()), - )); - } - } - } - } - - fn parse_tag_params(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> CrimResult<()> { - loop { - if ctx.cur().is_none() { - return Err(CrimError::eos("tag parameters")); - } - match ctx.cur().unwrap().symbol() { - SymbolType::Literal(s) => { - tb.add_param( ParamValue::Literal(s.to_string()) ); - ctx.next(); - } - SymbolType::Token(_) | SymbolType::Sharp => { - if ctx.peek().is("tag data", |s| *s == SymbolType::Equals)? { - break; - } - tb.add_param( self.parse_identifier( ctx )? ); - } - _ => { - break; - } - } - } - Ok(()) - } - - fn parse_identifier(&self, ctx: &mut Context ) -> CrimResult { - let mut id = Identifier::new(); - - if ctx.cur().is("identifier",|s| matches!( s, SymbolType::Sharp))? { - id.push( IdentifierValue::Root ); - ctx.next(); - } - loop { - if ctx.cur().is("identifier",|s| matches!( s, SymbolType::Token(_) ))? { - if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() { - id.push( IdentifierValue::Name(s.to_string()) ); - } - } else { - return Err(CrimError::parse( ctx.cur().unwrap().start().clone(), format!("Expeceted identifier, found {:?}", ctx.cur().unwrap().symbol() ) ) ); - } - - if !ctx.next().is("identifier seperator",|s| *s == SymbolType::Period )? { - break; - } - ctx.next(); - } - - Ok(ParamValue::Identifier(id)) - } - - fn parse_tag_props(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> CrimResult<()> { - loop { - if ctx.cur().is_none() { - return Err(CrimError::eos("tag properties")); - } - if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() { - if ctx.peek().is_some_and(|s|s.check_type(|t| matches!(t,SymbolType::Equals))) { - ctx.next(); - if let Some(sym) = ctx.next() && - let SymbolType::Literal(lv) = sym.symbol() { - tb.add_prop( s.to_string(), lv.to_string() ); - } else { - return Err(CrimError::parse(Position::none(),"Expected quoted literal string".to_string())); - } - } else { - break; - } - } else { - break; - } - ctx.next(); - } - Ok(()) - } -} - -#[derive(PartialEq,Copy,Clone,Debug)] -enum TagType { - Unknown, - Unary, - MultinaryOpen, - MultinaryMid, - MultinaryClose, -} - -#[derive(Debug)] -enum Entry<'a>{ - Token(Token), - TagBuilder(TagBuilder<'a>), -} - -type EntryList<'a> = Vec>; - -trait EntryListConverter { - fn has_outputs(&self) -> bool; - fn to_outputs(&mut self, ctx: &mut Context ) -> CrimResult>; - fn to_tokens(&mut self, ctx: &mut Context) -> CrimResult>; -} - -impl<'a> EntryListConverter for EntryList<'a> { - fn has_outputs(&self) -> bool { - self.iter().any(|e| - if let Entry::TagBuilder(tb) = e - && tb.is_name(&SymbolType::Output) { - true - } else { - false - } - ) - } - - fn to_outputs(&mut self, ctx: &mut Context) -> CrimResult> { - let mut outputs = Vec::new(); - - if self.has_outputs() { - // We have an explicit output, we cant have anything else - for e in self.drain(..) { - let tb = if let Entry::TagBuilder(tb) = e { - tb - } else { - continue; - }; - if let BuildResult::Output(out) = tb.build(ctx)? { - outputs.push( out ); - } - } - } else { - // No outputs, so we create one implicit output - outputs.push(Output { - name: "content".into(), - code: self.to_tokens(ctx)? - }); - } - - Ok(outputs) - } - - fn to_tokens(&mut self, ctx: &mut Context) -> CrimResult> { - let mut tokens : Vec = Vec::new(); - for e in self.drain(..) { - match e { - Entry::Token(token) => { - tokens.push( token ); - } - Entry::TagBuilder(tb) => { - if let BuildResult::Token(token) = tb.build(ctx)? { - tokens.push( token ); - } else { - return Err(CrimError::broken( Position::none(), "Non-token result built.")); - } - } - } - } - Ok(tokens) - } -} - -#[derive(Debug)] -struct TagBuilder<'a> { - name: Option>, - params: Vec, - props: Properties, - children: Vec>, - tag_type: TagType, - start: Symbol<'a>, - chain: Vec>, -} - -#[derive(Debug)] -enum ParamValue { - Literal(String), - Identifier(Identifier), -} - -enum BuildResult { - View(View), - Output(Output), - Token(Token), -} - -impl<'a> TagBuilder<'a> { - pub fn new(start: &Symbol<'a>) -> TagBuilder<'a> { - TagBuilder { - name: None, - params: Vec::new(), - props: Properties::new(), - children: Vec::new(), - tag_type: TagType::Unknown, - start: start.clone(), - chain: Vec::new(), - } - } - - pub fn set_end(&mut self, end: &Symbol) -> CrimResult<()> { - if *self.start.symbol() == SymbolType::StartFlat { - if *end.symbol() == SymbolType::EndFlat { - self.tag_type = TagType::Unary; - } else if *end.symbol() == SymbolType::EndPoint { - self.tag_type = TagType::MultinaryOpen; - } else { - return Err(CrimError::broken( *end.start(), "Invalid bracket token type.")); - } - } else if *self.start.symbol() == SymbolType::StartPoint { - if *end.symbol() == SymbolType::EndFlat { - self.tag_type = TagType::MultinaryClose; - } else if *end.symbol() == SymbolType::EndPoint { - self.tag_type = TagType::MultinaryMid; - } else { - return Err(CrimError::broken( *end.start(), "Invalid bracket token type.")); - } - } else { - return Err(CrimError::broken( *self.start.start(), "Invalid bracket token type.")); - } - Ok(()) - } - - pub fn start_pos(&self) -> Position { - *self.start.start() - } - - pub fn is_unary(&self) -> bool { - self.tag_type == TagType::Unary - } -/* - pub fn is_multinary_open(&self) -> bool { - self.tag_type == TagType::MultinaryOpen - } -*/ - pub fn can_have_params(&self) -> bool { - match self.name.unwrap().symbol() { - SymbolType::View | SymbolType::Output | SymbolType::Loop => true, - SymbolType::Show | SymbolType::If | SymbolType::ElIf | - SymbolType::Else => false, - _ => false, - } - } - - pub fn can_have_expr(&self) -> bool { - match self.name.unwrap().symbol() { - SymbolType::View | SymbolType::Output | SymbolType::Loop => false, - SymbolType::Show | SymbolType::If | SymbolType::ElIf | - SymbolType::Else => true, - _ => false, - } - } - - pub fn can_have_props(&self) -> bool { - true - } -/* - pub fn can_have_children(&self) -> bool { - if self.tag_type == TagType::MultinaryOpen || - self.tag_type == TagType::MultinaryMid { - true - } else { - false - } - } -*/ - pub fn is_name(&self, name: &SymbolType<'a>) -> bool { - if let Some(a) = self.name { - a.symbol() == name - } else { - false - } - } - - pub fn name(&self) -> Option> { - self.name - } -/* - pub fn params(&self) -> &Vec { - &self.params - } -*/ - pub fn set_name(&mut self, name: Symbol<'a>) { - self.name = Some(name); - } - - pub fn add_param(&mut self, param: ParamValue) { - self.params.push( param ); - } - - pub fn add_prop(&mut self, key: String, value: String) { - self.props.insert( key, value ); - } - - pub fn add_child(&mut self, tb: Entry<'a>) { - self.children.push( tb ); - } -/* - pub fn append_children(&mut self, children: &mut Vec::>) { - self.children.append( children ); - } -*/ - pub fn add_chain(&mut self, tb: Entry<'a>) { - self.chain.push( tb ); - } -/* - pub fn set_type(&mut self, tag_type: TagType) { - self.tag_type = tag_type; - } -*/ - pub fn tag_type(&self) -> TagType { - self.tag_type - } - - pub fn build(mut self, ctx: &mut Context ) -> CrimResult { - if let Some(sym) = self.name { - match sym.symbol() { - SymbolType::View => { - let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) { - s.to_string() - } else { - return Err(CrimError::parse(Position::none(), "Expected string literal for view name.".to_string())); - }; - Ok(BuildResult::View(View { - name: name, - source: ctx.source(), - outputs: self.children.to_outputs(ctx)?, - //theme: Option - layout: self.props.get("layout").cloned(), - })) - } - SymbolType::Output => { - let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) { - s.to_string() - } else { - return Err(CrimError::parse(Position::none(), "Expected string literal for output name.".into())); - }; - Ok(BuildResult::Output(Output { - name: name, - code: self.children.to_tokens(ctx)?, - })) - } - SymbolType::Loop => { - let id = if let ParamValue::Identifier(id) - = self.params.swap_remove(0) { - id - } else { - return Err(CrimError::parse( - Position::none(), - "Identifier for loop variable name.".into()) - ); - }; - Ok(BuildResult::Token( - Token::Loop(id, self.children.to_tokens(ctx)?) - )) - } - SymbolType::Show => { - let id = if let ParamValue::Identifier(id) - = self.params.swap_remove(0) { - id - } else { - return Err(CrimError::parse( - Position::none(), - "Identifier for show variable name.".into()) - ); - }; - Ok(BuildResult::Token(Token::Show(id, self.props))) - } - SymbolType::If => { - let id = if let ParamValue::Identifier(id) - = self.params.swap_remove(0) { - id - } else { - return Err(CrimError::parse( - Position::none(), - "Identifier for if variable name.".into()) - ); - }; - - let is_else = if self.chain.len() == 1 && - let Entry::TagBuilder(tb) = &self.chain[0] && - tb.is_name(&SymbolType::Else) && - tb.params.len() == 0 { - true - } else { - false - }; - - if is_else && - let Entry::TagBuilder(mut tb) = self.chain.swap_remove(0) { - self.chain.clear(); - self.chain.append(&mut tb.children); - } - Ok(BuildResult::Token(Token::If(id, self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?))) - } - SymbolType::ElIf => { - let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { - id - } else { - return Err(CrimError::parse( - Position::none(), - "Identifier for elif variable name.".into()) - ); - }; - - // this is copied from if, since they're the same this - // should be encapsulated and moved into a function... - // ...but I can't do that right now. - let is_else = if self.chain.len() == 1 && - let Entry::TagBuilder(tb) = &self.chain[0] && - tb.is_name(&SymbolType::Else) && - tb.params.len() == 0 { - true - } else { - false - }; - - if is_else && - let Entry::TagBuilder(mut tb) = self.chain.swap_remove(0) { - self.chain.clear(); - self.chain.append(&mut tb.children); - } - - - Ok(BuildResult::Token(Token::If(id, self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?))) - } - SymbolType::Else => { - Ok(BuildResult::Token(Token::If(Identifier::new(), self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?))) - } - _ => { - Err(CrimError::parse( self.start_pos(), "Bad tag type".into())) - } - } - } else { - Err(CrimError::parse(self.start_pos(), "Bad tag type".into())) - } - } -} diff --git a/src/position.rs b/src/position.rs deleted file mode 100644 index 24f5309..0000000 --- a/src/position.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::fmt; -use std::cmp::Ordering; - -#[derive(Copy,Clone)] -pub struct Position { - pub line: u32, - pub column: u32, -} - -impl Position { - pub fn new( line: u32, column: u32 ) -> Self { - Self { - line, column, - } - } - - pub fn none() -> Self { - Self { - line: 0, - column: 0, - } - } - - pub fn is_none(&self) -> bool { - self.line == 0 || self.column == 0 - } -} - -impl fmt::Debug for Position { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.is_none() { - write!(f, "none") - } else { - write!(f, "{}:{}", self.line, self.column ) - } - } -} - -impl PartialOrd for Position { - fn partial_cmp(&self, other: &Self) -> Option { - let ord = self.line.partial_cmp( &other.line ); - if Some(Ordering::Equal) == ord { - self.column.partial_cmp( &other.column ) - } else { - ord - } - } -} - -impl Ord for Position { - fn cmp(&self, other: &Self) -> Ordering { - let ord = self.line.cmp( &other.line ); - if Ordering::Equal == ord { - self.column.cmp( &other.column ) - } else { - ord - } - } -} - -impl PartialEq for Position { - fn eq(&self, other: &Self) -> bool { - self.line == other.line && self.column == other.column - } -} - -impl Eq for Position {} - -#[derive(Copy,Clone)] -pub struct Range { - start: Position, - end: Position, -} - -impl Range { - pub fn new( start: Position, end: Position ) -> Self { - Self { - start, end, - } - } - - pub fn include(&mut self, pos: &Position ) { - if *pos < self.start { - self.start = pos.clone(); - } - if *pos > self.end { - self.end = pos.clone(); - } - } -} - -impl fmt::Debug for Range { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}-{:?}", self.start, self.end ) - } -} -- cgit v1.2.3