implemented method call

This commit is contained in:
Matthieu Jolimaitre 2024-10-25 15:52:47 +02:00
parent d3095d5980
commit d269425943
8 changed files with 352 additions and 253 deletions

37
example/data/list.mc Normal file
View file

@ -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"]);

View file

@ -1,7 +1,7 @@
#![no_std]
use alloc::string::ToString;
use microlang::Context;
use microlang::eval::Context;
extern crate alloc;

View file

@ -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();

View file

@ -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.");

View file

@ -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<O>: Parser<char, O, Error = Simple<char>> + Clone + 'static {}
impl<T: Parser<char, O, Error = Simple<char>> + Clone + 'static, O> Parse<O> for T {}
pub type ParseError = Cheap<char>;
pub trait Parse<O>: Parser<char, O, Error = ParseError> + Clone + 'static {}
impl<T: Parser<char, O, Error = ParseError> + Clone + 'static, O> Parse<O> for T {}
fn pad<O: 'static>(parser: impl Parse<O>) -> impl Parse<O> {
let comment = just("//")
@ -258,6 +262,7 @@ pub enum Expr {
Access(Box<Expr>, String),
Call(Box<Expr>, Vec<Expr>),
Index(Box<Expr>, Box<Expr>),
Method(Box<Expr>, String, Vec<Expr>),
}
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<Instr>, expr: impl Parse<Expr>) -> impl Parse<Self> {
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())

230
src/eval.rs Normal file
View file

@ -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<RefCell<Scope>>,
}
#[derive(Debug)]
pub enum Error {
ParsingErr(Vec<ParseError>),
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<Value>, &mut Context) -> FunRes + 'static) {
let def = FunImpl::Built(Rc::new(op) as Rc<BuiltFun>);
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<RefCell<Scope>> {
self.frame.clone()
}
}
#[derive(Debug, Clone)]
pub struct Scope {
pub parent: Option<Rc<RefCell<Self>>>,
pub defs: HashMap<String, Value>,
}
impl Scope {
pub fn new(parent: Option<Rc<RefCell<Self>>>) -> Self {
let defs = HashMap::new();
Self { defs, parent }
}
pub fn child<F: FnOnce(&mut Context) -> 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<F: FnOnce(&mut Context) -> O, O>(context: &mut Context, captures: Rc<RefCell<Self>>, 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<Value, EvalError> {
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<RefCell<HashMap<String, Value>>>),
Array(Rc<RefCell<Vec<Value>>>),
Function(Rc<RefCell<Function>>),
}
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) => "<function>".to_string(),
}
}
}
pub enum InstrOutput {
Short(Short),
Value(Value),
}
#[derive(Debug, Clone)]
pub struct Function {
pub capturing: Rc<RefCell<Scope>>,
pub def: FunImpl,
}
#[derive(Clone)]
pub enum FunImpl {
Def(FunDef),
Built(Rc<BuiltFun>),
}
pub type FunRes = Result<Value, EvalError>;
pub type BuiltFun = dyn Fn(Vec<Value>, &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<Value>, context: &mut Context) -> Result<Value, EvalError> {
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)),
}
}),
}
}
}

View file

@ -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<RefCell<Scope>>,
}
#[derive(Debug)]
pub enum Error {
ParsingErr(Vec<Simple<char>>),
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<Value>, &mut Context) -> FunRes + 'static) {
let def = FunImpl::Built(Rc::new(op) as Rc<BuiltFun>);
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<RefCell<Scope>> {
self.frame.clone()
}
}
#[derive(Debug, Clone)]
pub struct Scope {
pub parent: Option<Rc<RefCell<Self>>>,
pub defs: HashMap<String, Value>,
}
impl Scope {
pub fn new(parent: Option<Rc<RefCell<Self>>>) -> Self {
let defs = HashMap::new();
Self { defs, parent }
}
pub fn child<F: FnOnce(&mut Context) -> 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<F: FnOnce(&mut Context) -> O, O>(context: &mut Context, captures: Rc<RefCell<Self>>, 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<Value, EvalError> {
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<RefCell<HashMap<String, Value>>>),
Array(Rc<RefCell<Vec<Value>>>),
Function(Rc<RefCell<Function>>),
}
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) => "<function>".to_string(),
}
}
}
pub enum InstrOutput {
Short(Short),
Value(Value),
}
#[derive(Debug, Clone)]
pub struct Function {
pub capturing: Rc<RefCell<Scope>>,
pub def: FunImpl,
}
#[derive(Clone)]
pub enum FunImpl {
Def(FunDef),
Built(Rc<BuiltFun>),
}
pub type FunRes = Result<Value, EvalError>;
pub type BuiltFun = dyn Fn(Vec<Value>, &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<Value>, context: &mut Context) -> Result<Value, EvalError> {
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;

34
src/prelude.rs Normal file
View file

@ -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()))
});
}