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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use crate::errors::Error;
|
use crate::errors::Error;
|
||||||
use crate::runtime::RT;
|
use crate::runtime;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::types::collections;
|
use crate::types::collections;
|
||||||
use crate::types::{Number, Symbol};
|
use crate::types::{BuiltinFunction, Number, Symbol};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
pub type AST = Vec<Expr>;
|
||||||
|
|
||||||
pub type PossibleExpr = Result<Expr, Error>;
|
pub type PossibleExpr = Result<Expr, Error>;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[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 {
|
fn location(&self) -> Location {
|
||||||
Location {
|
Location {
|
||||||
position: 0,
|
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
|
pub trait Callable {
|
||||||
/// we want to show in a repl as the result of an evaluation and needs
|
fn apply(&self, rt: &runtime::Runtime, scope: &Scope, args: collections::List) -> Expr;
|
||||||
/// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Sym(Symbol),
|
Sym(Symbol),
|
||||||
Str(String),
|
Str(String),
|
||||||
|
//Fn(Function),
|
||||||
|
BuiltinFn(BuiltinFunction),
|
||||||
Num(Number),
|
Num(Number),
|
||||||
Comment,
|
|
||||||
Error(String),
|
Error(String),
|
||||||
Cons(collections::List),
|
Cons(collections::List),
|
||||||
Nil,
|
Nil,
|
||||||
|
Comment,
|
||||||
NoMatch,
|
NoMatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,25 +92,49 @@ impl Expr {
|
||||||
pub fn make_number(n: Number) -> Expr {
|
pub fn make_number(n: Number) -> Expr {
|
||||||
Expr::Num(n)
|
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 {
|
impl fmt::Display for Expr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self.get_node() {
|
||||||
Expr::Num(n) => n.fmt(f),
|
Some(n) => n.fmt(f),
|
||||||
Expr::Sym(s) => s.fmt(f),
|
_ => match self {
|
||||||
_ => write!(f, "NA"),
|
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 {
|
impl Node for Expr {
|
||||||
fn string_repr(&self, rt: &RT) -> String {
|
fn get_type_str(&self) -> &str {
|
||||||
match self {
|
match self.get_node() {
|
||||||
Expr::Num(n) => n.string_repr(rt),
|
Some(x) => x.get_type_str(),
|
||||||
Expr::Sym(s) => s.string_repr(rt),
|
None => match self {
|
||||||
Expr::Cons(c) => c.string_repr(rt),
|
Self::Comment => "comment",
|
||||||
_ => "NA".to_string(),
|
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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use crate::namespace::Namespace;
|
use crate::namespace::Namespace;
|
||||||
use crate::types::Expression;
|
use crate::types::Node;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct Compiler {
|
pub struct Compiler {
|
||||||
|
|
|
@ -14,25 +14,77 @@
|
||||||
* 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, StringRepr};
|
use crate::ast::{Callable, Expr, Node, PossibleExpr};
|
||||||
use crate::errors::err;
|
use crate::errors::err;
|
||||||
|
use crate::namespace;
|
||||||
use crate::reader::read_string;
|
use crate::reader::read_string;
|
||||||
use crate::runtime::RT;
|
use crate::runtime;
|
||||||
use crate::scope::Scope;
|
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 {
|
match expr {
|
||||||
Expr::Num(n) => n.eval(rt, scope),
|
Expr::Sym(s) => format!(
|
||||||
// TODO: find a better way to attach the ns name to the symbol. This
|
"#'{}/{}",
|
||||||
// is ugly.
|
namespace::get_name(runtime::current_ns(rt)),
|
||||||
Expr::Sym(s) => s
|
&s.name
|
||||||
.clone_with_ns(rt.current_ns().name.clone())
|
),
|
||||||
.eval(rt, scope),
|
_ => 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),
|
_ => 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 {
|
if exprs.len() == 0 {
|
||||||
return Ok(Expr::NoMatch);
|
return Ok(Expr::NoMatch);
|
||||||
}
|
}
|
||||||
|
@ -40,27 +92,31 @@ pub fn eval(rt: &RT, exprs: Vec<Expr>) -> PossibleExpr {
|
||||||
let mut ret: PossibleExpr = Ok(Expr::NoMatch);
|
let mut ret: PossibleExpr = Ok(Expr::NoMatch);
|
||||||
|
|
||||||
for expr in exprs.iter() {
|
for expr in exprs.iter() {
|
||||||
ret = eval_expr(rt, rt.current_scope(), expr.clone());
|
ret = eval_expr(rt, scope, expr.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_eval_print(rt: &RT, input: &str) {
|
pub fn read_eval_print(rt: &runtime::Runtime, input: &str) {
|
||||||
match read_string(input) {
|
match read_string(input) {
|
||||||
Ok(exprs) => {
|
Ok(exprs) => {
|
||||||
if rt.is_debug() {
|
if runtime::is_debug(rt) {
|
||||||
println!("Read Result: \n{:?}\n", exprs);
|
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);
|
println!("Eval Result: \n{:?}\n", result_expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
match result_expr {
|
match result_expr {
|
||||||
Ok(expr) => println!("{}", expr.string_repr(rt)),
|
Ok(expr) => println!("{}", string_repr(rt, &expr)),
|
||||||
Err(e) => println!("{}", e),
|
Err(e) => println!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,18 @@
|
||||||
* 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/>.
|
||||||
*/
|
*/
|
||||||
|
#![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 clap::{load_yaml, App, ArgMatches};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
@ -35,12 +47,12 @@ fn repl(args: ArgMatches) {
|
||||||
debug = true;
|
debug = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rt = runtime::RT::new();
|
let rt = runtime::create_runtime();
|
||||||
|
|
||||||
rt.create_ns("user".to_string(), None);
|
runtime::create_ns(&rt, "user".to_string(), None);
|
||||||
rt.set_current_ns("user".to_string());
|
runtime::set_current_ns(&rt, "user".to_string());
|
||||||
rt.set_debug_mode(debug);
|
runtime::set_debug_mode(&rt, debug);
|
||||||
repl::repl(rt);
|
repl::repl(&rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> io::Result<()> {
|
fn main() -> io::Result<()> {
|
||||||
|
|
|
@ -16,12 +16,14 @@
|
||||||
*/
|
*/
|
||||||
use crate::ast::{Expr, PossibleExpr};
|
use crate::ast::{Expr, PossibleExpr};
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Namespace {
|
pub struct Namespace {
|
||||||
/// Root scope of the namespace
|
/// Root scope of the namespace
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub source_file: Option<String>,
|
pub source_file: Option<String>,
|
||||||
root_scope: Scope,
|
pub root_scope: Scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Namespace {
|
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 {
|
pub fn lookup_external(&self, target: &str, key: &str) -> PossibleExpr {
|
||||||
Ok(Expr::Nil)
|
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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use crate::core::read_eval_print;
|
use crate::core::read_eval_print;
|
||||||
use crate::runtime::RT;
|
use crate::namespace;
|
||||||
|
use crate::runtime;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::Editor;
|
use rustyline::Editor;
|
||||||
|
|
||||||
pub fn repl(rt: RT) {
|
pub fn repl(rt: &runtime::Runtime) {
|
||||||
let mut rl = Editor::<()>::new();
|
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.");
|
println!("No previous history.");
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let prompt = format!("{}> ", rt.current_ns().name);
|
let prompt = format!("{}> ", namespace::get_name(runtime::current_ns(rt)));
|
||||||
let readline = rl.readline(&prompt);
|
let readline = rl.readline(&prompt);
|
||||||
match readline {
|
match readline {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
|
|
|
@ -14,14 +14,17 @@
|
||||||
* 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;
|
||||||
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;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
const SERENE_HISTORY_FILE: &'static str = ".serene.hitory";
|
const SERENE_HISTORY_FILE: &'static str = ".serene.hitory";
|
||||||
|
|
||||||
|
pub type Runtime = Arc<RwLock<RT>>;
|
||||||
|
|
||||||
pub struct RT {
|
pub struct RT {
|
||||||
/// This hashmap contains all the namespaces that has to be compiled and
|
/// This hashmap contains all the namespaces that has to be compiled and
|
||||||
/// maps two different keys to the same namespace. Since namespace names
|
/// maps two different keys to the same namespace. Since namespace names
|
||||||
|
@ -32,62 +35,80 @@ pub struct RT {
|
||||||
/// two entries in this hashmap. One would be the ns name itself which
|
/// two entries in this hashmap. One would be the ns name itself which
|
||||||
/// is `abc.xyz` in this case and the otherone would be
|
/// is `abc.xyz` in this case and the otherone would be
|
||||||
/// `/path/to/abc/xyz.srn` file that contains the ns.
|
/// `/path/to/abc/xyz.srn` file that contains the ns.
|
||||||
pub namespaces: HashMap<String, Namespace>,
|
pub namespaces: HashMap<String, RwLock<Namespace>>,
|
||||||
current_ns_name: Option<String>,
|
pub current_ns_name: Option<String>,
|
||||||
debug: bool,
|
pub debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RT {
|
pub fn create_runtime() -> Runtime {
|
||||||
pub fn new() -> RT {
|
Arc::new(RwLock::new(RT {
|
||||||
RT {
|
|
||||||
namespaces: HashMap::new(),
|
namespaces: HashMap::new(),
|
||||||
current_ns_name: None,
|
current_ns_name: None,
|
||||||
debug: false,
|
debug: false,
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new namespace with the given `ns_name` and add it to the current
|
/// Create a new namespace with the given `ns_name` and add it to the current
|
||||||
/// runtime.
|
/// runtime.
|
||||||
pub fn create_ns(&mut self, ns_name: String, source_file: Option<String>) {
|
pub fn create_ns(rt: &Runtime, ns_name: String, source_file: Option<String>) {
|
||||||
self.namespaces
|
let mut r = match rt.write() {
|
||||||
.insert(ns_name.clone(), Namespace::new(ns_name, source_file));
|
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
|
/// Set the current ns to the given `ns_name`. The `ns_name` has to be
|
||||||
/// part of the runtime already.
|
/// part of the runtime already.
|
||||||
pub fn set_current_ns(&mut self, ns_name: String) {
|
pub fn set_current_ns(rt: &Runtime, ns_name: String) {
|
||||||
match self.namespaces.get(&ns_name) {
|
let mut r = match rt.write() {
|
||||||
Some(_) => self.current_ns_name = Some(ns_name),
|
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),
|
None => panic!("The given namespace '{}' doesn't exit", ns_name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_ns(&self) -> &Namespace {
|
pub fn current_ns(rt: &Runtime) -> &RwLock<Namespace> {
|
||||||
if let None = self.current_ns_name {
|
match rt.read() {
|
||||||
// `current_ns_name` has to be not None all the time.
|
Ok(mut r) => {
|
||||||
panic!("No namespace has been set to current.");
|
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."),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.namespaces
|
|
||||||
.get(&self.current_ns_name.clone().unwrap())
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
Err(_) => panic!("Poisoned runtime!"),
|
||||||
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(rt: &Runtime, v: bool) {
|
||||||
self.debug = v;
|
let mut r = match rt.write() {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => panic!("Poisoned runtime!"),
|
||||||
|
};
|
||||||
|
r.debug = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_debug(&self) -> bool {
|
pub fn is_debug(rt: &Runtime) -> bool {
|
||||||
self.debug
|
match rt.read() {
|
||||||
|
Ok(r) => r.debug,
|
||||||
|
Err(_) => panic!("Poisoned runtime!"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn history_file_path(&self) -> String {
|
// TODO: Move this function to somewhere else
|
||||||
|
pub fn history_file_path() -> String {
|
||||||
match env::var("HOME") {
|
match env::var("HOME") {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
let history = Path::new(&v).join(SERENE_HISTORY_FILE).clone();
|
let history = Path::new(&v).join(SERENE_HISTORY_FILE).clone();
|
||||||
|
@ -96,4 +117,18 @@ impl RT {
|
||||||
Err(_) => SERENE_HISTORY_FILE.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 crate::ast::Expr;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub type Scope = Arc<RwLock<UnSafeScope>>;
|
||||||
|
|
||||||
/// This struct describes the values in the scope.
|
/// This struct describes the values in the scope.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct ScopeElement {
|
pub struct ScopeElement {
|
||||||
pub expr: Expr,
|
pub expr: Expr,
|
||||||
pub public: bool,
|
pub public: bool,
|
||||||
|
@ -26,19 +29,20 @@ pub struct ScopeElement {
|
||||||
/// Scopes in **Serene** are simply represented by hashmaps. Each
|
/// Scopes in **Serene** are simply represented by hashmaps. Each
|
||||||
/// Scope optionally has a parent scope that lookups fallback to
|
/// Scope optionally has a parent scope that lookups fallback to
|
||||||
/// if the lookup key is missing from the current scope.
|
/// if the lookup key is missing from the current scope.
|
||||||
pub struct Scope {
|
#[derive(Debug, Clone)]
|
||||||
parent: Option<Box<Scope>>,
|
pub struct UnSafeScope {
|
||||||
|
parent: Option<Scope>,
|
||||||
symbol_table: HashMap<String, ScopeElement>,
|
symbol_table: HashMap<String, ScopeElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl UnSafeScope {
|
||||||
pub fn new(_parent: Option<Scope>) -> Scope {
|
pub fn new(_parent: Option<Scope>) -> Scope {
|
||||||
let p = match _parent {
|
let p = match _parent {
|
||||||
Some(x) => Some(Box::new(x)),
|
Some(x) => Some(Box::new(x)),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Scope {
|
UnSafeScope {
|
||||||
parent: p,
|
parent: p,
|
||||||
symbol_table: HashMap::new(),
|
symbol_table: HashMap::new(),
|
||||||
}
|
}
|
||||||
|
@ -62,3 +66,11 @@ impl Scope {
|
||||||
self.symbol_table.insert(key.to_string(), v);
|
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
|
* 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/>.
|
||||||
*/
|
*/
|
||||||
|
pub mod builtin_function;
|
||||||
pub mod collections;
|
pub mod collections;
|
||||||
pub mod number;
|
pub mod number;
|
||||||
pub mod symbol;
|
pub mod symbol;
|
||||||
|
|
||||||
|
pub use self::builtin_function::BuiltinFunction;
|
||||||
pub use self::collections::{List, Seq};
|
pub use self::collections::{List, Seq};
|
||||||
pub use self::number::Number;
|
pub use self::number::Number;
|
||||||
pub use self::symbol::Symbol;
|
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::builtins::def;
|
||||||
use crate::ast::{Expr, Expression, PossibleExpr, StringRepr};
|
use crate::ast::{Expr, Node};
|
||||||
use crate::errors::err;
|
|
||||||
use crate::runtime::RT;
|
|
||||||
use crate::scope::Scope;
|
|
||||||
use crate::types::collections::core::Seq;
|
use crate::types::collections::core::Seq;
|
||||||
use crate::types::Symbol;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
|
@ -68,20 +64,9 @@ impl Seq<Expr> for List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expression for List {
|
impl Node for List {
|
||||||
fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr {
|
fn get_type_str(&self) -> &str {
|
||||||
if self.count() == 0 {
|
"List"
|
||||||
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())),
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,9 +82,3 @@ impl fmt::Display for List {
|
||||||
write!(f, "({})", &elems.join(" "))
|
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
|
* 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, StringRepr};
|
use crate::ast::Node;
|
||||||
use crate::runtime::RT;
|
|
||||||
use crate::scope::Scope;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
// Note: I kept the number implementation simple for now
|
// Note: I kept the number implementation simple for now
|
||||||
|
@ -47,9 +45,9 @@ impl PartialEq for Number {
|
||||||
|
|
||||||
impl Eq for Number {}
|
impl Eq for Number {}
|
||||||
|
|
||||||
impl Expression for Number {
|
impl Node for Number {
|
||||||
fn eval(&self, _rt: &RT, _scope: &Scope) -> PossibleExpr {
|
fn get_type_str(&self) -> &str {
|
||||||
Ok(Expr::Num(self.clone()))
|
"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
|
* 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, Location, PossibleExpr, StringRepr};
|
use crate::ast::Node;
|
||||||
use crate::errors::err;
|
|
||||||
use crate::runtime::RT;
|
|
||||||
use crate::scope::Scope;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -26,24 +23,12 @@ pub struct Symbol {
|
||||||
|
|
||||||
/// This field holds the ns specifier part of the symbol. For example
|
/// This field holds the ns specifier part of the symbol. For example
|
||||||
/// in case of `somens/somesym`, this field will hold `somens`.
|
/// in case of `somens/somesym`, this field will hold `somens`.
|
||||||
target_ns: Option<String>,
|
pub 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 {
|
impl Symbol {
|
||||||
pub fn new(name: String, target_ns: Option<String>) -> Symbol {
|
pub fn new(name: String, target_ns: Option<String>) -> Symbol {
|
||||||
Symbol {
|
Symbol { name, target_ns }
|
||||||
name,
|
|
||||||
target_ns,
|
|
||||||
ns: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_ns_qualified(&self) -> bool {
|
pub fn is_ns_qualified(&self) -> bool {
|
||||||
|
@ -53,18 +38,6 @@ impl Symbol {
|
||||||
pub fn is_def(&self) -> bool {
|
pub fn is_def(&self) -> bool {
|
||||||
self.name == "def"
|
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 {
|
||||||
|
@ -75,23 +48,9 @@ impl PartialEq for Symbol {
|
||||||
|
|
||||||
impl Eq for Symbol {}
|
impl Eq for Symbol {}
|
||||||
|
|
||||||
impl Expression for Symbol {
|
impl Node for Symbol {
|
||||||
fn eval(&self, rt: &RT, scope: &Scope) -> PossibleExpr {
|
fn get_type_str(&self) -> &str {
|
||||||
if self.is_ns_qualified() {
|
"Symbol"
|
||||||
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()
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,9 +59,3 @@ impl fmt::Display for Symbol {
|
||||||
write!(f, "{}", &self.name)
|
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