Moving the eval function to the core module
This commit is contained in:
parent
5c6518f7d6
commit
cff76ac5a5
|
@ -15,12 +15,14 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::errors::Error;
|
||||
use crate::runtime::RT;
|
||||
use crate::runtime;
|
||||
use crate::scope::Scope;
|
||||
use crate::types::collections;
|
||||
use crate::types::{Number, Symbol};
|
||||
use crate::types::{BuiltinFunction, Number, Symbol};
|
||||
use std::fmt;
|
||||
|
||||
pub type AST = Vec<Expr>;
|
||||
|
||||
pub type PossibleExpr = Result<Expr, Error>;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
|
@ -35,7 +37,7 @@ impl fmt::Display for Location {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Expression {
|
||||
pub trait Node: fmt::Display {
|
||||
fn location(&self) -> Location {
|
||||
Location {
|
||||
position: 0,
|
||||
|
@ -43,27 +45,26 @@ pub trait Expression {
|
|||
}
|
||||
}
|
||||
|
||||
fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr;
|
||||
fn get_type_str(&self) -> &str {
|
||||
"Some type"
|
||||
}
|
||||
}
|
||||
|
||||
/// It differs from the `fmt::Display` in the way that anything that
|
||||
/// we want to show in a repl as the result of an evaluation and needs
|
||||
/// the runtime details has to implement this trait. But we use the
|
||||
/// `fmt::Display` as a formatter and in a way that it doesn't need the
|
||||
/// runtime.
|
||||
pub trait StringRepr {
|
||||
fn string_repr(&self, rt: &RT) -> String;
|
||||
pub trait Callable {
|
||||
fn apply(&self, rt: &runtime::Runtime, scope: &Scope, args: collections::List) -> Expr;
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub enum Expr {
|
||||
Sym(Symbol),
|
||||
Str(String),
|
||||
//Fn(Function),
|
||||
BuiltinFn(BuiltinFunction),
|
||||
Num(Number),
|
||||
Comment,
|
||||
Error(String),
|
||||
Cons(collections::List),
|
||||
Nil,
|
||||
Comment,
|
||||
NoMatch,
|
||||
}
|
||||
|
||||
|
@ -91,25 +92,49 @@ impl Expr {
|
|||
pub fn make_number(n: Number) -> Expr {
|
||||
Expr::Num(n)
|
||||
}
|
||||
|
||||
pub fn get_node(&self) -> Option<&dyn Node> {
|
||||
match self {
|
||||
Self::Num(x) => Some(x),
|
||||
Self::Sym(x) => Some(x),
|
||||
Self::Cons(x) => Some(x),
|
||||
Self::BuiltinFn(x) => Some(x),
|
||||
//Self::Str(x) => x,
|
||||
// Self:://Fn(Function),
|
||||
//Self::Error(String),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
match self.get_node() {
|
||||
Some(n) => n.fmt(f),
|
||||
_ => match self {
|
||||
Self::Comment => write!(f, "comment"),
|
||||
Self::Error(_) => write!(f, "error"),
|
||||
Self::Nil => write!(f, "nil"),
|
||||
Self::NoMatch => write!(f, "noMatch"),
|
||||
_ => write!(f, "Should Not happen"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StringRepr for Expr {
|
||||
fn string_repr(&self, rt: &RT) -> String {
|
||||
match self {
|
||||
Expr::Num(n) => n.string_repr(rt),
|
||||
Expr::Sym(s) => s.string_repr(rt),
|
||||
Expr::Cons(c) => c.string_repr(rt),
|
||||
_ => "NA".to_string(),
|
||||
impl Node for Expr {
|
||||
fn get_type_str(&self) -> &str {
|
||||
match self.get_node() {
|
||||
Some(x) => x.get_type_str(),
|
||||
None => match self {
|
||||
Self::Comment => "comment",
|
||||
Self::Error(_) => "error",
|
||||
Self::Nil => "nil",
|
||||
Self::NoMatch => "noMatch",
|
||||
_ => {
|
||||
panic!("This shouldn't happen. Checkout `get_node` and `get_type_str` on Expr")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::namespace::Namespace;
|
||||
use crate::types::Expression;
|
||||
use crate::types::Node;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Compiler {
|
||||
|
|
|
@ -14,25 +14,77 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::ast::{Expr, Expression, PossibleExpr, StringRepr};
|
||||
use crate::ast::{Callable, Expr, Node, PossibleExpr};
|
||||
use crate::errors::err;
|
||||
use crate::namespace;
|
||||
use crate::reader::read_string;
|
||||
use crate::runtime::RT;
|
||||
use crate::runtime;
|
||||
use crate::scope::Scope;
|
||||
use crate::types::collections::Seq;
|
||||
|
||||
fn eval_expr(rt: &RT, scope: &Scope, expr: Expr) -> PossibleExpr {
|
||||
pub fn string_repr(rt: &runtime::Runtime, expr: &Expr) -> String {
|
||||
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),
|
||||
Expr::Sym(s) => format!(
|
||||
"#'{}/{}",
|
||||
namespace::get_name(runtime::current_ns(rt)),
|
||||
&s.name
|
||||
),
|
||||
_ => format!("{}", expr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_expr(rt: &runtime::Runtime, scope: &Scope, expr: Expr) -> PossibleExpr {
|
||||
match &expr {
|
||||
// ** Number evaluation
|
||||
Expr::Num(_) => Ok(expr),
|
||||
|
||||
// ** Symbol evaluation
|
||||
Expr::Sym(s) => {
|
||||
if s.is_ns_qualified() {
|
||||
return namespace::lookup_external(
|
||||
runtime::current_ns(rt),
|
||||
&s.target_ns.clone().unwrap(),
|
||||
&s.name,
|
||||
);
|
||||
}
|
||||
|
||||
match scope.lookup(&s.name) {
|
||||
Some(e) => Ok(e.expr.clone()),
|
||||
_ => Err(err(format!(
|
||||
"Undefined binding {} in ns '{}' at {}",
|
||||
string_repr(rt, &expr),
|
||||
namespace::get_name(runtime::current_ns(rt)),
|
||||
s.location()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
// ** List evaluation
|
||||
Expr::Cons(l) => {
|
||||
if l.count() == 0 {
|
||||
return Ok(Expr::Nil);
|
||||
}
|
||||
|
||||
let first = l.first().unwrap();
|
||||
let rest = l.rest();
|
||||
match eval_expr(rt, scope, first) {
|
||||
Ok(e) => match e {
|
||||
//Expr::Fn(f) => f.apply(rt, scope, rest),
|
||||
Expr::BuiltinFn(b) => Ok(b.apply(rt, scope, rest)),
|
||||
_ => Err(err(format!(
|
||||
"Can't cast '{}' to functions at {}",
|
||||
e.get_type_str(),
|
||||
l.location(),
|
||||
))),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
_ => Ok(expr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(rt: &RT, exprs: Vec<Expr>) -> PossibleExpr {
|
||||
pub fn eval(rt: &runtime::Runtime, scope: &Scope, exprs: Vec<Expr>) -> PossibleExpr {
|
||||
if exprs.len() == 0 {
|
||||
return Ok(Expr::NoMatch);
|
||||
}
|
||||
|
@ -40,27 +92,31 @@ pub fn eval(rt: &RT, exprs: Vec<Expr>) -> PossibleExpr {
|
|||
let mut ret: PossibleExpr = Ok(Expr::NoMatch);
|
||||
|
||||
for expr in exprs.iter() {
|
||||
ret = eval_expr(rt, rt.current_scope(), expr.clone());
|
||||
ret = eval_expr(rt, scope, expr.clone());
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn read_eval_print(rt: &RT, input: &str) {
|
||||
pub fn read_eval_print(rt: &runtime::Runtime, input: &str) {
|
||||
match read_string(input) {
|
||||
Ok(exprs) => {
|
||||
if rt.is_debug() {
|
||||
if runtime::is_debug(rt) {
|
||||
println!("Read Result: \n{:?}\n", exprs);
|
||||
}
|
||||
|
||||
let result_expr = eval(rt, exprs);
|
||||
let result_expr = eval(
|
||||
rt,
|
||||
namespace::get_root_scope(&runtime::current_ns(rt)),
|
||||
exprs,
|
||||
);
|
||||
|
||||
if rt.is_debug() {
|
||||
if runtime::is_debug(rt) {
|
||||
println!("Eval Result: \n{:?}\n", result_expr);
|
||||
}
|
||||
|
||||
match result_expr {
|
||||
Ok(expr) => println!("{}", expr.string_repr(rt)),
|
||||
Ok(expr) => println!("{}", string_repr(rt, &expr)),
|
||||
Err(e) => println!("{}", e),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,19 @@
|
|||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
*/
|
||||
#![deny(
|
||||
missing_docs,
|
||||
missing_debug_implementations,
|
||||
missing_copy_implementations,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unsafe_code,
|
||||
unstable_features,
|
||||
unused_import_braces,
|
||||
unused_qualifications
|
||||
)]
|
||||
|
||||
use clap::{load_yaml, App, ArgMatches};
|
||||
use std::io;
|
||||
|
||||
|
@ -35,12 +47,12 @@ fn repl(args: ArgMatches) {
|
|||
debug = true;
|
||||
}
|
||||
|
||||
let mut rt = runtime::RT::new();
|
||||
let rt = runtime::create_runtime();
|
||||
|
||||
rt.create_ns("user".to_string(), None);
|
||||
rt.set_current_ns("user".to_string());
|
||||
rt.set_debug_mode(debug);
|
||||
repl::repl(rt);
|
||||
runtime::create_ns(&rt, "user".to_string(), None);
|
||||
runtime::set_current_ns(&rt, "user".to_string());
|
||||
runtime::set_debug_mode(&rt, debug);
|
||||
repl::repl(&rt);
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
*/
|
||||
use crate::ast::{Expr, PossibleExpr};
|
||||
use crate::scope::Scope;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Namespace {
|
||||
/// Root scope of the namespace
|
||||
pub name: String,
|
||||
pub source_file: Option<String>,
|
||||
root_scope: Scope,
|
||||
pub root_scope: Scope,
|
||||
}
|
||||
|
||||
impl Namespace {
|
||||
|
@ -33,11 +35,53 @@ impl Namespace {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn current_scope(&self) -> &Scope {
|
||||
&self.root_scope
|
||||
}
|
||||
|
||||
pub fn lookup_external(&self, target: &str, key: &str) -> PossibleExpr {
|
||||
Ok(Expr::Nil)
|
||||
}
|
||||
|
||||
pub fn define_global(&mut self, name: &str, expr: Expr, public: bool) {
|
||||
self.root_scope.insert(&name, expr, public);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn define_global(ns: &RwLock<Namespace>, name: &str, expr: Expr, public: bool) {
|
||||
match ns.write() {
|
||||
Ok(mut n) => n.define_global(name, expr, public),
|
||||
Err(_) => panic!("Poisoned write lock while defining a global: '{:?}'", ns),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_external(ns: &RwLock<Namespace>, target: &str, key: &str) -> PossibleExpr {
|
||||
match ns.read() {
|
||||
Ok(n) => n.lookup_external(target, key),
|
||||
Err(_) => panic!("Poisoned write lock while defining a global: '{:?}'", ns),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_name(ns: &RwLock<Namespace>) -> String {
|
||||
match ns.read() {
|
||||
Ok(n) => n.name.clone(),
|
||||
Err(_) => panic!("Poisoned write lock while defining a global: '{:?}'", ns),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_root_scope(ns: &RwLock<Namespace>) -> &Scope {
|
||||
match ns.read() {
|
||||
Ok(n) => &n.root_scope,
|
||||
Err(_) => panic!(
|
||||
"Poisoned write lock while getting the root scope: '{:?}'",
|
||||
ns
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ns_define_global() {
|
||||
let mut ns = Namespace::new("ns1".to_string(), None);
|
||||
assert_eq!(ns.root_scope.lookup("blah").is_none(), true);
|
||||
|
||||
ns.define_global("something", Expr::Nil, true);
|
||||
let result = ns.root_scope.lookup("something").unwrap();
|
||||
assert_eq!(result.expr, Expr::Nil);
|
||||
assert_eq!(result.public, true);
|
||||
}
|
||||
|
|
|
@ -15,20 +15,21 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::core::read_eval_print;
|
||||
use crate::runtime::RT;
|
||||
use crate::namespace;
|
||||
use crate::runtime;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::Editor;
|
||||
|
||||
pub fn repl(rt: RT) {
|
||||
pub fn repl(rt: &runtime::Runtime) {
|
||||
let mut rl = Editor::<()>::new();
|
||||
let history = rt.history_file_path();
|
||||
let history = runtime::history_file_path();
|
||||
|
||||
if rl.load_history(&history).is_err() && rt.is_debug() {
|
||||
if rl.load_history(&history).is_err() && runtime::is_debug(&rt) {
|
||||
println!("No previous history.");
|
||||
}
|
||||
|
||||
loop {
|
||||
let prompt = format!("{}> ", rt.current_ns().name);
|
||||
let prompt = format!("{}> ", namespace::get_name(runtime::current_ns(rt)));
|
||||
let readline = rl.readline(&prompt);
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
|
|
|
@ -13,15 +13,18 @@
|
|||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
*/
|
||||
use crate::ast::Expr;
|
||||
use crate::namespace::Namespace;
|
||||
use crate::scope::Scope;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
const SERENE_HISTORY_FILE: &'static str = ".serene.hitory";
|
||||
|
||||
pub type Runtime = Arc<RwLock<RT>>;
|
||||
|
||||
pub struct RT {
|
||||
/// This hashmap contains all the namespaces that has to be compiled and
|
||||
/// maps two different keys to the same namespace. Since namespace names
|
||||
|
@ -32,68 +35,100 @@ pub struct RT {
|
|||
/// two entries in this hashmap. One would be the ns name itself which
|
||||
/// is `abc.xyz` in this case and the otherone would be
|
||||
/// `/path/to/abc/xyz.srn` file that contains the ns.
|
||||
pub namespaces: HashMap<String, Namespace>,
|
||||
current_ns_name: Option<String>,
|
||||
debug: bool,
|
||||
pub namespaces: HashMap<String, RwLock<Namespace>>,
|
||||
pub current_ns_name: Option<String>,
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
impl RT {
|
||||
pub fn new() -> RT {
|
||||
RT {
|
||||
namespaces: HashMap::new(),
|
||||
current_ns_name: None,
|
||||
debug: false,
|
||||
}
|
||||
pub fn create_runtime() -> Runtime {
|
||||
Arc::new(RwLock::new(RT {
|
||||
namespaces: HashMap::new(),
|
||||
current_ns_name: None,
|
||||
debug: false,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Create a new namespace with the given `ns_name` and add it to the current
|
||||
/// runtime.
|
||||
pub fn create_ns(rt: &Runtime, ns_name: String, source_file: Option<String>) {
|
||||
let mut r = match rt.write() {
|
||||
Ok(r) => r,
|
||||
|
||||
Err(_) => panic!("Poisoned runtime!"),
|
||||
};
|
||||
|
||||
r.namespaces.insert(
|
||||
ns_name.clone(),
|
||||
RwLock::new(Namespace::new(ns_name, source_file)),
|
||||
);
|
||||
}
|
||||
|
||||
/// Set the current ns to the given `ns_name`. The `ns_name` has to be
|
||||
/// part of the runtime already.
|
||||
pub fn set_current_ns(rt: &Runtime, ns_name: String) {
|
||||
let mut r = match rt.write() {
|
||||
Ok(r) => r,
|
||||
Err(_) => panic!("Poisoned runtime!"),
|
||||
};
|
||||
|
||||
match r.namespaces.get(&ns_name) {
|
||||
Some(_) => r.current_ns_name = Some(ns_name),
|
||||
None => panic!("The given namespace '{}' doesn't exit", ns_name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new namespace with the given `ns_name` and add it to the current
|
||||
/// runtime.
|
||||
pub fn create_ns(&mut self, ns_name: String, source_file: Option<String>) {
|
||||
self.namespaces
|
||||
.insert(ns_name.clone(), Namespace::new(ns_name, source_file));
|
||||
}
|
||||
|
||||
/// Set the current ns to the given `ns_name`. The `ns_name` has to be
|
||||
/// part of the runtime already.
|
||||
pub fn set_current_ns(&mut self, ns_name: String) {
|
||||
match self.namespaces.get(&ns_name) {
|
||||
Some(_) => self.current_ns_name = Some(ns_name),
|
||||
None => panic!("The given namespace '{}' doesn't exit", ns_name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_ns(&self) -> &Namespace {
|
||||
if let None = self.current_ns_name {
|
||||
// `current_ns_name` has to be not None all the time.
|
||||
panic!("No namespace has been set to current.");
|
||||
}
|
||||
|
||||
self.namespaces
|
||||
.get(&self.current_ns_name.clone().unwrap())
|
||||
.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;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_debug(&self) -> bool {
|
||||
self.debug
|
||||
}
|
||||
|
||||
pub fn history_file_path(&self) -> String {
|
||||
match env::var("HOME") {
|
||||
Ok(v) => {
|
||||
let history = Path::new(&v).join(SERENE_HISTORY_FILE).clone();
|
||||
history.to_str().unwrap().into()
|
||||
pub fn current_ns(rt: &Runtime) -> &RwLock<Namespace> {
|
||||
match rt.read() {
|
||||
Ok(mut r) => {
|
||||
let ns_name = r.current_ns_name.clone().unwrap();
|
||||
match r.namespaces.get(&ns_name) {
|
||||
Some(x) => x,
|
||||
_ => panic!("No namespace has been set to current."),
|
||||
}
|
||||
Err(_) => SERENE_HISTORY_FILE.into(),
|
||||
}
|
||||
Err(_) => panic!("Poisoned runtime!"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_debug_mode(rt: &Runtime, v: bool) {
|
||||
let mut r = match rt.write() {
|
||||
Ok(r) => r,
|
||||
Err(_) => panic!("Poisoned runtime!"),
|
||||
};
|
||||
r.debug = v;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_debug(rt: &Runtime) -> bool {
|
||||
match rt.read() {
|
||||
Ok(r) => r.debug,
|
||||
Err(_) => panic!("Poisoned runtime!"),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move this function to somewhere else
|
||||
pub fn history_file_path() -> String {
|
||||
match env::var("HOME") {
|
||||
Ok(v) => {
|
||||
let history = Path::new(&v).join(SERENE_HISTORY_FILE).clone();
|
||||
history.to_str().unwrap().into()
|
||||
}
|
||||
Err(_) => SERENE_HISTORY_FILE.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_runtime_ns_mutation() {
|
||||
let rt = create_runtime();
|
||||
create_ns(&rt, "user".to_string(), None);
|
||||
set_current_ns(&rt, "user".to_string());
|
||||
let ns = current_ns(&rt).read().unwrap();
|
||||
assert_eq!(ns.root_scope.lookup("blah").is_none(), true);
|
||||
|
||||
let ns = current_ns(&rt).write().unwrap();
|
||||
ns.define_global("something", Expr::Nil, true);
|
||||
let result = ns.root_scope.lookup("something").unwrap();
|
||||
assert_eq!(result.expr, Expr::Nil);
|
||||
assert_eq!(result.public, true);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,10 @@
|
|||
use crate::ast::Expr;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub type Scope = Arc<RwLock<UnSafeScope>>;
|
||||
|
||||
/// This struct describes the values in the scope.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScopeElement {
|
||||
pub expr: Expr,
|
||||
pub public: bool,
|
||||
|
@ -26,19 +29,20 @@ pub struct ScopeElement {
|
|||
/// Scopes in **Serene** are simply represented by hashmaps. Each
|
||||
/// Scope optionally has a parent scope that lookups fallback to
|
||||
/// if the lookup key is missing from the current scope.
|
||||
pub struct Scope {
|
||||
parent: Option<Box<Scope>>,
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnSafeScope {
|
||||
parent: Option<Scope>,
|
||||
symbol_table: HashMap<String, ScopeElement>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
impl UnSafeScope {
|
||||
pub fn new(_parent: Option<Scope>) -> Scope {
|
||||
let p = match _parent {
|
||||
Some(x) => Some(Box::new(x)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Scope {
|
||||
UnSafeScope {
|
||||
parent: p,
|
||||
symbol_table: HashMap::new(),
|
||||
}
|
||||
|
@ -62,3 +66,11 @@ impl Scope {
|
|||
self.symbol_table.insert(key.to_string(), v);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope() {
|
||||
let mut scope = Scope::new(None);
|
||||
scope.insert("sym1", Expr::Nil, true);
|
||||
let sym = scope.lookup("sym1").unwrap();
|
||||
assert_eq!(sym.expr, Expr::Nil);
|
||||
}
|
||||
|
|
|
@ -14,10 +14,12 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
pub mod builtin_function;
|
||||
pub mod collections;
|
||||
pub mod number;
|
||||
pub mod symbol;
|
||||
|
||||
pub use self::builtin_function::BuiltinFunction;
|
||||
pub use self::collections::{List, Seq};
|
||||
pub use self::number::Number;
|
||||
pub use self::symbol::Symbol;
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/* Serene --- Yet an other Lisp
|
||||
*
|
||||
* Copyright (c) 2020 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::ast::{Callable, Expr, Node};
|
||||
use crate::runtime;
|
||||
use crate::scope::Scope;
|
||||
use crate::types::collections::List;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub type BuiltinHandler = fn(rt: &runtime::Runtime, scope: &Scope, args: List) -> Expr;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BuiltinFunction {
|
||||
pub name: String,
|
||||
ns: String,
|
||||
handler_function: BuiltinHandler,
|
||||
}
|
||||
|
||||
impl PartialEq for BuiltinFunction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.name == other.name) && (self.ns == other.ns)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BuiltinFunction {}
|
||||
|
||||
impl Node for BuiltinFunction {
|
||||
fn get_type_str(&self) -> &str {
|
||||
"Function"
|
||||
}
|
||||
}
|
||||
|
||||
impl Callable for BuiltinFunction {
|
||||
fn apply(&self, rt: &runtime::Runtime, scope: &Scope, args: List) -> Expr {
|
||||
let f = self.handler_function;
|
||||
f(rt, scope, args)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BuiltinFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}/{}", &self.ns, &self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BuiltinFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("BuiltinFunction")
|
||||
.field("name", &self.name)
|
||||
.field("ns", &self.ns)
|
||||
.finish()
|
||||
}
|
||||
}
|
|
@ -16,12 +16,8 @@
|
|||
*/
|
||||
|
||||
//use crate::builtins::def;
|
||||
use crate::ast::{Expr, Expression, PossibleExpr, StringRepr};
|
||||
use crate::errors::err;
|
||||
use crate::runtime::RT;
|
||||
use crate::scope::Scope;
|
||||
use crate::ast::{Expr, Node};
|
||||
use crate::types::collections::core::Seq;
|
||||
use crate::types::Symbol;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
|
@ -68,20 +64,9 @@ impl Seq<Expr> for List {
|
|||
}
|
||||
}
|
||||
|
||||
impl Expression for List {
|
||||
fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr {
|
||||
if self.count() == 0 {
|
||||
return Ok(Expr::Nil);
|
||||
}
|
||||
|
||||
let first = self.first().unwrap();
|
||||
let rest = self.rest();
|
||||
|
||||
Ok(Expr::Cons(self.clone()))
|
||||
// match first {
|
||||
// Expr::Sym(sum) => {}
|
||||
// _ => Err(err("NotImplemented".to_string())),
|
||||
// }
|
||||
impl Node for List {
|
||||
fn get_type_str(&self) -> &str {
|
||||
"List"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,9 +82,3 @@ impl fmt::Display for List {
|
|||
write!(f, "({})", &elems.join(" "))
|
||||
}
|
||||
}
|
||||
|
||||
impl StringRepr for List {
|
||||
fn string_repr(&self, rt: &RT) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/* Serene --- Yet an other Lisp
|
||||
*
|
||||
* Copyright (c) 2020 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::ast::Node;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Function {
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
//impl Eq for Function {}
|
||||
|
||||
impl Node for Function {
|
||||
fn get_type_str(&self) -> &str {
|
||||
"Function"
|
||||
}
|
||||
}
|
||||
|
||||
// impl fmt::Display for Function {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// write!(f, "{}", &self.name)
|
||||
// }
|
||||
// }
|
|
@ -14,9 +14,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::ast::{Expr, Expression, PossibleExpr, StringRepr};
|
||||
use crate::runtime::RT;
|
||||
use crate::scope::Scope;
|
||||
use crate::ast::Node;
|
||||
use std::fmt;
|
||||
|
||||
// Note: I kept the number implementation simple for now
|
||||
|
@ -47,9 +45,9 @@ impl PartialEq for Number {
|
|||
|
||||
impl Eq for Number {}
|
||||
|
||||
impl Expression for Number {
|
||||
fn eval(&self, _rt: &RT, _scope: &Scope) -> PossibleExpr {
|
||||
Ok(Expr::Num(self.clone()))
|
||||
impl Node for Number {
|
||||
fn get_type_str(&self) -> &str {
|
||||
"Number"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,9 +59,3 @@ impl fmt::Display for Number {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StringRepr for Number {
|
||||
fn string_repr(&self, rt: &RT) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::ast::{Expr, Expression, Location, PossibleExpr, StringRepr};
|
||||
use crate::errors::err;
|
||||
use crate::runtime::RT;
|
||||
use crate::scope::Scope;
|
||||
use crate::ast::Node;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -26,24 +23,12 @@ pub struct Symbol {
|
|||
|
||||
/// 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>,
|
||||
pub target_ns: Option<String>,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn new(name: String, target_ns: Option<String>) -> Symbol {
|
||||
Symbol {
|
||||
name,
|
||||
target_ns,
|
||||
ns: None,
|
||||
}
|
||||
Symbol { name, target_ns }
|
||||
}
|
||||
|
||||
pub fn is_ns_qualified(&self) -> bool {
|
||||
|
@ -53,18 +38,6 @@ impl Symbol {
|
|||
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 {
|
||||
|
@ -75,23 +48,9 @@ impl PartialEq for Symbol {
|
|||
|
||||
impl Eq for Symbol {}
|
||||
|
||||
impl Expression for Symbol {
|
||||
fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr {
|
||||
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 Node for Symbol {
|
||||
fn get_type_str(&self) -> &str {
|
||||
"Symbol"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,9 +59,3 @@ impl fmt::Display for Symbol {
|
|||
write!(f, "{}", &self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl StringRepr for Symbol {
|
||||
fn string_repr(&self, rt: &RT) -> String {
|
||||
format!("#'{}/{}", &rt.current_ns().name, &self.name)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue