Create the symbol lookup in Symbol's eval fn
This commit is contained in:
parent
9181292651
commit
31c2083ddb
|
@ -19,10 +19,30 @@ use crate::runtime::RT;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::types::collections;
|
use crate::types::collections;
|
||||||
use crate::types::{Number, Symbol};
|
use crate::types::{Number, Symbol};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
pub type PossibleExpr = Result<Expr, Error>;
|
pub type PossibleExpr = Result<Expr, Error>;
|
||||||
|
|
||||||
|
#[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 {
|
pub trait Expression {
|
||||||
|
fn location(&self) -> Location {
|
||||||
|
Location {
|
||||||
|
position: 0,
|
||||||
|
file_path: "NotImplemented".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr;
|
fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +71,8 @@ impl Expr {
|
||||||
collections::List::new_empty()
|
collections::List::new_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_symbol(v: String) -> Expr {
|
pub fn make_symbol(v: String, target_ns: Option<String>) -> Expr {
|
||||||
Expr::Sym(Symbol::new(v))
|
Expr::Sym(Symbol::new(v, target_ns))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_string(v: String) -> Expr {
|
pub fn make_string(v: String) -> Expr {
|
||||||
|
@ -64,12 +84,12 @@ impl Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Expression for Expr {
|
impl fmt::Display for Expr {
|
||||||
// fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
// match self {
|
match self {
|
||||||
// Expr::Sym(s) => {
|
Expr::Num(n) => n.fmt(f),
|
||||||
// s.eval
|
Expr::Sym(s) => s.fmt(f),
|
||||||
// }
|
_ => write!(f, "NA"),
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
|
@ -14,21 +14,36 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use crate::ast::{Expr, PossibleExpr};
|
use crate::ast::{Expr, Expression, PossibleExpr};
|
||||||
use crate::errors::err;
|
use crate::errors::err;
|
||||||
use crate::reader::read_string;
|
use crate::reader::read_string;
|
||||||
use crate::runtime::RT;
|
use crate::runtime::RT;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
|
||||||
fn eval_expr(rt: &RT, scope: &Scope, expr: Expr) -> PossibleExpr {
|
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<Expr>) -> PossibleExpr {
|
pub fn eval(rt: &RT, exprs: Vec<Expr>) -> PossibleExpr {
|
||||||
match exprs.last() {
|
if exprs.len() == 0 {
|
||||||
Some(e) => Ok(e.clone()),
|
return Ok(Expr::NoMatch);
|
||||||
_ => Err(err("NotImplemented".to_string())),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
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() {
|
if rt.is_debug() {
|
||||||
println!("Eval Result: \n{:?}\n", result_expr);
|
println!("Eval Result: \n{:?}\n", result_expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match result_expr {
|
||||||
|
Ok(expr) => println!("{}", expr),
|
||||||
|
Err(e) => println!("{}", e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => println!("Error: {}", e),
|
Err(e) => println!("Error: {}", e),
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
pub trait IError {
|
pub trait IError {
|
||||||
fn message(&self) -> &str;
|
fn message(&self) -> &str;
|
||||||
|
@ -24,6 +25,12 @@ pub struct Error {
|
||||||
msg: String,
|
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 {
|
impl IError for Error {
|
||||||
fn message(&self) -> &str {
|
fn message(&self) -> &str {
|
||||||
&self.msg
|
&self.msg
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
use crate::ast::{Expr, PossibleExpr};
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
|
||||||
pub struct Namespace {
|
pub struct Namespace {
|
||||||
|
@ -23,7 +24,7 @@ pub struct Namespace {
|
||||||
root_scope: Scope,
|
root_scope: Scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> Namespace {
|
impl Namespace {
|
||||||
pub fn new(name: String, source_file: Option<String>) -> Namespace {
|
pub fn new(name: String, source_file: Option<String>) -> Namespace {
|
||||||
Namespace {
|
Namespace {
|
||||||
name,
|
name,
|
||||||
|
@ -31,4 +32,12 @@ impl<'ctx> Namespace {
|
||||||
root_scope: Scope::new(None),
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ impl ExprReader {
|
||||||
|
|
||||||
fn read_quoted_expr<T: Read>(&mut self, reader: &mut BufReader<T>) -> ReadResult {
|
fn read_quoted_expr<T: Read>(&mut self, reader: &mut BufReader<T>) -> ReadResult {
|
||||||
let rest = self.read_expr(reader)?;
|
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))
|
Ok(Expr::make_list(&elements))
|
||||||
}
|
}
|
||||||
|
@ -102,12 +102,15 @@ impl ExprReader {
|
||||||
// Move forward in the buffer since we peeked it
|
// Move forward in the buffer since we peeked it
|
||||||
let _ = self.get_char(reader, true);
|
let _ = self.get_char(reader, true);
|
||||||
let rest = self.read_expr(reader)?;
|
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))
|
Ok(Expr::make_list(&elements))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let rest = self.read_expr(reader)?;
|
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))
|
Ok(Expr::make_list(&elements))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +118,7 @@ impl ExprReader {
|
||||||
|
|
||||||
fn read_quasiquoted_expr<T: Read>(&mut self, reader: &mut BufReader<T>) -> ReadResult {
|
fn read_quasiquoted_expr<T: Read>(&mut self, reader: &mut BufReader<T>) -> ReadResult {
|
||||||
let rest = self.read_expr(reader)?;
|
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))
|
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<T: Read>(&mut self, reader: &mut BufReader<T>) -> Option<char> {
|
fn read_escape_char<T: Read>(&mut self, reader: &mut BufReader<T>) -> Option<char> {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use crate::namespace::Namespace;
|
use crate::namespace::Namespace;
|
||||||
|
use crate::scope::Scope;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -72,6 +73,10 @@ impl RT {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn current_scope(&self) -> &Scope {
|
||||||
|
self.current_ns().current_scope()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_debug_mode(&mut self, v: bool) {
|
pub fn set_debug_mode(&mut self, v: bool) {
|
||||||
self.debug = v;
|
self.debug = v;
|
||||||
|
|
|
@ -19,8 +19,8 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
/// This struct describes the values in the scope.
|
/// This struct describes the values in the scope.
|
||||||
pub struct ScopeElement {
|
pub struct ScopeElement {
|
||||||
element_type: Expr,
|
pub expr: Expr,
|
||||||
public: bool,
|
pub public: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scopes in **Serene** are simply represented by hashmaps. Each
|
/// 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) {
|
pub fn insert(&mut self, key: &str, expr: Expr, public: bool) {
|
||||||
let v = ScopeElement {
|
let v = ScopeElement { public, expr };
|
||||||
public,
|
|
||||||
element_type: val,
|
|
||||||
};
|
|
||||||
self.symbol_table.insert(key.to_string(), v);
|
self.symbol_table.insert(key.to_string(), v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
use crate::ast::{Expr, Expression, PossibleExpr};
|
use crate::ast::{Expr, Expression, PossibleExpr};
|
||||||
use crate::runtime::RT;
|
use crate::runtime::RT;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
// Note: I kept the number implementation simple for now
|
// Note: I kept the number implementation simple for now
|
||||||
// but we need to decide on our approach to numbers, are
|
// but we need to decide on our approach to numbers, are
|
||||||
|
@ -47,7 +48,16 @@ impl PartialEq for Number {
|
||||||
impl Eq for Number {}
|
impl Eq for Number {}
|
||||||
|
|
||||||
impl Expression 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()))
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,13 +14,57 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use crate::ast::{Expr, Expression, PossibleExpr};
|
use crate::ast::{Expr, Expression, Location, PossibleExpr};
|
||||||
|
use crate::errors::err;
|
||||||
use crate::runtime::RT;
|
use crate::runtime::RT;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Symbol {
|
pub struct Symbol {
|
||||||
pub name: String,
|
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<String>,
|
||||||
|
/// 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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Symbol {
|
||||||
|
pub fn new(name: String, target_ns: Option<String>) -> 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 {
|
impl PartialEq for Symbol {
|
||||||
|
@ -33,16 +77,32 @@ impl Eq for Symbol {}
|
||||||
|
|
||||||
impl Expression for Symbol {
|
impl Expression for Symbol {
|
||||||
fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr {
|
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 {
|
impl fmt::Display for Symbol {
|
||||||
pub fn new(name: String) -> Self {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
Symbol { name }
|
match &self.ns {
|
||||||
}
|
Some(n) => write!(f, "#'{}/{}", &n, &self.name),
|
||||||
|
_ => panic!(
|
||||||
pub fn is_def(&self) -> bool {
|
"Displaying symbol '{:?}' without evaluating it.",
|
||||||
self.name == "def"
|
&self.name
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue