add loop examples

This commit is contained in:
Matthieu Jolimaitre 2024-10-24 07:03:35 +02:00
parent 6350e4a40f
commit dbeec4ee76
5 changed files with 110 additions and 49 deletions

View file

@ -0,0 +1,4 @@
let msg = "hello world";
msg

22
example/data/loops.mc Normal file
View file

@ -0,0 +1,22 @@
let i = 0;
loop {
i = i + 1;
print("i", i);
if (i > 5) break;
}
fn for(array, op) {
let index = 0;
loop {
let item = array[index];
if (item == none) break;
op(item, index);
index = index + 1;
}
}
for([1, 2, 3], (item, index) => {
print("at index", index, "item", item);
});

View file

@ -1,15 +1,21 @@
use std::{env::args, fs}; use std::{env::args, fs};
use microlang::Context; use microlang::{Context, Value};
pub fn main() { pub fn main() {
unsafe { backtrace_on_stack_overflow::enable() }; unsafe { backtrace_on_stack_overflow::enable() };
let mut context = Context::empty(); let mut context = Context::empty();
context.define_built("print", |values, _| {
for val in values {
print!("{} ", val.serialize());
}
println!();
Ok(Value::None)
});
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.");
for line in lines.lines() { match context.eval(lines) {
match context.eval(line.into()) {
Err(err) => panic!("Failed : {err:?}"), Err(err) => panic!("Failed : {err:?}"),
Ok((_, value)) => println!("{}", value.serialize()), Ok((_, value)) => println!("{}", value.serialize()),
} }
}
} }

View file

@ -6,7 +6,7 @@ use crate::{util::Boxable, Context, EvalError, FunImpl, Function, Value};
use chumsky::{ use chumsky::{
error::Simple, error::Simple,
prelude::{end, just, none_of, one_of, recursive, Recursive}, prelude::{end, just, none_of, one_of, recursive, Recursive},
text::{whitespace, TextParser}, text::TextParser,
Parser, Parser,
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
@ -17,10 +17,7 @@ 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 {} impl<T: Parser<char, O, Error = Simple<char>> + Clone + 'static, O> Parse<O> for T {}
fn lx(word: &'static str) -> impl Parse<&'static str> { fn lx(word: &'static str) -> impl Parse<&'static str> {
whitespace() just(word).padded().labelled("token")
.ignore_then(just(word))
.then_ignore(whitespace())
.labelled("token")
} }
fn id() -> impl Parse<String> { fn id() -> impl Parse<String> {
@ -41,8 +38,6 @@ pub struct Block {
impl Block { impl Block {
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> {
instr instr
.clone()
.then_ignore(lx(";"))
.repeated() .repeated()
.then(expr.map(Boxable::to_box).or_not()) .then(expr.map(Boxable::to_box).or_not())
.map(|(instructions, expression)| Self { .map(|(instructions, expression)| Self {
@ -97,6 +92,7 @@ impl FunDef {
pub fn parser_lamb(instr: impl Parse<Instr>, expr: impl Parse<Expr>) -> impl Parse<Self> { pub fn parser_lamb(instr: impl Parse<Instr>, expr: impl Parse<Expr>) -> impl Parse<Self> {
let arguments = id().padded().separated_by(lx(",")); let arguments = id().padded().separated_by(lx(","));
(lx("(").ignore_then(arguments).then_ignore(lx(")"))) (lx("(").ignore_then(arguments).then_ignore(lx(")")))
.then_ignore(lx("=>"))
.then(lx("{").ignore_then(Block::parser(instr, expr)).then_ignore(lx("}"))) .then(lx("{").ignore_then(Block::parser(instr, expr)).then_ignore(lx("}")))
.map(|(args, body)| Self { args, body }) .map(|(args, body)| Self { args, body })
.labelled("lambda") .labelled("lambda")
@ -121,27 +117,30 @@ pub enum Instr {
impl Instr { impl Instr {
pub fn parser(expr: impl Parse<Expr>) -> impl Parse<Self> { pub fn parser(expr: impl Parse<Expr>) -> impl Parse<Self> {
recursive(|instr: Recursive<'_, _, Self, _>| { recursive(|instr: Recursive<'_, _, Self, _>| {
(lx("{") (lx(";").map(|_| Self::Nop).labelled("no operation"))
.or(lx("{")
.ignore_then(Block::parser(instr.clone(), expr.clone())) .ignore_then(Block::parser(instr.clone(), expr.clone()))
.then_ignore(lx("}")) .then_ignore(lx("}"))
.map(Self::Block)) .map(Self::Block)
.labelled("block instruction") .labelled("block instruction"))
.or(lx("let") .or(lx("let")
.ignore_then(id()) .ignore_then(id())
.then_ignore(lx("=")) .then_ignore(lx("="))
.then(expr.clone()) .then(expr.clone())
.map(|(id, ex)| Self::Init(id, ex))) .then_ignore(lx(";"))
.labelled("variable initialization") .map(|(id, ex)| Self::Init(id, ex))
.labelled("variable initialization"))
.or(lx("if") .or(lx("if")
.ignore_then(lx("(").ignore_then(expr.clone()).then_ignore(lx(")"))) .ignore_then(lx("(").ignore_then(expr.clone()).then_ignore(lx(")")))
.then((instr.clone()).then((lx("else").ignore_then(instr.clone())).or_not())) .then((instr.clone()).then((lx("else").ignore_then(instr.clone())).or_not()))
.map(|(cond, (then, els))| Self::Branch(cond, then.to_box(), els.unwrap_or(Instr::Nop).to_box()))) .map(|(cond, (then, els))| Self::Branch(cond, then.to_box(), els.unwrap_or(Instr::Nop).to_box()))
.labelled("condition instruction") .labelled("condition instruction"))
.or(lx("loop") .or(lx("loop")
.ignore_then(instr.clone()) .ignore_then(instr.clone())
.map(|body| Self::Loop(body.to_box()))) .map(|body| Self::Loop(body.to_box()))
.labelled("loop instruction") .labelled("loop instruction"))
.or(ShortDef::parser(expr.clone()) .or(ShortDef::parser(expr.clone())
.then_ignore(lx(";"))
.map(Self::Short) .map(Self::Short)
.labelled("shortcut instruction")) .labelled("shortcut instruction"))
.or(FunDef::parser_def(instr, expr.clone()) .or(FunDef::parser_def(instr, expr.clone())
@ -150,9 +149,10 @@ impl Instr {
.or(id() .or(id()
.then_ignore(lx("=")) .then_ignore(lx("="))
.then(expr.clone()) .then(expr.clone())
.then_ignore(lx(";"))
.map(|(id, ex)| Self::Assign(id, ex)) .map(|(id, ex)| Self::Assign(id, ex))
.labelled("variable assigniation.")) .labelled("variable assigniation"))
.or(expr.map(Self::Expr)) .or(expr.then_ignore(lx(";")).map(Self::Expr))
}) })
.labelled("instruction") .labelled("instruction")
} }
@ -164,7 +164,7 @@ impl Instr {
Self::Expr(expr) => no_short(expr.eval(context)?), Self::Expr(expr) => no_short(expr.eval(context)?),
Self::Init(name, expr) => { Self::Init(name, expr) => {
let value = expr.eval(context)?; let value = expr.eval(context)?;
context.scope().borrow_mut().append(name.clone(), value); context.scope().borrow_mut().define(name.clone(), value);
no_short(()) no_short(())
} }
Self::Assign(name, expr) => { Self::Assign(name, expr) => {
@ -262,7 +262,7 @@ impl Expr {
.map(|(e, a)| Self::Access(e.to_box(), a))) .map(|(e, a)| Self::Access(e.to_box(), a)))
.or((left.clone()) .or((left.clone())
.then_ignore(lx("(")) .then_ignore(lx("("))
.then(expr.clone().then_ignore(lx(",")).repeated()) .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((left.clone()) .or((left.clone())
@ -354,6 +354,21 @@ impl Op {
Ok(match (self, l, r) { Ok(match (self, l, r) {
(Self::Add, Value::Str(l), Value::Str(r)) => Value::Str(format!("{l}{r}")), (Self::Add, Value::Str(l), Value::Str(r)) => Value::Str(format!("{l}{r}")),
(Self::Add, Value::Num(l), Value::Num(r)) => Value::Num(l + r), (Self::Add, Value::Num(l), Value::Num(r)) => Value::Num(l + r),
(Self::Sub, Value::Num(l), Value::Num(r)) => Value::Num(l - r),
(Self::Div, Value::Num(l), Value::Num(r)) => Value::Num(l / r),
(Self::Prod, Value::Num(l), Value::Num(r)) => Value::Num(l * r),
(Self::Lt, Value::Num(l), Value::Num(r)) => Value::Bool(l < r),
(Self::Gt, Value::Num(l), Value::Num(r)) => Value::Bool(l > r),
(Self::Leq, Value::Num(l), Value::Num(r)) => Value::Bool(l <= r),
(Self::Geq, Value::Num(l), Value::Num(r)) => Value::Bool(l >= r),
(Self::Eq, Value::None, Value::None) => Value::Bool(true),
(Self::Eq, Value::Num(l), Value::Num(r)) => Value::Bool(l == r),
(Self::Eq, Value::Str(l), Value::Str(r)) => Value::Bool(l == r),
(Self::Eq, Value::Array(l), Value::Array(r)) => Value::Bool(Rc::ptr_eq(l, r)),
(Self::Eq, Value::Object(l), Value::Object(r)) => Value::Bool(Rc::ptr_eq(l, r)),
(Self::Eq, Value::Function(l), Value::Function(r)) => Value::Bool(Rc::ptr_eq(l, r)),
(Self::Eq, _, _) => Value::Bool(false),
(Self::Neq, _, _) => UniOp::Inv.apply(&Self::Eq.apply(l, r)?)?,
_ => Err(EvalError::WrongType)?, _ => Err(EvalError::WrongType)?,
}) })
} }
@ -400,6 +415,7 @@ impl Litteral {
.ignore_then(char.repeated()) .ignore_then(char.repeated())
.then_ignore(just("\"")) .then_ignore(just("\""))
.map(|s| Self::Str(s.iter().collect()))) .map(|s| Self::Str(s.iter().collect())))
.or(just("none").map(|_| Self::None))
.or(just("true") .or(just("true")
.map(|_| true) .map(|_| true)
.or(just("false").map(|_| false)) .or(just("false").map(|_| false))

View file

@ -53,6 +53,19 @@ impl Context {
Ok((parsed, value)) 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>> { pub fn scope(&self) -> Rc<RefCell<Scope>> {
self.frame.clone() self.frame.clone()
} }
@ -88,7 +101,7 @@ impl Scope {
result result
} }
pub fn append(&mut self, name: String, value: Value) { pub fn define(&mut self, name: String, value: Value) {
self.defs.insert(name, value); self.defs.insert(name, value);
} }
@ -209,7 +222,7 @@ impl Function {
FunImpl::Def(def) => Scope::alt(context, self.capturing.clone(), |context| { FunImpl::Def(def) => Scope::alt(context, self.capturing.clone(), |context| {
let names = def.args.iter().cloned(); let names = def.args.iter().cloned();
for (name, value) in names.zip(args.into_iter().chain(repeat(Value::None))) { for (name, value) in names.zip(args.into_iter().chain(repeat(Value::None))) {
context.scope().borrow_mut().append(name, value); context.scope().borrow_mut().define(name, value);
} }
match def.body.eval(context) { match def.body.eval(context) {
Err(err) => Err(err), Err(err) => Err(err),