`|>` operator folding

pull/4/head
azur 2022-12-20 21:56:01 +07:00
parent cfca9828f6
commit ff79c39cde
7 changed files with 203 additions and 59 deletions

2
Cargo.lock generated
View File

@ -39,7 +39,7 @@ dependencies = [
name = "compiler"
version = "0.1.0"
dependencies = [
"parser",
"lower",
"vm",
]

View File

@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
parser = { path = "../parser" }
lower = { path = "../lower" }
vm = { path = "../vm" }

View File

@ -1,6 +1,6 @@
#![allow(clippy::new_without_default)]
#![allow(clippy::only_used_in_recursion)]
use parser::{Expr, Literal, Span, Stmt};
use lower::model::{BinaryOp, Expr, Literal, Stmt, UnaryOp};
use vm::model::Instr;
pub struct Compiler {}
@ -26,58 +26,58 @@ impl Compiler {
let mut instrs = vec![];
let count = xs.len();
for x in xs {
instrs.extend(self.compile_expr(x.0));
instrs.extend(self.compile_expr(x));
}
instrs.push(Instr::ListMake(count));
instrs
}
Expr::Unary(op, x) => {
let mut instrs = self.compile_expr(x.0);
instrs.extend(match op.0 {
parser::UnaryOp::Neg => vec![Instr::NumPush(-1), Instr::NumMul],
parser::UnaryOp::Not => vec![Instr::BoolNot],
let mut instrs = self.compile_expr(*x);
instrs.extend(match op {
UnaryOp::Neg => vec![Instr::NumPush(-1), Instr::NumMul],
UnaryOp::Not => vec![Instr::BoolNot],
});
instrs
}
Expr::Binary(op, x, y) => {
let mut instrs = self.compile_expr(y.0);
instrs.extend(self.compile_expr(x.0));
instrs.push(match op.0 {
parser::BinaryOp::Add => Instr::NumAdd,
parser::BinaryOp::Sub => Instr::NumSub,
parser::BinaryOp::Mul => Instr::NumMul,
parser::BinaryOp::Div => Instr::NumDiv,
parser::BinaryOp::Eq => Instr::NumEq,
parser::BinaryOp::Ne => Instr::NumNe,
parser::BinaryOp::Lt => Instr::NumLt,
parser::BinaryOp::Gt => Instr::NumGt,
parser::BinaryOp::Le => Instr::NumLe,
parser::BinaryOp::Ge => Instr::NumGe,
parser::BinaryOp::And => Instr::BoolAnd,
parser::BinaryOp::Or => Instr::BoolOr,
parser::BinaryOp::Pipe => todo!(),
let mut instrs = self.compile_expr(*y);
instrs.extend(self.compile_expr(*x));
instrs.push(match op {
BinaryOp::Add => Instr::NumAdd,
BinaryOp::Sub => Instr::NumSub,
BinaryOp::Mul => Instr::NumMul,
BinaryOp::Div => Instr::NumDiv,
BinaryOp::Eq => Instr::NumEq,
BinaryOp::Ne => Instr::NumNe,
BinaryOp::Lt => Instr::NumLt,
BinaryOp::Gt => Instr::NumGt,
BinaryOp::Le => Instr::NumLe,
BinaryOp::Ge => Instr::NumGe,
BinaryOp::And => Instr::BoolAnd,
BinaryOp::Or => Instr::BoolOr,
BinaryOp::Pipe => todo!(),
});
instrs
}
Expr::Lambda(args, body) => {
vec![Instr::FuncMake(args, self.compile_expr(body.0))]
vec![Instr::FuncMake(args, self.compile_expr(*body))]
}
Expr::Call(f, xs) => {
let mut instrs = vec![];
for x in xs {
instrs.extend(self.compile_expr(x.0));
instrs.extend(self.compile_expr(x));
}
match &f.0 {
Expr::Sym(fname) => match fname.as_str() {
match *f {
Expr::Sym(ref fname) => match fname.as_str() {
"print" => instrs.push(Instr::Print),
"println" => instrs.push(Instr::PrintLn),
_ => {
instrs.extend(self.compile_expr(f.0));
instrs.extend(self.compile_expr(*f));
instrs.push(Instr::FuncApply);
}
},
Expr::Lambda(_, _) => {
instrs.extend(self.compile_expr(f.0));
instrs.extend(self.compile_expr(*f));
instrs.push(Instr::FuncApply);
}
_ => todo!(),
@ -89,7 +89,7 @@ impl Compiler {
let binds = binds
.into_iter()
.flat_map(|(name, expr)| {
let mut instrs = self.compile_expr(expr.0);
let mut instrs = self.compile_expr(expr);
instrs.extend(vec![Instr::Set(name)]);
instrs
})
@ -101,7 +101,7 @@ impl Compiler {
instrs.extend(vec![
Instr::FuncMake(
vec![],
binds.into_iter().chain(self.compile_expr(e.0)).collect(),
binds.into_iter().chain(self.compile_expr(*e)).collect(),
),
Instr::FuncApply,
]);
@ -113,10 +113,10 @@ impl Compiler {
instrs
}
Expr::If(c, t, f) => {
let mut instrs = self.compile_expr(c.0);
let t = self.compile_expr(t.0);
let mut instrs = self.compile_expr(*c);
let t = self.compile_expr(*t);
if let Some(f) = f {
let f = self.compile_expr(f.0);
let f = self.compile_expr(*f);
instrs.push(Instr::JumpIfFalse(t.len() + 1));
instrs.extend(t);
instrs.push(Instr::Jump(f.len()));
@ -130,7 +130,7 @@ impl Compiler {
Expr::Do(es) => {
let mut instrs = vec![];
for e in es {
instrs.extend(self.compile_expr(e.0));
instrs.extend(self.compile_expr(e));
}
instrs
}
@ -141,11 +141,11 @@ impl Compiler {
match stmt {
Stmt::Fun(name, args, body) => {
let is_main = name == "main";
let mut instrs = match body.0 {
let mut instrs = match body {
// If the body is a lambda then we don't have to compile
// it into a function
Expr::Lambda(_, _) => self.compile_expr(body.0),
_ => vec![Instr::FuncMake(args, self.compile_expr(body.0))],
Expr::Lambda(_, _) => self.compile_expr(body),
_ => vec![Instr::FuncMake(args, self.compile_expr(body))],
};
instrs.push(Instr::Set(name));
if is_main {

View File

@ -1,5 +1,5 @@
use compiler::Compiler;
use lower::Lower;
use lower::{model::converts, Lower};
use parser::{lex, parse, report};
use vm::exec::Executor;
@ -12,10 +12,11 @@ fn main() {
let (ast, parse_errors) = parse(tokens, src.len());
if let Some(ast) = ast {
let stripped = converts(ast);
let mut lower = Lower::new();
let last = lower.opt_stmts(ast.iter().map(|(s, _)| s.clone()).collect());
let lowered = lower.opt_stmts(stripped);
let mut compiler = Compiler::new();
let instrs = compiler.compile_program(last);
let instrs = compiler.compile_program(lowered);
// instrs.iter().for_each(|i| println!("{:?}", i));
let mut executor = Executor::new(instrs);
match executor.run() {

View File

@ -8,5 +8,9 @@ end
fun double x = x * 2
fun main = do
x |> \a -> println(a)
1
|> \a -> succ(a)
|> \b -> double(b)
|> \c -> println(c)
end

View File

@ -1,7 +1,6 @@
#![allow(clippy::new_without_default)]
use parser::*;
type SExpr = (Expr, std::ops::Range<usize>);
pub mod model;
use crate::model::{BinaryOp, Expr, Stmt};
pub struct Lower {}
@ -12,42 +11,52 @@ impl Lower {
pub fn opt_stmts(&self, ss: Vec<Stmt>) -> Vec<Stmt> {
ss.into_iter()
.flat_map(|s| self.opt_stmt(s.clone()).unwrap_or_else(|| vec![s]))
.map(|s| self.opt_stmt(s.clone()).unwrap_or(s))
.collect()
}
pub fn opt_stmt(&self, s: Stmt) -> Option<Vec<Stmt>> {
pub fn opt_stmt(&self, s: Stmt) -> Option<Stmt> {
match s {
// Stmt::Fun(name, args, body) => Some(vec![Stmt::Fun(
// name,
// args,
// self.opt_expr(body.0).unwrap_or_else(|| vec![body.0]),
// )]),
Stmt::Fun(name, args, body) => Some(Stmt::Fun(
name,
args,
self.opt_expr(body.clone()).unwrap_or(body),
)),
_ => None,
}
}
pub fn opt_exprs(&self, es: Vec<Expr>) -> Vec<Expr> {
es.into_iter()
.flat_map(|e| self.opt_expr(e.clone()).unwrap_or_else(|| vec![e]))
.map(|e| self.opt_expr(e.clone()).unwrap_or(e))
.collect()
}
pub fn opt_expr(&self, e: Expr) -> Option<Vec<Expr>> {
pub fn opt_expr(&self, e: Expr) -> Option<Expr> {
match e {
Expr::Binary((BinaryOp::Pipe, _), left, right) => Some(self.fold_pipe(*left, *right)),
Expr::Binary(BinaryOp::Pipe, left, right) => Some(self.fold_pipe(*left, *right)),
Expr::Lambda(args, body) => Some(Expr::Lambda(
args,
Box::new(self.opt_expr(*body.clone()).unwrap_or(*body)),
)),
Expr::Do(es) => Some(Expr::Do(self.opt_exprs(es))),
_ => None,
}
}
fn fold_pipe(&self, left: SExpr, right: SExpr) -> Vec<Expr> {
vec![Expr::Call(Box::new(right), vec![left])]
fn fold_pipe(&self, left: Expr, right: Expr) -> Expr {
Expr::Call(
Box::new(self.opt_expr(right.clone()).unwrap_or(right)),
vec![self.opt_expr(left.clone()).unwrap_or(left)],
)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::model::convert_expr;
use parser::{lex, parse_expr};
#[test]
fn test_fold_pipe() {
@ -62,8 +71,9 @@ mod test {
assert!(es.is_empty());
let ex = ex.unwrap();
let ex = convert_expr(ex);
let l = Lower::new();
let ex = l.opt_expr(ex.0).unwrap();
let ex = l.opt_expr(ex).unwrap();
println!("{:?}", ex);
}
}

129
lower/src/model.rs Normal file
View File

@ -0,0 +1,129 @@
use crate::model;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Literal {
Num(i64),
Bool(bool),
Str(String),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UnaryOp {
Neg,
Not,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Lt,
Le,
Gt,
Ge,
Eq,
Ne,
And,
Or,
Pipe,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Expr {
Error,
Literal(Literal),
Sym(String),
Vec(Vec<Self>),
Unary(UnaryOp, Box<Self>),
Binary(BinaryOp, Box<Self>, Box<Self>),
Lambda(Vec<String>, Box<Self>),
Call(Box<Self>, Vec<Self>),
Let(Vec<(String, Self)>, Option<Box<Self>>),
If(Box<Self>, Box<Self>, Option<Box<Self>>),
Do(Vec<Expr>),
}
#[derive(Clone, Debug)]
pub enum Stmt {
Fun(String, Vec<String>, Expr),
}
pub fn converts(s: Vec<(parser::Stmt, std::ops::Range<usize>)>) -> Vec<model::Stmt> {
s.into_iter().map(|(s, _)| convert(s)).collect()
}
pub fn convert(s: parser::Stmt) -> model::Stmt {
match s {
parser::Stmt::Fun(name, args, body) => model::Stmt::Fun(name, args, convert_expr(body)),
}
}
pub fn convert_expr(e: (parser::Expr, std::ops::Range<usize>)) -> model::Expr {
match e.0 {
parser::Expr::Error => model::Expr::Error,
parser::Expr::Literal(l) => match l {
parser::Literal::Num(n) => model::Expr::Literal(model::Literal::Num(n)),
parser::Literal::Bool(b) => model::Expr::Literal(model::Literal::Bool(b)),
parser::Literal::Str(s) => model::Expr::Literal(model::Literal::Str(s)),
},
parser::Expr::Sym(s) => model::Expr::Sym(s),
parser::Expr::Vec(es) => model::Expr::Vec(es.into_iter().map(convert_expr).collect()),
parser::Expr::Unary(op, e) => {
model::Expr::Unary(convert_unary_op(op.0), Box::new(convert_expr(*e)))
}
parser::Expr::Binary(op, left, right) => model::Expr::Binary(
convert_binary_op(op.0),
Box::new(convert_expr(*left)),
Box::new(convert_expr(*right)),
),
parser::Expr::Lambda(args, body) => {
model::Expr::Lambda(args, Box::new(convert_expr(*body)))
}
parser::Expr::Call(f, args) => model::Expr::Call(
Box::new(convert_expr(*f)),
args.into_iter().map(convert_expr).collect(),
),
parser::Expr::Let(bindings, body) => model::Expr::Let(
bindings
.into_iter()
.map(|(s, e)| (s, convert_expr(e)))
.collect(),
body.map(|e| Box::new(convert_expr(*e))),
),
parser::Expr::If(cond, then, else_) => model::Expr::If(
Box::new(convert_expr(*cond)),
Box::new(convert_expr(*then)),
else_.map(|e| Box::new(convert_expr(*e))),
),
parser::Expr::Do(es) => model::Expr::Do(es.into_iter().map(convert_expr).collect()),
}
}
pub fn convert_unary_op(op: parser::UnaryOp) -> model::UnaryOp {
match op {
parser::UnaryOp::Neg => model::UnaryOp::Neg,
parser::UnaryOp::Not => model::UnaryOp::Not,
}
}
pub fn convert_binary_op(op: parser::BinaryOp) -> model::BinaryOp {
match op {
parser::BinaryOp::Add => model::BinaryOp::Add,
parser::BinaryOp::Sub => model::BinaryOp::Sub,
parser::BinaryOp::Mul => model::BinaryOp::Mul,
parser::BinaryOp::Div => model::BinaryOp::Div,
parser::BinaryOp::Lt => model::BinaryOp::Lt,
parser::BinaryOp::Le => model::BinaryOp::Le,
parser::BinaryOp::Gt => model::BinaryOp::Gt,
parser::BinaryOp::Ge => model::BinaryOp::Ge,
parser::BinaryOp::Eq => model::BinaryOp::Eq,
parser::BinaryOp::Ne => model::BinaryOp::Ne,
parser::BinaryOp::And => model::BinaryOp::And,
parser::BinaryOp::Or => model::BinaryOp::Or,
parser::BinaryOp::Pipe => model::BinaryOp::Pipe,
}
}