Browse Source

thread locations through

Getty Ritter 2 years ago
parent
commit
ee90e70f44
4 changed files with 162 additions and 73 deletions
  1. 55 25
      src/ast.rs
  2. 32 25
      src/grammar.lalrpop
  3. 34 23
      src/interp.rs
  4. 41 0
      src/lexer.rs

+ 55 - 25
src/ast.rs

@@ -1,6 +1,8 @@
 use std::fmt;
+pub use crate::lexer::Located;
 
-pub type Name = string_interner::DefaultSymbol;
+pub type StrRef = string_interner::DefaultSymbol;
+pub type Name = Located<StrRef>;
 
 pub struct ASTArena {
     strings: string_interner::StringInterner,
@@ -21,17 +23,17 @@ impl ASTArena {
         }
     }
 
-    pub fn expr_nil(&self) -> ExprRef {
-        ExprRef { idx: 0 }
+    pub fn expr_nil(&self) -> ExprId {
+        ExprId { idx: 0 }
     }
 
-    pub fn add_expr(&mut self, e: Expr) -> ExprRef {
+    pub fn add_expr(&mut self, e: Expr) -> ExprId {
         let idx = self.exprs.len();
         self.exprs.push(e);
-        ExprRef { idx }
+        ExprId { idx }
     }
 
-    pub fn add_string(&mut self, s: &str) -> Name {
+    pub fn add_string(&mut self, s: &str) -> string_interner::DefaultSymbol {
         self.strings.get_or_intern(s)
     }
 
@@ -39,27 +41,28 @@ impl ASTArena {
         match stmt {
             Stmt::Puts(expr) => {
                 write!(f, "Puts ")?;
-                self.show_expr(&self[*expr], f, 0)
+                self.show_expr(&self[expr.item], f, 0)
             }
-            Stmt::Fix(name) => writeln!(f, "Fix {}", &self[*name]),
+            Stmt::Fix(name) => writeln!(f, "Fix {}", &self[name.item]),
             Stmt::Assn(fixed, name, expr) => {
                 write!(
                     f,
                     "Assn {} {} ",
                     if *fixed { "fixed" } else { "" },
-                    &self[*name]
+                    &self[name.item]
                 )?;
-                self.show_expr(&self[*expr], f, 0)
+                self.show_expr(&self[expr.item], f, 0)
             }
             Stmt::LitAssn(fixed, name, strs) => {
                 write!(
                     f,
                     "LitAssn {} {}, [ ",
                     if *fixed { "fixed" } else { "" },
-                    &self[*name],
+                    &self[name.item],
                 )?;
                 for str in strs.iter() {
-                    write!(f, " {} ", str)?;
+                    let s = &self[str.item];
+                    write!(f, " {} ", s)?;
                 }
                 writeln!(f, "]")
             }
@@ -76,8 +79,8 @@ impl ASTArena {
     fn show_pat(&self, pat: &Pat, f: &mut fmt::Formatter) -> fmt::Result {
         match pat {
             Pat::Wildcard => write!(f, "_"),
-            Pat::Var(n) => write!(f, "{}", &self[*n]),
-            Pat::Lit(Literal::Atom(n)) => write!(f, "{}", &self[*n]),
+            Pat::Var(n) => write!(f, "{}", &self[n.item]),
+            Pat::Lit(Literal::Atom(n)) => write!(f, "{}", &self[n.item]),
             Pat::Lit(lit) => write!(f, "{:?}", lit),
             Pat::Tup(tup) => {
                 write!(f, "Tup( ")?;
@@ -93,8 +96,8 @@ impl ASTArena {
     fn show_expr(&self, expr: &Expr, f: &mut fmt::Formatter, depth: usize) -> fmt::Result {
         match expr {
             Expr::Nil => writeln!(f, "Nil"),
-            Expr::Var(v) => writeln!(f, "Var({})", &self[*v]),
-            Expr::Lit(Literal::Atom(n)) => writeln!(f, "Lit(Atom({}))", &self[*n]),
+            Expr::Var(v) => writeln!(f, "Var({})", &self[v.item]),
+            Expr::Lit(Literal::Atom(n)) => writeln!(f, "Lit(Atom({}))", &self[n.item]),
             Expr::Lit(lit) => writeln!(f, "{:?}", lit),
             Expr::Range(from, to) => {
                 writeln!(f, "Range(")?;
@@ -158,7 +161,7 @@ impl ASTArena {
                     f,
                     "Let({}{}",
                     if *fixed { "fixed " } else { "" },
-                    &self[*name]
+                    &self[name.item]
                 )?;
                 self.indent(f, depth + 2)?;
                 self.show_expr(&self[*expr], f, depth + 2)?;
@@ -211,6 +214,14 @@ impl std::ops::Index<ExprRef> for ASTArena {
     type Output = Expr;
 
     fn index(&self, rf: ExprRef) -> &Self::Output {
+        &self.exprs[rf.item.idx]
+    }
+}
+
+impl std::ops::Index<ExprId> for ASTArena {
+    type Output = Expr;
+
+    fn index(&self, rf: ExprId) -> &Self::Output {
         &self.exprs[rf.idx]
     }
 }
@@ -236,7 +247,7 @@ impl<'a> std::fmt::Debug for Printable<'a, Expr> {
 }
 
 /// A top-level Matzo statement
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone)]
 pub enum Stmt {
     /// evaluate and print the value of an expression
     Puts(ExprRef),
@@ -246,7 +257,7 @@ pub enum Stmt {
     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>),
+    LitAssn(bool, Name, Vec<Name>),
 }
 
 impl Stmt {
@@ -259,7 +270,7 @@ impl Stmt {
 }
 
 /// A Matzo expression
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone)]
 pub enum Expr {
     Var(Name),
     Cat(Vec<ExprRef>),
@@ -274,20 +285,28 @@ pub enum Expr {
     Nil,
 }
 
+pub type ExprRef = Located<ExprId>;
+
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub struct ExprRef {
+pub struct ExprId {
     idx: usize,
 }
 
+impl ExprId {
+    pub fn nil(&self) -> bool {
+        self.idx == 0
+    }
+}
+
 /// A single case in an anonymous function or `case` statement
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone)]
 pub struct Case {
     pub pat: Pat,
     pub expr: ExprRef,
 }
 
 /// A pattern, e.g. in an anonymous function or `case` statement
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone)]
 pub enum Pat {
     Var(Name),
     Wildcard,
@@ -297,7 +316,7 @@ pub enum Pat {
 
 /// A single element in a choice, with an optional weight (which
 /// defaults to 1) and a value
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone)]
 pub struct Choice {
     pub weight: Option<i64>,
     pub value: ExprRef,
@@ -311,9 +330,20 @@ impl Choice {
 }
 
 /// An atomic literal: a string, a number, or an atom
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone)]
 pub enum Literal {
     Str(String),
     Atom(Name),
     Num(i64),
 }
+
+impl PartialEq for Literal {
+    fn eq(&self, other: &Literal) -> bool {
+        match (self, other) {
+            (Literal::Str(s1), Literal::Str(s2)) => s1 == s2,
+            (Literal::Atom(a1), Literal::Atom(a2)) => a1.item == a2.item,
+            (Literal::Num(n1), Literal::Num(n2)) => n1 == n2,
+            (_, _) => false,
+        }
+    }
+}

+ 32 - 25
src/grammar.lalrpop

@@ -47,9 +47,9 @@ pub Stmts: Vec<Stmt> = {
 };
 
 pub Stmt: Stmt = {
-    "puts" <Expr> => Stmt::Puts(<>),
-    <fixed:"fix"?> <name:Name> ":=" <expr:Expr> => Stmt::Assn(fixed.is_some(), name, expr),
-    <fixed:"fix"?> <name:Name> "::=" <strs:(<AtomLit>)*> =>
+    "puts" <Located<Expr>> => Stmt::Puts(<>),
+    <fixed:"fix"?> <name:Name> ":=" <expr:Located<Expr>> => Stmt::Assn(fixed.is_some(), name, expr),
+    <fixed:"fix"?> <name:Name> "::=" <strs:(<Located<AtomLit>>)*> =>
         Stmt::LitAssn(
             fixed.is_some(),
             name,
@@ -58,19 +58,20 @@ pub Stmt: Stmt = {
     "fix" <Name> => Stmt::Fix(<>),
 };
 
-pub AtomLit: String = {
-    "var" => <>.to_string(),
-    "atom" => <>.to_string(),
+pub AtomLit: string_interner::DefaultSymbol = {
+    "var" => ast.add_string(<>),
+    "atom" => ast.add_string(<>),
 };
 
 pub Name: Name = {
-    "var" => ast.add_string(<>),
+    <start: @L> <str:"var"> <end: @R> =>
+        Located::new(ast.add_string(str), Span { start: start as u32, end: end as u32 }),
 };
 
-pub Expr: ExprRef = {
+pub Expr: ExprId = {
     <mut ts:(<Choice> "|")*> <t:Choice> => {
         if ts.len() == 0 {
-            t.value
+            t.value.item
         } else {
             ts.push(t);
             ast.add_expr(Expr::Chc(ts))
@@ -79,52 +80,52 @@ pub Expr: ExprRef = {
 };
 
 pub Choice: Choice = {
-    <weight:"num"> ":" <value:Term> => Choice {
+    <weight:"num"> ":" <value:Located<Term>> => Choice {
         weight: Some(weight),
         value
     },
-    <value:Term> => Choice {
+    <value:Located<Term>> => Choice {
         weight: None,
         value
     }
 };
 
-pub Term: ExprRef = {
-    <mut bs:(<Branch>)*> => {
+pub Term: ExprId = {
+    <mut bs:(<Located<Branch>>)*> => {
         match bs.len() {
             0 => ast.expr_nil(),
-            1 => bs.pop().unwrap(),
+            1 => bs.pop().unwrap().item,
             _ => ast.add_expr(Expr::Cat(<>)),
         }
     }
 };
 
-pub Branch: ExprRef = {
-    <l:Branch> "." <r:Subbranch> => ast.add_expr(Expr::Ap(l, r)),
+pub Branch: ExprId = {
+    <l:Located<Branch>> "." <r:Located<Subbranch>> => ast.add_expr(Expr::Ap(l, r)),
     <Subbranch> => <>,
 };
 
-pub Subbranch: ExprRef = {
-    <l:Subbranch> ".." <r:Leaf> => ast.add_expr(Expr::Range(l, r)),
+pub Subbranch: ExprId = {
+    <l:Located<Subbranch>> ".." <r:Located<Leaf>> => ast.add_expr(Expr::Range(l, r)),
     <Leaf> => <>,
 }
 
-pub Leaf: ExprRef = {
+pub Leaf: ExprId = {
     <Literal> => ast.add_expr(Expr::Lit(<>)),
     <Name> => ast.add_expr(Expr::Var(<>)),
-    "<" <mut es:(<Expr> ",")*> <e:Expr> ">" => {
-        if es.len() == 0 && e == ast.expr_nil() {
+    "<" <mut es:(<Located<Expr>> ",")*> <e:Located<Expr>> ">" => {
+        if es.len() == 0 && e.item.nil() {
             ast.add_expr(Expr::Tup(Vec::new()))
         } else {
             es.push(e);
             ast.add_expr(Expr::Tup(es))
         }
     },
-    "let" <fixed:"fix"?> <name:Name> ":=" <e1:Expr> "in" "{" <e2:Expr> "}" =>
+    "let" <fixed:"fix"?> <name:Name> ":=" <e1:Located<Expr>> "in" "{" <e2:Located<Expr>> "}" =>
         ast.add_expr(Expr::Let(fixed.is_some(), name, e1, e2)),
     "{" <cs:Cases> "}" =>
         ast.add_expr(Expr::Fun(cs)),
-    "case" <e:Expr> "in" "{" <cs:Cases> "}" =>
+    "case" <e:Located<Expr>> "in" "{" <cs:Cases> "}" =>
         ast.add_expr(Expr::Case(e, cs)),
     "(" <e:Expr> ")" => e,
 };
@@ -137,7 +138,7 @@ pub Cases: Vec<Case> = {
 };
 
 pub Case: Case = {
-    <pat:Pat> "=>" <expr:Expr> => Case { pat, expr },
+    <pat:Pat> "=>" <expr:Located<Expr>> => Case { pat, expr },
 };
 
 pub Pat: Pat = {
@@ -154,5 +155,11 @@ pub Pat: Pat = {
 pub Literal: Literal = {
     "num" => Literal::Num(<>),
     "str" => Literal::Str(<>),
-    "atom" => Literal::Atom(ast.add_string(<>)),
+    <atom:Located<"atom">> => Literal::Atom(atom.map(|x| ast.add_string(x))),
+};
+
+#[inline]
+Located<T>: Located<T> = {
+    <start: @L> <data: T> <end: @R> =>
+        Located::new(data, Span { start: start as u32, end: end as u32 }),
 };

+ 34 - 23
src/interp.rs

@@ -1,4 +1,5 @@
 use crate::ast::*;
+use crate::lexer::Span;
 use crate::rand::*;
 
 use anyhow::{anyhow, bail, Error};
@@ -8,7 +9,7 @@ use std::fmt;
 use std::io;
 use std::rc::Rc;
 
-/// A `Value` is a representation of the resut of evaluation. Note
+/// A `Value` is a representation of the result of evaluation. Note
 /// that a `Value` is a representation of something in _weak head
 /// normal form_: i.e. for compound expressions (right now just
 /// tuples) it might contain other values but it might contain
@@ -231,7 +232,7 @@ type Env = Option<Rc<Scope>>;
 /// `Thunk`s, along with a parent pointer.
 #[derive(Debug)]
 pub struct Scope {
-    vars: HashMap<Name, Thunk>,
+    vars: HashMap<StrRef, Thunk>,
     parent: Env,
 }
 
@@ -257,7 +258,7 @@ pub struct State {
     ast: RefCell<ASTArena>,
     /// The root scope of the program, which contains all the
     /// top-level definitions and builtins.
-    root_scope: RefCell<HashMap<Name, Thunk>>,
+    root_scope: RefCell<HashMap<StrRef, Thunk>>,
     /// The thread-local RNG.
     rand: RefCell<Box<dyn MatzoRand>>,
     /// The instantiated parser used to parse Matzo programs
@@ -319,14 +320,14 @@ impl State {
     /// indicates the missing name.
     fn lookup(&self, env: &Env, name: Name) -> Result<Thunk, Error> {
         if let Some(env) = env {
-            if let Some(ne) = env.vars.get(&name) {
+            if let Some(ne) = env.vars.get(&name.item) {
                 Ok(ne.clone())
             } else {
                 self.lookup(&env.parent, name)
             }
         } else {
-            match self.root_scope.borrow().get(&name) {
-                None => bail!("no such thing: {}", &self.ast.borrow()[name]),
+            match self.root_scope.borrow().get(&name.item) {
+                None => bail!("no such thing: {}", &self.ast.borrow()[name.item]),
                 Some(ne) => Ok(ne.clone()),
             }
         }
@@ -440,7 +441,7 @@ impl State {
                 let val = self.force(val)?;
                 self.root_scope
                     .borrow_mut()
-                    .insert(*name, Thunk::Value(val));
+                    .insert(name.item, Thunk::Value(val));
             }
 
             // assign a given expression to a name, forcing it to a
@@ -453,7 +454,7 @@ impl State {
                 } else {
                     Thunk::Expr(*expr, None)
                 };
-                self.root_scope.borrow_mut().insert(*name, thunk);
+                self.root_scope.borrow_mut().insert(name.item, thunk);
             }
 
             // assign a simple disjunction of strings to a name,
@@ -461,27 +462,37 @@ impl State {
             Stmt::LitAssn(fixed, name, strs) => {
                 if *fixed {
                     let choice = &strs[self.rand.borrow_mut().gen_range_usize(0, strs.len())];
+                    let str = self.ast.borrow()[choice.item].to_string();
                     self.root_scope.borrow_mut().insert(
-                        *name,
-                        Thunk::Value(Value::Lit(Literal::Str(choice.clone()))),
+                        name.item,
+                        Thunk::Value(Value::Lit(Literal::Str(str))),
                     );
                     return Ok(());
                 }
 
-                let choices = strs
+                let choices: Vec<Choice> = strs
                     .iter()
-                    .map(|s| Choice {
-                        weight: None,
-                        value: self
-                            .ast
-                            .borrow_mut()
-                            .add_expr(Expr::Lit(Literal::Str(s.clone()))),
+                    .map(|s| {
+                        let str = self.ast.borrow()[s.item].to_string();
+                        Choice {
+                            weight: None,
+                            value: Located {
+                                span: s.span,
+                                item: self.ast.borrow_mut().add_expr(Expr::Lit(Literal::Str(str))),
+                            },
+                        }
                     })
                     .collect();
-                let choices = self.ast.borrow_mut().add_expr(Expr::Chc(choices));
+                let choices = Located {
+                    span: Span {
+                        start: choices.first().unwrap().value.span.start,
+                        end: choices.last().unwrap().value.span.end,
+                    },
+                    item: self.ast.borrow_mut().add_expr(Expr::Chc(choices)),
+                };
                 self.root_scope
                     .borrow_mut()
-                    .insert(*name, Thunk::Expr(choices, None));
+                    .insert(name.item, Thunk::Expr(choices, None));
             }
         }
         Ok(())
@@ -516,7 +527,7 @@ impl State {
     /// Given an `ExprRef` and an environment, fetch that expression
     /// and then evalute it in that environment
     fn eval(&self, expr_ref: ExprRef, env: &Env) -> Result<Value, Error> {
-        let expr = &self.ast.borrow()[expr_ref];
+        let expr = &self.ast.borrow()[expr_ref.item];
         match expr {
             // literals should be mostly cheap-ish to copy, so a
             // literal evaluates to a `Value` that's a copy of the
@@ -608,9 +619,9 @@ impl State {
                 if *fixed {
                     let val = self.eval(*val, env)?;
                     let val = self.force(val)?;
-                    new_scope.insert(*name, Thunk::Value(val));
+                    new_scope.insert(name.item, Thunk::Value(val));
                 } else {
-                    new_scope.insert(*name, Thunk::Expr(*val, env.clone()));
+                    new_scope.insert(name.item, Thunk::Expr(*val, env.clone()));
                 };
                 let new_scope = Rc::new(Scope {
                     vars: new_scope,
@@ -695,7 +706,7 @@ impl State {
             // build a new scope from the bindings discovered
             let mut new_scope = HashMap::new();
             for (name, binding) in bindings {
-                new_scope.insert(name, binding);
+                new_scope.insert(name.item, binding);
             }
 
             let new_scope = Rc::new(Scope {

+ 41 - 0
src/lexer.rs

@@ -1,5 +1,46 @@
 use logos::{Lexer, Logos};
 
+/// A location in a source file
+#[derive(Debug, Clone, Copy)]
+pub struct Span {
+    pub start: u32,
+    pub end: u32,
+}
+
+impl Span {
+    pub fn empty() -> Span {
+        Span {
+            start: u32::MAX,
+            end: u32::MAX,
+        }
+    }
+
+    pub fn exists(&self) -> bool {
+        self.start != u32::MAX && self.end != u32::MAX
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Located<T> {
+    pub span: Span,
+    pub item: T,
+}
+
+impl<T> Located<T> {
+    pub fn new(item: T, span: Span) -> Located<T> {
+        Located { span, item }
+    }
+}
+
+impl <T: Clone> Located<T> {
+    pub fn map<R>(&self, func: impl FnOnce(T) -> R) -> Located<R> {
+        Located {
+            span: self.span,
+            item: func(self.item.clone()),
+        }
+    }
+}
+
 fn parse_num<'a>(lex: &mut Lexer<'a, Token<'a>>) -> Option<i64> {
     let slice = lex.slice();
     slice.parse().ok()