From d26942594323a7a83ab93ebbd8f354f30aa5fd63 Mon Sep 17 00:00:00 2001 From: Matthieu Jolimaitre Date: Fri, 25 Oct 2024 15:52:47 +0200 Subject: [PATCH] implemented method call --- example/data/list.mc | 37 +++++++ example/no_std.rs | 2 +- example/repl.rs | 2 +- example/run.rs | 8 +- src/ast.rs | 58 +++++++---- src/eval.rs | 230 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 234 +------------------------------------------ src/prelude.rs | 34 +++++++ 8 files changed, 352 insertions(+), 253 deletions(-) create mode 100644 example/data/list.mc create mode 100644 src/eval.rs create mode 100644 src/prelude.rs diff --git a/example/data/list.mc b/example/data/list.mc new file mode 100644 index 0000000..42afefd --- /dev/null +++ b/example/data/list.mc @@ -0,0 +1,37 @@ +// Constructor. +fn List() { + let result = { + data: [], + push: (self, item) => { + push(self.data, item); + }, + pop: (self) => { + return pop(self.data); + }, + len: (self) => { + return len(self.data); + } + }; + + return result; +} + +// Instantiation. +let list = List(); +print("new list", list); + +// Pushing 5 items in a loop. +let index = 0; +loop { + if (index >= 5) break; + list.push(index); + index = index + 1; +} + +print("pushed list", list); // Misc accesses. +print("length list", list.len()); // Misc accesses. +print("poped item", list.pop()); // Misc accesses. +print("poped list", list); // Misc accesses. + +print("list.data", list.data); +print("list[\"data\"]", list["data"]); diff --git a/example/no_std.rs b/example/no_std.rs index 468bee4..489fab7 100644 --- a/example/no_std.rs +++ b/example/no_std.rs @@ -1,7 +1,7 @@ #![no_std] use alloc::string::ToString; -use microlang::Context; +use microlang::eval::Context; extern crate alloc; diff --git a/example/repl.rs b/example/repl.rs index 2e948a1..0d0c31e 100644 --- a/example/repl.rs +++ b/example/repl.rs @@ -1,6 +1,6 @@ use std::io::{stdin, stdout, Write}; -use microlang::Context; +use microlang::eval::Context; pub fn main() { let mut context = Context::empty(); diff --git a/example/run.rs b/example/run.rs index 5898172..42fe5dd 100644 --- a/example/run.rs +++ b/example/run.rs @@ -1,6 +1,9 @@ use std::{env::args, fs}; -use microlang::{Context, Value}; +use microlang::{ + eval::{Context, Value}, + prelude::prelude, +}; pub fn main() { // Context instantiation. @@ -15,6 +18,9 @@ pub fn main() { Ok(Value::None) }); + // Import prelude. + prelude(&mut context); + // Script to evaluate. let lines = fs::read_to_string(args().nth(1).expect("Pass a file as arg 1.")).expect("File could not be read."); diff --git a/src/ast.rs b/src/ast.rs index a7a4536..0370519 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -3,9 +3,12 @@ use core::cell::RefCell; use alloc::{boxed::Box, format, rc::Rc, string::String, vec::Vec}; use libm::floor; -use crate::{util::Boxable, Context, EvalError, FunImpl, Function, Value}; +use crate::{ + eval::{Context, EvalError, FunImpl, Function, Value}, + util::Boxable, +}; use chumsky::{ - error::Simple, + error::{Cheap, Simple}, prelude::{end, filter, just, none_of, one_of, recursive, Recursive}, text::whitespace, Parser, @@ -14,8 +17,9 @@ use hashbrown::HashMap; extern crate alloc; -pub trait Parse: Parser> + Clone + 'static {} -impl> + Clone + 'static, O> Parse for T {} +pub type ParseError = Cheap; +pub trait Parse: Parser + Clone + 'static {} +impl + Clone + 'static, O> Parse for T {} fn pad(parser: impl Parse) -> impl Parse { let comment = just("//") @@ -258,6 +262,7 @@ pub enum Expr { Access(Box, String), Call(Box, Vec), Index(Box, Box), + Method(Box, String, Vec), } impl Expr { @@ -268,13 +273,30 @@ impl Expr { .or((UniOp::parser().then(expr.clone())).map(|(o, e)| Self::UniOp(o, e.to_box()))) .or(pad(id()).map(Self::Var)); - let call = atom + let method = atom + .clone() + .then_ignore(lx(".")) + .then(pad(id())) + .then_ignore(lx("(")) + .then(expr.clone().separated_by(lx(","))) + .then_ignore(lx(")")) + .map(|((i, m), a)| Self::Method(i.to_box(), m, a)) + .or(atom); + + let access = method + .clone() + .then_ignore(lx(".")) + .then(pad(id())) + .map(|(e, a)| Self::Access(e.to_box(), a)) + .or(method); + + let call = access .clone() .then_ignore(lx("(")) .then(expr.clone().separated_by(lx(","))) .then_ignore(lx(")")) .map(|(e, a)| Self::Call(e.to_box(), a)) - .or(atom); + .or(access); let index = call .clone() @@ -284,18 +306,11 @@ impl Expr { .map(|(e, i)| Self::Index(e.to_box(), i.to_box())) .or(call); - let access = index - .clone() - .then_ignore(lx(".")) - .then(pad(id())) - .map(|(e, a)| Self::Access(e.to_box(), a)) - .or(index); - - let ops = access + let ops = index .clone() .then(Op::parser().then(expr.clone()).repeated().at_least(1)) .map(|(f, o)| Self::BinOps(f.to_box(), o)) - .or(access); + .or(index); ops.labelled("expression") }) @@ -331,6 +346,13 @@ impl Expr { let fun = fun.borrow(); fun.call(args, context) } + Self::Method(i, m, a) => { + let fun = Self::Access(i.clone(), m.into()).to_box(); + let mut args = a.clone(); + args.insert(0, i.as_ref().clone()); + Self::Call(fun, args) + } + .eval(context), Self::Index(expr, index) => { let value = expr.eval(context)?; let index = index.eval(context)?; @@ -433,9 +455,9 @@ pub enum Litteral { impl Litteral { pub fn parser(instr: impl Parse, expr: impl Parse) -> impl Parse { - let char = none_of('"') - .or(just("\\\\").map(|_| '\\')) - .or(just("\\\"").map(|_| '"')); + let char = (just("\\\\").map(|_| '\\')) + .or(just("\\\"").map(|_| '"')) + .or(none_of('"')); let digit = one_of("0123456789"); (just("\"") .ignore_then(char.repeated()) diff --git a/src/eval.rs b/src/eval.rs new file mode 100644 index 0000000..0bf2495 --- /dev/null +++ b/src/eval.rs @@ -0,0 +1,230 @@ +use core::{cell::RefCell, fmt::Debug, iter::repeat}; + +use alloc::{ + rc::Rc, + string::{String, ToString}, + vec::Vec, +}; + +use crate::ast::{Block, FunDef, ParseError, Short}; +use chumsky::Parser; +use hashbrown::HashMap; + +#[derive(Debug)] +pub struct Context { + pub frame: Rc>, +} + +#[derive(Debug)] +pub enum Error { + ParsingErr(Vec), + EvalErr(EvalError), +} + +#[derive(Debug)] +pub enum EvalError { + Exit, + NotFound(String), + IllegalShort(Short), + WrongType, // add type names ? +} + +impl Context { + pub fn empty() -> Self { + let frame = Rc::new(RefCell::new(Scope::new(None))); + Self { frame } + } + + pub fn eval(&mut self, script: String) -> Result<(Block, Value), Error> { + let parsed = match Block::program_parser().parse(script) { + Ok(parsed) => parsed, + Err(err) => Err(Error::ParsingErr(err))?, + }; + let value = match parsed.eval(self) { + Ok((_short, value)) => value, + Err(err) => Err(Error::EvalErr(err))?, + }; + Ok((parsed, value)) + } + + pub fn define(&mut self, name: impl ToString, value: Value) { + self.scope().borrow_mut().define(name.to_string(), value) + } + + pub fn define_built(&mut self, name: impl ToString, op: impl Fn(Vec, &mut Context) -> FunRes + 'static) { + let def = FunImpl::Built(Rc::new(op) as Rc); + let function = Function { + capturing: Rc::new(RefCell::new(Scope::new(None))), + def, + }; + self.define(name.to_string(), Value::Function(Rc::new(RefCell::new(function)))) + } + + pub fn scope(&self) -> Rc> { + self.frame.clone() + } +} + +#[derive(Debug, Clone)] +pub struct Scope { + pub parent: Option>>, + pub defs: HashMap, +} + +impl Scope { + pub fn new(parent: Option>>) -> Self { + let defs = HashMap::new(); + Self { defs, parent } + } + + pub fn child O, O>(context: &mut Context, op: F) -> O { + let parent = context.frame.clone(); + let child = Rc::new(RefCell::new(Scope::new(Some(parent.clone())))); + context.frame = child; + let result = op(context); + context.frame = parent; + result + } + + pub fn alt O, O>(context: &mut Context, captures: Rc>, op: F) -> O { + let parent = context.frame.clone(); + let alt = Rc::new(RefCell::new(Scope::new(Some(captures.clone())))); + context.frame = alt; + let result = op(context); + context.frame = parent; + result + } + + pub fn define(&mut self, name: String, value: Value) { + self.defs.insert(name, value); + } + + pub fn update(&mut self, name: &str, value: Value) -> Result<(), EvalError> { + let Some(variable) = self.defs.get_mut(name) else { + if let Some(parent) = &self.parent { + return parent.borrow_mut().update(name, value); + } + return Err(EvalError::NotFound(name.into())); + }; + *variable = value; + Ok(()) + } + + pub fn get(&self, name: &str) -> Result { + let Some(value) = self.defs.get(name) else { + if let Some(parent) = &self.parent { + return parent.borrow_mut().get(name); + } + return Err(EvalError::NotFound(name.into())); + }; + Ok(value.clone()) + } +} + +#[derive(Debug, Clone)] +pub enum Value { + None, + Bool(bool), + Num(f64), + Str(String), + Object(Rc>>), + Array(Rc>>), + Function(Rc>), +} + +impl Value { + pub fn serialize(&self) -> String { + match self { + Self::None => "none".to_string(), + Self::Bool(b) => b.to_string(), + Self::Num(n) => n.to_string(), + Self::Str(s) => s.to_string(), + Self::Object(o) => { + let mut res = "{".to_string(); + let mut first = true; + for (name, value) in o.borrow().iter() { + if first { + res += " "; + res += name; + res += ": "; + res += &value.serialize(); + first = false; + } else { + res += ", "; + res += name; + res += ": "; + res += &value.serialize(); + } + } + res += " }"; + res + } + Self::Array(a) => { + let mut res = "[".to_string(); + let mut first = true; + for value in a.borrow().iter() { + if first { + res += " "; + res += &value.serialize(); + first = false; + } else { + res += ", "; + res += &value.serialize(); + } + } + res += " ]"; + res + } + Self::Function(_f) => "".to_string(), + } + } +} + +pub enum InstrOutput { + Short(Short), + Value(Value), +} + +#[derive(Debug, Clone)] +pub struct Function { + pub capturing: Rc>, + pub def: FunImpl, +} + +#[derive(Clone)] +pub enum FunImpl { + Def(FunDef), + Built(Rc), +} + +pub type FunRes = Result; +pub type BuiltFun = dyn Fn(Vec, &mut Context) -> FunRes; + +impl Debug for FunImpl { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Built(_) => f.debug_struct("Built").finish(), + Self::Def(def) => f.debug_tuple("Def").field(def).finish(), + } + } +} + +impl Function { + pub fn call(&self, args: Vec, context: &mut Context) -> Result { + match &self.def { + FunImpl::Built(op) => op(args, context), + FunImpl::Def(def) => Scope::alt(context, self.capturing.clone(), |context| { + let names = def.args.iter().cloned(); + for (name, value) in names.zip(args.into_iter().chain(repeat(Value::None))) { + context.scope().borrow_mut().define(name, value); + } + match def.body.eval(context) { + Err(err) => Err(err), + Ok((None, value)) => Ok(value), + Ok((Some(Short::Return(ret)), _)) => Ok(ret), + Ok((Some(s), _)) => Err(EvalError::IllegalShort(s)), + } + }), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 995aba6..1a020d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,237 +2,7 @@ mod util; -use core::{cell::RefCell, fmt::Debug, iter::repeat}; - -use alloc::{ - rc::Rc, - string::{String, ToString}, - vec::Vec, -}; - -use ast::{Block, FunDef, Short}; -use chumsky::{error::Simple, Parser}; -use hashbrown::HashMap; - extern crate alloc; - -#[derive(Debug)] -pub struct Context { - pub frame: Rc>, -} - -#[derive(Debug)] -pub enum Error { - ParsingErr(Vec>), - EvalErr(EvalError), -} - -#[derive(Debug)] -pub enum EvalError { - Exit, - NotFound(String), - IllegalShort(Short), - WrongType, // add type names ? -} - -impl Context { - pub fn empty() -> Self { - let frame = Rc::new(RefCell::new(Scope::new(None))); - Self { frame } - } - - pub fn eval(&mut self, script: String) -> Result<(Block, Value), Error> { - let parsed = match Block::program_parser().parse(script) { - Ok(parsed) => parsed, - Err(err) => Err(Error::ParsingErr(err))?, - }; - let value = match parsed.eval(self) { - Ok((_short, value)) => value, - Err(err) => Err(Error::EvalErr(err))?, - }; - Ok((parsed, value)) - } - - pub fn define(&mut self, name: impl ToString, value: Value) { - self.scope().borrow_mut().define(name.to_string(), value) - } - - pub fn define_built(&mut self, name: impl ToString, op: impl Fn(Vec, &mut Context) -> FunRes + 'static) { - let def = FunImpl::Built(Rc::new(op) as Rc); - let function = Function { - capturing: Rc::new(RefCell::new(Scope::new(None))), - def, - }; - self.define(name.to_string(), Value::Function(Rc::new(RefCell::new(function)))) - } - - pub fn scope(&self) -> Rc> { - self.frame.clone() - } -} - -#[derive(Debug, Clone)] -pub struct Scope { - pub parent: Option>>, - pub defs: HashMap, -} - -impl Scope { - pub fn new(parent: Option>>) -> Self { - let defs = HashMap::new(); - Self { defs, parent } - } - - pub fn child O, O>(context: &mut Context, op: F) -> O { - let parent = context.frame.clone(); - let child = Rc::new(RefCell::new(Scope::new(Some(parent.clone())))); - context.frame = child; - let result = op(context); - context.frame = parent; - result - } - - pub fn alt O, O>(context: &mut Context, captures: Rc>, op: F) -> O { - let parent = context.frame.clone(); - let alt = Rc::new(RefCell::new(Scope::new(Some(captures.clone())))); - context.frame = alt; - let result = op(context); - context.frame = parent; - result - } - - pub fn define(&mut self, name: String, value: Value) { - self.defs.insert(name, value); - } - - pub fn update(&mut self, name: &str, value: Value) -> Result<(), EvalError> { - let Some(variable) = self.defs.get_mut(name) else { - if let Some(parent) = &self.parent { - return parent.borrow_mut().update(name, value); - } - return Err(EvalError::NotFound(name.into())); - }; - *variable = value; - Ok(()) - } - - pub fn get(&self, name: &str) -> Result { - let Some(value) = self.defs.get(name) else { - if let Some(parent) = &self.parent { - return parent.borrow_mut().get(name); - } - return Err(EvalError::NotFound(name.into())); - }; - Ok(value.clone()) - } -} - -#[derive(Debug, Clone)] -pub enum Value { - None, - Bool(bool), - Num(f64), - Str(String), - Object(Rc>>), - Array(Rc>>), - Function(Rc>), -} - -impl Value { - pub fn serialize(&self) -> String { - match self { - Self::None => "none".to_string(), - Self::Bool(b) => b.to_string(), - Self::Num(n) => n.to_string(), - Self::Str(s) => s.to_string(), - Self::Object(o) => { - let mut res = "{".to_string(); - let mut first = true; - for (name, value) in o.borrow().iter() { - if first { - res += " "; - res += name; - res += ": "; - res += &value.serialize(); - first = false; - } else { - res += ", "; - res += name; - res += ": "; - res += &value.serialize(); - } - } - res += " }"; - res - } - Self::Array(a) => { - let mut res = "[".to_string(); - let mut first = true; - for value in a.borrow().iter() { - if first { - res += " "; - res += &value.serialize(); - first = false; - } else { - res += ", "; - res += &value.serialize(); - } - } - res += " ]"; - res - } - Self::Function(_f) => "".to_string(), - } - } -} - -pub enum InstrOutput { - Short(Short), - Value(Value), -} - -#[derive(Debug, Clone)] -pub struct Function { - pub capturing: Rc>, - pub def: FunImpl, -} - -#[derive(Clone)] -pub enum FunImpl { - Def(FunDef), - Built(Rc), -} - -pub type FunRes = Result; -pub type BuiltFun = dyn Fn(Vec, &mut Context) -> FunRes; - -impl Debug for FunImpl { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Built(_) => f.debug_struct("Built").finish(), - Self::Def(def) => f.debug_tuple("Def").field(def).finish(), - } - } -} - -impl Function { - pub fn call(&self, args: Vec, context: &mut Context) -> Result { - match &self.def { - FunImpl::Built(op) => op(args, context), - FunImpl::Def(def) => Scope::alt(context, self.capturing.clone(), |context| { - let names = def.args.iter().cloned(); - for (name, value) in names.zip(args.into_iter().chain(repeat(Value::None))) { - context.scope().borrow_mut().define(name, value); - } - match def.body.eval(context) { - Err(err) => Err(err), - Ok((None, value)) => Ok(value), - Ok((Some(Short::Return(ret)), _)) => Ok(ret), - Ok((Some(s), _)) => Err(EvalError::IllegalShort(s)), - } - }), - } - } -} - pub mod ast; +pub mod eval; +pub mod prelude; diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..543f5d5 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,34 @@ +use crate::eval::{Context, EvalError, Value}; + +pub fn prelude(context: &mut Context) { + context.define_built("push", |args, _ctx| { + let mut args = args.into_iter(); + let list = args.next().ok_or(EvalError::NotFound("$1".into()))?; + let Value::Array(list) = list else { + return Err(EvalError::WrongType); + }; + list.borrow_mut().extend(args); + Ok(Value::None) + }); + + context.define_built("pop", |args, _ctx| { + let list = args.first().ok_or(EvalError::NotFound("$1".into()))?; + let Value::Array(list) = list else { + return Err(EvalError::WrongType); + }; + Ok(list.borrow_mut().pop().unwrap_or(Value::None)) + }); + + context.define_built("len", |args, _ctx| { + let list = args.first().ok_or(EvalError::NotFound("$1".into()))?; + let Value::Array(list) = list else { + return Err(EvalError::WrongType); + }; + Ok(Value::Num(list.borrow().len() as f64)) + }); + + context.define_built("str", |args, _ctx| { + let first = args.first().ok_or(EvalError::NotFound("$1".into()))?; + Ok(Value::Str(first.serialize())) + }); +}