mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
Rename stuff, Initialize IRs
This commit is contained in:
parent
019bc88186
commit
ca469f096e
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -212,6 +212,10 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ir"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
|
|
@ -4,4 +4,5 @@ members = [
|
||||||
"bin",
|
"bin",
|
||||||
"syntax",
|
"syntax",
|
||||||
"typing",
|
"typing",
|
||||||
|
"ir",
|
||||||
]
|
]
|
|
@ -9,3 +9,7 @@ chumsky = "1.0.0-alpha.3"
|
||||||
clap = { version = "4.2.4", features = ["derive"] }
|
clap = { version = "4.2.4", features = ["derive"] }
|
||||||
syntax = { path = "../syntax" }
|
syntax = { path = "../syntax" }
|
||||||
typing = { path = "../typing" }
|
typing = { path = "../typing" }
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "hc"
|
||||||
|
path = "src/main.rs"
|
8
ir/Cargo.toml
Normal file
8
ir/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "ir"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
190
ir/src/lib.rs
Normal file
190
ir/src/lib.rs
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum IRExpr<'src> {
|
||||||
|
Int(i64),
|
||||||
|
Var(&'src str),
|
||||||
|
Call(&'src str, Vec<Self>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IRExpr<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
match self {
|
||||||
|
IRExpr::Int(x) => write!(f, "{x}"),
|
||||||
|
IRExpr::Var(x) => write!(f, "{x}"),
|
||||||
|
IRExpr::Call(name, args) => {
|
||||||
|
write!(f, "{name}(")?;
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
if i > 0 { write!(f, ", ")?; }
|
||||||
|
write!(f, "{arg}")?;
|
||||||
|
}
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum IR<'src> {
|
||||||
|
Define {
|
||||||
|
name: &'src str,
|
||||||
|
value: Box<IRExpr<'src>>,
|
||||||
|
},
|
||||||
|
IRExpr(IRExpr<'src>),
|
||||||
|
Block {
|
||||||
|
id: usize,
|
||||||
|
body: Vec<Self>,
|
||||||
|
},
|
||||||
|
Func {
|
||||||
|
name: &'src str,
|
||||||
|
args: Vec<&'src str>,
|
||||||
|
body: Vec<Self>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_ir(ir: &IR, indent: usize) -> String {
|
||||||
|
let mut s = String::new();
|
||||||
|
for _ in 0..indent { s.push(' '); }
|
||||||
|
match ir {
|
||||||
|
IR::Define { name, value } => s.push_str(&format!("{name} = {value}")),
|
||||||
|
IR::IRExpr(expr) => s.push_str(&format!("{expr}")),
|
||||||
|
IR::Block { id, body } => {
|
||||||
|
s.push_str(&format!("{id}:\n"));
|
||||||
|
for ir in body {
|
||||||
|
s.push_str(&display_ir(ir, indent + 4));
|
||||||
|
s.push_str("\n");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
IR::Func { name, args, body } => {
|
||||||
|
s.push_str(&format!("{name} "));
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
if i > 0 { s.push_str(" "); }
|
||||||
|
s.push_str(&format!("{arg}"));
|
||||||
|
}
|
||||||
|
s.push_str(":\n");
|
||||||
|
for ir in body {
|
||||||
|
s.push_str(&display_ir(ir, indent + 4));
|
||||||
|
s.push_str("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IR<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
write!(f, "{}", display_ir(self, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{
|
||||||
|
IR::*,
|
||||||
|
IRExpr::*
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ir() {
|
||||||
|
let fns = [
|
||||||
|
Func {
|
||||||
|
name: "my_add",
|
||||||
|
args: vec!["a", "b"],
|
||||||
|
body: vec![
|
||||||
|
Block {
|
||||||
|
id: 0,
|
||||||
|
body: vec![
|
||||||
|
Define {
|
||||||
|
name: "v0",
|
||||||
|
value: Call(
|
||||||
|
"add",
|
||||||
|
vec![
|
||||||
|
Var("a"),
|
||||||
|
Var("b"),
|
||||||
|
]
|
||||||
|
).into(),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Func {
|
||||||
|
name: "factorial",
|
||||||
|
args: vec!["n"],
|
||||||
|
body: vec![
|
||||||
|
Block {
|
||||||
|
id: 0,
|
||||||
|
body: vec![
|
||||||
|
Define {
|
||||||
|
name: "v0",
|
||||||
|
value: Call(
|
||||||
|
"eq",
|
||||||
|
vec![
|
||||||
|
Var("n"),
|
||||||
|
Int(1),
|
||||||
|
]
|
||||||
|
).into(),
|
||||||
|
},
|
||||||
|
IRExpr(Call(
|
||||||
|
"jf",
|
||||||
|
vec![
|
||||||
|
Var("v0"),
|
||||||
|
Int(1),
|
||||||
|
]
|
||||||
|
)),
|
||||||
|
IRExpr(Call(
|
||||||
|
"ret",
|
||||||
|
vec![
|
||||||
|
Var("n"),
|
||||||
|
]
|
||||||
|
)),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Block {
|
||||||
|
id: 1,
|
||||||
|
body: vec![
|
||||||
|
Define {
|
||||||
|
name: "v0",
|
||||||
|
value: Call(
|
||||||
|
"isub",
|
||||||
|
vec![
|
||||||
|
Var("n"),
|
||||||
|
Int(1),
|
||||||
|
]
|
||||||
|
).into(),
|
||||||
|
},
|
||||||
|
Define {
|
||||||
|
name: "v1",
|
||||||
|
value: Call(
|
||||||
|
"call",
|
||||||
|
vec![
|
||||||
|
Var("factorial"),
|
||||||
|
Var("v0"),
|
||||||
|
]
|
||||||
|
).into(),
|
||||||
|
},
|
||||||
|
Define {
|
||||||
|
name: "v2",
|
||||||
|
value: Call(
|
||||||
|
"imul",
|
||||||
|
vec![
|
||||||
|
Var("n"),
|
||||||
|
Var("v1"),
|
||||||
|
]
|
||||||
|
).into(),
|
||||||
|
},
|
||||||
|
IRExpr(Call(
|
||||||
|
"ret",
|
||||||
|
vec![
|
||||||
|
Var("v2"),
|
||||||
|
]
|
||||||
|
)),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
fns.iter().for_each(|ir| println!("{}", ir));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
let add = \x : num, y : num -> num = x + y;
|
|
229
spec.md
229
spec.md
|
@ -1,229 +0,0 @@
|
||||||
# Specification
|
|
||||||
|
|
||||||
## Syntax
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Expressions
|
|
||||||
|
|
||||||
- Literals
|
|
||||||
|
|
||||||
A literal is a value that is written directly
|
|
||||||
into the source code.
|
|
||||||
|
|
||||||
- Number
|
|
||||||
|
|
||||||
An number literal is of type `f64` and can
|
|
||||||
be expressed with or without a decimal point.
|
|
||||||
- Examples: `1`, `3.14`, `.5`
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
Number:
|
|
||||||
Digits + (maybe '.' + Digits).
|
|
||||||
(* Optional whole number, e.g. .5 *)
|
|
||||||
('.' + Digits).
|
|
||||||
Digits:
|
|
||||||
one or more of 0..9.
|
|
||||||
```
|
|
||||||
|
|
||||||
- String
|
|
||||||
|
|
||||||
A string literal can consist of zero or more
|
|
||||||
characters enclosed in double quotes (`"`)
|
|
||||||
- Examples: `"Hello, World"`,
|
|
||||||
`"They said \"Hi\""`,
|
|
||||||
`"Foo\nBar"`
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
String:
|
|
||||||
'"' + (zero or more of Character) + '"'.
|
|
||||||
Character:
|
|
||||||
any character except '"' or '\'.
|
|
||||||
escape sequences.
|
|
||||||
```
|
|
||||||
|
|
||||||
- Boolean
|
|
||||||
|
|
||||||
A boolean literal can be either `true` or
|
|
||||||
`false`.
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
Boolean:
|
|
||||||
'true' or 'false'.
|
|
||||||
```
|
|
||||||
|
|
||||||
- Unit
|
|
||||||
|
|
||||||
A unit literal is a value that represents
|
|
||||||
the absence of a value.
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
Unit:
|
|
||||||
'()'.
|
|
||||||
```
|
|
||||||
|
|
||||||
- Identifiers
|
|
||||||
|
|
||||||
An identifier is a name that is used to refer
|
|
||||||
to a variable, function, or other entity.
|
|
||||||
- Examples: `foo`, `barBaz`, `add2`
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
Identifier:
|
|
||||||
(Letter + zero or more of LetterOrDigit) but
|
|
||||||
not any of Keywords.
|
|
||||||
Letter:
|
|
||||||
one of a..z or A..Z.
|
|
||||||
LetterOrDigit:
|
|
||||||
Letter or one of 0..9.
|
|
||||||
```
|
|
||||||
|
|
||||||
- Operators
|
|
||||||
|
|
||||||
An operator is a symbol that is used to
|
|
||||||
represent an operation.
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
Binary:
|
|
||||||
one of (
|
|
||||||
(* Arithmetic *)
|
|
||||||
+ - * / %
|
|
||||||
(* Comparison *)
|
|
||||||
== != < <= > >=
|
|
||||||
(* Logical *)
|
|
||||||
&& ||
|
|
||||||
).
|
|
||||||
Unary:
|
|
||||||
one of (- !).
|
|
||||||
```
|
|
||||||
|
|
||||||
- Application (Function Call)
|
|
||||||
|
|
||||||
An application is an expression that calls a
|
|
||||||
function with a list of arguments.
|
|
||||||
It is not necessary that the callee is a
|
|
||||||
function, but it must be an expression that
|
|
||||||
evaluates to a function.
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
Arguments:
|
|
||||||
zero or more of Expression delimited by ','.
|
|
||||||
Application:
|
|
||||||
Expression + '(' + Arguments + ')'.
|
|
||||||
```
|
|
||||||
|
|
||||||
- Examples:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
foo(1, 2, 3)
|
|
||||||
(\x -> x + 1)(2)
|
|
||||||
```
|
|
||||||
|
|
||||||
- If-Else
|
|
||||||
|
|
||||||
An if-else expression is an expression that
|
|
||||||
evaluates to one of two expressions depending
|
|
||||||
on the value of a condition.
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
IfElse:
|
|
||||||
'if' + Expression + 'then' + Expression + 'else' + Expression.
|
|
||||||
```
|
|
||||||
|
|
||||||
- Examples:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
if true then 1 else 2
|
|
||||||
if 1 == 2 then "foo" else "bar"
|
|
||||||
```
|
|
||||||
|
|
||||||
- Let Binding(s)
|
|
||||||
|
|
||||||
There are 2 types of let bindings:
|
|
||||||
- "Imperative" let bindings, which are
|
|
||||||
similar to variable declarations in
|
|
||||||
imperative languages (Javascript, Rust, etc.).
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
Bindings:
|
|
||||||
one or more of Binding delimited by ','.
|
|
||||||
Let:
|
|
||||||
'let' + Bindings.
|
|
||||||
```
|
|
||||||
|
|
||||||
- Example:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let x = 1 // -> ()
|
|
||||||
x + 1 // -> 2
|
|
||||||
```
|
|
||||||
|
|
||||||
- "Functional" let bindings, which are
|
|
||||||
similar to variable declarations in
|
|
||||||
functional languages (ML-family, etc.).
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
LetIn:
|
|
||||||
'let' + Bindings + 'in' + Expression.
|
|
||||||
```
|
|
||||||
|
|
||||||
- Example:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let x = 1, y = 2 in
|
|
||||||
x + y // -> 3
|
|
||||||
```
|
|
||||||
|
|
||||||
- Block & Return
|
|
||||||
|
|
||||||
A block is a sequence of expressions that are
|
|
||||||
evaluated in order and the value of the last
|
|
||||||
expression is returned (if not ended with a
|
|
||||||
semicolon).
|
|
||||||
|
|
||||||
A return expression is an expression that
|
|
||||||
will exit the current block and return the
|
|
||||||
value of the expression. It is not necessary
|
|
||||||
to use a return expression in a block, but
|
|
||||||
it could be useful for early termination.
|
|
||||||
|
|
||||||
Any use of a return expression outside of a
|
|
||||||
block is not allowed.
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
Block:
|
|
||||||
'{' + zero or more of Expression + '}'.
|
|
||||||
Return:
|
|
||||||
'return' + Expression.
|
|
||||||
```
|
|
||||||
|
|
||||||
- Examples:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
{
|
|
||||||
let x = 1;
|
|
||||||
let y = 2;
|
|
||||||
x + y
|
|
||||||
}
|
|
||||||
```
|
|
||||||
```rust
|
|
||||||
fun foo(): num = {
|
|
||||||
if true then
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
let bar = 42;
|
|
||||||
bar
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keywords
|
|
||||||
|
|
||||||
Keywords are reserved words that cannot be
|
|
||||||
used as identifiers. They are used to
|
|
||||||
represent constructs of the language.
|
|
||||||
|
|
||||||
```ebnf
|
|
||||||
Keywords:
|
|
||||||
if then else
|
|
||||||
let fun return
|
|
||||||
```
|
|
|
@ -10,7 +10,7 @@ pub enum Delim { Paren, Brack, Brace }
|
||||||
// 'src is the lifetime of the source code string.
|
// 'src is the lifetime of the source code string.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Token<'src> {
|
pub enum Token<'src> {
|
||||||
Unit, Bool(bool), Num(f64), Str(&'src str),
|
Unit, Bool(bool), Int(i64), Str(&'src str),
|
||||||
Ident(&'src str),
|
Ident(&'src str),
|
||||||
|
|
||||||
Add, Sub, Mul, Div, Rem,
|
Add, Sub, Mul, Div, Rem,
|
||||||
|
@ -29,7 +29,7 @@ impl<'src> Display for Token<'src> {
|
||||||
match self {
|
match self {
|
||||||
Token::Unit => write!(f, "()"),
|
Token::Unit => write!(f, "()"),
|
||||||
Token::Bool(b) => write!(f, "{}", b),
|
Token::Bool(b) => write!(f, "{}", b),
|
||||||
Token::Num(n) => write!(f, "{}", n),
|
Token::Int(n) => write!(f, "{}", n),
|
||||||
Token::Str(s) => write!(f, "\"{}\"", s),
|
Token::Str(s) => write!(f, "\"{}\"", s),
|
||||||
Token::Ident(s) => write!(f, "{}", s),
|
Token::Ident(s) => write!(f, "{}", s),
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ pub type Span = SimpleSpan<usize>;
|
||||||
pub enum Lit<'src> {
|
pub enum Lit<'src> {
|
||||||
Unit,
|
Unit,
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Num(f64),
|
Int(i64),
|
||||||
Str(&'src str),
|
Str(&'src str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,3 @@
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod ty;
|
pub mod ty;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use chumsky::prelude::*;
|
|
||||||
use super::{ expr::*, parser::* };
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple() {
|
|
||||||
let src = "let x = 1 + (), y = foo in x + !(y)";
|
|
||||||
|
|
||||||
let (ts, errs) = lexer().parse(src).into_output_errors();
|
|
||||||
|
|
||||||
assert!(errs.is_empty());
|
|
||||||
assert_eq!(ts, Some(vec![
|
|
||||||
(Token::Let, Span::new(0, 3)),
|
|
||||||
(Token::Ident("x"), Span::new(4, 5)),
|
|
||||||
(Token::Assign, Span::new(6, 7)),
|
|
||||||
(Token::Num(1.0), Span::new(8, 9)),
|
|
||||||
(Token::Add, Span::new(10, 11)),
|
|
||||||
(Token::Unit, Span::new(12, 14)),
|
|
||||||
(Token::Comma, Span::new(14, 15)),
|
|
||||||
(Token::Ident("y"), Span::new(16, 17)),
|
|
||||||
(Token::Assign, Span::new(18, 19)),
|
|
||||||
(Token::Ident("foo"), Span::new(20, 23)),
|
|
||||||
(Token::In, Span::new(24, 26)),
|
|
||||||
(Token::Ident("x"), Span::new(27, 28)),
|
|
||||||
(Token::Add, Span::new(29, 30)),
|
|
||||||
(Token::Not, Span::new(31, 32)),
|
|
||||||
(Token::Open(Delim::Paren), Span::new(32, 33)),
|
|
||||||
(Token::Ident("y"), Span::new(33, 34)),
|
|
||||||
(Token::Close(Delim::Paren), Span::new(34, 35)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,12 +3,17 @@ use chumsky::prelude::*;
|
||||||
use super::{ expr::*, ty::Type };
|
use super::{ expr::*, ty::Type };
|
||||||
|
|
||||||
pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, extra::Err<Rich<'src, char, Span>>> {
|
pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, extra::Err<Rich<'src, char, Span>>> {
|
||||||
let num = text::int(10)
|
// let num = text::int(10)
|
||||||
.then(just('.').then(text::digits(10)).or_not())
|
// .then(just('.').then(text::digits(10)).or_not())
|
||||||
|
// .slice()
|
||||||
|
// .from_str()
|
||||||
|
// .unwrapped()
|
||||||
|
// .map(Token::Int);
|
||||||
|
let int = text::int(10)
|
||||||
.slice()
|
.slice()
|
||||||
.from_str()
|
.from_str()
|
||||||
.unwrapped()
|
.unwrapped()
|
||||||
.map(Token::Num);
|
.map(Token::Int);
|
||||||
|
|
||||||
let strn = just('"')
|
let strn = just('"')
|
||||||
.ignore_then(none_of('"').repeated())
|
.ignore_then(none_of('"').repeated())
|
||||||
|
@ -31,7 +36,7 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e
|
||||||
"false" => Token::Bool(false),
|
"false" => Token::Bool(false),
|
||||||
"let" => Token::Let,
|
"let" => Token::Let,
|
||||||
"in" => Token::In,
|
"in" => Token::In,
|
||||||
"func" => Token::Func,
|
"fn" => Token::Func,
|
||||||
"return" => Token::Return,
|
"return" => Token::Return,
|
||||||
"if" => Token::If,
|
"if" => Token::If,
|
||||||
"then" => Token::Then,
|
"then" => Token::Then,
|
||||||
|
@ -75,15 +80,20 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e
|
||||||
));
|
));
|
||||||
|
|
||||||
let token = choice((
|
let token = choice((
|
||||||
num,
|
int,
|
||||||
strn,
|
strn,
|
||||||
word,
|
word,
|
||||||
sym,
|
sym,
|
||||||
delim,
|
delim,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let comment = just("//")
|
||||||
|
.then(any().and_is(just('\n').not()).repeated())
|
||||||
|
.padded();
|
||||||
|
|
||||||
token
|
token
|
||||||
.map_with_span(move |tok, span| (tok, span))
|
.map_with_span(move |tok, span| (tok, span))
|
||||||
|
.padded_by(comment.repeated())
|
||||||
.padded()
|
.padded()
|
||||||
// If we get an error, skip to the next character and try again.
|
// If we get an error, skip to the next character and try again.
|
||||||
.recover_with(skip_then_retry_until(any().ignored(), end()))
|
.recover_with(skip_then_retry_until(any().ignored(), end()))
|
||||||
|
@ -114,7 +124,7 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
||||||
let lit = select! {
|
let lit = select! {
|
||||||
Token::Unit => Expr::Lit(Lit::Unit),
|
Token::Unit => Expr::Lit(Lit::Unit),
|
||||||
Token::Bool(b) => Expr::Lit(Lit::Bool(b)),
|
Token::Bool(b) => Expr::Lit(Lit::Bool(b)),
|
||||||
Token::Num(n) => Expr::Lit(Lit::Num(n)),
|
Token::Int(n) => Expr::Lit(Lit::Int(n)),
|
||||||
Token::Str(s) => Expr::Lit(Lit::Str(s)),
|
Token::Str(s) => Expr::Lit(Lit::Str(s)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -132,20 +142,25 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
||||||
)
|
)
|
||||||
.map(|e: Spanned<Expr>| e.0);
|
.map(|e: Spanned<Expr>| e.0);
|
||||||
|
|
||||||
// \x : t, y : t -> rt = e
|
// func (x t, y t) : rt = e
|
||||||
let lambda = just(Token::Lambda)
|
// func x, y = e
|
||||||
|
let lambda = just(Token::Func)
|
||||||
.ignore_then(
|
.ignore_then(
|
||||||
(
|
symbol
|
||||||
symbol.then(
|
.map(|s| (s, None))
|
||||||
just(Token::Colon)
|
.or(symbol
|
||||||
.ignore_then(type_parser())
|
.then(type_parser())
|
||||||
.or_not())
|
.delimited_by(
|
||||||
).separated_by(just(Token::Comma))
|
just(Token::Open(Delim::Paren)),
|
||||||
.allow_trailing()
|
just(Token::Close(Delim::Paren)),
|
||||||
|
)
|
||||||
|
.map(|(s, t)| (s, Some(t)))
|
||||||
|
)
|
||||||
|
.repeated()
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
just(Token::Arrow)
|
just(Token::Colon)
|
||||||
.ignore_then(type_parser())
|
.ignore_then(type_parser())
|
||||||
.or_not()
|
.or_not()
|
||||||
)
|
)
|
||||||
|
@ -219,7 +234,8 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
||||||
.or(if_)
|
.or(if_)
|
||||||
.or(block)
|
.or(block)
|
||||||
.map_with_span(|e, s| (e, s))
|
.map_with_span(|e, s| (e, s))
|
||||||
.boxed();
|
.boxed()
|
||||||
|
.labelled("(atomic) expression");
|
||||||
|
|
||||||
let call = atom
|
let call = atom
|
||||||
.then(
|
.then(
|
||||||
|
@ -322,7 +338,7 @@ pub fn type_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
||||||
recursive(|ty| {
|
recursive(|ty| {
|
||||||
let lit_ty = select! {
|
let lit_ty = select! {
|
||||||
Token::Ident("Bool") => Type::Bool,
|
Token::Ident("Bool") => Type::Bool,
|
||||||
Token::Ident("Num") => Type::Num,
|
Token::Ident("Int") => Type::Int,
|
||||||
Token::Ident("Str") => Type::Str,
|
Token::Ident("Str") => Type::Str,
|
||||||
// TODO: Support type variables in both the parser and the type checker.
|
// TODO: Support type variables in both the parser and the type checker.
|
||||||
Token::Ident(_) => Type::Var(69),
|
Token::Ident(_) => Type::Var(69),
|
||||||
|
@ -362,9 +378,11 @@ pub fn type_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
||||||
})
|
})
|
||||||
.map(Type::Tuple);
|
.map(Type::Tuple);
|
||||||
|
|
||||||
let array = just(Token::Open(Delim::Brack))
|
let array = ty.clone()
|
||||||
.ignore_then(ty.clone())
|
.delimited_by(
|
||||||
.then_ignore(just(Token::Close(Delim::Brack)))
|
just(Token::Open(Delim::Brack)),
|
||||||
|
just(Token::Close(Delim::Brack)),
|
||||||
|
)
|
||||||
.map(|t| Type::Array(Box::new(t)));
|
.map(|t| Type::Array(Box::new(t)));
|
||||||
|
|
||||||
lit_ty
|
lit_ty
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt::{self, Display, Formatter};
|
||||||
// TODO: Introduce lifetime here to reduce cloning.
|
// TODO: Introduce lifetime here to reduce cloning.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Unit, Bool, Num, Str,
|
Unit, Bool, Int, Str,
|
||||||
Var(usize), // This type is only used during type inference.
|
Var(usize), // This type is only used during type inference.
|
||||||
Func(Vec<Type>, Box<Type>),
|
Func(Vec<Type>, Box<Type>),
|
||||||
Tuple(Vec<Type>),
|
Tuple(Vec<Type>),
|
||||||
|
@ -15,7 +15,7 @@ impl Display for Type {
|
||||||
match *self {
|
match *self {
|
||||||
Type::Unit => write!(f, "Unit"),
|
Type::Unit => write!(f, "Unit"),
|
||||||
Type::Bool => write!(f, "Bool"),
|
Type::Bool => write!(f, "Bool"),
|
||||||
Type::Num => write!(f, "Num"),
|
Type::Int => write!(f, "Int"),
|
||||||
Type::Str => write!(f, "Str"),
|
Type::Str => write!(f, "Str"),
|
||||||
Type::Var(id) => write!(f, "{}", itoa(id)),
|
Type::Var(id) => write!(f, "{}", itoa(id)),
|
||||||
Type::Func(ref args, ref ret) => {
|
Type::Func(ref args, ref ret) => {
|
||||||
|
|
12
test.hlm
12
test.hlm
|
@ -1 +1,11 @@
|
||||||
let f = \f, g, h, a, b, c, d = ;
|
let addi = fn x y = x + y;
|
||||||
|
|
||||||
|
let factorial = fn x =
|
||||||
|
if x == 1
|
||||||
|
then x
|
||||||
|
else x * factorial(x - 1);
|
||||||
|
|
||||||
|
let result = factorial(addi(2, 3));
|
||||||
|
|
||||||
|
let println = fn x = ();
|
||||||
|
println(result);
|
||||||
|
|
18
test.ssa
Normal file
18
test.ssa
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
my_add x y:
|
||||||
|
0:
|
||||||
|
v0 = iadd x y
|
||||||
|
ret v0
|
||||||
|
|
||||||
|
factorial x:
|
||||||
|
0:
|
||||||
|
v0 = eq x 1
|
||||||
|
jf v0 1
|
||||||
|
ret x
|
||||||
|
1:
|
||||||
|
v0 = isub x 1
|
||||||
|
v1 = call factorial v0
|
||||||
|
v2 = imul x v1
|
||||||
|
ret v2
|
||||||
|
|
||||||
|
v0 = call my_add 2 3
|
||||||
|
v1 = call factorial v0
|
4
test2.hlm
Normal file
4
test2.hlm
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
let factorial = fn x =
|
||||||
|
if x == 1
|
||||||
|
then x
|
||||||
|
else x * factorial(x - 1);
|
|
@ -68,7 +68,7 @@ impl<'src> Infer<'src> {
|
||||||
fn occurs(&self, i: usize, t: Type) -> bool {
|
fn occurs(&self, i: usize, t: Type) -> bool {
|
||||||
use Type::*;
|
use Type::*;
|
||||||
match t {
|
match t {
|
||||||
Unit | Bool | Num | Str => false,
|
Unit | Bool | Int | Str => false,
|
||||||
Var(j) => {
|
Var(j) => {
|
||||||
if let Some(t) = self.subst(j) {
|
if let Some(t) = self.subst(j) {
|
||||||
if t != Var(j) {
|
if t != Var(j) {
|
||||||
|
@ -92,7 +92,7 @@ impl<'src> Infer<'src> {
|
||||||
// Literal types
|
// Literal types
|
||||||
(Unit, Unit)
|
(Unit, Unit)
|
||||||
| (Bool, Bool)
|
| (Bool, Bool)
|
||||||
| (Num, Num)
|
| (Int, Int)
|
||||||
| (Str, Str) => Ok(()),
|
| (Str, Str) => Ok(()),
|
||||||
|
|
||||||
// Variable
|
// Variable
|
||||||
|
@ -298,9 +298,9 @@ impl<'src> Infer<'src> {
|
||||||
self.add_constraint(expected, Type::Bool, span);
|
self.add_constraint(expected, Type::Bool, span);
|
||||||
ok!(TExpr::Lit(Lit::Bool(b)))
|
ok!(TExpr::Lit(Lit::Bool(b)))
|
||||||
}
|
}
|
||||||
Lit::Num(i) => {
|
Lit::Int(i) => {
|
||||||
self.add_constraint(expected, Type::Num, span);
|
self.add_constraint(expected, Type::Int, span);
|
||||||
ok!(TExpr::Lit(Lit::Num(i)))
|
ok!(TExpr::Lit(Lit::Int(i)))
|
||||||
}
|
}
|
||||||
Lit::Str(s) => {
|
Lit::Str(s) => {
|
||||||
self.add_constraint(expected, Type::Str, span);
|
self.add_constraint(expected, Type::Str, span);
|
||||||
|
@ -326,14 +326,14 @@ impl<'src> Infer<'src> {
|
||||||
// The type of the left and right hand side are inferred and
|
// The type of the left and right hand side are inferred and
|
||||||
// the expected type is determined by the operator
|
// the expected type is determined by the operator
|
||||||
Expr::Unary(op, e) => match op {
|
Expr::Unary(op, e) => match op {
|
||||||
// Numeric operators (Num -> Num)
|
// Numeric operators (Int -> Int)
|
||||||
UnaryOp::Neg => {
|
UnaryOp::Neg => {
|
||||||
let (te, err) = self.infer(unbox!(e), Type::Num);
|
let (te, err) = self.infer(unbox!(e), Type::Int);
|
||||||
self.add_constraint(expected, Type::Num, span);
|
self.add_constraint(expected, Type::Int, span);
|
||||||
(TExpr::Unary {
|
(TExpr::Unary {
|
||||||
op,
|
op,
|
||||||
expr: (Box::new(te), span),
|
expr: (Box::new(te), span),
|
||||||
ret_ty: Type::Num,
|
ret_ty: Type::Int,
|
||||||
}, err)
|
}, err)
|
||||||
},
|
},
|
||||||
// Boolean operators (Bool -> Bool)
|
// Boolean operators (Bool -> Bool)
|
||||||
|
@ -348,22 +348,22 @@ impl<'src> Infer<'src> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Expr::Binary(op, lhs, rhs) => match op {
|
Expr::Binary(op, lhs, rhs) => match op {
|
||||||
// Numeric operators (Num -> Num -> Num)
|
// Numeric operators (Int -> Int -> Int)
|
||||||
BinaryOp::Add
|
BinaryOp::Add
|
||||||
| BinaryOp::Sub
|
| BinaryOp::Sub
|
||||||
| BinaryOp::Mul
|
| BinaryOp::Mul
|
||||||
| BinaryOp::Div
|
| BinaryOp::Div
|
||||||
| BinaryOp::Rem
|
| BinaryOp::Rem
|
||||||
=> {
|
=> {
|
||||||
let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Num);
|
let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Int);
|
||||||
let (rt, errs1) = self.infer(unbox!(rhs), Type::Num);
|
let (rt, errs1) = self.infer(unbox!(rhs), Type::Int);
|
||||||
errs0.extend(errs1);
|
errs0.extend(errs1);
|
||||||
self.add_constraint(expected, Type::Num, span);
|
self.add_constraint(expected, Type::Int, span);
|
||||||
(TExpr::Binary {
|
(TExpr::Binary {
|
||||||
op,
|
op,
|
||||||
lhs: (Box::new(lt), lhs.1),
|
lhs: (Box::new(lt), lhs.1),
|
||||||
rhs: (Box::new(rt), rhs.1),
|
rhs: (Box::new(rt), rhs.1),
|
||||||
ret_ty: Type::Num,
|
ret_ty: Type::Int,
|
||||||
}, errs0)
|
}, errs0)
|
||||||
},
|
},
|
||||||
// Boolean operators (Bool -> Bool -> Bool)
|
// Boolean operators (Bool -> Bool -> Bool)
|
||||||
|
@ -528,8 +528,8 @@ impl<'src> Infer<'src> {
|
||||||
},
|
},
|
||||||
Expr::Define { name, ty, value } => {
|
Expr::Define { name, ty, value } => {
|
||||||
let ty = ty.unwrap_or(self.fresh());
|
let ty = ty.unwrap_or(self.fresh());
|
||||||
let (val_ty, errs) = self.infer(unbox!(value), ty.clone());
|
|
||||||
self.env.insert(name.clone(), ty.clone());
|
self.env.insert(name.clone(), ty.clone());
|
||||||
|
let (val_ty, errs) = self.infer(unbox!(value), ty.clone());
|
||||||
|
|
||||||
self.constraints.push((expected, Type::Unit, e.1));
|
self.constraints.push((expected, Type::Unit, e.1));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue