diff options
-rw-r--r-- | src/ast.rs | 13 | ||||
-rw-r--r-- | src/eval.rs | 174 | ||||
-rw-r--r-- | src/lib.rs | 25 | ||||
-rw-r--r-- | src/parser.rs | 40 |
4 files changed, 170 insertions, 82 deletions
@@ -59,7 +59,7 @@ pub enum Statement { | |||
59 | #[derive(Debug, Eq, PartialEq, Clone)] | 59 | #[derive(Debug, Eq, PartialEq, Clone)] |
60 | pub enum Expr { | 60 | pub enum Expr { |
61 | Node, | 61 | Node, |
62 | FieldAccess(Vec<Identifier>), | 62 | FieldAccess(Box<Expr>, Identifier), |
63 | Unit, | 63 | Unit, |
64 | Lit(Literal), | 64 | Lit(Literal), |
65 | Ident(Identifier), | 65 | Ident(Identifier), |
@@ -67,7 +67,7 @@ pub enum Expr { | |||
67 | Bin(Box<Expr>, BinOp, Box<Expr>), | 67 | Bin(Box<Expr>, BinOp, Box<Expr>), |
68 | Unary(Box<Expr>, UnaryOp), | 68 | Unary(Box<Expr>, UnaryOp), |
69 | Call(Call), | 69 | Call(Call), |
70 | IfExpr(If), | 70 | If(IfExpr), |
71 | Block(Block), | 71 | Block(Block), |
72 | } | 72 | } |
73 | 73 | ||
@@ -75,6 +75,13 @@ impl Expr { | |||
75 | pub fn boxed(self) -> Box<Expr> { | 75 | pub fn boxed(self) -> Box<Expr> { |
76 | Box::new(self) | 76 | Box::new(self) |
77 | } | 77 | } |
78 | |||
79 | pub fn as_ident(self) -> Option<Identifier> { | ||
80 | match self { | ||
81 | Self::Ident(i) => Some(i), | ||
82 | _ => None, | ||
83 | } | ||
84 | } | ||
78 | } | 85 | } |
79 | 86 | ||
80 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 87 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
@@ -164,7 +171,7 @@ pub struct Declaration { | |||
164 | } | 171 | } |
165 | 172 | ||
166 | #[derive(Debug, Eq, PartialEq, Clone)] | 173 | #[derive(Debug, Eq, PartialEq, Clone)] |
167 | pub struct If { | 174 | pub struct IfExpr { |
168 | pub condition: Box<Expr>, | 175 | pub condition: Box<Expr>, |
169 | pub then: Block, | 176 | pub then: Block, |
170 | pub else_: Block, | 177 | pub else_: Block, |
diff --git a/src/eval.rs b/src/eval.rs index 1a3f8a8..7822419 100644 --- a/src/eval.rs +++ b/src/eval.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! tree walking interpreter for tbsp | 1 | //! tree walking interpreter for tbsp |
2 | 2 | ||
3 | use crate::ast; | 3 | use crate::{ast, Wrap}; |
4 | use std::{collections::HashMap, fmt}; | 4 | use std::{collections::HashMap, fmt}; |
5 | 5 | ||
6 | #[derive(Debug, PartialEq, Eq, Clone)] | 6 | #[derive(Debug, PartialEq, Eq, Clone)] |
@@ -38,10 +38,11 @@ pub enum Value { | |||
38 | Integer(i128), | 38 | Integer(i128), |
39 | String(String), | 39 | String(String), |
40 | Boolean(bool), | 40 | Boolean(bool), |
41 | Node, | 41 | Node(NodeId), |
42 | FieldAccess(Vec<String>), | ||
43 | } | 42 | } |
44 | 43 | ||
44 | type NodeId = usize; | ||
45 | |||
45 | impl Value { | 46 | impl Value { |
46 | fn ty(&self) -> ast::Type { | 47 | fn ty(&self) -> ast::Type { |
47 | match self { | 48 | match self { |
@@ -49,8 +50,7 @@ impl Value { | |||
49 | Self::Integer(_) => ast::Type::Integer, | 50 | Self::Integer(_) => ast::Type::Integer, |
50 | Self::String(_) => ast::Type::String, | 51 | Self::String(_) => ast::Type::String, |
51 | Self::Boolean(_) => ast::Type::Boolean, | 52 | Self::Boolean(_) => ast::Type::Boolean, |
52 | Self::Node => ast::Type::Node, | 53 | Self::Node(_) => ast::Type::Node, |
53 | Self::FieldAccess(_) => ast::Type::Node, | ||
54 | } | 54 | } |
55 | } | 55 | } |
56 | 56 | ||
@@ -106,6 +106,16 @@ impl Value { | |||
106 | } | 106 | } |
107 | } | 107 | } |
108 | 108 | ||
109 | fn as_node(&self) -> std::result::Result<NodeId, Error> { | ||
110 | match self { | ||
111 | Self::Node(id) => Ok(*id), | ||
112 | v => Err(Error::TypeMismatch { | ||
113 | expected: ast::Type::Node, | ||
114 | got: v.ty(), | ||
115 | }), | ||
116 | } | ||
117 | } | ||
118 | |||
109 | fn add(&self, other: &Self) -> Result { | 119 | fn add(&self, other: &Self) -> Result { |
110 | match (self, other) { | 120 | match (self, other) { |
111 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s + *o)), | 121 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s + *o)), |
@@ -267,8 +277,7 @@ impl fmt::Display for Value { | |||
267 | Self::Integer(i) => write!(f, "{i}"), | 277 | Self::Integer(i) => write!(f, "{i}"), |
268 | Self::String(s) => write!(f, "{s}"), | 278 | Self::String(s) => write!(f, "{s}"), |
269 | Self::Boolean(b) => write!(f, "{b}"), | 279 | Self::Boolean(b) => write!(f, "{b}"), |
270 | Self::Node => write!(f, "<node>"), | 280 | Self::Node(id) => write!(f, "<node #{id}>"), |
271 | Self::FieldAccess(items) => write!(f, "<node>.{}", items.join(".")), | ||
272 | } | 281 | } |
273 | } | 282 | } |
274 | } | 283 | } |
@@ -373,15 +382,17 @@ pub enum Error { | |||
373 | 382 | ||
374 | pub type Result = std::result::Result<Value, Error>; | 383 | pub type Result = std::result::Result<Value, Error>; |
375 | 384 | ||
376 | pub struct Context<'a> { | 385 | pub struct Context { |
377 | variables: HashMap<ast::Identifier, Variable>, | 386 | variables: HashMap<ast::Identifier, Variable>, |
378 | language: tree_sitter::Language, | 387 | language: tree_sitter::Language, |
379 | visitors: Visitors, | 388 | visitors: Visitors, |
380 | input_src: Option<String>, | 389 | input_src: Option<String>, |
381 | cursor: Option<tree_sitter::TreeCursor<'a>>, | 390 | cursor: Option<tree_sitter::TreeCursor<'static>>, |
391 | tree: Option<&'static tree_sitter::Tree>, | ||
392 | cache: HashMap<NodeId, tree_sitter::Node<'static>>, | ||
382 | } | 393 | } |
383 | 394 | ||
384 | impl<'a> fmt::Debug for Context<'a> { | 395 | impl fmt::Debug for Context { |
385 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 396 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
386 | f.debug_struct("Context") | 397 | f.debug_struct("Context") |
387 | .field("variables", &self.variables) | 398 | .field("variables", &self.variables) |
@@ -400,7 +411,7 @@ impl<'a> fmt::Debug for Context<'a> { | |||
400 | } | 411 | } |
401 | } | 412 | } |
402 | 413 | ||
403 | impl<'a> Context<'a> { | 414 | impl Context { |
404 | pub fn new(language: tree_sitter::Language) -> Self { | 415 | pub fn new(language: tree_sitter::Language) -> Self { |
405 | Self { | 416 | Self { |
406 | visitors: Default::default(), | 417 | visitors: Default::default(), |
@@ -408,9 +419,44 @@ impl<'a> Context<'a> { | |||
408 | language, | 419 | language, |
409 | input_src: None, | 420 | input_src: None, |
410 | cursor: None, | 421 | cursor: None, |
422 | tree: None, | ||
423 | cache: HashMap::default(), | ||
411 | } | 424 | } |
412 | } | 425 | } |
413 | 426 | ||
427 | pub fn cache_node(&mut self, node: tree_sitter::Node<'static>) { | ||
428 | self.cache.entry(node.id()).or_insert(node); | ||
429 | } | ||
430 | |||
431 | pub fn get_node_by_id(&mut self, id: usize) -> Option<tree_sitter::Node<'static>> { | ||
432 | let root_node = self.tree.as_ref().map(|t| t.root_node())?; | ||
433 | self.get_node_by_id_helper(root_node, id) | ||
434 | } | ||
435 | |||
436 | fn get_node_by_id_helper( | ||
437 | &mut self, | ||
438 | start: tree_sitter::Node<'static>, | ||
439 | id: usize, | ||
440 | ) -> Option<tree_sitter::Node<'static>> { | ||
441 | self.cache_node(start); | ||
442 | |||
443 | if let Some(found) = self.cache.get(&id) { | ||
444 | return Some(*found); | ||
445 | } | ||
446 | |||
447 | if start.id() == id { | ||
448 | return Some(start); | ||
449 | } else { | ||
450 | for child in start.children(&mut start.walk()) { | ||
451 | if let Some(n) = self.get_node_by_id_helper(child, id) { | ||
452 | return Some(n); | ||
453 | }; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | None | ||
458 | } | ||
459 | |||
414 | pub fn with_program(mut self, program: ast::Program) -> std::result::Result<Self, Error> { | 460 | pub fn with_program(mut self, program: ast::Program) -> std::result::Result<Self, Error> { |
415 | for stanza in program.stanzas.into_iter() { | 461 | for stanza in program.stanzas.into_iter() { |
416 | self.visitors.insert(stanza, &self.language)?; | 462 | self.visitors.insert(stanza, &self.language)?; |
@@ -423,8 +469,10 @@ impl<'a> Context<'a> { | |||
423 | self | 469 | self |
424 | } | 470 | } |
425 | 471 | ||
426 | pub fn with_cursor(mut self, cursor: tree_sitter::TreeCursor<'a>) -> Self { | 472 | pub fn with_tree(mut self, tree: tree_sitter::Tree) -> Self { |
427 | self.cursor = Some(cursor); | 473 | let tree = Box::leak(Box::new(tree)); |
474 | self.cursor = Some(tree.walk()); | ||
475 | self.tree = Some(tree); | ||
428 | self | 476 | self |
429 | } | 477 | } |
430 | 478 | ||
@@ -436,10 +484,10 @@ impl<'a> Context<'a> { | |||
436 | ast::Expr::Bin(lhs, op, rhs) => self.eval_bin(&*lhs, *op, &*rhs), | 484 | ast::Expr::Bin(lhs, op, rhs) => self.eval_bin(&*lhs, *op, &*rhs), |
437 | ast::Expr::Unary(expr, op) => self.eval_unary(&*expr, *op), | 485 | ast::Expr::Unary(expr, op) => self.eval_unary(&*expr, *op), |
438 | ast::Expr::Call(call) => self.eval_call(&*call), | 486 | ast::Expr::Call(call) => self.eval_call(&*call), |
439 | ast::Expr::IfExpr(if_expr) => self.eval_if(if_expr), | 487 | ast::Expr::If(if_expr) => self.eval_if(if_expr), |
440 | ast::Expr::Block(block) => self.eval_block(block), | 488 | ast::Expr::Block(block) => self.eval_block(block), |
441 | ast::Expr::Node => Ok(Value::Node), | 489 | ast::Expr::Node => self.eval_node(), |
442 | ast::Expr::FieldAccess(items) => Ok(Value::FieldAccess(items.to_owned())), | 490 | ast::Expr::FieldAccess(expr, items) => self.eval_field_access(expr, items), |
443 | } | 491 | } |
444 | } | 492 | } |
445 | 493 | ||
@@ -451,6 +499,16 @@ impl<'a> Context<'a> { | |||
451 | } | 499 | } |
452 | } | 500 | } |
453 | 501 | ||
502 | fn eval_node(&mut self) -> Result { | ||
503 | self.cursor | ||
504 | .as_ref() | ||
505 | .ok_or(Error::CurrentNodeNotPresent)? | ||
506 | .node() | ||
507 | .id() | ||
508 | .wrap(Value::Node) | ||
509 | .wrap_ok() | ||
510 | } | ||
511 | |||
454 | fn lookup(&mut self, ident: &ast::Identifier) -> std::result::Result<&Variable, Error> { | 512 | fn lookup(&mut self, ident: &ast::Identifier) -> std::result::Result<&Variable, Error> { |
455 | self.variables | 513 | self.variables |
456 | .get(ident) | 514 | .get(ident) |
@@ -469,14 +527,14 @@ impl<'a> Context<'a> { | |||
469 | ty: ast::Type, | 527 | ty: ast::Type, |
470 | ) -> std::result::Result<&mut Variable, Error> { | 528 | ) -> std::result::Result<&mut Variable, Error> { |
471 | if self.lookup(ident).is_err() { | 529 | if self.lookup(ident).is_err() { |
472 | Ok(self | 530 | self.variables |
473 | .variables | ||
474 | .entry(ident.to_owned()) | 531 | .entry(ident.to_owned()) |
475 | .or_insert_with(|| Variable { | 532 | .or_insert_with(|| Variable { |
476 | name: ident.to_owned(), | 533 | name: ident.to_owned(), |
477 | value: Value::default(ty), | 534 | value: Value::default(ty), |
478 | ty, | 535 | ty, |
479 | })) | 536 | }) |
537 | .wrap_ok() | ||
480 | } else { | 538 | } else { |
481 | Err(Error::AlreadyBound(ident.to_owned())) | 539 | Err(Error::AlreadyBound(ident.to_owned())) |
482 | } | 540 | } |
@@ -574,7 +632,7 @@ impl<'a> Context<'a> { | |||
574 | } | 632 | } |
575 | } | 633 | } |
576 | 634 | ||
577 | fn eval_if(&mut self, if_expr: &ast::If) -> Result { | 635 | fn eval_if(&mut self, if_expr: &ast::IfExpr) -> Result { |
578 | let cond = self.eval_expr(&if_expr.condition)?; | 636 | let cond = self.eval_expr(&if_expr.condition)?; |
579 | 637 | ||
580 | if cond.as_boolean()? { | 638 | if cond.as_boolean()? { |
@@ -638,32 +696,9 @@ impl<'a> Context<'a> { | |||
638 | } | 696 | } |
639 | } | 697 | } |
640 | ("text", [arg]) => { | 698 | ("text", [arg]) => { |
641 | let node = match self.eval_expr(arg)? { | 699 | let v = self.eval_expr(arg)?; |
642 | Value::Node => self | 700 | let id = v.as_node()?; |
643 | .cursor | 701 | let node = self.get_node_by_id(id).unwrap(); |
644 | .as_ref() | ||
645 | .ok_or(Error::CurrentNodeNotPresent)? | ||
646 | .node(), | ||
647 | Value::FieldAccess(fields) => { | ||
648 | let mut node = self | ||
649 | .cursor | ||
650 | .as_ref() | ||
651 | .ok_or(Error::CurrentNodeNotPresent)? | ||
652 | .node(); | ||
653 | for field in &fields { | ||
654 | node = node | ||
655 | .child_by_field_name(field.as_bytes()) | ||
656 | .ok_or_else(|| Error::FailedLookup(field.to_owned()))?; | ||
657 | } | ||
658 | node | ||
659 | } | ||
660 | v => { | ||
661 | return Err(Error::TypeMismatch { | ||
662 | expected: ast::Type::Node, | ||
663 | got: v.ty(), | ||
664 | }) | ||
665 | } | ||
666 | }; | ||
667 | let text = node | 702 | let text = node |
668 | .utf8_text(self.input_src.as_ref().unwrap().as_bytes()) | 703 | .utf8_text(self.input_src.as_ref().unwrap().as_bytes()) |
669 | .unwrap(); | 704 | .unwrap(); |
@@ -689,7 +724,7 @@ impl<'a> Context<'a> { | |||
689 | 724 | ||
690 | fn eval_statement(&mut self, stmt: &ast::Statement) -> Result { | 725 | fn eval_statement(&mut self, stmt: &ast::Statement) -> Result { |
691 | match stmt { | 726 | match stmt { |
692 | ast::Statement::Bare(expr) => self.eval_expr(expr).map(|_| Value::Unit), | 727 | ast::Statement::Bare(expr) => self.eval_expr(expr), |
693 | ast::Statement::Declaration(decl) => self.eval_declaration(decl), | 728 | ast::Statement::Declaration(decl) => self.eval_declaration(decl), |
694 | } | 729 | } |
695 | } | 730 | } |
@@ -701,6 +736,24 @@ impl<'a> Context<'a> { | |||
701 | Ok(Value::Unit) | 736 | Ok(Value::Unit) |
702 | } | 737 | } |
703 | 738 | ||
739 | fn eval_field_access(&mut self, expr: &ast::Expr, field: &ast::Identifier) -> Result { | ||
740 | let v = self.eval_expr(expr)?; | ||
741 | let base = v.as_node()?; | ||
742 | let base_node = self.get_node_by_id(base).unwrap(); | ||
743 | base_node | ||
744 | .child_by_field_name(field) | ||
745 | .ok_or(Error::InvalidNodeKind(field.clone())) | ||
746 | .map(|n| n.id()) | ||
747 | .map(Value::Node) | ||
748 | } | ||
749 | |||
750 | fn goto_first_child(&mut self) -> bool { | ||
751 | self.cursor | ||
752 | .as_mut() | ||
753 | .map(|c| c.goto_first_child()) | ||
754 | .unwrap_or_default() | ||
755 | } | ||
756 | |||
704 | pub fn eval(&mut self) -> Result { | 757 | pub fn eval(&mut self) -> Result { |
705 | let visitors = std::mem::take(&mut self.visitors); | 758 | let visitors = std::mem::take(&mut self.visitors); |
706 | let mut has_next = true; | 759 | let mut has_next = true; |
@@ -710,29 +763,29 @@ impl<'a> Context<'a> { | |||
710 | self.eval_block(&visitors.begin)?; | 763 | self.eval_block(&visitors.begin)?; |
711 | 764 | ||
712 | while has_next { | 765 | while has_next { |
713 | let current_node = self.cursor.as_mut().unwrap().node(); | 766 | let current_node = self.cursor.as_ref().unwrap().node(); |
714 | postorder.push(current_node); | 767 | postorder.push(current_node); |
715 | 768 | ||
716 | let visitor = visitors.get_by_node(current_node); | 769 | let visitor = visitors.get_by_node(current_node); |
717 | 770 | ||
718 | visitor.map(|v| self.eval_block(&v.enter)); | 771 | if let Some(v) = visitor { |
772 | self.eval_block(&v.enter)?; | ||
773 | } | ||
719 | 774 | ||
720 | has_next = self.cursor.as_mut().unwrap().goto_first_child(); | 775 | has_next = self.goto_first_child(); |
721 | 776 | ||
722 | if !has_next { | 777 | if !has_next { |
723 | has_next = self.cursor.as_mut().unwrap().goto_next_sibling(); | 778 | has_next = self.cursor.as_mut().unwrap().goto_next_sibling(); |
724 | postorder | 779 | if let Some(v) = postorder.pop().and_then(|n| visitors.get_by_node(n)) { |
725 | .pop() | 780 | self.eval_block(&v.leave)?; |
726 | .and_then(|n| visitors.get_by_node(n)) | 781 | } |
727 | .map(|v| self.eval_block(&v.leave)); | ||
728 | } | 782 | } |
729 | 783 | ||
730 | while !has_next && self.cursor.as_mut().unwrap().goto_parent() { | 784 | while !has_next && self.cursor.as_mut().unwrap().goto_parent() { |
731 | has_next = self.cursor.as_mut().unwrap().goto_next_sibling(); | 785 | has_next = self.cursor.as_mut().unwrap().goto_next_sibling(); |
732 | postorder | 786 | if let Some(v) = postorder.pop().and_then(|n| visitors.get_by_node(n)) { |
733 | .pop() | 787 | self.eval_block(&v.leave)?; |
734 | .and_then(|n| visitors.get_by_node(n)) | 788 | } |
735 | .map(|v| self.eval_block(&v.leave)); | ||
736 | } | 789 | } |
737 | } | 790 | } |
738 | 791 | ||
@@ -748,12 +801,11 @@ pub fn evaluate(file: &str, program: &str, language: tree_sitter::Language) -> R | |||
748 | let _ = parser.set_language(&language); | 801 | let _ = parser.set_language(&language); |
749 | 802 | ||
750 | let tree = parser.parse(file, None).unwrap(); | 803 | let tree = parser.parse(file, None).unwrap(); |
751 | let cursor = tree.walk(); | ||
752 | 804 | ||
753 | let program = ast::Program::new().from_str(program).unwrap(); | 805 | let program = ast::Program::new().from_str(program).unwrap(); |
754 | let mut ctx = Context::new(language) | 806 | let mut ctx = Context::new(language) |
755 | .with_input(file.to_owned()) | 807 | .with_input(file.to_owned()) |
756 | .with_cursor(cursor) | 808 | .with_tree(tree) |
757 | .with_program(program)?; | 809 | .with_program(program)?; |
758 | 810 | ||
759 | ctx.eval() | 811 | ctx.eval() |
@@ -857,7 +909,7 @@ mod test { | |||
857 | name: "a".to_owned(), | 909 | name: "a".to_owned(), |
858 | init: Some(ast::Expr::int(1).boxed()), | 910 | init: Some(ast::Expr::int(1).boxed()), |
859 | }), | 911 | }), |
860 | ast::Statement::Bare(ast::Expr::IfExpr(ast::If { | 912 | ast::Statement::Bare(ast::Expr::If(ast::IfExpr { |
861 | condition: ast::Expr::true_().boxed(), | 913 | condition: ast::Expr::true_().boxed(), |
862 | then: ast::Block { | 914 | then: ast::Block { |
863 | body: vec![ast::Statement::Bare(ast::Expr::Bin( | 915 | body: vec![ast::Statement::Bare(ast::Expr::Bin( |
@@ -1,6 +1,29 @@ | |||
1 | mod ast; | 1 | mod ast; |
2 | mod eval; | 2 | mod eval; |
3 | mod parser; | 3 | pub mod parser; |
4 | mod string; | 4 | mod string; |
5 | 5 | ||
6 | pub use eval::evaluate; | 6 | pub use eval::evaluate; |
7 | |||
8 | trait Wrap<T> { | ||
9 | fn wrap<F, U>(self, f: F) -> U | ||
10 | where | ||
11 | F: Fn(T) -> U, | ||
12 | Self: Sized; | ||
13 | |||
14 | fn wrap_ok<E>(self) -> Result<Self, E> | ||
15 | where | ||
16 | Self: Sized, | ||
17 | { | ||
18 | Ok(self) | ||
19 | } | ||
20 | } | ||
21 | |||
22 | impl<T> Wrap<T> for T { | ||
23 | fn wrap<F, U>(self, f: F) -> U | ||
24 | where | ||
25 | F: Fn(T) -> U, | ||
26 | { | ||
27 | f(self) | ||
28 | } | ||
29 | } | ||
diff --git a/src/parser.rs b/src/parser.rs index d705a11..4ec8e57 100644 --- a/src/parser.rs +++ b/src/parser.rs | |||
@@ -4,7 +4,7 @@ use nom::{ | |||
4 | character::complete::{alpha1, alphanumeric1, char, multispace0, multispace1, one_of}, | 4 | character::complete::{alpha1, alphanumeric1, char, multispace0, multispace1, one_of}, |
5 | combinator::{map, opt, recognize, value}, | 5 | combinator::{map, opt, recognize, value}, |
6 | error::ParseError, | 6 | error::ParseError, |
7 | multi::{many0, many0_count, many1, separated_list0, separated_list1}, | 7 | multi::{fold_many0, many0, many0_count, many1, separated_list0}, |
8 | sequence::{delimited, pair, preceded, terminated, tuple}, | 8 | sequence::{delimited, pair, preceded, terminated, tuple}, |
9 | IResult, Parser, | 9 | IResult, Parser, |
10 | }; | 10 | }; |
@@ -169,24 +169,27 @@ fn parse_mul<'a>(i: &'a str) -> IResult<&'a str, Expr> { | |||
169 | let div = parse_op("/", BinOp::Arith(ArithOp::Div)); | 169 | let div = parse_op("/", BinOp::Arith(ArithOp::Div)); |
170 | let mod_ = parse_op("%", BinOp::Arith(ArithOp::Mod)); | 170 | let mod_ = parse_op("%", BinOp::Arith(ArithOp::Mod)); |
171 | let op = alt((mul, div, mod_)); | 171 | let op = alt((mul, div, mod_)); |
172 | let recursive = parse_binary(parse_atom, op, parse_mul); | 172 | let recursive = parse_binary(parse_field_access, op, parse_mul); |
173 | let base = parse_atom; | 173 | let base = parse_field_access; |
174 | alt((recursive, base)).parse(i) | 174 | alt((recursive, base)).parse(i) |
175 | } | 175 | } |
176 | 176 | ||
177 | fn parse_field_access<'a>(i: &'a str) -> IResult<&'a str, Vec<Identifier>> { | 177 | fn parse_field_access<'a>(i: &'a str) -> IResult<&'a str, Expr> { |
178 | let node = tag("node"); | 178 | let trailing = map(tuple((ws(char('.')), ws(parse_ident))), |(_, i)| i); |
179 | let dot = ws(char('.')); | 179 | let (i, base) = parse_atom(i)?; |
180 | let fields = separated_list1(ws(char('.')), map(parse_name, str::to_owned)); | 180 | |
181 | map(tuple((node, dot, fields)), |(_, _, fields)| fields)(i) | 181 | fold_many0( |
182 | trailing, | ||
183 | move || base.clone(), | ||
184 | move |acc, new| Expr::FieldAccess(acc.boxed(), new), | ||
185 | )(i) | ||
182 | } | 186 | } |
183 | 187 | ||
184 | fn parse_atom<'a>(i: &'a str) -> IResult<&'a str, Expr> { | 188 | fn parse_atom<'a>(i: &'a str) -> IResult<&'a str, Expr> { |
185 | let inner = alt(( | 189 | let inner = alt(( |
186 | map(parse_field_access, Expr::FieldAccess), | ||
187 | map(tag("node"), |_| Expr::Node), | 190 | map(tag("node"), |_| Expr::Node), |
188 | map(parse_block, Expr::Block), | 191 | map(parse_block, Expr::Block), |
189 | map(parse_if, Expr::IfExpr), | 192 | map(parse_if, Expr::If), |
190 | map(parse_call, Expr::Call), | 193 | map(parse_call, Expr::Call), |
191 | map(parse_lit, Expr::Lit), | 194 | map(parse_lit, Expr::Lit), |
192 | map(parse_ident, Expr::Ident), | 195 | map(parse_ident, Expr::Ident), |
@@ -217,7 +220,7 @@ fn parse_block<'a>(i: &'a str) -> IResult<&'a str, Block> { | |||
217 | delimited(open, statements, close).parse(i) | 220 | delimited(open, statements, close).parse(i) |
218 | } | 221 | } |
219 | 222 | ||
220 | fn parse_if<'a>(i: &'a str) -> IResult<&'a str, If> { | 223 | fn parse_if<'a>(i: &'a str) -> IResult<&'a str, IfExpr> { |
221 | let if_ = delimited(multispace0, tag("if"), multispace1); | 224 | let if_ = delimited(multispace0, tag("if"), multispace1); |
222 | 225 | ||
223 | let open = char('('); | 226 | let open = char('('); |
@@ -231,7 +234,7 @@ fn parse_if<'a>(i: &'a str) -> IResult<&'a str, If> { | |||
231 | 234 | ||
232 | map( | 235 | map( |
233 | tuple((if_, open, condition, close, then, else_)), | 236 | tuple((if_, open, condition, close, then, else_)), |
234 | |(_, _, condition, _, then, else_)| If { | 237 | |(_, _, condition, _, then, else_)| IfExpr { |
235 | condition: condition.boxed(), | 238 | condition: condition.boxed(), |
236 | then, | 239 | then, |
237 | else_: else_.unwrap_or_default(), | 240 | else_: else_.unwrap_or_default(), |
@@ -571,7 +574,7 @@ mod test { | |||
571 | assert_eq!(parse_expr(r#" node "#), Ok(("", Expr::Node))); | 574 | assert_eq!(parse_expr(r#" node "#), Ok(("", Expr::Node))); |
572 | assert_eq!( | 575 | assert_eq!( |
573 | parse_expr(r#" node.foo "#), | 576 | parse_expr(r#" node.foo "#), |
574 | Ok(("", Expr::FieldAccess(vec!["foo".to_owned()]))) | 577 | Ok(("", Expr::FieldAccess(Expr::Node.boxed(), "foo".to_owned()))) |
575 | ); | 578 | ); |
576 | assert_eq!( | 579 | assert_eq!( |
577 | parse_expr( | 580 | parse_expr( |
@@ -581,7 +584,10 @@ mod test { | |||
581 | ), | 584 | ), |
582 | Ok(( | 585 | Ok(( |
583 | "", | 586 | "", |
584 | Expr::FieldAccess(vec!["foo".to_owned(), "bar".to_owned()]) | 587 | Expr::FieldAccess( |
588 | Expr::FieldAccess(Expr::Node.boxed(), "foo".to_owned()).boxed(), | ||
589 | "bar".to_owned() | ||
590 | ) | ||
585 | )) | 591 | )) |
586 | ); | 592 | ); |
587 | } | 593 | } |
@@ -600,7 +606,7 @@ mod test { | |||
600 | ), | 606 | ), |
601 | Ok(( | 607 | Ok(( |
602 | "", | 608 | "", |
603 | Expr::IfExpr(If { | 609 | Expr::If(IfExpr { |
604 | condition: Expr::Bin( | 610 | condition: Expr::Bin( |
605 | Expr::int(1).boxed(), | 611 | Expr::int(1).boxed(), |
606 | BinOp::Cmp(CmpOp::Eq), | 612 | BinOp::Cmp(CmpOp::Eq), |
@@ -713,7 +719,7 @@ mod test { | |||
713 | parse_statement("if (true) { true; };"), | 719 | parse_statement("if (true) { true; };"), |
714 | Ok(( | 720 | Ok(( |
715 | "", | 721 | "", |
716 | Statement::Bare(Expr::IfExpr(If { | 722 | Statement::Bare(Expr::If(IfExpr { |
717 | condition: Expr::true_().boxed(), | 723 | condition: Expr::true_().boxed(), |
718 | then: Block { | 724 | then: Block { |
719 | body: vec![Statement::Bare(Expr::true_())] | 725 | body: vec![Statement::Bare(Expr::true_())] |
@@ -726,7 +732,7 @@ mod test { | |||
726 | parse_expr("if (true) { true; } else { true; }"), | 732 | parse_expr("if (true) { true; } else { true; }"), |
727 | Ok(( | 733 | Ok(( |
728 | "", | 734 | "", |
729 | Expr::IfExpr(If { | 735 | Expr::If(IfExpr { |
730 | condition: Expr::true_().boxed(), | 736 | condition: Expr::true_().boxed(), |
731 | then: Block { | 737 | then: Block { |
732 | body: vec![Statement::Bare(Expr::true_())] | 738 | body: vec![Statement::Bare(Expr::true_())] |