implemented method call
This commit is contained in:
parent
d3095d5980
commit
d269425943
8 changed files with 352 additions and 253 deletions
37
example/data/list.mc
Normal file
37
example/data/list.mc
Normal 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"]);
|
|
@ -1,7 +1,7 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use microlang::Context;
|
use microlang::eval::Context;
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{stdin, stdout, Write};
|
||||||
|
|
||||||
use microlang::Context;
|
use microlang::eval::Context;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let mut context = Context::empty();
|
let mut context = Context::empty();
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use std::{env::args, fs};
|
use std::{env::args, fs};
|
||||||
|
|
||||||
use microlang::{Context, Value};
|
use microlang::{
|
||||||
|
eval::{Context, Value},
|
||||||
|
prelude::prelude,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
// Context instantiation.
|
// Context instantiation.
|
||||||
|
@ -15,6 +18,9 @@ pub fn main() {
|
||||||
Ok(Value::None)
|
Ok(Value::None)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Import prelude.
|
||||||
|
prelude(&mut context);
|
||||||
|
|
||||||
// Script to evaluate.
|
// 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.");
|
let lines = fs::read_to_string(args().nth(1).expect("Pass a file as arg 1.")).expect("File could not be read.");
|
||||||
|
|
||||||
|
|
58
src/ast.rs
58
src/ast.rs
|
@ -3,9 +3,12 @@ use core::cell::RefCell;
|
||||||
use alloc::{boxed::Box, format, rc::Rc, string::String, vec::Vec};
|
use alloc::{boxed::Box, format, rc::Rc, string::String, vec::Vec};
|
||||||
use libm::floor;
|
use libm::floor;
|
||||||
|
|
||||||
use crate::{util::Boxable, Context, EvalError, FunImpl, Function, Value};
|
use crate::{
|
||||||
|
eval::{Context, EvalError, FunImpl, Function, Value},
|
||||||
|
util::Boxable,
|
||||||
|
};
|
||||||
use chumsky::{
|
use chumsky::{
|
||||||
error::Simple,
|
error::{Cheap, Simple},
|
||||||
prelude::{end, filter, just, none_of, one_of, recursive, Recursive},
|
prelude::{end, filter, just, none_of, one_of, recursive, Recursive},
|
||||||
text::whitespace,
|
text::whitespace,
|
||||||
Parser,
|
Parser,
|
||||||
|
@ -14,8 +17,9 @@ use hashbrown::HashMap;
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub trait Parse<O>: Parser<char, O, Error = Simple<char>> + Clone + 'static {}
|
pub type ParseError = Cheap<char>;
|
||||||
impl<T: Parser<char, O, Error = Simple<char>> + Clone + 'static, O> Parse<O> for T {}
|
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> {
|
fn pad<O: 'static>(parser: impl Parse<O>) -> impl Parse<O> {
|
||||||
let comment = just("//")
|
let comment = just("//")
|
||||||
|
@ -258,6 +262,7 @@ pub enum Expr {
|
||||||
Access(Box<Expr>, String),
|
Access(Box<Expr>, String),
|
||||||
Call(Box<Expr>, Vec<Expr>),
|
Call(Box<Expr>, Vec<Expr>),
|
||||||
Index(Box<Expr>, Box<Expr>),
|
Index(Box<Expr>, Box<Expr>),
|
||||||
|
Method(Box<Expr>, String, Vec<Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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((UniOp::parser().then(expr.clone())).map(|(o, e)| Self::UniOp(o, e.to_box())))
|
||||||
.or(pad(id()).map(Self::Var));
|
.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()
|
.clone()
|
||||||
.then_ignore(lx("("))
|
.then_ignore(lx("("))
|
||||||
.then(expr.clone().separated_by(lx(",")))
|
.then(expr.clone().separated_by(lx(",")))
|
||||||
.then_ignore(lx(")"))
|
.then_ignore(lx(")"))
|
||||||
.map(|(e, a)| Self::Call(e.to_box(), a))
|
.map(|(e, a)| Self::Call(e.to_box(), a))
|
||||||
.or(atom);
|
.or(access);
|
||||||
|
|
||||||
let index = call
|
let index = call
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -284,18 +306,11 @@ impl Expr {
|
||||||
.map(|(e, i)| Self::Index(e.to_box(), i.to_box()))
|
.map(|(e, i)| Self::Index(e.to_box(), i.to_box()))
|
||||||
.or(call);
|
.or(call);
|
||||||
|
|
||||||
let access = index
|
let ops = index
|
||||||
.clone()
|
|
||||||
.then_ignore(lx("."))
|
|
||||||
.then(pad(id()))
|
|
||||||
.map(|(e, a)| Self::Access(e.to_box(), a))
|
|
||||||
.or(index);
|
|
||||||
|
|
||||||
let ops = access
|
|
||||||
.clone()
|
.clone()
|
||||||
.then(Op::parser().then(expr.clone()).repeated().at_least(1))
|
.then(Op::parser().then(expr.clone()).repeated().at_least(1))
|
||||||
.map(|(f, o)| Self::BinOps(f.to_box(), o))
|
.map(|(f, o)| Self::BinOps(f.to_box(), o))
|
||||||
.or(access);
|
.or(index);
|
||||||
|
|
||||||
ops.labelled("expression")
|
ops.labelled("expression")
|
||||||
})
|
})
|
||||||
|
@ -331,6 +346,13 @@ impl Expr {
|
||||||
let fun = fun.borrow();
|
let fun = fun.borrow();
|
||||||
fun.call(args, context)
|
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) => {
|
Self::Index(expr, index) => {
|
||||||
let value = expr.eval(context)?;
|
let value = expr.eval(context)?;
|
||||||
let index = index.eval(context)?;
|
let index = index.eval(context)?;
|
||||||
|
@ -433,9 +455,9 @@ pub enum Litteral {
|
||||||
|
|
||||||
impl Litteral {
|
impl Litteral {
|
||||||
pub fn parser(instr: impl Parse<Instr>, expr: impl Parse<Expr>) -> impl Parse<Self> {
|
pub fn parser(instr: impl Parse<Instr>, expr: impl Parse<Expr>) -> impl Parse<Self> {
|
||||||
let char = none_of('"')
|
let char = (just("\\\\").map(|_| '\\'))
|
||||||
.or(just("\\\\").map(|_| '\\'))
|
.or(just("\\\"").map(|_| '"'))
|
||||||
.or(just("\\\"").map(|_| '"'));
|
.or(none_of('"'));
|
||||||
let digit = one_of("0123456789");
|
let digit = one_of("0123456789");
|
||||||
(just("\"")
|
(just("\"")
|
||||||
.ignore_then(char.repeated())
|
.ignore_then(char.repeated())
|
||||||
|
|
230
src/eval.rs
Normal file
230
src/eval.rs
Normal 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)),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
234
src/lib.rs
234
src/lib.rs
|
@ -2,237 +2,7 @@
|
||||||
|
|
||||||
mod util;
|
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;
|
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 ast;
|
||||||
|
pub mod eval;
|
||||||
|
pub mod prelude;
|
||||||
|
|
34
src/prelude.rs
Normal file
34
src/prelude.rs
Normal 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()))
|
||||||
|
});
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue