Преглед на файлове

implement the fix statement

Getty Ritter преди 2 години
родител
ревизия
f65783f898
променени са 5 файла, в които са добавени 62 реда и са изтрити 16 реда
  1. 17 8
      src/ast.rs
  2. 5 3
      src/grammar.lalrpop
  3. 33 5
      src/interp.rs
  4. 3 0
      src/lexer.rs
  5. 4 0
      src/repl.rs

+ 17 - 8
src/ast.rs

@@ -40,14 +40,23 @@ impl ASTArena {
                 write!(f, "Puts ")?;
                 self.show_expr(expr, f, 0)
             }
-            Stmt::Fix(name) =>
-                writeln!(f, "Fix({})", &self[*name]),
-            Stmt::Assn(name, expr) => {
-                write!(f, "Assn {} ", &self[*name])?;
+            Stmt::Fix(name) => writeln!(f, "Fix {}", &self[*name]),
+            Stmt::Assn(fixed, name, expr) => {
+                write!(
+                    f,
+                    "Assn {} {} ",
+                    if *fixed { "fixed" } else { "" },
+                    &self[*name]
+                )?;
                 self.show_expr(expr, f, 0)
             }
-            Stmt::LitAssn(name, strs) => {
-                write!(f, "LitAssn({}, [ ", &self[*name])?;
+            Stmt::LitAssn(fixed, name, strs) => {
+                write!(
+                    f,
+                    "LitAssn {} {}, [ ",
+                    if *fixed { "fixed" } else { "" },
+                    &self[*name],
+                )?;
                 for str in strs.iter() {
                     write!(f, " {} ", str)?;
                 }
@@ -179,8 +188,8 @@ impl std::ops::Index<string_interner::DefaultSymbol> for ASTArena {
 pub enum Stmt {
     Puts(Expr),
     Fix(Name),
-    Assn(Name, Expr),
-    LitAssn(Name, Vec<String>),
+    Assn(bool, Name, Expr),
+    LitAssn(bool, Name, Vec<String>),
 }
 
 impl Stmt {

+ 5 - 3
src/grammar.lalrpop

@@ -27,6 +27,7 @@ extern {
         "case" => Token::Case,
         "let" => Token::Let,
         "in" => Token::In,
+        "fix" => Token::Fix,
         "var" => Token::Var(<&'input str>),
         "atom" => Token::Atom(<&'input str>),
         "num" => Token::Num(<i64>),
@@ -46,9 +47,10 @@ pub Stmts: Vec<Stmt> = {
 
 pub Stmt: Stmt = {
     "puts" <Expr> => Stmt::Puts(<>),
-    <Name> ":=" <Expr> => Stmt::Assn(<>),
-    <name:Name> "::=" <strs:(<"var">)*> =>
-        Stmt::LitAssn(name, strs.iter().map(|x| x.to_string()).collect()),
+    <fixed:"fix"?> <name:Name> ":=" <expr:Expr> => Stmt::Assn(fixed.is_some(), name, expr),
+    <fixed:"fix"?> <name:Name> "::=" <strs:(<"var">)*> =>
+        Stmt::LitAssn(fixed.is_some(), name, strs.iter().map(|x| x.to_string()).collect()),
+    "fix" <Name> => Stmt::Fix(<>),
 };
 
 pub Name: Name = {

+ 33 - 5
src/interp.rs

@@ -8,7 +8,7 @@ macro_rules! bail {
     ($fmt:expr, $($e:expr),*) => { return Err(Error { message: format!($fmt, $($e),*), }) }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub enum Value {
     Lit(Literal),
     Tup(Vec<Value>),
@@ -138,6 +138,7 @@ impl fmt::Debug for BuiltinFunc {
 
 enum NamedItem {
     Expr(Expr),
+    Value(Value),
     Builtin(&'static BuiltinFunc),
 }
 
@@ -217,10 +218,37 @@ impl State {
                 let val = self.eval(expr)?;
                 println!("{}", val.to_string());
             }
-            Stmt::Assn(name, expr) => {
-                self.scope.insert(*name, NamedItem::Expr(expr.clone()));
+
+            Stmt::Fix(name) => {
+                let expr = match self.scope.get(name) {
+                    None => bail!("no such thing: {:?}", name),
+                    Some(NamedItem::Expr(e)) => e.clone(),
+                    // 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));
             }
-            Stmt::LitAssn(name, strs) => {
+
+            Stmt::Assn(fixed, name, expr) => {
+                if *fixed {
+                    let val = self.eval(expr)?;
+                    self.scope.insert(*name, NamedItem::Value(val));
+                } else {
+                    self.scope.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(
+                        *name,
+                        NamedItem::Value(Value::Lit(Literal::Str(choice.clone()))),
+                    );
+                    return Ok(());
+                }
+
                 let choices = strs
                     .iter()
                     .map(|s| Choice {
@@ -231,7 +259,6 @@ impl State {
                 self.scope
                     .insert(*name, NamedItem::Expr(Expr::Chc(choices)));
             }
-            _ => bail!("unimplemented"),
         }
         Ok(())
     }
@@ -242,6 +269,7 @@ impl State {
             Expr::Var(v) => {
                 let e = match self.scope.get(v) {
                     Some(NamedItem::Expr(e)) => e.clone(),
+                    Some(NamedItem::Value(v)) => return Ok(v.clone()),
                     Some(NamedItem::Builtin(b)) => return Ok(Value::Builtin(b)),
                     None => bail!("no such thing: {:?}", v),
                 };

+ 3 - 0
src/lexer.rs

@@ -81,6 +81,9 @@ pub enum Token<'a> {
     #[token("in")]
     In,
 
+    #[token("fix")]
+    Fix,
+
     #[regex(r"\p{Ll}(\pL|[0-9_-])*")]
     Var(&'a str),
 

+ 4 - 0
src/repl.rs

@@ -23,6 +23,10 @@ impl Completer for Repl {
         pos: usize,
         _ctx: &rustyline::Context<'_>,
     ) -> rustyline::Result<(usize, Vec<String>)> {
+        if pos == 0 {
+            return Ok((pos, Vec::new()));
+        }
+
         if let Some(c) = line.chars().nth(pos - 1) {
             if c.is_alphabetic() {
                 // this means we're looking at maybe something