Browse Source

add Expr::Nil to handle empty tuple

Getty Ritter 3 years ago
parent
commit
ac57d9d3ec
4 changed files with 21 additions and 14 deletions
  1. 8 6
      src/ast.rs
  2. 10 6
      src/grammar.lalrpop
  3. 3 0
      src/interp.rs
  4. 0 2
      tests/exprs.parsed

+ 8 - 6
src/ast.rs

@@ -79,6 +79,7 @@ impl ASTArena {
 
     fn show_expr(&self, expr: &Expr, f: &mut fmt::Formatter, depth: usize) -> fmt::Result {
         match expr {
+            Expr::Nil => writeln!(f, "Nil"),
             Expr::Var(v) => writeln!(f, "Var({})", &self[*v]),
             Expr::Lit(Literal::Atom(n)) => writeln!(f, "Lit(Atom({}))", &self[*n]),
             Expr::Lit(lit) => writeln!(f, "{:?}", lit),
@@ -194,7 +195,7 @@ impl<'a> std::fmt::Debug for Printable<'a, Expr> {
 }
 
 /// A top-level Matzo statement
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum Stmt {
     /// evaluate and print the value of an expression
     Puts(Expr),
@@ -217,7 +218,7 @@ impl Stmt {
 }
 
 /// A Matzo expression
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum Expr {
     Var(Name),
     Cat(Vec<Expr>),
@@ -228,17 +229,18 @@ pub enum Expr {
     Let(Name, Box<Expr>, Box<Expr>),
     Fun(Vec<Case>),
     Range(Box<Expr>, Box<Expr>),
+    Nil,
 }
 
 /// A single case in an anonymous function or `case` statement
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Case {
     pub pat: Pat,
     pub expr: Expr,
 }
 
 /// A pattern, e.g. in an anonymous function or `case` statement
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum Pat {
     Var(Name),
     Lit(Literal),
@@ -247,7 +249,7 @@ pub enum Pat {
 
 /// A single element in a choice, with an optional weight (which
 /// defaults to 1) and a value
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Choice {
     pub weight: Option<i64>,
     pub value: Expr,
@@ -261,7 +263,7 @@ impl Choice {
 }
 
 /// An atomic literal: a string, a number, or an atom
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum Literal {
     Str(String),
     Atom(Name),

+ 10 - 6
src/grammar.lalrpop

@@ -81,10 +81,10 @@ pub Choice: Choice = {
 
 pub Term: Expr = {
     <mut bs:(<Branch>)*> => {
-        if bs.len() == 1 {
-            bs.pop().unwrap()
-        } else {
-            Expr::Cat(<>)
+        match bs.len() {
+            0 => Expr::Nil,
+            1 => bs.pop().unwrap(),
+            _ => Expr::Cat(<>),
         }
     }
 };
@@ -103,8 +103,12 @@ pub Leaf: Expr = {
     <Literal> => Expr::Lit(<>),
     <Name> => Expr::Var(<>),
     "<" <mut es:(<Expr> ",")*> <e:Expr> ">" => {
-        es.push(e);
-        Expr::Tup(es)
+        if es.len() == 0 && e == Expr::Nil {
+            Expr::Tup(Vec::new())
+        } else {
+            es.push(e);
+            Expr::Tup(es)
+        }
     },
     "let" <name:Name> ":=" <e1:Expr> "in" "{" <e2:Expr> "}" =>
         Expr::Let(name, Box::new(e1), Box::new(e2)),

+ 3 - 0
src/interp.rs

@@ -13,6 +13,7 @@ pub enum Value {
     Lit(Literal),
     Tup(Vec<Value>),
     Builtin(&'static BuiltinFunc),
+    Nil,
 }
 
 impl Value {
@@ -32,6 +33,7 @@ impl Value {
 
     fn with_str<U>(&self, f: impl FnOnce(&str) -> U) -> U {
         match self {
+            Value::Nil => f(""),
             Value::Lit(Literal::Str(s)) => f(s),
             Value::Lit(Literal::Atom(s)) => f(&format!("{:?}", s)),
             Value::Lit(Literal::Num(n)) => f(&format!("{}", n)),
@@ -266,6 +268,7 @@ impl State {
     fn eval(&mut self, expr: &Expr) -> Result<Value, Error> {
         match expr {
             Expr::Lit(l) => Ok(Value::Lit(l.clone())),
+            Expr::Nil => Ok(Value::Nil),
             Expr::Var(v) => {
                 let e = match self.scope.get(v) {
                     Some(NamedItem::Expr(e)) => e.clone(),

+ 0 - 2
tests/exprs.parsed

@@ -25,8 +25,6 @@ Puts Ap(
 )
 
 Puts Tup(
-  Cat(
-  )
 )
 
 Puts Tup(