summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lexer.rs27
-rw-r--r--src/lib.rs132
-rw-r--r--src/parser.rs65
3 files changed, 165 insertions, 59 deletions
diff --git a/src/lexer.rs b/src/lexer.rs
index 0724df2..2e43914 100644
--- a/src/lexer.rs
+++ b/src/lexer.rs
@@ -20,6 +20,7 @@ pub enum SymbolType<'a> {
20 Output, 20 Output,
21 Show, 21 Show,
22 Equals, 22 Equals,
23 Period,
23 Token(&'a str), 24 Token(&'a str),
24 Literal(&'a str), 25 Literal(&'a str),
25 Text(&'a str), 26 Text(&'a str),
@@ -78,13 +79,20 @@ pub struct Lexer<'a> {
78 pos: Position, 79 pos: Position,
79} 80}
80 81
81fn is_valid_token_char( c: char ) -> bool { 82fn is_valid_token_char( c: char, first: bool ) -> bool {
82 if c.is_whitespace() { 83 if c.is_whitespace() {
83 return false; 84 return false;
84 } 85 }
85 match c { 86 if first {
86 'a'..'z' | 'A'..'Z' | '0'..'9' | '_' | '-' => true, 87 match c {
87 _ => false 88 'a'..'z' | 'A'..'Z' | '_' => true,
89 _ => false
90 }
91 } else {
92 match c {
93 'a'..'z' | 'A'..'Z' | '0'..'9' | '_' => true,
94 _ => false
95 }
88 } 96 }
89} 97}
90 98
@@ -214,13 +222,18 @@ impl<'a> Lexer<'a> {
214 self.next(); 222 self.next();
215 return Some(Symbol::new(SymbolType::Equals, self.pos, self.pos)); 223 return Some(Symbol::new(SymbolType::Equals, self.pos, self.pos));
216 } 224 }
225 Some('.') => {
226 self.next();
227 return Some(Symbol::new(SymbolType::Period, self.pos, self.pos));
228 }
217 _ => {} 229 _ => {}
218 } 230 }
219 let start = self.cur_index(); 231 let start = self.cur_index();
220 let start_pos = self.pos.clone(); 232 let start_pos = self.pos.clone();
221 233
222 while self.next().is_some_and(|ch| is_valid_token_char(ch) ) && 234 let mut first = true;
223 !self.is_end_tag() {} 235 while self.next().is_some_and(|ch| is_valid_token_char(ch, first) ) &&
236 !self.is_end_tag() { first = false; }
224 let end = self.cur_index(); 237 let end = self.cur_index();
225 let end_pos = self.pos.clone(); 238 let end_pos = self.pos.clone();
226 let s = &self.data[start..end]; 239 let s = &self.data[start..end];
diff --git a/src/lib.rs b/src/lib.rs
index 7790b9a..a2bfc37 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -38,14 +38,14 @@ struct View {
38} 38}
39 39
40impl View { 40impl View {
41 fn process(&self, input: &Value) -> Result<Value, ParseError> { 41 fn process(&self, input: &impl MappedStructure) -> Result<Context, ParseError> {
42 let mut out_vars = HashMap::new(); 42 let mut out_vars = Context::new();
43 for output in &self.outputs { 43 for output in &self.outputs {
44 let mut buf = String::new(); 44 let mut buf = String::new();
45 exec( input, &output.code, &mut buf )?; 45 exec( input, &output.code, &mut buf )?;
46 out_vars.insert( output.name.clone(), Value::String(buf) ); 46 out_vars.insert( output.name.clone(), Value::String(buf) );
47 } 47 }
48 Ok(Value::Dictionary(out_vars)) 48 Ok(out_vars)
49 } 49 }
50} 50}
51 51
@@ -59,17 +59,60 @@ type Properties = HashMap<String, String>;
59 59
60#[derive(Debug)] 60#[derive(Debug)]
61enum Token { 61enum Token {
62 Show(String,Properties), 62 Show(Identifier,Properties),
63 Text(String), 63 Text(String),
64} 64}
65 65
66#[derive(Debug)] 66#[derive(Debug,Clone)]
67pub enum Value { 67pub enum Value {
68 Dictionary(HashMap<String,Value>), 68 Dictionary(HashMap<String,Value>),
69 List(Vec<Value>), 69 List(Vec<Value>),
70 String(String), 70 String(String),
71 Int(i64), 71 Int(i64),
72 Float(f64), 72 Float(f64),
73 Identifier(Identifier),
74}
75
76#[derive(Debug,Clone)]
77pub enum IdentifierValue {
78 Name(String),
79 Index(usize),
80}
81
82pub type Identifier = Vec<IdentifierValue>;
83pub type Context = HashMap<String,Value>;
84
85pub trait MappedStructure {
86 fn get_value(&self, id: &Identifier) -> Option<Value>;
87}
88
89impl MappedStructure for Context {
90 fn get_value(&self, id: &Identifier) -> Option<Value> {
91 if id.len() == 0 {
92 return None;
93 }
94 let mut cur_val : Option<&Value> = if let IdentifierValue::Name(s) = &id[0] {
95 self.get(s)
96 } else {
97 return None;
98 };
99 if cur_val.is_none() {
100 return None;
101 }
102
103 for idx in 1..id.len() {
104 if let IdentifierValue::Name(s) = &id[idx] &&
105 let Some(Value::Dictionary(d)) = &cur_val {
106 cur_val.replace( if let Some(v) = d.get(s) {
107 v
108 } else {
109 return None;
110 });
111 }
112 }
113
114 return cur_val.cloned();
115 }
73} 116}
74 117
75impl Crimtag { 118impl Crimtag {
@@ -157,7 +200,7 @@ impl Crimtag {
157 } 200 }
158 } 201 }
159 202
160 pub fn render_partial(&self, view: &str, input: &Value ) -> Result<Value,ParseError> { 203 pub fn render_partial(&self, view: &str, input: &impl MappedStructure ) -> Result<Context,ParseError> {
161 if let Some(view) = self.views.get(view) { 204 if let Some(view) = self.views.get(view) {
162 return view.process( input ) 205 return view.process( input )
163 } else { 206 } else {
@@ -165,14 +208,13 @@ impl Crimtag {
165 } 208 }
166 } 209 }
167 210
168 pub fn render(&self, view: &str, input: &Value ) -> Result<String,ParseError> { 211 pub fn render(&self, view: &str, input: &impl MappedStructure ) -> Result<String,ParseError> {
169 if let Some(view) = self.views.get( view ) { 212 if let Some(view) = self.views.get( view ) {
170 let output = view.process( input )?; 213 let mut output = view.process( input )?;
171 if let Some(l) = &view.layout { 214 if let Some(l) = &view.layout {
172 self.render( l, &output ) 215 self.render( l, &output )
173 } else { 216 } else {
174 if let Value::Dictionary(mut d) = output && 217 if let Some(content) = output.remove("content") &&
175 let Some(content) = d.remove("content") &&
176 let Value::String(s) = content { 218 let Value::String(s) = content {
177 219
178 Ok(s) 220 Ok(s)
@@ -187,38 +229,40 @@ impl Crimtag {
187 } 229 }
188} 230}
189 231
190fn exec(input: &Value, tokens: &Vec<Token>, buf: &mut String) -> Result<(),ParseError> { 232fn exec(input: &impl MappedStructure, tokens: &Vec<Token>, buf: &mut String) -> Result<(),ParseError> {
191 for token in tokens { 233 for token in tokens {
192 match token { 234 match token {
193 Token::Show(s,p) => { 235 Token::Show(ident,p) => {
194 let format = if let Some(s) = p.get("format") { 236 let format = if let Some(s) = p.get("format") {
195 s 237 s
196 } else { 238 } else {
197 &"".to_string() 239 &"".to_string()
198 }; 240 };
199 if let Value::Dictionary(d) = input && 241 if let Some(value) = input.get_value( &ident ) {
200 let Some(value) = d.get(s) { 242 match value {
201 match value { 243 Value::Dictionary(_) => {
202 Value::Dictionary(_) => { 244 buf.push_str("<dictionary>");
203 buf.push_str("<dictionary>"); 245 }
204 } 246 Value::List(_) => {
205 Value::List(_) => { 247 buf.push_str("<list>");
206 buf.push_str("<list>"); 248 }
207 } 249 Value::String(s) => {
208 Value::String(s) => { 250 buf.push_str(s.as_str());
209 buf.push_str(s); 251 }
210 } 252 Value::Int(i) => {
211 Value::Int(i) => { 253 buf.push_str(&i.to_string());
212 buf.push_str(&i.to_string()); 254 }
213 } 255 Value::Float(f) => {
214 Value::Float(f) => { 256 if format == "" {
215 if format == "" { 257 buf.push_str(&f.to_string());
216 buf.push_str(&f.to_string()); 258 } else {
217 } else { 259 buf.push_str(&f.to_string());
218 buf.push_str(&f.to_string());
219 }
220 } 260 }
221 } 261 }
262 Value::Identifier(_) => {
263 // ???
264 }
265 }
222 } 266 }
223 } 267 }
224 Token::Text(s) => { 268 Token::Text(s) => {
@@ -254,14 +298,14 @@ mod tests {
254 let mut ct = Crimtag::new(); 298 let mut ct = Crimtag::new();
255 if let Err(e) = ct.load_static(r#"Here is a sample 299 if let Err(e) = ct.load_static(r#"Here is a sample
256[|view "index" theme="standard"|><html><body>Hi</body></html><|view|] 300[|view "index" theme="standard"|><html><body>Hi</body></html><|view|]
257That was fun! now a placeholder: [|view "placeholder"|] and now one with an implicit [|view "simple"|>Body [|show "content"|] and end<|view|] aoeu 301That was fun! now a placeholder: [|view "placeholder"|] and now one with an implicit [|view "simple"|>Body -> [|show content|] <- and end<|view|] aoeu
258 302
259[|view "index" layout="simple"|>Whatever man!<|view|] 303[|view "person" layout="simple"|>Welcome [|show person.last_name|], [|show person.first_name|]! It's lovely to see you again.<|view|]
260 304
261Now with explicit outputs: 305Now with explicit outputs:
262[|view "complex"|> 306[|view "complex"|>
263 [|output "content"|> 307 [|output "content"|>
264 Here's a [|show "name"|]. 308 Here's a [|show name|].
265 <|output|] 309 <|output|]
266 [|output "sidebar"|> 310 [|output "sidebar"|>
267 What's up world? 311 What's up world?
@@ -274,13 +318,15 @@ Now with explicit outputs:
274 } else { 318 } else {
275 println!("It finished"); 319 println!("It finished");
276 if let Ok(v) = ct.render( 320 if let Ok(v) = ct.render(
277 "index", 321 "person",
278 &Value::Dictionary( 322 &Context::from([
279 HashMap::from([ 323 ("hi".to_string(),Value::String("hi".to_string())),
280 ("hi".to_string(),Value::String("hi".to_string())) 324 ("person".to_string(),Value::Dictionary(HashMap::from([
281 ]) 325 ("first_name".to_string(), Value::String("Bob".to_string())),
282 ) 326 ("last_name".to_string(), Value::String("Smith".to_string())),
283 ) { 327 ]))),
328 ])
329 ) {
284 println!("View result: {:?}", v ); 330 println!("View result: {:?}", v );
285 } else { 331 } else {
286 println!("Error?"); 332 println!("Error?");
diff --git a/src/parser.rs b/src/parser.rs
index 297230c..5f53557 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -274,7 +274,7 @@ impl Parser {
274 SymbolType::StartFlat => { 274 SymbolType::StartFlat => {
275 let subtb = self.parse_tag_body( ctx )?; 275 let subtb = self.parse_tag_body( ctx )?;
276 if subtb.is_name(&SymbolType::Output) { 276 if subtb.is_name(&SymbolType::Output) {
277 outputs.push(subtb.build_output(ctx)?); 277 outputs.push(subtb.build_output()?);
278 } else { 278 } else {
279 children.push(subtb.build()?); 279 children.push(subtb.build()?);
280 } 280 }
@@ -437,16 +437,43 @@ impl Parser {
437 } 437 }
438 match ctx.cur().unwrap().symbol() { 438 match ctx.cur().unwrap().symbol() {
439 SymbolType::Literal(s) => { 439 SymbolType::Literal(s) => {
440 tb.add_param( s.to_string() ); 440 tb.add_param( ParamValue::Literal(s.to_string()) );
441 ctx.next();
442 }
443 SymbolType::Token(_) => {
444 if ctx.peek().is("tag data", |s| *s == SymbolType::Equals)? {
445 break;
446 }
447 tb.add_param( self.parse_identifier( ctx )? );
441 } 448 }
442 _ => { 449 _ => {
443 break; 450 break;
444 } 451 }
445 } 452 }
446 ctx.next();
447 } 453 }
448 Ok(()) 454 Ok(())
449 } 455 }
456
457 fn parse_identifier(&self, ctx: &mut Context ) -> ParseResult<ParamValue> {
458 let mut id = Identifier::new();
459
460 loop {
461 if ctx.cur().is("identifier",|s| matches!( s, SymbolType::Token(_) ))? {
462 if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() {
463 id.push( IdentifierValue::Name(s.to_string()) );
464 }
465 } else {
466 return Err(ParseError::new( ctx.cur().unwrap().start().clone(), &format!("Expeceted identifier, found {:?}", ctx.cur().unwrap().symbol() ) ) );
467 }
468
469 if !ctx.next().is("identifier seperator",|s| *s == SymbolType::Period )? {
470 break;
471 }
472 ctx.next();
473 }
474
475 Ok(ParamValue::Identifier(id))
476 }
450 477
451 fn parse_tag_props(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { 478 fn parse_tag_props(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> {
452 loop { 479 loop {
@@ -483,13 +510,18 @@ enum TagType {
483 510
484struct TagBuilder<'a> { 511struct TagBuilder<'a> {
485 name: Option<Symbol<'a>>, 512 name: Option<Symbol<'a>>,
486 params: Vec<String>, 513 params: Vec<ParamValue>,
487 props: Properties, 514 props: Properties,
488 children: Vec<Token>, 515 children: Vec<Token>,
489 tag_type: TagType, 516 tag_type: TagType,
490 start_pos: Position, 517 start_pos: Position,
491} 518}
492 519
520enum ParamValue {
521 Literal(String),
522 Identifier(Identifier),
523}
524
493impl<'a> TagBuilder<'a> { 525impl<'a> TagBuilder<'a> {
494 pub fn new(start: &Symbol) -> TagBuilder<'a> { 526 pub fn new(start: &Symbol) -> TagBuilder<'a> {
495 TagBuilder { 527 TagBuilder {
@@ -538,7 +570,7 @@ impl<'a> TagBuilder<'a> {
538 self.name = Some(name); 570 self.name = Some(name);
539 } 571 }
540 572
541 pub fn add_param(&mut self, param: String) { 573 pub fn add_param(&mut self, param: ParamValue) {
542 self.params.push( param ); 574 self.params.push( param );
543 } 575 }
544 576
@@ -565,8 +597,13 @@ impl<'a> TagBuilder<'a> {
565 pub fn build_view(mut self, ctx: &Context, outputs: Vec<Output>) -> ParseResult<View> { 597 pub fn build_view(mut self, ctx: &Context, outputs: Vec<Output>) -> ParseResult<View> {
566 if let Some(sym) = self.name { 598 if let Some(sym) = self.name {
567 if *sym.symbol() == SymbolType::View { 599 if *sym.symbol() == SymbolType::View {
600 let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) {
601 s.to_string()
602 } else {
603 return Err(ParseError::new(Position::none(), "Expected string literal for view name."));
604 };
568 Ok(View { 605 Ok(View {
569 name: self.params.swap_remove(0), 606 name: name,
570 source: ctx.source(), 607 source: ctx.source(),
571 outputs: outputs, 608 outputs: outputs,
572 //theme: Option<String> 609 //theme: Option<String>
@@ -580,11 +617,16 @@ impl<'a> TagBuilder<'a> {
580 } 617 }
581 } 618 }
582 619
583 pub fn build_output(mut self, ctx: &Context) -> ParseResult<Output> { 620 pub fn build_output(mut self) -> ParseResult<Output> {
584 if let Some(sym) = self.name { 621 if let Some(sym) = self.name {
585 if *sym.symbol() == SymbolType::Output { 622 if *sym.symbol() == SymbolType::Output {
623 let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) {
624 s.to_string()
625 } else {
626 return Err(ParseError::new(Position::none(), "Expected string literal for output name."));
627 };
586 Ok(Output { 628 Ok(Output {
587 name: self.params.swap_remove(0), 629 name: name,
588 code: self.children, 630 code: self.children,
589 }) 631 })
590 } else { 632 } else {
@@ -599,7 +641,12 @@ impl<'a> TagBuilder<'a> {
599 if let Some(sym) = self.name { 641 if let Some(sym) = self.name {
600 match sym.symbol() { 642 match sym.symbol() {
601 SymbolType::Show => { 643 SymbolType::Show => {
602 Ok(Token::Show(self.params.swap_remove(0), self.props)) 644 let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) {
645 id
646 } else {
647 return Err(ParseError::new(Position::none(), "Identifier for show variable name."));
648 };
649 Ok(Token::Show(id, self.props))
603 } 650 }
604 _ => { 651 _ => {
605 Err(ParseError { 652 Err(ParseError {