Sfoglia il codice sorgente

start implementing closures

Getty Ritter 2 anni fa
parent
commit
4e6ac41ea3
1 ha cambiato i file con 72 aggiunte e 42 eliminazioni
  1. 72 42
      src/interp.rs

+ 72 - 42
src/interp.rs

@@ -3,6 +3,7 @@ use rand::Rng;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt;
+use std::rc::Rc;
 
 macro_rules! bail {
     ($fmt:expr) => { return Err(Error { message: format!($fmt), }) };
@@ -14,6 +15,7 @@ pub enum Value {
     Lit(Literal),
     Tup(Vec<Value>),
     Builtin(&'static BuiltinFunc),
+    Closure(Closure),
     Nil,
 }
 
@@ -51,6 +53,7 @@ impl Value {
                 f(&buf)
             }
             Value::Builtin(func) => f(&format!("#<builtin {}>", func.name)),
+            Value::Closure(_) => f(&format!("#<lambda ...>")),
         }
     }
 }
@@ -63,7 +66,7 @@ impl fmt::Display for Value {
 
 pub struct BuiltinFunc {
     name: &'static str,
-    callback: &'static dyn Fn(&State, ExprRef) -> Result<Value, Error>,
+    callback: &'static dyn Fn(&State, ExprRef, &Env) -> Result<Value, Error>,
 }
 
 #[derive(Debug)]
@@ -94,7 +97,7 @@ impl std::error::Error for Error {}
 const BUILTINS: &[BuiltinFunc] = &[
     BuiltinFunc {
         name: "rep",
-        callback: &|state: &State, expr: ExprRef| -> Result<Value, Error> {
+        callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
             let (rep, expr) = {
                 let ast = state.ast.borrow();
                 let args = match &ast[expr] {
@@ -107,16 +110,16 @@ const BUILTINS: &[BuiltinFunc] = &[
                 (args[0], args[1])
             };
             let mut buf = String::new();
-            let num = state.eval(rep)?.as_num()?;
+            let num = state.eval(rep, env)?.as_num()?;
             for _ in 0..num {
-                buf.push_str(&state.eval(expr)?.as_str()?.to_string());
+                buf.push_str(&state.eval(expr, env)?.as_str()?.to_string());
             }
             Ok(Value::Lit(Literal::Str(buf)))
         },
     },
     BuiltinFunc {
         name: "length",
-        callback: &|state: &State, expr: ExprRef| -> Result<Value, Error> {
+        callback: &|state: &State, expr: ExprRef, _env: &Env| -> Result<Value, Error> {
             let ast = state.ast.borrow();
             let args = match &ast[expr] {
                 Expr::Tup(tup) => tup,
@@ -127,15 +130,15 @@ const BUILTINS: &[BuiltinFunc] = &[
     },
     BuiltinFunc {
         name: "to-upper",
-        callback: &|state: &State, expr: ExprRef| -> Result<Value, Error> {
-            let s = state.eval(expr)?;
+        callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
+            let s = state.eval(expr, env)?;
             Ok(Value::Lit(Literal::Str(s.as_str()?.to_uppercase())))
         },
     },
     BuiltinFunc {
         name: "to-lower",
-        callback: &|state: &State, expr: ExprRef| -> Result<Value, Error> {
-            let s = state.eval(expr)?;
+        callback: &|state: &State, expr: ExprRef, env: &Env| -> Result<Value, Error> {
+            let s = state.eval(expr, env)?;
             Ok(Value::Lit(Literal::Str(s.as_str()?.to_lowercase())))
         },
     },
@@ -147,15 +150,29 @@ impl fmt::Debug for BuiltinFunc {
     }
 }
 
+#[derive(Debug, Clone)]
 enum NamedItem {
     Expr(ExprRef),
     Value(Value),
     Builtin(&'static BuiltinFunc),
 }
 
+type Env = Option<Rc<Scope>>;
+#[derive(Debug)]
+pub struct Scope {
+    vars: HashMap<Name, NamedItem>,
+    parent: Env,
+}
+
+#[derive(Debug, Clone)]
+pub struct Closure {
+    expr: ExprRef,
+    scope: Env,
+}
+
 pub struct State {
     ast: RefCell<ASTArena>,
-    scope: RefCell<HashMap<Name, NamedItem>>,
+    root_scope: RefCell<HashMap<Name, NamedItem>>,
     rand: RefCell<rand::rngs::ThreadRng>,
     parser: crate::grammar::StmtsParser,
 }
@@ -169,20 +186,35 @@ impl Default for State {
 impl State {
     pub fn new() -> State {
         let s = State {
-            scope: RefCell::new(HashMap::new()),
+            root_scope: RefCell::new(HashMap::new()),
             rand: RefCell::new(rand::thread_rng()),
             parser: crate::grammar::StmtsParser::new(),
             ast: RefCell::new(ASTArena::new()),
         };
         for builtin in BUILTINS {
             let sym = s.ast.borrow_mut().add_string(builtin.name);
-            s.scope
+            s.root_scope
                 .borrow_mut()
                 .insert(sym, NamedItem::Builtin(builtin));
         }
         s
     }
 
+    fn lookup(&self, env: &Env, name: Name) -> Result<NamedItem, Error> {
+        if let Some(env) = env {
+            if let Some(ne) = env.vars.get(&name) {
+                Ok(ne.clone())
+            } else {
+                self.lookup(&env.parent, name)
+            }
+        } else {
+            match self.root_scope.borrow().get(&name) {
+                None => bail!("no such thing: {:?}", name),
+                Some(ne) => Ok(ne.clone()),
+            }
+        }
+    }
+
     pub fn run(&self, src: &str) -> Result<(), Error> {
         let lexed = crate::lexer::tokens(src);
         let stmts = self.parser.parse(&mut self.ast.borrow_mut(), lexed)?;
@@ -218,7 +250,7 @@ impl State {
 
     pub fn autocomplete(&self, fragment: &str, at_beginning: bool) -> Vec<String> {
         let mut possibilities = Vec::new();
-        for name in self.scope.borrow().keys() {
+        for name in self.root_scope.borrow().keys() {
             if self.ast.borrow()[*name].starts_with(fragment) {
                 possibilities.push(self.ast.borrow()[*name].to_string());
             }
@@ -232,27 +264,26 @@ impl State {
     pub fn execute(&self, stmt: &Stmt) -> Result<(), Error> {
         match stmt {
             Stmt::Puts(expr) => {
-                let val = self.eval(*expr)?;
+                let val = self.eval(*expr, &None)?;
                 println!("{}", val.to_string());
             }
 
             Stmt::Fix(name) => {
-                let expr = match self.scope.borrow().get(name) {
-                    None => bail!("no such thing: {:?}", name),
-                    Some(NamedItem::Expr(e)) => *e,
+                let expr = match self.lookup(&None, *name)? {
+                    NamedItem::Expr(e) => e,
                     // if it's not an expr, then our work here is done
                     _ => return Ok(()),
                 };
-                let val = self.eval(expr)?;
-                self.scope.borrow_mut().insert(*name, NamedItem::Value(val));
+                let val = self.eval(expr, &None)?;
+                self.root_scope.borrow_mut().insert(*name, NamedItem::Value(val));
             }
 
             Stmt::Assn(fixed, name, expr) => {
                 if *fixed {
-                    let val = self.eval(*expr)?;
-                    self.scope.borrow_mut().insert(*name, NamedItem::Value(val));
+                    let val = self.eval(*expr, &None)?;
+                    self.root_scope.borrow_mut().insert(*name, NamedItem::Value(val));
                 } else {
-                    self.scope
+                    self.root_scope
                         .borrow_mut()
                         .insert(*name, NamedItem::Expr(*expr));
                 }
@@ -261,7 +292,7 @@ impl State {
             Stmt::LitAssn(fixed, name, strs) => {
                 if *fixed {
                     let choice = &strs[self.rand.borrow_mut().gen_range(0..strs.len())];
-                    self.scope.borrow_mut().insert(
+                    self.root_scope.borrow_mut().insert(
                         *name,
                         NamedItem::Value(Value::Lit(Literal::Str(choice.clone()))),
                     );
@@ -279,7 +310,7 @@ impl State {
                     })
                     .collect();
                 let choices = self.ast.borrow_mut().add_expr(Expr::Chc(choices));
-                self.scope
+                self.root_scope
                     .borrow_mut()
                     .insert(*name, NamedItem::Expr(choices));
             }
@@ -287,27 +318,26 @@ impl State {
         Ok(())
     }
 
-    fn eval(&self, expr: ExprRef) -> Result<Value, Error> {
+    fn eval(&self, expr: ExprRef, env: &Env) -> Result<Value, Error> {
         let expr = &self.ast.borrow()[expr];
         match expr {
             Expr::Lit(l) => Ok(Value::Lit(l.clone())),
             Expr::Nil => Ok(Value::Nil),
             Expr::Var(v) => {
-                let e = match self.scope.borrow().get(&v) {
-                    Some(NamedItem::Expr(e)) => *e,
-                    Some(NamedItem::Value(v)) => return Ok(v.clone()),
-                    Some(NamedItem::Builtin(b)) => return Ok(Value::Builtin(b)),
-                    None => bail!("no such thing: {:?}", v),
+                let e = match self.lookup(env, *v)? {
+                    NamedItem::Expr(e) => e,
+                    NamedItem::Value(v) => return Ok(v.clone()),
+                    NamedItem::Builtin(b) => return Ok(Value::Builtin(b)),
                 };
-                self.eval(e)
+                self.eval(e, env)
             }
             Expr::Cat(cat) => {
                 if cat.len() == 1 {
-                    self.eval(cat[0])
+                    self.eval(cat[0], env)
                 } else {
                     let mut buf = String::new();
                     for expr in cat {
-                        let val = self.eval(*expr)?;
+                        let val = self.eval(*expr, env)?;
                         buf.push_str(&val.to_string());
                     }
                     Ok(Value::Lit(Literal::Str(buf)))
@@ -315,24 +345,24 @@ impl State {
             }
             Expr::Chc(choices) => {
                 if choices.len() == 1 {
-                    self.eval(choices[0].value)
+                    self.eval(choices[0].value, env)
                 } else {
-                    self.choose(&choices)
+                    self.choose(&choices, env)
                 }
             }
             Expr::Tup(values) => Ok(Value::Tup(
                 values
                     .iter()
-                    .map(|v| self.eval(*v))
+                    .map(|v| self.eval(*v, env))
                     .collect::<Result<Vec<Value>, Error>>()?,
             )),
-            Expr::Ap(fun, arg) => match self.eval(*fun)? {
-                Value::Builtin(builtin) => (builtin.callback)(self, *arg),
+            Expr::Ap(fun, arg) => match self.eval(*fun, env)? {
+                Value::Builtin(builtin) => (builtin.callback)(self, *arg, env),
                 _ => bail!("bad function: {:?}", fun),
             },
             Expr::Range(from, to) => {
-                let from = self.eval(*from)?.as_num()?;
-                let to = self.eval(*to)?.as_num()?;
+                let from = self.eval(*from, env)?.as_num()?;
+                let to = self.eval(*to, env)?.as_num()?;
                 Ok(Value::Lit(Literal::Num(
                     self.rand.borrow_mut().gen_range(from..=to),
                 )))
@@ -341,12 +371,12 @@ impl State {
         }
     }
 
-    fn choose(&self, choices: &[Choice]) -> Result<Value, Error> {
+    fn choose(&self, choices: &[Choice], env: &Env) -> Result<Value, Error> {
         let max = choices.iter().map(Choice::weight).sum();
         let mut choice = self.rand.borrow_mut().gen_range(0..max);
         for ch in choices {
             if choice < ch.weight() {
-                return self.eval(ch.value);
+                return self.eval(ch.value, env);
             }
             choice -= ch.weight();
         }