From 31c2083ddbf6d10ab932df3ea94435709e75da41 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Mon, 2 Nov 2020 00:06:55 +0000 Subject: [PATCH] Create the symbol lookup in Symbol's eval fn --- bootstrap/src/ast.rs | 42 ++++++++++++++----- bootstrap/src/core.rs | 30 +++++++++++--- bootstrap/src/errors.rs | 7 ++++ bootstrap/src/namespace.rs | 13 +++++- bootstrap/src/reader.rs | 28 ++++++++++--- bootstrap/src/runtime.rs | 5 +++ bootstrap/src/scope.rs | 11 ++--- bootstrap/src/types/number.rs | 12 +++++- bootstrap/src/types/symbol.rs | 78 +++++++++++++++++++++++++++++++---- 9 files changed, 186 insertions(+), 40 deletions(-) diff --git a/bootstrap/src/ast.rs b/bootstrap/src/ast.rs index e44c455..a7df436 100644 --- a/bootstrap/src/ast.rs +++ b/bootstrap/src/ast.rs @@ -19,10 +19,30 @@ use crate::runtime::RT; use crate::scope::Scope; use crate::types::collections; use crate::types::{Number, Symbol}; +use std::fmt; pub type PossibleExpr = Result; +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct Location { + position: i64, + file_path: String, +} + +impl fmt::Display for Location { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "'{}:{}'", &self.file_path, &self.position) + } +} + pub trait Expression { + fn location(&self) -> Location { + Location { + position: 0, + file_path: "NotImplemented".to_string(), + } + } + fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr; } @@ -51,8 +71,8 @@ impl Expr { collections::List::new_empty() } - pub fn make_symbol(v: String) -> Expr { - Expr::Sym(Symbol::new(v)) + pub fn make_symbol(v: String, target_ns: Option) -> Expr { + Expr::Sym(Symbol::new(v, target_ns)) } pub fn make_string(v: String) -> Expr { @@ -64,12 +84,12 @@ impl Expr { } } -// impl Expression for Expr { -// fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr { -// match self { -// Expr::Sym(s) => { -// s.eval -// } -// } -// } -// } +impl fmt::Display for Expr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Expr::Num(n) => n.fmt(f), + Expr::Sym(s) => s.fmt(f), + _ => write!(f, "NA"), + } + } +} diff --git a/bootstrap/src/core.rs b/bootstrap/src/core.rs index 4934358..8012bda 100644 --- a/bootstrap/src/core.rs +++ b/bootstrap/src/core.rs @@ -14,21 +14,36 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -use crate::ast::{Expr, PossibleExpr}; +use crate::ast::{Expr, Expression, PossibleExpr}; use crate::errors::err; use crate::reader::read_string; use crate::runtime::RT; use crate::scope::Scope; fn eval_expr(rt: &RT, scope: &Scope, expr: Expr) -> PossibleExpr { - Ok(expr) + match expr { + Expr::Num(n) => n.eval(rt, scope), + // TODO: find a better way to attach the ns name to the symbol. This + // is ugly. + Expr::Sym(s) => s + .clone_with_ns(rt.current_ns().name.clone()) + .eval(rt, scope), + _ => Ok(expr), + } } pub fn eval(rt: &RT, exprs: Vec) -> PossibleExpr { - match exprs.last() { - Some(e) => Ok(e.clone()), - _ => Err(err("NotImplemented".to_string())), + if exprs.len() == 0 { + return Ok(Expr::NoMatch); } + + let mut ret: PossibleExpr = Ok(Expr::NoMatch); + + for expr in exprs.iter() { + ret = eval_expr(rt, rt.current_scope(), expr.clone()); + } + + ret } pub fn read_eval_print(rt: &RT, input: &str) { @@ -43,6 +58,11 @@ pub fn read_eval_print(rt: &RT, input: &str) { if rt.is_debug() { println!("Eval Result: \n{:?}\n", result_expr); } + + match result_expr { + Ok(expr) => println!("{}", expr), + Err(e) => println!("{}", e), + } } Err(e) => println!("Error: {}", e), } diff --git a/bootstrap/src/errors.rs b/bootstrap/src/errors.rs index 5387b0a..7031c06 100644 --- a/bootstrap/src/errors.rs +++ b/bootstrap/src/errors.rs @@ -14,6 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +use std::fmt; pub trait IError { fn message(&self) -> &str; @@ -24,6 +25,12 @@ pub struct Error { msg: String, } +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error: {}", &self.msg) + } +} + impl IError for Error { fn message(&self) -> &str { &self.msg diff --git a/bootstrap/src/namespace.rs b/bootstrap/src/namespace.rs index 54964e9..3bfd487 100644 --- a/bootstrap/src/namespace.rs +++ b/bootstrap/src/namespace.rs @@ -13,7 +13,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . -*/ + */ +use crate::ast::{Expr, PossibleExpr}; use crate::scope::Scope; pub struct Namespace { @@ -23,7 +24,7 @@ pub struct Namespace { root_scope: Scope, } -impl<'ctx> Namespace { +impl Namespace { pub fn new(name: String, source_file: Option) -> Namespace { Namespace { name, @@ -31,4 +32,12 @@ impl<'ctx> Namespace { root_scope: Scope::new(None), } } + + pub fn current_scope(&self) -> &Scope { + &self.root_scope + } + + pub fn lookup_external(&self, target: &str, key: &str) -> PossibleExpr { + Ok(Expr::Nil) + } } diff --git a/bootstrap/src/reader.rs b/bootstrap/src/reader.rs index c35e8e6..3f20494 100644 --- a/bootstrap/src/reader.rs +++ b/bootstrap/src/reader.rs @@ -91,7 +91,7 @@ impl ExprReader { fn read_quoted_expr(&mut self, reader: &mut BufReader) -> ReadResult { let rest = self.read_expr(reader)?; - let elements = vec![Expr::make_symbol("quote".to_string()), rest]; + let elements = vec![Expr::make_symbol("quote".to_string(), None), rest]; Ok(Expr::make_list(&elements)) } @@ -102,12 +102,15 @@ impl ExprReader { // Move forward in the buffer since we peeked it let _ = self.get_char(reader, true); let rest = self.read_expr(reader)?; - let elements = vec![Expr::make_symbol("unquote-splicing".to_string()), rest]; + let elements = vec![ + Expr::make_symbol("unquote-splicing".to_string(), None), + rest, + ]; Ok(Expr::make_list(&elements)) } _ => { let rest = self.read_expr(reader)?; - let elements = vec![Expr::make_symbol("unquote".to_string()), rest]; + let elements = vec![Expr::make_symbol("unquote".to_string(), None), rest]; Ok(Expr::make_list(&elements)) } } @@ -115,7 +118,7 @@ impl ExprReader { fn read_quasiquoted_expr(&mut self, reader: &mut BufReader) -> ReadResult { let rest = self.read_expr(reader)?; - let elements = vec![Expr::make_symbol("quasiquote".to_string()), rest]; + let elements = vec![Expr::make_symbol("quasiquote".to_string(), None), rest]; Ok(Expr::make_list(&elements)) } @@ -199,7 +202,22 @@ impl ExprReader { } } - Ok(Expr::make_symbol(symbol)) + let tmp = &symbol; + let mut parts: Vec<&str> = tmp.split("/").collect(); + + parts.reverse(); + + match parts.len() { + 1 => Ok(Expr::make_symbol(symbol, None)), + 2 => Ok(Expr::make_symbol( + parts[0].to_string(), + parts.last().map(|x| x.to_string()), + )), + _ => Err(format!( + "Multiple NSs won't work: {} at {}", + &symbol, self.location + )), + } } fn read_escape_char(&mut self, reader: &mut BufReader) -> Option { diff --git a/bootstrap/src/runtime.rs b/bootstrap/src/runtime.rs index b93f29a..0577819 100644 --- a/bootstrap/src/runtime.rs +++ b/bootstrap/src/runtime.rs @@ -15,6 +15,7 @@ * along with this program. If not, see . */ use crate::namespace::Namespace; +use crate::scope::Scope; use std::collections::HashMap; use std::env; use std::path::Path; @@ -72,6 +73,10 @@ impl RT { .unwrap() } + pub fn current_scope(&self) -> &Scope { + self.current_ns().current_scope() + } + #[inline] pub fn set_debug_mode(&mut self, v: bool) { self.debug = v; diff --git a/bootstrap/src/scope.rs b/bootstrap/src/scope.rs index fe4bf44..4c2cc4e 100644 --- a/bootstrap/src/scope.rs +++ b/bootstrap/src/scope.rs @@ -19,8 +19,8 @@ use std::collections::HashMap; /// This struct describes the values in the scope. pub struct ScopeElement { - element_type: Expr, - public: bool, + pub expr: Expr, + pub public: bool, } /// Scopes in **Serene** are simply represented by hashmaps. Each @@ -57,11 +57,8 @@ impl Scope { } } - pub fn insert(&mut self, key: &str, val: Expr, public: bool) { - let v = ScopeElement { - public, - element_type: val, - }; + pub fn insert(&mut self, key: &str, expr: Expr, public: bool) { + let v = ScopeElement { public, expr }; self.symbol_table.insert(key.to_string(), v); } } diff --git a/bootstrap/src/types/number.rs b/bootstrap/src/types/number.rs index 3aab914..4371caa 100644 --- a/bootstrap/src/types/number.rs +++ b/bootstrap/src/types/number.rs @@ -17,6 +17,7 @@ use crate::ast::{Expr, Expression, PossibleExpr}; use crate::runtime::RT; use crate::scope::Scope; +use std::fmt; // Note: I kept the number implementation simple for now // but we need to decide on our approach to numbers, are @@ -47,7 +48,16 @@ impl PartialEq for Number { impl Eq for Number {} impl Expression for Number { - fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr { + fn eval(&self, _rt: &RT, _scope: &Scope) -> PossibleExpr { Ok(Expr::Num(self.clone())) } } + +impl fmt::Display for Number { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Integer(n) => write!(f, "{}", n), + Self::Float(n) => write!(f, "{}", n), + } + } +} diff --git a/bootstrap/src/types/symbol.rs b/bootstrap/src/types/symbol.rs index be1b710..ed1b080 100644 --- a/bootstrap/src/types/symbol.rs +++ b/bootstrap/src/types/symbol.rs @@ -14,13 +14,57 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -use crate::ast::{Expr, Expression, PossibleExpr}; +use crate::ast::{Expr, Expression, Location, PossibleExpr}; +use crate::errors::err; use crate::runtime::RT; use crate::scope::Scope; +use std::fmt; #[derive(Debug, Clone)] pub struct Symbol { pub name: String, + + /// This field holds the ns specifier part of the symbol. For example + /// in case of `somens/somesym`, this field will hold `somens`. + target_ns: Option, + /// Name of the namespace which this symbol is in. It doesn't mean + /// the namespace which this symbol is defined. For example Let's + /// say we're in ns A, and there is a sumbol `B/x`. This symbol + /// refers to the symbol `x` in ns B and it's not the same as + /// the symbol `x` in ns B. They are two different symbols pointing + /// to the same value. the `ns` value of the one in ns A would be `A` + /// and the one in B would be `B`. + ns: Option, +} + +impl Symbol { + pub fn new(name: String, target_ns: Option) -> Symbol { + Symbol { + name, + target_ns, + ns: None, + } + } + + pub fn is_ns_qualified(&self) -> bool { + !self.target_ns.is_none() + } + + pub fn is_def(&self) -> bool { + self.name == "def" + } + + /// Only clones the symbol if ns isn't set yet. + pub fn clone_with_ns(self, ns_name: String) -> Symbol { + if let Some(_) = self.ns { + return self; + } + + Symbol { + ns: Some(ns_name), + ..self.clone() + } + } } impl PartialEq for Symbol { @@ -33,16 +77,32 @@ impl Eq for Symbol {} impl Expression for Symbol { fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr { - Ok(Expr::Sym(self.clone())) + if self.is_ns_qualified() { + return rt + .current_ns() + .lookup_external(&self.target_ns.clone().unwrap(), &self.name); + } + + match scope.lookup(&self.name) { + Some(e) => Ok(e.expr.clone()), + _ => Err(err(format!( + "Undefined binding {} in ns '{}' at {}", + self, + self.ns.clone().unwrap(), + self.location() + ))), + } } } -impl Symbol { - pub fn new(name: String) -> Self { - Symbol { name } - } - - pub fn is_def(&self) -> bool { - self.name == "def" +impl fmt::Display for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.ns { + Some(n) => write!(f, "#'{}/{}", &n, &self.name), + _ => panic!( + "Displaying symbol '{:?}' without evaluating it.", + &self.name + ), + } } }