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]
|
||||
|
||||
use alloc::string::ToString;
|
||||
use microlang::Context;
|
||||
use microlang::eval::Context;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.");
|
||||
|
||||
|
|
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 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
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;
|
||||
|
||||
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
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