Просмотр исходного кода

switch to using an ExprRef approach

Getty Ritter 2 лет назад
Родитель
Сommit
ed28852265
3 измененных файлов с 142 добавлено и 101 удалено
  1. 47 22
      src/ast.rs
  2. 17 17
      src/grammar.lalrpop
  3. 78 62
      src/interp.rs

+ 47 - 22
src/ast.rs

@@ -4,6 +4,7 @@ pub type Name = string_interner::DefaultSymbol;
 
 pub struct ASTArena {
     strings: string_interner::StringInterner,
+    exprs: Vec<Expr>,
 }
 
 impl Default for ASTArena {
@@ -16,9 +17,20 @@ impl ASTArena {
     pub fn new() -> ASTArena {
         ASTArena {
             strings: string_interner::StringInterner::new(),
+            exprs: vec![Expr::Nil],
         }
     }
 
+    pub fn expr_nil(&self) -> ExprRef {
+        ExprRef { idx: 0 }
+    }
+
+    pub fn add_expr(&mut self, e: Expr) -> ExprRef {
+        let idx = self.exprs.len();
+        self.exprs.push(e);
+        ExprRef { idx }
+    }
+
     pub fn add_string(&mut self, s: &str) -> Name {
         self.strings.get_or_intern(s)
     }
@@ -27,7 +39,7 @@ impl ASTArena {
         match stmt {
             Stmt::Puts(expr) => {
                 write!(f, "Puts ")?;
-                self.show_expr(expr, f, 0)
+                self.show_expr(&self[*expr], f, 0)
             }
             Stmt::Fix(name) => writeln!(f, "Fix {}", &self[*name]),
             Stmt::Assn(fixed, name, expr) => {
@@ -37,7 +49,7 @@ impl ASTArena {
                     if *fixed { "fixed" } else { "" },
                     &self[*name]
                 )?;
-                self.show_expr(expr, f, 0)
+                self.show_expr(&self[*expr], f, 0)
             }
             Stmt::LitAssn(fixed, name, strs) => {
                 write!(
@@ -86,9 +98,9 @@ impl ASTArena {
             Expr::Range(from, to) => {
                 writeln!(f, "Range(")?;
                 self.indent(f, depth + 2)?;
-                self.show_expr(from, f, depth + 2)?;
+                self.show_expr(&self[*from], f, depth + 2)?;
                 self.indent(f, depth + 2)?;
-                self.show_expr(to, f, depth + 2)?;
+                self.show_expr(&self[*to], f, depth + 2)?;
                 self.indent(f, depth)?;
                 writeln!(f, ")")
             }
@@ -96,9 +108,9 @@ impl ASTArena {
             Expr::Ap(func, arg) => {
                 writeln!(f, "Ap(")?;
                 self.indent(f, depth + 2)?;
-                self.show_expr(func, f, depth + 2)?;
+                self.show_expr(&self[*func], f, depth + 2)?;
                 self.indent(f, depth + 2)?;
-                self.show_expr(arg, f, depth + 2)?;
+                self.show_expr(&self[*arg], f, depth + 2)?;
                 self.indent(f, depth)?;
                 writeln!(f, ")")
             }
@@ -107,7 +119,7 @@ impl ASTArena {
                 writeln!(f, "Tup(")?;
                 for e in expr {
                     self.indent(f, depth + 2)?;
-                    self.show_expr(e, f, depth + 2)?;
+                    self.show_expr(&self[*e], f, depth + 2)?;
                 }
                 self.indent(f, depth)?;
                 writeln!(f, ")")
@@ -117,7 +129,7 @@ impl ASTArena {
                 writeln!(f, "Cat(")?;
                 for e in expr {
                     self.indent(f, depth + 2)?;
-                    self.show_expr(e, f, depth + 2)?;
+                    self.show_expr(&self[*e], f, depth + 2)?;
                 }
                 self.indent(f, depth)?;
                 writeln!(f, ")")
@@ -130,10 +142,10 @@ impl ASTArena {
                         self.indent(f, depth + 2)?;
                         writeln!(f, "{}:", s)?;
                         self.indent(f, depth + 4)?;
-                        self.show_expr(&e.value, f, depth + 4)?;
+                        self.show_expr(&self[e.value], f, depth + 4)?;
                     } else {
                         self.indent(f, depth + 2)?;
-                        self.show_expr(&e.value, f, depth + 2)?;
+                        self.show_expr(&self[e.value], f, depth + 2)?;
                     }
                 }
                 self.indent(f, depth)?;
@@ -143,9 +155,9 @@ impl ASTArena {
             Expr::Let(name, expr, body) => {
                 writeln!(f, "Let({}", &self[*name])?;
                 self.indent(f, depth + 2)?;
-                self.show_expr(expr, f, depth + 2)?;
+                self.show_expr(&self[*expr], f, depth + 2)?;
                 self.indent(f, depth + 2)?;
-                self.show_expr(body, f, depth + 2)?;
+                self.show_expr(&self[*body], f, depth + 2)?;
                 self.indent(f, depth)?;
                 writeln!(f, ")")
             }
@@ -157,7 +169,7 @@ impl ASTArena {
                     self.show_pat(&case.pat, f)?;
                     writeln!(f, " =>")?;
                     self.indent(f, depth + 4)?;
-                    self.show_expr(&case.expr, f, depth + 4)?;
+                    self.show_expr(&self[case.expr], f, depth + 4)?;
                 }
                 self.indent(f, depth)?;
                 writeln!(f, ")")
@@ -174,6 +186,14 @@ impl std::ops::Index<string_interner::DefaultSymbol> for ASTArena {
     }
 }
 
+impl std::ops::Index<ExprRef> for ASTArena {
+    type Output = Expr;
+
+    fn index(&self, rf: ExprRef) -> &Self::Output {
+        &self.exprs[rf.idx]
+    }
+}
+
 /// A `Printable` struct is a bundle of another value and an
 /// `ASTArena`, which allows us to fetch the various indices and
 /// dereference the interned strings.
@@ -198,11 +218,11 @@ impl<'a> std::fmt::Debug for Printable<'a, Expr> {
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum Stmt {
     /// evaluate and print the value of an expression
-    Puts(Expr),
+    Puts(ExprRef),
     /// replace a named item with the forced version of that item
     Fix(Name),
     /// assign a value to a name which may or may not be forced
-    Assn(bool, Name, Expr),
+    Assn(bool, Name, ExprRef),
     /// assign one of a set of strings to a name, which may or may not
     /// be forced
     LitAssn(bool, Name, Vec<String>),
@@ -221,22 +241,27 @@ impl Stmt {
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum Expr {
     Var(Name),
-    Cat(Vec<Expr>),
+    Cat(Vec<ExprRef>),
     Chc(Vec<Choice>),
     Lit(Literal),
-    Ap(Box<Expr>, Box<Expr>),
-    Tup(Vec<Expr>),
-    Let(Name, Box<Expr>, Box<Expr>),
+    Ap(ExprRef, ExprRef),
+    Tup(Vec<ExprRef>),
+    Let(Name, ExprRef, ExprRef),
     Fun(Vec<Case>),
-    Range(Box<Expr>, Box<Expr>),
+    Range(ExprRef, ExprRef),
     Nil,
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct ExprRef {
+    idx: usize,
+}
+
 /// A single case in an anonymous function or `case` statement
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Case {
     pub pat: Pat,
-    pub expr: Expr,
+    pub expr: ExprRef,
 }
 
 /// A pattern, e.g. in an anonymous function or `case` statement
@@ -252,7 +277,7 @@ pub enum Pat {
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Choice {
     pub weight: Option<i64>,
-    pub value: Expr,
+    pub value: ExprRef,
 }
 
 impl Choice {

+ 17 - 17
src/grammar.lalrpop

@@ -57,13 +57,13 @@ pub Name: Name = {
     "var" => ast.add_string(<>),
 };
 
-pub Expr: Expr = {
+pub Expr: ExprRef = {
     <mut ts:(<Choice> "|")*> <t:Choice> => {
         if ts.len() == 0 {
             t.value
         } else {
             ts.push(t);
-            Expr::Chc(ts)
+            ast.add_expr(Expr::Chc(ts))
         }
     }
 };
@@ -79,42 +79,42 @@ pub Choice: Choice = {
     }
 };
 
-pub Term: Expr = {
+pub Term: ExprRef = {
     <mut bs:(<Branch>)*> => {
         match bs.len() {
-            0 => Expr::Nil,
+            0 => ast.expr_nil(),
             1 => bs.pop().unwrap(),
-            _ => Expr::Cat(<>),
+            _ => ast.add_expr(Expr::Cat(<>)),
         }
     }
 };
 
-pub Branch: Expr = {
-    <l:Branch> "." <r:Subbranch> => Expr::Ap(Box::new(l), Box::new(r)),
+pub Branch: ExprRef = {
+    <l:Branch> "." <r:Subbranch> => ast.add_expr(Expr::Ap(l, r)),
     <Subbranch> => <>,
 };
 
-pub Subbranch: Expr = {
-    <l:Subbranch> ".." <r:Leaf> => Expr::Range(Box::new(l), Box::new(r)),
+pub Subbranch: ExprRef = {
+    <l:Subbranch> ".." <r:Leaf> => ast.add_expr(Expr::Range(l, r)),
     <Leaf> => <>,
 }
 
-pub Leaf: Expr = {
-    <Literal> => Expr::Lit(<>),
-    <Name> => Expr::Var(<>),
+pub Leaf: ExprRef = {
+    <Literal> => ast.add_expr(Expr::Lit(<>)),
+    <Name> => ast.add_expr(Expr::Var(<>)),
     "<" <mut es:(<Expr> ",")*> <e:Expr> ">" => {
-        if es.len() == 0 && e == Expr::Nil {
-            Expr::Tup(Vec::new())
+        if es.len() == 0 && e == ast.expr_nil() {
+            ast.add_expr(Expr::Tup(Vec::new()))
         } else {
             es.push(e);
-            Expr::Tup(es)
+            ast.add_expr(Expr::Tup(es))
         }
     },
     "let" <name:Name> ":=" <e1:Expr> "in" "{" <e2:Expr> "}" =>
-        Expr::Let(name, Box::new(e1), Box::new(e2)),
+        ast.add_expr(Expr::Let(name, e1, e2)),
     "{" <mut cs:(<Case> ";")*> <c:Case> "}" => {
         cs.push(c);
-        Expr::Fun(cs)
+        ast.add_expr(Expr::Fun(cs))
     },
     "(" <e:Expr> ")" => e,
 };

+ 78 - 62
src/interp.rs

@@ -1,5 +1,6 @@
 use crate::ast::*;
 use rand::Rng;
+use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt;
 
@@ -62,7 +63,7 @@ impl fmt::Display for Value {
 
 pub struct BuiltinFunc {
     name: &'static str,
-    callback: &'static dyn Fn(&mut State, &Expr) -> Result<Value, Error>,
+    callback: &'static dyn Fn(&State, ExprRef) -> Result<Value, Error>,
 }
 
 #[derive(Debug)]
@@ -93,23 +94,31 @@ impl std::error::Error for Error {}
 const BUILTINS: &[BuiltinFunc] = &[
     BuiltinFunc {
         name: "rep",
-        callback: &|state: &mut State, expr: &Expr| -> Result<Value, Error> {
-            let args = match expr {
-                Expr::Tup(tup) => tup,
-                _ => bail!("`rep`: expected tuple"),
+        callback: &|state: &State, expr: ExprRef| -> Result<Value, Error> {
+            let (rep, expr) = {
+                let ast = state.ast.borrow();
+                let args = match &ast[expr] {
+                    Expr::Tup(tup) => tup,
+                    _ => bail!("`rep`: expected tuple"),
+                };
+                if args.len() != 2 {
+                    bail!("`rep`: expected two arguments, got {}", args.len())
+                }
+                (args[0], args[1])
             };
-            if args.len() != 2 {
-                bail!("`rep`: expected two arguments, got {}", args.len())
+            let mut buf = String::new();
+            let num = state.eval(rep)?.as_num()?;
+            for _ in 0..num {
+                buf.push_str(&state.eval(expr)?.as_str()?.to_string());
             }
-            let num = state.eval(&args[0])?.as_num()?;
-            let rep = (0..num).map(|_| args[1].clone()).collect();
-            state.eval(&Expr::Cat(rep))
+            Ok(Value::Lit(Literal::Str(buf)))
         },
     },
     BuiltinFunc {
         name: "length",
-        callback: &|_state: &mut State, expr: &Expr| -> Result<Value, Error> {
-            let args = match expr {
+        callback: &|state: &State, expr: ExprRef| -> Result<Value, Error> {
+            let ast = state.ast.borrow();
+            let args = match &ast[expr] {
                 Expr::Tup(tup) => tup,
                 _ => bail!("`length`: expected tuple"),
             };
@@ -118,14 +127,14 @@ const BUILTINS: &[BuiltinFunc] = &[
     },
     BuiltinFunc {
         name: "to-upper",
-        callback: &|state: &mut State, expr: &Expr| -> Result<Value, Error> {
+        callback: &|state: &State, expr: ExprRef| -> Result<Value, Error> {
             let s = state.eval(expr)?;
             Ok(Value::Lit(Literal::Str(s.as_str()?.to_uppercase())))
         },
     },
     BuiltinFunc {
         name: "to-lower",
-        callback: &|state: &mut State, expr: &Expr| -> Result<Value, Error> {
+        callback: &|state: &State, expr: ExprRef| -> Result<Value, Error> {
             let s = state.eval(expr)?;
             Ok(Value::Lit(Literal::Str(s.as_str()?.to_lowercase())))
         },
@@ -139,15 +148,15 @@ impl fmt::Debug for BuiltinFunc {
 }
 
 enum NamedItem {
-    Expr(Expr),
+    Expr(ExprRef),
     Value(Value),
     Builtin(&'static BuiltinFunc),
 }
 
 pub struct State {
-    ast: ASTArena,
-    scope: HashMap<Name, NamedItem>,
-    rand: rand::rngs::ThreadRng,
+    ast: RefCell<ASTArena>,
+    scope: RefCell<HashMap<Name, NamedItem>>,
+    rand: RefCell<rand::rngs::ThreadRng>,
     parser: crate::grammar::StmtsParser,
 }
 
@@ -159,36 +168,40 @@ impl Default for State {
 
 impl State {
     pub fn new() -> State {
-        let mut s = State {
-            scope: HashMap::new(),
-            rand: rand::thread_rng(),
+        let s = State {
+            scope: RefCell::new(HashMap::new()),
+            rand: RefCell::new(rand::thread_rng()),
             parser: crate::grammar::StmtsParser::new(),
-            ast: ASTArena::new(),
+            ast: RefCell::new(ASTArena::new()),
         };
         for builtin in BUILTINS {
-            let sym = s.ast.add_string(builtin.name);
-            s.scope.insert(sym, NamedItem::Builtin(builtin));
+            let sym = s.ast.borrow_mut().add_string(builtin.name);
+            s.scope.borrow_mut().insert(sym, NamedItem::Builtin(builtin));
         }
         s
     }
 
-    pub fn run(&mut self, src: &str) -> Result<(), Error> {
+    pub fn run(&self, src: &str) -> Result<(), Error> {
         let lexed = crate::lexer::tokens(src);
-        let stmts = self.parser.parse(&mut self.ast, lexed)?;
+        let stmts = self.parser.parse(&mut self.ast.borrow_mut(), lexed)?;
         for stmt in stmts {
             self.execute(&stmt)?;
         }
         Ok(())
     }
 
-    pub fn run_repl(&mut self, src: &str) -> Result<(), Error> {
+    pub fn run_repl(&self, src: &str) -> Result<(), Error> {
         let lexed = crate::lexer::tokens(src);
-        let stmts = match self.parser.parse(&mut self.ast, lexed) {
+        let stmts = {
+            let mut ast = self.ast.borrow_mut();
+            self.parser.parse(&mut ast, lexed)
+        };
+        let stmts = match stmts {
             Ok(stmts) => stmts,
             Err(err) => {
                 let with_puts = format!("puts {}", src);
                 let lexed = crate::lexer::tokens(&with_puts);
-                if let Ok(stmts) = self.parser.parse(&mut self.ast, lexed) {
+                if let Ok(stmts) = self.parser.parse(&mut self.ast.borrow_mut(), lexed) {
                     stmts
                 } else {
                     return Err(err.into());
@@ -203,9 +216,9 @@ impl State {
 
     pub fn autocomplete(&self, fragment: &str, at_beginning: bool) -> Vec<String> {
         let mut possibilities = Vec::new();
-        for name in self.scope.keys() {
-            if self.ast[*name].starts_with(fragment) {
-                possibilities.push(self.ast[*name].to_string());
+        for name in self.scope.borrow().keys() {
+            if self.ast.borrow()[*name].starts_with(fragment) {
+                possibilities.push(self.ast.borrow()[*name].to_string());
             }
         }
         if at_beginning && "puts".starts_with(fragment) {
@@ -214,37 +227,37 @@ impl State {
         possibilities
     }
 
-    pub fn execute(&mut self, stmt: &Stmt) -> Result<(), Error> {
+    pub fn execute(&self, stmt: &Stmt) -> Result<(), Error> {
         match stmt {
             Stmt::Puts(expr) => {
-                let val = self.eval(expr)?;
+                let val = self.eval(*expr)?;
                 println!("{}", val.to_string());
             }
 
             Stmt::Fix(name) => {
-                let expr = match self.scope.get(name) {
+                let expr = match self.scope.borrow().get(name) {
                     None => bail!("no such thing: {:?}", name),
-                    Some(NamedItem::Expr(e)) => e.clone(),
+                    Some(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.insert(*name, NamedItem::Value(val));
+                let val = self.eval(expr)?;
+                self.scope.borrow_mut().insert(*name, NamedItem::Value(val));
             }
 
             Stmt::Assn(fixed, name, expr) => {
                 if *fixed {
-                    let val = self.eval(expr)?;
-                    self.scope.insert(*name, NamedItem::Value(val));
+                    let val = self.eval(*expr)?;
+                    self.scope.borrow_mut().insert(*name, NamedItem::Value(val));
                 } else {
-                    self.scope.insert(*name, NamedItem::Expr(expr.clone()));
+                    self.scope.borrow_mut().insert(*name, NamedItem::Expr(expr.clone()));
                 }
             }
 
             Stmt::LitAssn(fixed, name, strs) => {
                 if *fixed {
-                    let choice = &strs[self.rand.gen_range(0..strs.len())];
-                    self.scope.insert(
+                    let choice = &strs[self.rand.borrow_mut().gen_range(0..strs.len())];
+                    self.scope.borrow_mut().insert(
                         *name,
                         NamedItem::Value(Value::Lit(Literal::Str(choice.clone()))),
                     );
@@ -255,36 +268,39 @@ impl State {
                     .iter()
                     .map(|s| Choice {
                         weight: None,
-                        value: Expr::Lit(Literal::Str(s.clone())),
+                        value: self.ast.borrow_mut().add_expr(Expr::Lit(Literal::Str(s.clone()))),
                     })
                     .collect();
+                let choices = self.ast.borrow_mut().add_expr(Expr::Chc(choices));
                 self.scope
-                    .insert(*name, NamedItem::Expr(Expr::Chc(choices)));
+                    .borrow_mut()
+                    .insert(*name, NamedItem::Expr(choices));
             }
         }
         Ok(())
     }
 
-    fn eval(&mut self, expr: &Expr) -> Result<Value, Error> {
+    fn eval(&self, expr: ExprRef) -> 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.get(v) {
-                    Some(NamedItem::Expr(e)) => e.clone(),
+                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),
                 };
-                self.eval(&e)
+                self.eval(e)
             }
             Expr::Cat(cat) => {
                 if cat.len() == 1 {
-                    self.eval(&cat[0])
+                    self.eval(cat[0])
                 } else {
                     let mut buf = String::new();
                     for expr in cat {
-                        let val = self.eval(expr)?;
+                        let val = self.eval(*expr)?;
                         buf.push_str(&val.to_string());
                     }
                     Ok(Value::Lit(Literal::Str(buf)))
@@ -292,36 +308,36 @@ impl State {
             }
             Expr::Chc(choices) => {
                 if choices.len() == 1 {
-                    self.eval(&choices[0].value)
+                    self.eval(choices[0].value)
                 } else {
-                    self.choose(choices)
+                    self.choose(&choices)
                 }
             }
             Expr::Tup(values) => Ok(Value::Tup(
                 values
                     .iter()
-                    .map(|v| self.eval(v))
+                    .map(|v| self.eval(*v))
                     .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)? {
+                Value::Builtin(builtin) => (builtin.callback)(self, *arg),
                 _ => bail!("bad function: {:?}", fun),
             },
             Expr::Range(from, to) => {
-                let from = self.eval(from)?.as_num()?;
-                let to = self.eval(to)?.as_num()?;
-                Ok(Value::Lit(Literal::Num(self.rand.gen_range(from..=to))))
+                let from = self.eval(*from)?.as_num()?;
+                let to = self.eval(*to)?.as_num()?;
+                Ok(Value::Lit(Literal::Num(self.rand.borrow_mut().gen_range(from..=to))))
             }
             _ => bail!("unimplemented: {:?}", expr),
         }
     }
 
-    fn choose(&mut self, choices: &[Choice]) -> Result<Value, Error> {
+    fn choose(&self, choices: &[Choice]) -> Result<Value, Error> {
         let max = choices.iter().map(Choice::weight).sum();
-        let mut choice = self.rand.gen_range(0..max);
+        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);
             }
             choice -= ch.weight();
         }