Ver código fonte

basic support for builtin functions and num ranges

Getty Ritter 2 anos atrás
pai
commit
b04674ea71
5 arquivos alterados com 89 adições e 12 exclusões
  1. 1 1
      examples/aquan.matzo
  2. 1 1
      examples/auran.matzo
  3. 1 0
      src/ast.rs
  4. 8 2
      src/grammar.lalrpop
  5. 78 8
      src/interp.rs

+ 1 - 1
examples/aquan.matzo

@@ -11,4 +11,4 @@ syll := 4: cons vowel | vowel;
 
 (* And finally, here's an output statement *)
 (* puts syll (6 @ syll); *)
-puts syll syll syll syll;
+puts syll rep.<1..6, syll>;

+ 1 - 1
examples/auran.matzo

@@ -1,4 +1,4 @@
-word := start (rep.4.syll) end;
+word := start (rep.<1..4,syll>) end;
 syll := vowel cons;
 start := 3: cons | 2: '';
 end := 3: vowel | vowel 's'| vowel 'n';

+ 1 - 0
src/ast.rs

@@ -20,6 +20,7 @@ pub enum Expr {
     Let(Name, Box<Expr>, Box<Expr>),
     Fun(Vec<Case>),
     Case(Box<Expr>, Vec<Case>),
+    Range(Box<Expr>, Box<Expr>),
 }
 
 #[derive(Debug, Clone)]

+ 8 - 2
src/grammar.lalrpop

@@ -14,6 +14,7 @@ match {
     ",",
     ";",
     ".",
+    "..",
     ":=",
     "::=",
     "puts",
@@ -73,10 +74,15 @@ pub Term: Expr = {
 };
 
 pub Branch: Expr = {
-    <l:Branch> "." <r:Leaf> => Expr::Ap(Box::new(l), Box::new(r)),
-    <Leaf> => <>,
+    <l:Branch> "." <r:Subbranch> => Expr::Ap(Box::new(l), Box::new(r)),
+    <Subbranch> => <>,
 };
 
+pub Subbranch: Expr = {
+    <l:Subbranch> ".." <r:Leaf> => Expr::Range(Box::new(l), Box::new(r)),
+    <Leaf> => <>,
+}
+
 pub Leaf: Expr = {
     <Literal> => Expr::Lit(<>),
     <Name> => Expr::Var(<>),

+ 78 - 8
src/interp.rs

@@ -1,11 +1,46 @@
 use crate::ast::*;
 use rand::Rng;
 use std::collections::HashMap;
+use std::fmt;
 
 #[derive(Debug)]
 pub enum Value {
     Lit(Literal),
     Tup(Vec<Value>),
+    Builtin(&'static BuiltinFunc),
+}
+
+pub struct BuiltinFunc {
+    name: &'static str,
+    callback: &'static dyn Fn(&mut State, &Expr) -> Value,
+}
+
+const BUILTINS: &[BuiltinFunc] = &[
+    BuiltinFunc {
+        name: "rep",
+        callback: &|state: &mut State, expr: &Expr| -> Value {
+            let args = match expr {
+                Expr::Tup(tup) => tup,
+                _ => panic!("`rep`: expected tuple"),
+            };
+            if args.len() != 2 {
+                panic!("`rep`: expected two arguments, got {}", args.len())
+            }
+            let num = match state.eval(&args[0]) {
+                Value::Lit(Literal::Num(n)) => n,
+                r => panic!("`rep`: expected first arg to be a number, but got {:?}", r),
+            };
+
+            let rep = (0..num).map(|_| args[1].clone()).collect();
+            state.eval(&Expr::Cat(rep))
+        }
+    },
+];
+
+impl fmt::Debug for BuiltinFunc {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        writeln!(fmt, "BuiltinFunc {{ name: {:?}, ... }}", self.name)
+    }
 }
 
 impl Value {
@@ -26,20 +61,35 @@ impl Value {
                 buf.push_str(">");
                 buf
             }
+            Value::Builtin(func) =>
+                format!("#<builtin {}>", func.name),
         }
     }
 }
 
+enum NamedItem {
+    Expr(Expr),
+    Builtin(&'static BuiltinFunc),
+}
+
 pub struct State {
-    scope: HashMap<String, Expr>,
+    scope: HashMap<String, NamedItem>,
     rand: rand::rngs::ThreadRng,
 }
 
 impl State {
     pub fn new() -> State {
-        State {
+        let mut s = State {
             scope: HashMap::new(),
             rand: rand::thread_rng(),
+        };
+        s.init_builtins();
+        s
+    }
+
+    fn init_builtins(&mut self) {
+        for builtin in BUILTINS {
+            self.scope.insert(builtin.name.to_string(), NamedItem::Builtin(builtin));
         }
     }
 
@@ -50,14 +100,14 @@ impl State {
                 println!("{}", val.to_string());
             }
             Stmt::Assn(name, expr) => {
-                self.scope.insert(name.to_string(), expr.clone());
+                self.scope.insert(name.to_string(), NamedItem::Expr(expr.clone()));
             }
             Stmt::LitAssn(name, strs) => {
                 let choices = strs.iter().map(|s| Choice {
                     weight: None,
                     value: Expr::Lit(Literal::Str(s.clone())),
                 }).collect();
-                self.scope.insert(name.to_string(), Expr::Chc(choices));
+                self.scope.insert(name.to_string(), NamedItem::Expr(Expr::Chc(choices)));
             }
             _ => panic!("unimplemented"),
         }
@@ -67,10 +117,13 @@ impl State {
         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);
+                let e = match self.scope.get(v) {
+                    Some(NamedItem::Expr(e)) =>
+                        e.clone(),
+                    Some(NamedItem::Builtin(b)) =>
+                        return Value::Builtin(b),
+                    None =>
+                        panic!("no such thing: {}", v),
                 };
                 self.eval(&e)
             }
@@ -95,6 +148,23 @@ impl State {
             }
             Expr::Tup(values) =>
                 Value::Tup(values.iter().map(|v| self.eval(v)).collect()),
+            Expr::Ap(fun, arg) => {
+                match self.eval(fun) {
+                    Value::Builtin(builtin) => (builtin.callback)(self, arg),
+                    _ => panic!("bad function: {:?}", fun),
+                }
+            }
+            Expr::Range(from, to) => {
+                let from = match self.eval(from) {
+                    Value::Lit(Literal::Num(n)) => n,
+                    e => panic!("bad start in range: {}", e.to_string()),
+                };
+                let to = match self.eval(to) {
+                    Value::Lit(Literal::Num(n)) => n,
+                    e => panic!("bad end in range: {}", e.to_string()),
+                };
+                Value::Lit(Literal::Num(self.rand.gen_range(from..=to)))
+            }
             _ => panic!("unimplemented: {:?}", expr),
         }
     }