Browse Source

start to interpret stuff

Getty Ritter 2 years ago
parent
commit
8d31ef6f57
9 changed files with 225 additions and 8 deletions
  1. 47 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 15 0
      examples/aquan.matzo
  4. 12 6
      src/ast.rs
  5. 7 1
      src/grammar.lalrpop
  6. 91 0
      src/interp.rs
  7. 1 0
      src/lib.rs
  8. 48 1
      src/main.rs
  9. 3 0
      tests/exprs.matzo

+ 47 - 0
Cargo.lock

@@ -260,6 +260,7 @@ dependencies = [
  "lalrpop",
  "lalrpop-util",
  "pretty_assertions",
+ "rand",
  "regex",
 ]
 
@@ -334,6 +335,12 @@ version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
 
+[[package]]
+name = "ppv-lite86"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
+
 [[package]]
 name = "precomputed-hash"
 version = "0.1.1"
@@ -370,6 +377,46 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "rand"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+dependencies = [
+ "rand_core",
+]
+
 [[package]]
 name = "redox_syscall"
 version = "0.2.10"

+ 1 - 0
Cargo.toml

@@ -20,6 +20,7 @@ path = "tools/regenerate.rs"
 
 [dependencies]
 regex = "1"
+rand = "*"
 lalrpop-util = { version = "*", features = ["lexer"] }
 
 [build-dependencies.lalrpop]

+ 15 - 0
examples/aquan.matzo

@@ -0,0 +1,15 @@
+(* Below is a literal assignment, which is
+ * identical to cons := "p"| "t" | "k" | "w" | "h" | "n"; *)
+(* cons ::= p t k w h n; *)
+cons := 'p'| 't' | 'k' | 'w' | 'h' | 'n';
+
+(* And this could also be done with two rules and a literal
+ * assignment *)
+vowel := ('a' | 'e' | 'i' | 'o' | 'u') (4: '' | '\'');
+
+(* Here is a weighted disjunction *)
+syll := 4: cons vowel | vowel;
+
+(* And finally, here's an output statement *)
+(* puts syll (6 @ syll); *)
+puts syll syll syll syll;

+ 12 - 6
src/ast.rs

@@ -1,6 +1,6 @@
 type Name = String;
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub enum Stmt {
     Puts(Expr),
     Fix(Name),
@@ -8,7 +8,7 @@ pub enum Stmt {
     LitAssn(Name, Vec<String>),
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub enum Expr {
     Var(Name),
     Cat(Vec<Expr>),
@@ -22,26 +22,32 @@ pub enum Expr {
     Case(Box<Expr>, Vec<Case>),
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Case {
     pub pat: Pat,
     pub expr: Expr,
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub enum Pat {
     Var(Name),
     Lit(Literal),
     Tup(Vec<Pat>),
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Choice {
     pub weight: Option<i64>,
     pub value: Expr,
 }
 
-#[derive(Debug)]
+impl Choice {
+    pub fn weight(&self) -> i64 {
+        self.weight.unwrap_or(1)
+    }
+}
+
+#[derive(Debug, Clone)]
 pub enum Literal {
     Str(String),
     Atom(String),

+ 7 - 1
src/grammar.lalrpop

@@ -13,6 +13,7 @@ match {
     ":",
     ",",
     ";",
+    ".",
     ":=",
     "::=",
     "puts",
@@ -67,7 +68,12 @@ pub Choice: Choice = {
 };
 
 pub Term: Expr = {
-    (<Leaf>)* => Expr::Cat(<>),
+    (<Branch>)* => Expr::Cat(<>),
+};
+
+pub Branch: Expr = {
+    <l:Branch> "." <r:Leaf> => Expr::Ap(Box::new(l), Box::new(r)),
+    <Leaf> => <>,
 };
 
 pub Leaf: Expr = {

+ 91 - 0
src/interp.rs

@@ -0,0 +1,91 @@
+use crate::ast::*;
+use std::collections::HashMap;
+use rand::Rng;
+
+#[derive(Debug)]
+pub enum Value {
+    Lit(Literal),
+}
+
+impl Value {
+    fn to_string(&self) -> String {
+        match self {
+            Value::Lit(Literal::Str(s)) => s.clone(),
+            Value::Lit(Literal::Atom(s)) => s.clone(),
+            Value::Lit(Literal::Num(n)) => format!("{}", n),
+        }
+    }
+}
+
+pub struct State {
+    scope: HashMap<String, Expr>,
+    rand: rand::rngs::ThreadRng,
+}
+
+impl State {
+    pub fn new() -> State {
+        State {
+            scope: HashMap::new(),
+            rand: rand::thread_rng(),
+        }
+    }
+
+    pub fn execute(&mut self, stmt: &Stmt) {
+        match stmt {
+            Stmt::Puts(expr) => {
+                let val = self.eval(expr);
+                println!("{}", val.to_string());
+            }
+            Stmt::Assn(name, expr) => {
+                self.scope.insert(name.to_string(), expr.clone());
+            }
+            _ => panic!("unimplemented"),
+        }
+    }
+
+    fn eval(&mut self, expr: &Expr) -> Value {
+        match expr {
+            Expr::Lit(l) => Value::Lit(l.clone()),
+            Expr::Var(v) => {
+                let e = if let Some(x) = self.scope.get(v) {
+                    x.clone()
+                } else {
+                    panic!("no such thing: {}", v);
+                };
+                self.eval(&e)
+            }
+            Expr::Cat(cat) => {
+                if cat.len() == 1 {
+                    self.eval(&cat[0])
+                } else {
+                    let mut buf = String::new();
+                    for expr in cat {
+                        let val = self.eval(expr);
+                        buf.push_str(&val.to_string());
+                    }
+                    Value::Lit(Literal::Str(buf))
+                }
+            }
+            Expr::Chc(choices) => {
+                if choices.len() == 1 {
+                    self.eval(&choices[0].value)
+                } else {
+                    self.choose(choices)
+                }
+            }
+            _ => panic!("unimplemented: {:?}", expr),
+        }
+    }
+
+    fn choose(&mut self, choices: &[Choice]) -> Value {
+        let max = choices.iter().map(Choice::weight).sum();
+        let mut choice = self.rand.gen_range(0..max);
+        for ch in choices {
+            if choice < ch.weight() {
+                return self.eval(&ch.value);
+            }
+            choice -= ch.weight();
+        }
+        panic!("unreachable")
+    }
+}

+ 1 - 0
src/lib.rs

@@ -2,6 +2,7 @@
 extern crate lalrpop_util;
 
 pub mod ast;
+pub mod interp;
 
 #[cfg(test)]
 pub mod test {

+ 48 - 1
src/main.rs

@@ -1,3 +1,50 @@
+use matzo::grammar::StmtsParser;
+use matzo::interp::State;
+
+use std::io::Write;
+
+fn run(src: &str) {
+    let stmts = StmtsParser::new().parse(&src).unwrap();
+    let mut state = State::new();
+    for stmt in stmts {
+        state.execute(&stmt);
+    }
+}
+
+fn run_repl() -> std::io::Result<()> {
+    let mut state = State::new();
+    let parser = StmtsParser::new();
+    let mut stdout = std::io::stdout();
+    let stdin = std::io::stdin();
+    let mut buf = String::new();
+
+    loop {
+        stdout.write(b">>> ")?;
+        stdout.flush()?;
+        buf.clear();
+        stdin.read_line(&mut buf)?;
+        let stmts = match parser.parse(&buf) {
+            Ok(stmts) => stmts,
+            Err(err) => {
+                eprintln!("{:?}", err);
+                continue;
+            }
+        };
+        for stmt in stmts {
+            state.execute(&stmt);
+        }
+    }
+}
+
 fn main() {
-    println!("{:?}", matzo::grammar::StmtsParser::new().parse("puts Foo Bar | Baz"));
+    let args = std::env::args().skip(1).collect::<Vec<String>>();
+    if args.is_empty() {
+        run_repl().unwrap();
+        return;
+    }
+
+    for arg in args {
+        let buf = std::fs::read_to_string(arg).unwrap();
+        run(&buf);
+    }
 }

+ 3 - 0
tests/exprs.matzo

@@ -4,3 +4,6 @@ puts This That The-Other;
 puts This | That | The-Other;
 (* weighted choice *)
 puts 5: This | That;
+
+(* application *)
+puts foo.bar.baz;