mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
pipe operator
This commit is contained in:
parent
26dd4f53e1
commit
9c782a0a29
|
@ -3,9 +3,9 @@ use std::fmt::Display;
|
||||||
use hir::{IR, IRKind, Value};
|
use hir::{IR, IRKind, Value};
|
||||||
|
|
||||||
const MODULE_INCLUDES: [&str; 3] = [
|
const MODULE_INCLUDES: [&str; 3] = [
|
||||||
"<stdbool.h>",
|
"<iostream>", // stdin `@read()` and stdout `@write()`
|
||||||
"<iostream>",
|
"<stdbool.h>", // bool type
|
||||||
"<string>",
|
"<string>", // string type
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct Codegen {
|
pub struct Codegen {
|
||||||
|
@ -28,47 +28,91 @@ impl Codegen {
|
||||||
self.emit(format!("#include {}\n", module));
|
self.emit(format!("#include {}\n", module));
|
||||||
}
|
}
|
||||||
for ir in irs {
|
for ir in irs {
|
||||||
self.emit(&self.gen_ir(&ir.kind));
|
self.emit(&self.gen_ir(&ir.kind, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_ir(&self, ir: &IRKind) -> String {
|
fn gen_ir(&self, ir: &IRKind, should_gen_semicolon: bool) -> String {
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! semicolon { () => { if should_gen_semicolon { ";" } else { "" } }; }
|
||||||
match ir {
|
match ir {
|
||||||
IRKind::Define { name, type_hint, value } => {
|
IRKind::Define { name, type_hint, value } => {
|
||||||
format!("{} {} = {};\n", type_hint, name, self.gen_ir(value))
|
format!(
|
||||||
|
"{} {} = {}{}\n",
|
||||||
|
type_hint,
|
||||||
|
name,
|
||||||
|
self.gen_ir(value, false),
|
||||||
|
semicolon!()
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
IRKind::Call { name, args } => {
|
IRKind::Call { name, args } => {
|
||||||
format!("{}({});\n", name, args.iter().map(|arg| self.gen_ir(arg)).collect::<Vec<_>>().join(", "))
|
format!(
|
||||||
|
"{}({}){}",
|
||||||
|
name,
|
||||||
|
args
|
||||||
|
.iter()
|
||||||
|
.map(|arg| self.gen_ir(arg, false))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
.trim_end_matches(";\n"),
|
||||||
|
semicolon!(),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
IRKind::Intrinsic { name, args } => {
|
IRKind::Intrinsic { name, args } => {
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"write" => { format!("std::cout << {};\n", self.gen_ir(&args[0])) },
|
"write" => { format!("std::cout << {};\n", self.gen_ir(&args[0], false)) },
|
||||||
"read" => { format!("std::cin >> {};\n", self.gen_ir(&args[0])) },
|
"read" => { format!("std::cin >> {};\n", self.gen_ir(&args[0], false)) },
|
||||||
_ => unreachable!(format!("Unknown intrinsic: {}", name)) // Shoul be handled by lowering
|
_ => unreachable!(format!("Unknown intrinsic: {}", name)) // Shoul be handled by lowering
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
IRKind::Fun { name, return_type_hint, args, body } => {
|
IRKind::Fun { name, return_type_hint, args, body } => {
|
||||||
let args = args.iter().map(|arg| format!("{} {}", arg.1, arg.0)).collect::<Vec<_>>().join(", ");
|
let args = args
|
||||||
format!("{} {}({}) {{\n{};\n}}\n", return_type_hint, name, args, self.gen_ir(body))
|
.iter()
|
||||||
|
.map(|arg| format!("{} {}", arg.1, arg.0))
|
||||||
|
.collect::<Vec<_>>().
|
||||||
|
join(", ");
|
||||||
|
format!(
|
||||||
|
"{} {}({}) {{\n{}\n}}\n",
|
||||||
|
return_type_hint,
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
self.gen_ir(body, false)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
IRKind::Return { value } => {
|
IRKind::Return { value } => {
|
||||||
format!("return {};\n", self.gen_ir(value))
|
format!(
|
||||||
|
"return {};\n",
|
||||||
|
self.gen_ir(value, false)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
IRKind::Do { body } => {
|
IRKind::Do { body } => {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
for expr in body {
|
for expr in body {
|
||||||
out.push_str(&self.gen_ir(&expr));
|
out.push_str(&self.gen_ir(&expr, true));
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
},
|
},
|
||||||
|
|
||||||
IRKind::If { cond, body, else_body } => {
|
IRKind::If { cond, body, else_body } => {
|
||||||
format!("if ({}) {{\n{}}} else {{\n{}}}\n", self.gen_ir(cond), self.gen_ir(body), self.gen_ir(else_body))
|
format!(
|
||||||
|
"if ({}) {{\n{}}} else {{\n{}}}\n",
|
||||||
|
self.gen_ir(cond, true),
|
||||||
|
self.gen_ir(body, true),
|
||||||
|
self.gen_ir(else_body, true),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
IRKind::Unary { op, right } => {
|
IRKind::Unary { op, right } => {
|
||||||
format!("{}{}", op, self.gen_ir(right))
|
format!("{}{}", op, self.gen_ir(right, false))
|
||||||
},
|
},
|
||||||
|
|
||||||
IRKind::Binary { left, op, right } => {
|
IRKind::Binary { left, op, right } => {
|
||||||
format!("{} {} {}", self.gen_ir(left), op, self.gen_ir(right))
|
format!("{} {} {}", self.gen_ir(left, false), op, self.gen_ir(right, false))
|
||||||
},
|
},
|
||||||
|
|
||||||
IRKind::Value { value } => {
|
IRKind::Value { value } => {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use parser::Expr;
|
use parser::Expr;
|
||||||
|
|
||||||
const INTRINSICS: [&str; 2] = ["write", "read"];
|
const INTRINSICS: [&str; 2] = [
|
||||||
|
"write",
|
||||||
|
"read",
|
||||||
|
];
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Value { Int(i64), Boolean(bool), String(String), Ident(String) }
|
pub enum Value { Int(i64), Boolean(bool), String(String), Ident(String) }
|
||||||
|
@ -97,6 +100,41 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
|
||||||
return (Some(ir_kind), None);
|
return (Some(ir_kind), None);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Expr::Pipe { lhs, rhs } => {
|
||||||
|
let lhs_ir = expr_to_ir(&lhs.0);
|
||||||
|
if_err_return!(lhs_ir.1);
|
||||||
|
|
||||||
|
match &rhs.0 {
|
||||||
|
call @ Expr::Call { name, args } => {
|
||||||
|
let name = match &name.0 {
|
||||||
|
Expr::Identifier(s) => s.clone(),
|
||||||
|
// Should never happen because the parser should have caught this
|
||||||
|
_ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None }))
|
||||||
|
};
|
||||||
|
|
||||||
|
let call = expr_to_ir(&call);
|
||||||
|
if_err_return!(call.1);
|
||||||
|
|
||||||
|
let mut largs = Vec::new();
|
||||||
|
for arg in &args.0 {
|
||||||
|
let arg = expr_to_ir(&arg.0);
|
||||||
|
if_err_return!(arg.1);
|
||||||
|
largs.push(arg.0.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args = vec![lhs_ir.0.unwrap()];
|
||||||
|
args.append(&mut largs);
|
||||||
|
|
||||||
|
return (Some(IRKind::Call { name, args }), None);
|
||||||
|
},
|
||||||
|
_ => return (None, Some(LoweringError {
|
||||||
|
span: rhs.1.clone(),
|
||||||
|
message: "Expected call".to_string(),
|
||||||
|
note: None
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
Expr::Let { name, type_hint, value } => {
|
Expr::Let { name, type_hint, value } => {
|
||||||
let value = expr_to_ir(&value.0);
|
let value = expr_to_ir(&value.0);
|
||||||
if_err_return!(value.1);
|
if_err_return!(value.1);
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub enum Token {
|
||||||
// Operators
|
// Operators
|
||||||
Plus, Minus, Multiply, Divide,
|
Plus, Minus, Multiply, Divide,
|
||||||
Not, Equal, NotEqual, Less, Greater,
|
Not, Equal, NotEqual, Less, Greater,
|
||||||
|
Pipe,
|
||||||
|
|
||||||
// Symbols & Delimiters
|
// Symbols & Delimiters
|
||||||
Assign,
|
Assign,
|
||||||
|
@ -50,6 +51,7 @@ impl std::fmt::Display for Token {
|
||||||
Token::NotEqual => write!(f, "!="),
|
Token::NotEqual => write!(f, "!="),
|
||||||
Token::Less => write!(f, "<"),
|
Token::Less => write!(f, "<"),
|
||||||
Token::Greater => write!(f, ">"),
|
Token::Greater => write!(f, ">"),
|
||||||
|
Token::Pipe => write!(f, "|>"),
|
||||||
|
|
||||||
Token::Assign => write!(f, "="),
|
Token::Assign => write!(f, "="),
|
||||||
Token::Dot => write!(f, "."),
|
Token::Dot => write!(f, "."),
|
||||||
|
@ -79,10 +81,15 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
|
||||||
just('-').to(Token::Minus),
|
just('-').to(Token::Minus),
|
||||||
just('*').to(Token::Multiply),
|
just('*').to(Token::Multiply),
|
||||||
just('/').to(Token::Divide),
|
just('/').to(Token::Divide),
|
||||||
|
|
||||||
just('!').to(Token::Not),
|
just('!').to(Token::Not),
|
||||||
just("==").to(Token::Equal),
|
just("==").to(Token::Equal),
|
||||||
|
|
||||||
|
just("|>").to(Token::Pipe),
|
||||||
|
|
||||||
just('<').to(Token::Less),
|
just('<').to(Token::Less),
|
||||||
just('>').to(Token::Greater),
|
just('>').to(Token::Greater),
|
||||||
|
|
||||||
just('=').to(Token::Assign),
|
just('=').to(Token::Assign),
|
||||||
just('.').to(Token::Dot),
|
just('.').to(Token::Dot),
|
||||||
just(',').to(Token::Comma),
|
just(',').to(Token::Comma),
|
||||||
|
|
|
@ -40,7 +40,7 @@ fn main() {
|
||||||
match args.options {
|
match args.options {
|
||||||
Options::Compile {
|
Options::Compile {
|
||||||
input: file_name,
|
input: file_name,
|
||||||
ast: _print_ast,
|
ast: print_ast,
|
||||||
log: should_log,
|
log: should_log,
|
||||||
output,
|
output,
|
||||||
} => {
|
} => {
|
||||||
|
@ -73,12 +73,16 @@ fn main() {
|
||||||
logif!(0, format!("Parsing took {}ms", start.elapsed().as_millis()));
|
logif!(0, format!("Parsing took {}ms", start.elapsed().as_millis()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if print_ast { log(0, format!("{:#?}", ast)); }
|
||||||
|
|
||||||
match ast {
|
match ast {
|
||||||
Some(ast) => {
|
Some(ast) => {
|
||||||
// Convert the AST to HIR
|
// Convert the AST to HIR
|
||||||
let (ir, lowering_error) = ast_to_ir(ast);
|
let (ir, lowering_error) = ast_to_ir(ast);
|
||||||
for err in lowering_error { diagnostics.add_lowering_error(err); }
|
for err in lowering_error { diagnostics.add_lowering_error(err); }
|
||||||
|
|
||||||
|
if print_ast { log(0, format!("{:#?}", ir)); }
|
||||||
|
|
||||||
// Report lowering errors if any
|
// Report lowering errors if any
|
||||||
if diagnostics.has_error() {
|
if diagnostics.has_error() {
|
||||||
diagnostics.display(src);
|
diagnostics.display(src);
|
||||||
|
|
|
@ -11,6 +11,7 @@ pub enum Expr {
|
||||||
Unary { op: String, rhs: Box<Spanned<Self>> },
|
Unary { op: String, rhs: Box<Spanned<Self>> },
|
||||||
Binary { lhs: Box<Spanned<Self>>, op: String, rhs: Box<Spanned<Self>> },
|
Binary { lhs: Box<Spanned<Self>>, op: String, rhs: Box<Spanned<Self>> },
|
||||||
Call { name: Box<Spanned<Self>>, args: Spanned<Vec<Spanned<Self>>> },
|
Call { name: Box<Spanned<Self>>, args: Spanned<Vec<Spanned<Self>>> },
|
||||||
|
Pipe { lhs: Box<Spanned<Self>>, rhs: Box<Spanned<Self>> },
|
||||||
Intrinsic { name: Box<Spanned<Self>>, args: Spanned<Vec<Spanned<Self>>> },
|
Intrinsic { name: Box<Spanned<Self>>, args: Spanned<Vec<Spanned<Self>>> },
|
||||||
|
|
||||||
Let {
|
Let {
|
||||||
|
@ -171,6 +172,21 @@ fn expr_parser() -> impl Parser<Token, Vec<Spanned<Expr>>, Error = Simple<Token>
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let pipe = compare.clone()
|
||||||
|
.then(
|
||||||
|
just(Token::Pipe)
|
||||||
|
.then(compare)
|
||||||
|
.repeated())
|
||||||
|
.foldl(|lhs, (_, rhs)| {
|
||||||
|
(
|
||||||
|
Expr::Pipe {
|
||||||
|
lhs: Box::new(lhs),
|
||||||
|
rhs: Box::new(rhs.clone()),
|
||||||
|
},
|
||||||
|
rhs.1,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let let_ = just(Token::KwLet)
|
let let_ = just(Token::KwLet)
|
||||||
.ignore_then(identifier)
|
.ignore_then(identifier)
|
||||||
.then_ignore(just(Token::Colon))
|
.then_ignore(just(Token::Colon))
|
||||||
|
@ -272,7 +288,7 @@ fn expr_parser() -> impl Parser<Token, Vec<Spanned<Expr>>, Error = Simple<Token>
|
||||||
.or(return_)
|
.or(return_)
|
||||||
.or(do_block)
|
.or(do_block)
|
||||||
.or(if_block)
|
.or(if_block)
|
||||||
.or(compare)
|
.or(pipe)
|
||||||
}).labelled("expression");
|
}).labelled("expression");
|
||||||
|
|
||||||
expr
|
expr
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
fun main: int = do
|
fun main: int = do
|
||||||
@writesss("Hello, World!\n");
|
@write("Hello, World!\n");
|
||||||
return 69;
|
return 69;
|
||||||
end;
|
end;
|
8
example/pipe.hz
Normal file
8
example/pipe.hz
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fun foo (xs: int): int = return xs + 1;
|
||||||
|
fun bar (xs: int) (x: int): int = return xs - x;
|
||||||
|
|
||||||
|
fun main: int = do
|
||||||
|
let res: int = foo(69)
|
||||||
|
|> bar(1);
|
||||||
|
@write(res);
|
||||||
|
end;
|
Loading…
Reference in a new issue