Browse Source

implement let

Getty Ritter 2 years ago
parent
commit
57d308454e
3 changed files with 26 additions and 7 deletions
  1. 8 3
      src/ast.rs
  2. 2 2
      src/grammar.lalrpop
  3. 16 2
      src/interp.rs

+ 8 - 3
src/ast.rs

@@ -153,8 +153,13 @@ impl ASTArena {
                 writeln!(f, ")")
             }
 
-            Expr::Let(name, expr, body) => {
-                writeln!(f, "Let({}", &self[*name])?;
+            Expr::Let(fixed, name, expr, body) => {
+                writeln!(
+                    f,
+                    "Let({}{}",
+                    if *fixed { "fixed " } else { "" },
+                    &self[*name]
+                )?;
                 self.indent(f, depth + 2)?;
                 self.show_expr(&self[*expr], f, depth + 2)?;
                 self.indent(f, depth + 2)?;
@@ -247,7 +252,7 @@ pub enum Expr {
     Lit(Literal),
     Ap(ExprRef, ExprRef),
     Tup(Vec<ExprRef>),
-    Let(Name, ExprRef, ExprRef),
+    Let(bool, Name, ExprRef, ExprRef),
     Fun(Vec<Case>),
     Range(ExprRef, ExprRef),
     Nil,

+ 2 - 2
src/grammar.lalrpop

@@ -120,8 +120,8 @@ pub Leaf: ExprRef = {
             ast.add_expr(Expr::Tup(es))
         }
     },
-    "let" <name:Name> ":=" <e1:Expr> "in" "{" <e2:Expr> "}" =>
-        ast.add_expr(Expr::Let(name, e1, e2)),
+    "let" <fixed:"fix"?> <name:Name> ":=" <e1:Expr> "in" "{" <e2:Expr> "}" =>
+        ast.add_expr(Expr::Let(fixed.is_some(), name, e1, e2)),
     "{" <mut cs:(<Case> ";")*> <c:Case> "}" => {
         cs.push(c);
         ast.add_expr(Expr::Fun(cs))

+ 16 - 2
src/interp.rs

@@ -3,8 +3,8 @@ use rand::Rng;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt;
-use std::rc::Rc;
 use std::io;
+use std::rc::Rc;
 
 macro_rules! bail {
     ($fmt:expr) => { return Err(Error { message: format!($fmt), }) };
@@ -477,7 +477,21 @@ impl State {
                 _ => bail!("Bad function: {:?}", func),
             },
 
-            _ => bail!("unimplemented: {:?}", expr),
+            Expr::Let(fixed, name, val, body) => {
+                let mut new_scope = HashMap::new();
+                if *fixed {
+                    let val = self.eval(*val, env)?;
+                    let val = self.force(val)?;
+                    new_scope.insert(*name, Thunk::Value(val));
+                } else {
+                    new_scope.insert(*name, Thunk::Expr(*val, env.clone()));
+                };
+                let new_scope = Rc::new(Scope {
+                    vars: new_scope,
+                    parent: env.clone(),
+                });
+                self.eval(*body, &Some(new_scope))
+            }
         }
     }