diff --git a/README.md b/README.md index 28c1e06..d6be399 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ You can create a multi-statement expression using either `()` syntax or the `~` ) ``` -### Global Scope (unimplemented) +### Global Scope You can introduce a variable to global scope using the `export` builtin function. diff --git a/src/executor.rs b/src/executor.rs index 7d13b2c..034f3a7 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -49,7 +49,7 @@ where I: Iterator> { exprs: &'a mut I, - globals: HashMap, + globals: &'a mut HashMap, locals: HashMap, } @@ -57,20 +57,15 @@ impl<'a, I> Executor<'a, I> where I: Iterator>, { - pub fn new(exprs: &'a mut I) -> Self { + pub fn new(exprs: &'a mut I, globals: &'a mut HashMap) -> Self { Self { exprs, - globals: HashMap::new(), + globals, locals: HashMap::new(), } } - pub fn globals(mut self, globals: HashMap) -> Self { - self.globals = globals; - self - } - - pub fn _add_global(mut self, k: String, v: Object) -> Self { + pub fn _add_global(self, k: String, v: Object) -> Self { self.globals.insert(k, v); self } @@ -237,11 +232,11 @@ where Err(RuntimeError::ImmutableError(ident.clone())) } else { let value = self.exec(body)?; + let g = self.globals.clone(); - Executor::new(self.exprs) - .globals(self.globals.clone()) + Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Object::value(value, self.globals.to_owned(), self.locals.to_owned())) + .add_local(ident, Object::value(value, g, self.locals.to_owned())) .exec(scope) } }, @@ -249,18 +244,18 @@ where if self.variable_exists(&ident) { Err(RuntimeError::ImmutableError(ident.clone())) } else { - Executor::new(self.exprs) - .globals(self.globals.clone()) + let g = self.globals.clone(); + Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(ident, Object::variable(*body, self.globals.to_owned(), self.locals.to_owned())) + .add_local(ident, Object::variable(*body, g, self.locals.to_owned())) .exec(scope) } }, ParseTree::FunctionDefinition(func, scope) => { - Executor::new(self.exprs) - .globals(self.globals.clone()) + let g = self.globals.clone(); + Executor::new(self.exprs, &mut self.globals) .locals(self.locals.clone()) - .add_local(func.name().unwrap().to_string(), Object::function(func, self.globals.clone(), self.locals.clone())) + .add_local(func.name().unwrap().to_string(), Object::function(func, g, self.locals.clone())) .exec(scope) }, ParseTree::Compose(x, y) => { @@ -376,6 +371,14 @@ where t => Err(RuntimeError::NoOverloadForTypes("fini".into(), vec![t])) }, ParseTree::Nop => Ok(Value::Nil), + ParseTree::Export(names) => { + for name in names { + let obj = self.locals.remove(&name).ok_or(RuntimeError::VariableUndefined(name.clone()))?; + self.globals.insert(name, obj); + } + + Ok(Value::Nil) + } } } } diff --git a/src/function.rs b/src/function.rs index 0072636..53fa101 100644 --- a/src/function.rs +++ b/src/function.rs @@ -50,22 +50,22 @@ impl Function { } pub(crate) fn call(&mut self, - globals: HashMap, + mut globals: HashMap, locals: HashMap, args: Vec) -> Result { let mut tree = vec![Ok(*self.body.clone())].into_iter(); + let g = globals.clone(); - let mut exec = Executor::new(&mut tree) - .locals(locals.clone()) - .globals(globals.clone()); + let mut exec = Executor::new(&mut tree, &mut globals) + .locals(locals.clone()); for (obj, name) in std::iter::zip(args.into_iter(), self.arg_names.clone().into_iter()) { exec = exec.add_local(name.clone(), obj); } if let Some(name) = self.name().map(|x| x.to_string()) { - exec = exec.add_local(name, Object::function(self.clone(), globals, locals)); + exec = exec.add_local(name, Object::function(self.clone(), g, locals)); } exec.next().unwrap() diff --git a/src/lib.rs b/src/lib.rs index 31cca04..49cfbf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,6 @@ use std::fmt::Display; use std::io::BufRead; use std::fmt; use std::iter::Peekable; -use std::marker::PhantomData; #[derive(Clone, Debug)] pub enum Type { @@ -139,9 +138,8 @@ impl Object { Cache::Uncached(tree) => { let mut tree = vec![Ok(tree)].into_iter(); - let mut exec = Executor::new(&mut tree) - .locals(self.locals.clone()) - .globals(self.globals.clone()); + let mut exec = Executor::new(&mut tree, &mut self.globals) + .locals(self.locals.clone()); let v = exec.next().unwrap()?; @@ -163,21 +161,23 @@ impl Object { pub struct Runtime<'a, R: BufRead> { tokenizer: Peekable>, + global_types: HashMap, + globals: HashMap, parser: Option>>, - phantom: PhantomData>>>, } impl<'a, R: BufRead> Runtime<'a, R> { pub fn new(reader: R) -> Self { Self { tokenizer: Tokenizer::new(reader).peekable(), + global_types: HashMap::new(), + globals: HashMap::new(), parser: None, - phantom: PhantomData, } } pub fn values(&'a mut self) -> impl Iterator> + 'a { - self.parser = Some(Parser::new(&mut self.tokenizer)); - Executor::new(self.parser.as_mut().unwrap()) + self.parser = Some(Parser::new(&mut self.tokenizer, &mut self.global_types)); + Executor::new(self.parser.as_mut().unwrap(), &mut self.globals) } } diff --git a/src/parser.rs b/src/parser.rs index 184a3e3..9973bcb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,10 @@ +use crate::executor::Executor; + use super::{Value, Type, Function, FunctionType}; use super::tokenizer::{Token, TokenizeError, Op}; +use std::borrow::{BorrowMut, Cow}; use std::error; use std::collections::HashMap; use std::fmt::Display; @@ -90,31 +93,32 @@ pub(crate) enum ParseTree { // Misc Print(Box), Nop, + Export(Vec), } /// Parses input tokens and produces ParseTrees for an Executor pub(crate) struct Parser<'a, I: Iterator>> { tokens: &'a mut Peekable, - globals: HashMap, + globals: &'a mut HashMap, locals: HashMap, } impl<'a, I: Iterator>> Parser<'a, I> { - pub fn new(tokens: &'a mut Peekable) -> Self { + pub fn new(tokens: &'a mut Peekable, globals: &'a mut HashMap) -> Self { Self { tokens: tokens, - globals: HashMap::new(), + globals, locals: HashMap::new() } } - pub fn globals(mut self, globals: HashMap) -> Self { - self.globals = globals; + pub fn add_global(self, k: String, v: Type) -> Self { + self.globals.insert(k, v); self } - pub fn _add_global(mut self, k: String, v: Type) -> Self { - self.globals.insert(k, v); + pub fn add_globals>(self, items: Items) -> Self { + items.for_each(|(name, t)| _ = self.globals.insert(name, t)); self } @@ -128,11 +132,20 @@ impl<'a, I: Iterator>> Parser<'a, I> { self } + pub fn add_locals>(mut self, items: Items) -> Self { + items.for_each(|(name, t)| _ = self.locals.insert(name, t)); + self + } + fn get_object_type(&self, ident: &String) -> Result<&Type, ParseError> { self.locals.get(ident).or(self.globals.get(ident)) .ok_or(ParseError::IdentifierUndefined(ident.clone())) } + fn get_object_types>(&self, items: Names) -> impl Iterator> { + items.map(|x| self.get_object_type(&x)) + } + fn parse(&mut self) -> Result { match self.tokens.next().ok_or(ParseError::NoInput)?.map_err(|e| ParseError::TokenizeError(e))? { Token::Constant(c) => Ok(ParseTree::Constant(c)), @@ -165,18 +178,16 @@ impl<'a, I: Iterator>> Parser<'a, I> { if let Token::Identifier(ident) = token { match op { Op::Equ => Ok(ParseTree::Equ(ident.clone(), - body.clone(), - Box::new(Parser::new(self.tokens.by_ref()) + body, + Box::new(Parser::new(self.tokens.by_ref(), self.globals.borrow_mut()) .locals(self.locals.clone()) - .globals(self.globals.clone()) .add_local(ident, Type::Any) .parse()?)) ), Op::LazyEqu => Ok(ParseTree::LazyEqu(ident.clone(), - body.clone(), - Box::new(Parser::new(self.tokens.by_ref()) + body, + Box::new(Parser::new(self.tokens.by_ref(), self.globals.borrow_mut()) .locals(self.locals.clone()) - .globals(self.globals.clone()) .add_local(ident, Type::Any) .parse()?)) ), @@ -191,8 +202,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { Ok(ParseTree::FunctionDefinition(f.clone(), Box::new( - Parser::new(self.tokens) - .globals(self.globals.clone()) + Parser::new(self.tokens, self.globals.borrow_mut()) .locals(self.locals.clone()) .add_local(f.name().unwrap().to_string(), Type::Function(f.get_type())) .parse()? @@ -237,8 +247,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { .into_iter() .peekable(); - let trees: Vec = Parser::new(&mut array_tokens) - .globals(self.globals.to_owned()) + let trees: Vec = Parser::new(&mut array_tokens, self.globals.borrow_mut()) .locals(self.locals.to_owned()) .collect::>()?; @@ -273,8 +282,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { .into_iter() .peekable(); - let trees: Vec = Parser::new(&mut tokens) - .globals(self.globals.to_owned()) + let trees: Vec = Parser::new(&mut tokens, self.globals.borrow_mut()) .locals(self.locals.to_owned()) .collect::>()?; @@ -302,6 +310,27 @@ impl<'a, I: Iterator>> Parser<'a, I> { Op::Tail => Ok(ParseTree::Tail(Box::new(self.parse()?))), Op::Init => Ok(ParseTree::Init(Box::new(self.parse()?))), Op::Fini => Ok(ParseTree::Fini(Box::new(self.parse()?))), + Op::Export => { + let list = self.parse()?; + let mut g = HashMap::new(); + let list = Executor::new(&mut vec![Ok(list)].into_iter(), &mut g).next().unwrap().map_err(|_| ParseError::NoInput)?; + + if let Value::Array(Type::String, items) = list { + let names = items.into_iter().map(|x| match x { + Value::String(s) => s, + _ => unreachable!(), + }); + + for name in names.clone() { + let t = self.locals.remove(&name).ok_or(ParseError::IdentifierUndefined(name.clone()))?; + self.globals.insert(name, t); + } + + Ok(ParseTree::Export(names.collect())) + } else { + Err(ParseError::NoInput) + } + } op => Err(ParseError::UnwantedToken(Token::Operator(op))), } } @@ -319,8 +348,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { } Ok(Function::lambda(t, args, Box::new( - Parser::new(self.tokens) - .globals(self.globals.clone()) + Parser::new(self.tokens, &mut self.globals) .locals(locals).parse()?))) } @@ -337,8 +365,7 @@ impl<'a, I: Iterator>> Parser<'a, I> { locals.insert(name.clone(), Type::Function(t.clone())); Ok(Function::named(&name, t, args, Box::new( - Parser::new(self.tokens) - .globals(self.globals.clone()) + Parser::new(self.tokens, &mut self.globals) .locals(locals).parse()?))) } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 410343b..b116b42 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -84,6 +84,7 @@ pub enum Op { Tail, Init, Fini, + Export, } #[derive(Debug, Clone, PartialEq)] @@ -128,6 +129,7 @@ impl Token { "tail" => Ok(Token::Operator(Op::Tail)), "init" => Ok(Token::Operator(Op::Init)), "fini" => Ok(Token::Operator(Op::Fini)), + "export" => Ok(Token::Operator(Op::Export)), // Types "Any" => Ok(Token::Type(Type::Any)), @@ -412,4 +414,20 @@ impl std::iter::Iterator for Tokenizer { Err(e) => Some(Err(TokenizeError::IO(e))), } } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use super::*; + + #[test] + fn a() { + let program = ":. map : f .? x [Any] -> [Any]"; + + let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap(); + + println!("{tokens:?}"); + } } \ No newline at end of file