mirror of
https://github.com/edg-l/edlang.git
synced 2024-11-23 00:18:24 +00:00
type info improvements
This commit is contained in:
parent
81b57d646d
commit
bab8eec51f
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -415,9 +415,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "1.0.10"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -461,7 +461,7 @@ dependencies = [
|
||||||
"petgraph",
|
"petgraph",
|
||||||
"pico-args",
|
"pico-args",
|
||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.7.1",
|
"regex-syntax 0.7.2",
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"term",
|
"term",
|
||||||
"tiny-keccak",
|
"tiny-keccak",
|
||||||
|
@ -491,9 +491,9 @@ checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.3.7"
|
version = "0.3.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
|
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "llvm-sys"
|
name = "llvm-sys"
|
||||||
|
@ -688,18 +688,18 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.58"
|
version = "1.0.59"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
|
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.27"
|
version = "1.0.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -726,13 +726,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.8.1"
|
version = "1.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax 0.7.1",
|
"regex-syntax 0.7.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -752,9 +752,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
|
@ -836,9 +836,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.16"
|
version = "2.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
|
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -969,9 +969,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
|
|
|
@ -16,7 +16,7 @@ clap = { version = "4.3.0", features = ["derive"] }
|
||||||
color-eyre = "0.6.2"
|
color-eyre = "0.6.2"
|
||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
lalrpop-util = { version = "0.20.0", features = ["lexer"] }
|
lalrpop-util = { version = "0.20.0", features = ["lexer"] }
|
||||||
regex = "1.8.1"
|
regex = "1.8.3"
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||||
inkwell = { version = "0.2.0", features = ["llvm16-0"] }
|
inkwell = { version = "0.2.0", features = ["llvm16-0"] }
|
||||||
|
|
29
simple.ed
29
simple.ed
|
@ -1,13 +1,18 @@
|
||||||
fn main(x: i64, z: i64) -> i64 {
|
struct Hello {
|
||||||
let y: i64 = 0;
|
x: i32,
|
||||||
if x == 5 {
|
y: i32,
|
||||||
if x == z {
|
}
|
||||||
y = 2 * x;
|
|
||||||
} else {
|
fn test(x: Hello) {
|
||||||
y = z;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
y = 3 * x;
|
fn works(x: i32) -> i32 {
|
||||||
}
|
return x * 4;
|
||||||
return y;
|
}
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
let y = 2;
|
||||||
|
let z = y;
|
||||||
|
return works(z);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Spanned<T> {
|
||||||
|
pub span: (usize, usize),
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Spanned<T> {
|
||||||
|
pub fn new(value: T, span: (usize, usize)) -> Self {
|
||||||
|
Self { value, span }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum OpCode {
|
pub enum OpCode {
|
||||||
Add,
|
Add,
|
||||||
|
@ -27,20 +39,26 @@ impl OpCode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum TypeExp {
|
||||||
|
Integer { bits: u32, signed: bool },
|
||||||
|
Boolean,
|
||||||
|
Array { of: Box<Self>, len: Option<u32> },
|
||||||
|
Pointer { target: Box<Self> },
|
||||||
|
Other { id: String },
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum LiteralValue {
|
pub enum LiteralValue {
|
||||||
String,
|
String(String),
|
||||||
Integer {
|
Integer(String),
|
||||||
bits: Option<u32>,
|
Boolean(bool),
|
||||||
signed: bool,
|
|
||||||
value: String,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Literal(LiteralValue),
|
Literal(LiteralValue),
|
||||||
Variable(String),
|
Variable(Spanned<String>),
|
||||||
Call {
|
Call {
|
||||||
function: String,
|
function: String,
|
||||||
args: Vec<Box<Self>>,
|
args: Vec<Box<Self>>,
|
||||||
|
@ -51,12 +69,12 @@ pub enum Expression {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Parameter {
|
pub struct Parameter {
|
||||||
pub ident: String,
|
pub ident: String,
|
||||||
pub type_name: String,
|
pub type_exp: TypeExp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parameter {
|
impl Parameter {
|
||||||
pub const fn new(ident: String, type_name: String) -> Self {
|
pub const fn new(ident: String, type_exp: TypeExp) -> Self {
|
||||||
Self { ident, type_name }
|
Self { ident, type_exp }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +83,7 @@ pub struct Function {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub params: Vec<Parameter>,
|
pub params: Vec<Parameter>,
|
||||||
pub body: Vec<Statement>,
|
pub body: Vec<Statement>,
|
||||||
pub return_type: Option<String>,
|
pub return_type: Option<TypeExp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
|
@ -73,7 +91,7 @@ impl Function {
|
||||||
name: String,
|
name: String,
|
||||||
params: Vec<Parameter>,
|
params: Vec<Parameter>,
|
||||||
body: Vec<Statement>,
|
body: Vec<Statement>,
|
||||||
return_type: Option<String>,
|
return_type: Option<TypeExp>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
@ -84,16 +102,39 @@ impl Function {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct StructField {
|
||||||
|
pub ident: String,
|
||||||
|
pub type_exp: TypeExp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructField {
|
||||||
|
pub const fn new(ident: String, type_name: TypeExp) -> Self {
|
||||||
|
Self {
|
||||||
|
ident,
|
||||||
|
type_exp: type_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Struct {
|
||||||
|
pub name: String,
|
||||||
|
pub fields: Vec<StructField>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Let {
|
Let {
|
||||||
name: String,
|
name: String,
|
||||||
value: Box<Expression>,
|
value: Box<Expression>,
|
||||||
type_name: Option<String>,
|
type_name: Option<TypeExp>,
|
||||||
|
span: (usize, usize),
|
||||||
},
|
},
|
||||||
Mutate {
|
Mutate {
|
||||||
name: String,
|
name: String,
|
||||||
value: Box<Expression>,
|
value: Box<Expression>,
|
||||||
|
span: (usize, usize),
|
||||||
},
|
},
|
||||||
If {
|
If {
|
||||||
condition: Box<Expression>,
|
condition: Box<Expression>,
|
||||||
|
@ -102,6 +143,7 @@ pub enum Statement {
|
||||||
},
|
},
|
||||||
Return(Option<Box<Expression>>),
|
Return(Option<Box<Expression>>),
|
||||||
Function(Function),
|
Function(Function),
|
||||||
|
Struct(Struct),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
100
src/check.rs
100
src/check.rs
|
@ -1,4 +1,3 @@
|
||||||
/*
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{self, Statement},
|
ast::{self, Statement},
|
||||||
codegen::ProgramData,
|
codegen::ProgramData,
|
||||||
|
@ -19,8 +18,37 @@ pub fn check<'a>(data: &'a ProgramData, ast: &ast::Program) -> Vec<Check<'a>> {
|
||||||
let mut errors = vec![];
|
let mut errors = vec![];
|
||||||
|
|
||||||
for statement in &ast.statements {
|
for statement in &ast.statements {
|
||||||
match &statement.value {
|
match &statement {
|
||||||
Statement::Assignment(_x) => {
|
Statement::Let { name: _, span, .. } => {
|
||||||
|
// can't have a top level assignment yet.
|
||||||
|
let snippet = Snippet {
|
||||||
|
title: Some(Annotation {
|
||||||
|
id: None,
|
||||||
|
label: Some("unexpected let at top level"),
|
||||||
|
annotation_type: AnnotationType::Error,
|
||||||
|
}),
|
||||||
|
footer: vec![],
|
||||||
|
slices: vec![Slice {
|
||||||
|
source: &data.source,
|
||||||
|
line_start: 1,
|
||||||
|
fold: true,
|
||||||
|
origin: None,
|
||||||
|
annotations: vec![SourceAnnotation {
|
||||||
|
label: "unexpected statement",
|
||||||
|
annotation_type: AnnotationType::Error,
|
||||||
|
range: *span,
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
opt: FormatOptions {
|
||||||
|
color: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let dl = DisplayList::from(snippet);
|
||||||
|
errors.push(Check::Error(dl));
|
||||||
|
}
|
||||||
|
Statement::Mutate { span, .. } => {
|
||||||
// can't have a top level assignment yet.
|
// can't have a top level assignment yet.
|
||||||
let snippet = Snippet {
|
let snippet = Snippet {
|
||||||
title: Some(Annotation {
|
title: Some(Annotation {
|
||||||
|
@ -33,11 +61,11 @@ pub fn check<'a>(data: &'a ProgramData, ast: &ast::Program) -> Vec<Check<'a>> {
|
||||||
source: &data.source,
|
source: &data.source,
|
||||||
line_start: 1,
|
line_start: 1,
|
||||||
fold: true,
|
fold: true,
|
||||||
origin: Some(&data.filename),
|
origin: None,
|
||||||
annotations: vec![SourceAnnotation {
|
annotations: vec![SourceAnnotation {
|
||||||
label: "unexpected statement",
|
label: "unexpected statement",
|
||||||
annotation_type: AnnotationType::Error,
|
annotation_type: AnnotationType::Error,
|
||||||
range: statement.span.into(),
|
range: *span,
|
||||||
}],
|
}],
|
||||||
}],
|
}],
|
||||||
opt: FormatOptions {
|
opt: FormatOptions {
|
||||||
|
@ -49,68 +77,8 @@ pub fn check<'a>(data: &'a ProgramData, ast: &ast::Program) -> Vec<Check<'a>> {
|
||||||
let dl = DisplayList::from(snippet);
|
let dl = DisplayList::from(snippet);
|
||||||
errors.push(Check::Error(dl));
|
errors.push(Check::Error(dl));
|
||||||
}
|
}
|
||||||
Statement::Definition(_) => {
|
_ => {}
|
||||||
// can't have a top level assignment yet.
|
|
||||||
let snippet = Snippet {
|
|
||||||
title: Some(Annotation {
|
|
||||||
id: None,
|
|
||||||
label: Some("unexpected definition at top level"),
|
|
||||||
annotation_type: AnnotationType::Error,
|
|
||||||
}),
|
|
||||||
footer: vec![],
|
|
||||||
slices: vec![Slice {
|
|
||||||
source: &data.source,
|
|
||||||
line_start: 1,
|
|
||||||
fold: true,
|
|
||||||
origin: Some(&data.filename),
|
|
||||||
annotations: vec![SourceAnnotation {
|
|
||||||
label: "unexpected statement",
|
|
||||||
annotation_type: AnnotationType::Error,
|
|
||||||
range: statement.span.into(),
|
|
||||||
}],
|
|
||||||
}],
|
|
||||||
opt: FormatOptions {
|
|
||||||
color: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let dl = DisplayList::from(snippet);
|
|
||||||
errors.push(Check::Error(dl));
|
|
||||||
}
|
|
||||||
Statement::Return(_x) => {
|
|
||||||
// can't have a top level assignment yet.
|
|
||||||
let snippet = Snippet {
|
|
||||||
title: Some(Annotation {
|
|
||||||
id: None,
|
|
||||||
label: Some("unexpected return"),
|
|
||||||
annotation_type: AnnotationType::Error,
|
|
||||||
}),
|
|
||||||
footer: vec![],
|
|
||||||
slices: vec![Slice {
|
|
||||||
source: &data.source,
|
|
||||||
line_start: 1,
|
|
||||||
fold: true,
|
|
||||||
origin: Some(&data.filename),
|
|
||||||
annotations: vec![SourceAnnotation {
|
|
||||||
label: "unexpected return",
|
|
||||||
annotation_type: AnnotationType::Error,
|
|
||||||
range: statement.span.into(),
|
|
||||||
}],
|
|
||||||
}],
|
|
||||||
opt: FormatOptions {
|
|
||||||
color: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let dl = DisplayList::from(snippet);
|
|
||||||
errors.push(Check::Error(dl));
|
|
||||||
}
|
|
||||||
Statement::Function(_function) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
490
src/codegen.rs
490
src/codegen.rs
|
@ -9,13 +9,14 @@ use inkwell::{
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
context::Context,
|
context::Context,
|
||||||
module::Module,
|
module::Module,
|
||||||
types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum},
|
types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum, StructType},
|
||||||
values::{BasicMetadataValueEnum, BasicValue, BasicValueEnum},
|
values::{BasicMetadataValueEnum, BasicValue, BasicValueEnum, FunctionValue},
|
||||||
IntPredicate,
|
IntPredicate,
|
||||||
};
|
};
|
||||||
use itertools::{Either, Itertools};
|
use itertools::{Either, Itertools};
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
use crate::ast::{self, Expression, Function, LiteralValue, OpCode, Statement};
|
use crate::ast::{self, Expression, Function, LiteralValue, OpCode, Statement, TypeExp};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ProgramData {
|
pub struct ProgramData {
|
||||||
|
@ -36,13 +37,32 @@ pub struct CodeGen<'ctx> {
|
||||||
context: &'ctx Context,
|
context: &'ctx Context,
|
||||||
pub module: Module<'ctx>,
|
pub module: Module<'ctx>,
|
||||||
builder: Builder<'ctx>,
|
builder: Builder<'ctx>,
|
||||||
fn_types: VariableTypes<'ctx>,
|
types: TypeStorage<'ctx>,
|
||||||
|
struct_types: StructTypeStorage<'ctx>,
|
||||||
|
// function to return type
|
||||||
|
functions: HashMap<String, (Vec<TypeExp>, Option<TypeExp>)>,
|
||||||
_program: ProgramData,
|
_program: ProgramData,
|
||||||
ast: ast::Program,
|
ast: ast::Program,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Variables<'ctx> = HashMap<String, (BasicValueEnum<'ctx>, usize)>;
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
type VariableTypes<'ctx> = HashMap<String, BasicTypeEnum<'ctx>>;
|
pub struct Variable<'ctx> {
|
||||||
|
pub value: BasicValueEnum<'ctx>,
|
||||||
|
pub phi_counter: usize,
|
||||||
|
pub type_exp: TypeExp,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Variables<'ctx> = HashMap<String, Variable<'ctx>>;
|
||||||
|
pub type TypeStorage<'ctx> = HashMap<TypeExp, BasicTypeEnum<'ctx>>;
|
||||||
|
|
||||||
|
/// Holds the struct type and maps fields to types and the location within the struct.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct StructTypeInfo<'ctx> {
|
||||||
|
ty: StructType<'ctx>,
|
||||||
|
fields: HashMap<String, (usize, TypeExp)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type StructTypeStorage<'ctx> = HashMap<String, StructTypeInfo<'ctx>>;
|
||||||
|
|
||||||
impl<'ctx> CodeGen<'ctx> {
|
impl<'ctx> CodeGen<'ctx> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -59,7 +79,9 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
builder: context.create_builder(),
|
builder: context.create_builder(),
|
||||||
_program,
|
_program,
|
||||||
ast,
|
ast,
|
||||||
fn_types: HashMap::new(),
|
types: HashMap::new(),
|
||||||
|
struct_types: HashMap::new(),
|
||||||
|
functions: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(codegen)
|
Ok(codegen)
|
||||||
|
@ -67,29 +89,69 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
|
|
||||||
pub fn compile_ast(&mut self) -> Result<()> {
|
pub fn compile_ast(&mut self) -> Result<()> {
|
||||||
let mut functions = vec![];
|
let mut functions = vec![];
|
||||||
let mut types: VariableTypes<'ctx> = HashMap::new();
|
let mut func_info = HashMap::new();
|
||||||
|
let mut types: TypeStorage<'ctx> = HashMap::new();
|
||||||
|
let mut struct_types: StructTypeStorage<'ctx> = HashMap::new();
|
||||||
|
|
||||||
// todo fix the grammar so top level statements are only functions and static vars.
|
// todo fix the grammar so top level statements are only functions and static vars.
|
||||||
|
|
||||||
// create the llvm functions first.
|
// create types
|
||||||
|
|
||||||
for statement in &self.ast.statements {
|
for statement in &self.ast.statements {
|
||||||
match &statement {
|
if let Statement::Struct(s) = &statement {
|
||||||
Statement::Let { .. } => unreachable!(),
|
let mut fields = HashMap::new();
|
||||||
Statement::Mutate { .. } => unreachable!(),
|
let mut field_types = vec![];
|
||||||
Statement::Return(_) => unreachable!(),
|
|
||||||
Statement::If { .. } => unreachable!(),
|
for (i, field) in s.fields.iter().enumerate() {
|
||||||
Statement::Function(function) => {
|
if !types.contains_key(&field.type_exp) {
|
||||||
functions.push(function);
|
types.insert(field.type_exp.clone(), self.get_llvm_type(&field.type_exp)?);
|
||||||
let ret_type = self.compile_function_signature(function)?;
|
|
||||||
if let Some(ret_type) = ret_type {
|
|
||||||
types.insert(function.name.clone(), ret_type);
|
|
||||||
}
|
}
|
||||||
|
let ty = self.get_llvm_type(&field.type_exp)?;
|
||||||
|
field_types.push(ty);
|
||||||
|
// todo: ensure alignment and padding here
|
||||||
|
fields.insert(field.ident.clone(), (i, field.type_exp.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty = self.context.struct_type(&field_types, false);
|
||||||
|
|
||||||
|
let struct_type = StructTypeInfo { fields, ty };
|
||||||
|
struct_types.insert(s.name.clone(), struct_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.struct_types = struct_types;
|
||||||
|
|
||||||
|
// create the llvm functions first.
|
||||||
|
for statement in &self.ast.statements {
|
||||||
|
if let Statement::Function(function) = &statement {
|
||||||
|
functions.push(function);
|
||||||
|
let (args, ret_type) = self.compile_function_signature(function)?;
|
||||||
|
let mut arg_types = vec![];
|
||||||
|
for arg in args {
|
||||||
|
if !types.contains_key(&arg) {
|
||||||
|
let ty = self.get_llvm_type(&arg)?;
|
||||||
|
types.insert(arg.clone(), ty);
|
||||||
|
}
|
||||||
|
arg_types.push(arg);
|
||||||
|
}
|
||||||
|
if let Some(ret_type) = ret_type {
|
||||||
|
let ret_type = if !types.contains_key(&ret_type) {
|
||||||
|
let ty = self.get_llvm_type(&ret_type)?;
|
||||||
|
types.insert(ret_type.clone(), ty);
|
||||||
|
ret_type
|
||||||
|
} else {
|
||||||
|
ret_type
|
||||||
|
};
|
||||||
|
func_info.insert(function.name.clone(), (arg_types, Some(ret_type)));
|
||||||
|
} else {
|
||||||
|
func_info.insert(function.name.clone(), (arg_types, None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fn_types = types;
|
self.types = types;
|
||||||
|
self.functions = func_info;
|
||||||
|
|
||||||
|
info!("functions:\n{:#?}", self.functions);
|
||||||
|
|
||||||
// implement them.
|
// implement them.
|
||||||
for function in functions {
|
for function in functions {
|
||||||
|
@ -106,42 +168,67 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
self.module.print_to_string().to_str().unwrap().to_string()
|
self.module.print_to_string().to_str().unwrap().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_llvm_type(&self, id: &str) -> Result<BasicTypeEnum<'ctx>> {
|
fn get_llvm_type(&self, id: &TypeExp) -> Result<BasicTypeEnum<'ctx>> {
|
||||||
|
if let Some(ty) = self.types.get(id) {
|
||||||
|
Ok(*ty)
|
||||||
|
} else {
|
||||||
Ok(match id {
|
Ok(match id {
|
||||||
"i64" => self.context.i64_type().as_basic_type_enum(),
|
TypeExp::Integer { bits, signed: _ } => self
|
||||||
"i32" => self.context.i32_type().as_basic_type_enum(),
|
.context
|
||||||
"i8" => self.context.i8_type().as_basic_type_enum(),
|
.custom_width_int_type(*bits)
|
||||||
"u8" => self.context.i8_type().as_basic_type_enum(),
|
.as_basic_type_enum(),
|
||||||
_ => todo!(),
|
TypeExp::Boolean => self.context.bool_type().as_basic_type_enum(),
|
||||||
|
TypeExp::Array { of, len } => {
|
||||||
|
let ty = self.get_llvm_type(of)?;
|
||||||
|
ty.array_type(len.unwrap()).as_basic_type_enum()
|
||||||
|
}
|
||||||
|
TypeExp::Pointer { target } => {
|
||||||
|
let ty = self.get_llvm_type(target)?;
|
||||||
|
ty.ptr_type(Default::default()).as_basic_type_enum()
|
||||||
|
}
|
||||||
|
TypeExp::Other { id } => self
|
||||||
|
.struct_types
|
||||||
|
.get(id)
|
||||||
|
.expect("struct type not found")
|
||||||
|
.ty
|
||||||
|
.as_basic_type_enum(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// creates the llvm function without the body, so other function bodies can call it.
|
/// creates the llvm function without the body, so other function bodies can call it.
|
||||||
fn compile_function_signature(
|
fn compile_function_signature(
|
||||||
&self,
|
&self,
|
||||||
function: &Function,
|
function: &Function,
|
||||||
) -> Result<Option<BasicTypeEnum<'ctx>>> {
|
) -> Result<(Vec<TypeExp>, Option<TypeExp>)> {
|
||||||
let args_types: Vec<BasicTypeEnum<'ctx>> = function
|
let args_types: Vec<BasicTypeEnum<'ctx>> = function
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|param| param.type_name.as_str())
|
.map(|param| ¶m.type_exp)
|
||||||
.map(|t| self.get_llvm_type(t))
|
.map(|t| self.get_llvm_type(t))
|
||||||
.try_collect()?;
|
.try_collect()?;
|
||||||
|
|
||||||
let args_types: Vec<BasicMetadataTypeEnum<'ctx>> =
|
let args_types: Vec<BasicMetadataTypeEnum<'ctx>> =
|
||||||
args_types.into_iter().map(|t| t.into()).collect_vec();
|
args_types.into_iter().map(|t| t.into()).collect_vec();
|
||||||
|
|
||||||
let fn_type = match &function.return_type {
|
let (fn_type, ret_type) = match &function.return_type {
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
let return_type = self.get_llvm_type(id)?;
|
let return_type = self.get_llvm_type(id)?;
|
||||||
return_type.fn_type(&args_types, false)
|
(return_type.fn_type(&args_types, false), Some(id.clone()))
|
||||||
}
|
}
|
||||||
None => self.context.void_type().fn_type(&args_types, false),
|
None => (self.context.void_type().fn_type(&args_types, false), None),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.module.add_function(&function.name, fn_type, None);
|
self.module.add_function(&function.name, fn_type, None);
|
||||||
|
|
||||||
Ok(fn_type.get_return_type())
|
Ok((
|
||||||
|
function
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.map(|param| param.type_exp.clone())
|
||||||
|
.collect(),
|
||||||
|
ret_type,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_function(&self, function: &Function) -> Result<()> {
|
fn compile_function(&self, function: &Function) -> Result<()> {
|
||||||
|
@ -151,15 +238,21 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
self.builder.position_at_end(entry_block);
|
self.builder.position_at_end(entry_block);
|
||||||
|
|
||||||
let mut variables: Variables = HashMap::new();
|
let mut variables: Variables = HashMap::new();
|
||||||
let mut types: VariableTypes = HashMap::new();
|
let mut types: TypeStorage = self.types.clone();
|
||||||
|
|
||||||
for (i, param) in function.params.iter().enumerate() {
|
for (i, param) in function.params.iter().enumerate() {
|
||||||
let id = param.ident.clone();
|
let id = ¶m.ident;
|
||||||
let param = func
|
let param_value = func
|
||||||
.get_nth_param(i.try_into().unwrap())
|
.get_nth_param(i.try_into().unwrap())
|
||||||
.expect("parameter");
|
.expect("parameter");
|
||||||
variables.insert(id.clone(), (param, 0));
|
variables.insert(
|
||||||
types.insert(id.clone(), param.get_type());
|
id.clone(),
|
||||||
|
Variable {
|
||||||
|
value: param_value,
|
||||||
|
phi_counter: 0,
|
||||||
|
type_exp: param.type_exp.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut has_return = false;
|
let mut has_return = false;
|
||||||
|
@ -168,7 +261,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
if let Statement::Return(_) = statement {
|
if let Statement::Return(_) = statement {
|
||||||
has_return = true
|
has_return = true
|
||||||
}
|
}
|
||||||
self.compile_statement(statement, &mut variables, &mut types)?;
|
self.compile_statement(function, func, statement, &mut variables, &mut types)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_return {
|
if !has_return {
|
||||||
|
@ -178,34 +271,46 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_expr_type(
|
fn find_expr_type(&self, expr: &Expression, variables: &Variables<'ctx>) -> Option<TypeExp> {
|
||||||
&self,
|
|
||||||
expr: &Expression,
|
|
||||||
types: &VariableTypes<'ctx>,
|
|
||||||
) -> Option<BasicTypeEnum<'ctx>> {
|
|
||||||
match expr {
|
match expr {
|
||||||
Expression::Literal(x) => match x {
|
Expression::Literal(x) => match x {
|
||||||
LiteralValue::String => todo!(),
|
LiteralValue::String(_s) => {
|
||||||
LiteralValue::Integer {
|
todo!("make internal string struct")
|
||||||
bits,
|
/* todo: internal string structure here
|
||||||
signed,
|
Some(
|
||||||
value,
|
self.context
|
||||||
} => bits.map(|bits| self.context.custom_width_int_type(bits).into()),
|
.i8_type()
|
||||||
|
.array_type(s.bytes().len() as u32 + 1)
|
||||||
|
.as_basic_type_enum(),
|
||||||
|
) */
|
||||||
|
}
|
||||||
|
LiteralValue::Integer(_) => Some(TypeExp::Integer {
|
||||||
|
bits: 32,
|
||||||
|
signed: true,
|
||||||
|
}),
|
||||||
|
LiteralValue::Boolean(_) => Some(TypeExp::Boolean),
|
||||||
|
},
|
||||||
|
Expression::Variable(x) => variables.get(&x.value).cloned().map(|x| x.type_exp),
|
||||||
|
Expression::Call { function, args: _ } => {
|
||||||
|
self.functions.get(function).unwrap().clone().1
|
||||||
|
}
|
||||||
|
Expression::BinaryOp(lhs, op, rhs) => match op {
|
||||||
|
OpCode::Eq | OpCode::Ne => Some(TypeExp::Boolean),
|
||||||
|
_ => self
|
||||||
|
.find_expr_type(lhs, variables)
|
||||||
|
.or_else(|| self.find_expr_type(rhs, variables)),
|
||||||
},
|
},
|
||||||
Expression::Variable(x) => types.get(x).cloned(),
|
|
||||||
Expression::Call { function, args } => types.get(function).cloned(),
|
|
||||||
Expression::BinaryOp(lhs, op, rhs) => self
|
|
||||||
.find_expr_type(lhs, types)
|
|
||||||
.or_else(|| self.find_expr_type(rhs, types)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_statement(
|
fn compile_statement(
|
||||||
&self,
|
&self,
|
||||||
|
function: &Function,
|
||||||
|
function_value: FunctionValue,
|
||||||
statement: &Statement,
|
statement: &Statement,
|
||||||
// value, assignments
|
// value, assignments
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
types: &mut VariableTypes<'ctx>,
|
types: &mut TypeStorage<'ctx>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match statement {
|
match statement {
|
||||||
// Variable assignment
|
// Variable assignment
|
||||||
|
@ -213,37 +318,65 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
type_name,
|
type_name,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
let type_hint = if let Some(type_name) = type_name {
|
let type_hint = if let Some(type_name) = type_name {
|
||||||
self.get_llvm_type(type_name)?
|
type_name.clone()
|
||||||
} else {
|
} else {
|
||||||
self.find_expr_type(value, types)
|
let type_exp = self
|
||||||
.expect("type should be found")
|
.find_expr_type(value, variables)
|
||||||
|
.expect("type should be found");
|
||||||
|
let ty = self.get_llvm_type(&type_exp)?;
|
||||||
|
types.insert(type_exp.clone(), ty);
|
||||||
|
type_exp
|
||||||
};
|
};
|
||||||
types.insert(name.clone(), type_hint);
|
|
||||||
|
|
||||||
let result = self
|
let (value, value_type) = self
|
||||||
.compile_expression(value, variables, types, Some(type_hint))?
|
.compile_expression(value, variables, types, Some(type_hint))?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
|
|
||||||
variables.insert(name.clone(), (result, 0));
|
if !types.contains_key(&value_type) {
|
||||||
|
let ty = self.get_llvm_type(&value_type)?;
|
||||||
|
types.insert(value_type.clone(), ty);
|
||||||
}
|
}
|
||||||
Statement::Mutate { name, value } => {
|
|
||||||
let type_hint = *types.get(name).expect("should exist");
|
info!("adding variable: name={}, ty={:?}", name, value_type);
|
||||||
let result = self
|
|
||||||
|
variables.insert(
|
||||||
|
name.clone(),
|
||||||
|
Variable {
|
||||||
|
value,
|
||||||
|
phi_counter: 0,
|
||||||
|
type_exp: value_type,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Statement::Mutate { name, value, .. } => {
|
||||||
|
let var = variables.get(name).cloned().expect("variable should exist");
|
||||||
|
let type_hint = var.type_exp;
|
||||||
|
|
||||||
|
let (value, value_type) = self
|
||||||
.compile_expression(value, variables, types, Some(type_hint))?
|
.compile_expression(value, variables, types, Some(type_hint))?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
|
|
||||||
let (old_val, acc) = variables.get(name).expect("variable should exist");
|
let var = variables.get_mut(name).expect("variable should exist");
|
||||||
variables.insert(name.clone(), (result, acc + 1));
|
var.phi_counter += 1;
|
||||||
|
var.value = value;
|
||||||
|
assert_eq!(var.type_exp, value_type, "variable type shouldn't change!");
|
||||||
|
info!("mutated variable: name={}, ty={:?}", name, var.type_exp);
|
||||||
}
|
}
|
||||||
Statement::Return(ret) => {
|
Statement::Return(ret) => {
|
||||||
if let Some(ret) = ret {
|
if let Some(ret) = ret {
|
||||||
let type_hint = self.find_expr_type(ret, types);
|
let type_hint = self
|
||||||
let result = self
|
.functions
|
||||||
|
.get(&function.name)
|
||||||
|
.expect("function should exist")
|
||||||
|
.clone()
|
||||||
|
.1;
|
||||||
|
let (value, _value_type) = self
|
||||||
.compile_expression(ret, variables, types, type_hint)?
|
.compile_expression(ret, variables, types, type_hint)?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
self.builder.build_return(Some(&result));
|
self.builder.build_return(Some(&value));
|
||||||
} else {
|
} else {
|
||||||
self.builder.build_return(None);
|
self.builder.build_return(None);
|
||||||
}
|
}
|
||||||
|
@ -253,21 +386,13 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
body,
|
body,
|
||||||
else_body,
|
else_body,
|
||||||
} => {
|
} => {
|
||||||
let type_hint_cond = self.find_expr_type(condition, types);
|
let (condition, _cond_type) = self
|
||||||
let condition = self
|
.compile_expression(condition, variables, types, Some(TypeExp::Boolean))?
|
||||||
.compile_expression(condition, variables, types, type_hint_cond)?
|
|
||||||
.expect("should produce a value");
|
.expect("should produce a value");
|
||||||
|
|
||||||
let func = self
|
let mut if_block = self.context.append_basic_block(function_value, "if");
|
||||||
.builder
|
let mut else_block = self.context.append_basic_block(function_value, "else");
|
||||||
.get_insert_block()
|
let merge_block = self.context.append_basic_block(function_value, "merge");
|
||||||
.unwrap()
|
|
||||||
.get_parent()
|
|
||||||
.expect("parent should exist");
|
|
||||||
|
|
||||||
let mut if_block = self.context.append_basic_block(func, "if");
|
|
||||||
let mut else_block = self.context.append_basic_block(func, "else");
|
|
||||||
let merge_block = self.context.append_basic_block(func, "merge");
|
|
||||||
|
|
||||||
self.builder.build_conditional_branch(
|
self.builder.build_conditional_branch(
|
||||||
condition.into_int_value(),
|
condition.into_int_value(),
|
||||||
|
@ -282,7 +407,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
let mut variables_if = variables.clone();
|
let mut variables_if = variables.clone();
|
||||||
self.builder.position_at_end(if_block);
|
self.builder.position_at_end(if_block);
|
||||||
for s in body {
|
for s in body {
|
||||||
self.compile_statement(s, &mut variables_if, types)?;
|
self.compile_statement(function, function_value, s, &mut variables_if, types)?;
|
||||||
}
|
}
|
||||||
self.builder.build_unconditional_branch(merge_block);
|
self.builder.build_unconditional_branch(merge_block);
|
||||||
if_block = self.builder.get_insert_block().unwrap(); // update for phi
|
if_block = self.builder.get_insert_block().unwrap(); // update for phi
|
||||||
|
@ -292,7 +417,13 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
self.builder.position_at_end(else_block);
|
self.builder.position_at_end(else_block);
|
||||||
|
|
||||||
for s in else_body {
|
for s in else_body {
|
||||||
self.compile_statement(s, &mut variables_else, types)?;
|
self.compile_statement(
|
||||||
|
function,
|
||||||
|
function_value,
|
||||||
|
s,
|
||||||
|
&mut variables_else,
|
||||||
|
types,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
self.builder.build_unconditional_branch(merge_block);
|
self.builder.build_unconditional_branch(merge_block);
|
||||||
else_block = self.builder.get_insert_block().unwrap(); // update for phi
|
else_block = self.builder.get_insert_block().unwrap(); // update for phi
|
||||||
|
@ -301,43 +432,52 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
self.builder.position_at_end(merge_block);
|
self.builder.position_at_end(merge_block);
|
||||||
|
|
||||||
let mut processed_vars = HashMap::new();
|
let mut processed_vars = HashMap::new();
|
||||||
for (name, (value, acc)) in variables_if {
|
for (name, new_var) in variables_if {
|
||||||
if variables.contains_key(&name) {
|
if variables.contains_key(&name) {
|
||||||
let (old_val, old_acc) = variables.get(&name).unwrap();
|
let old_var = variables.get(&name).unwrap();
|
||||||
if acc > *old_acc {
|
if new_var.phi_counter > old_var.phi_counter {
|
||||||
let phi = self
|
let phi = self
|
||||||
.builder
|
.builder
|
||||||
.build_phi(old_val.get_type(), &format!("{name}_phi"));
|
.build_phi(old_var.value.get_type(), &format!("{name}_phi"));
|
||||||
phi.add_incoming(&[(&value, if_block)]);
|
phi.add_incoming(&[(&new_var.value, if_block)]);
|
||||||
processed_vars.insert(name, (value, phi));
|
processed_vars.insert(name, (phi, new_var.type_exp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if else_body.is_some() {
|
if else_body.is_some() {
|
||||||
for (name, (value, acc)) in variables_else {
|
for (name, new_var) in variables_else {
|
||||||
if variables.contains_key(&name) {
|
if variables.contains_key(&name) {
|
||||||
let (old_val, old_acc) = variables.get(&name).unwrap();
|
let old_var = variables.get(&name).unwrap();
|
||||||
if acc > *old_acc {
|
if new_var.phi_counter > old_var.phi_counter {
|
||||||
if let Some((_, phi)) = processed_vars.get(&name) {
|
if let Some((phi, _)) = processed_vars.get(&name) {
|
||||||
phi.add_incoming(&[(&value, else_block)]);
|
phi.add_incoming(&[(&new_var.value, else_block)]);
|
||||||
} else {
|
} else {
|
||||||
let phi = self
|
let phi = self.builder.build_phi(
|
||||||
.builder
|
old_var.value.get_type(),
|
||||||
.build_phi(old_val.get_type(), &format!("{name}_phi"));
|
&format!("{name}_phi"),
|
||||||
phi.add_incoming(&[(&value, else_block)]);
|
);
|
||||||
processed_vars.insert(name, (value, phi));
|
phi.add_incoming(&[(&old_var.value, else_block)]);
|
||||||
|
processed_vars.insert(name, (phi, new_var.type_exp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, (_, phi)) in processed_vars {
|
for (name, (phi, type_exp)) in processed_vars {
|
||||||
variables.insert(name, (phi.as_basic_value(), 0));
|
variables.insert(
|
||||||
|
name,
|
||||||
|
Variable {
|
||||||
|
value: phi.as_basic_value(),
|
||||||
|
phi_counter: 0,
|
||||||
|
type_exp,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Function(_function) => unreachable!(),
|
Statement::Function(_) => unreachable!(),
|
||||||
|
Statement::Struct(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -347,11 +487,11 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
&self,
|
&self,
|
||||||
expr: &Expression,
|
expr: &Expression,
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
types: &mut VariableTypes<'ctx>,
|
types: &mut TypeStorage<'ctx>,
|
||||||
type_hint: Option<BasicTypeEnum<'ctx>>,
|
type_hint: Option<TypeExp>,
|
||||||
) -> Result<Option<BasicValueEnum<'ctx>>> {
|
) -> Result<Option<(BasicValueEnum<'ctx>, TypeExp)>> {
|
||||||
Ok(match expr {
|
Ok(match expr {
|
||||||
Expression::Variable(term) => Some(self.compile_variable(term, variables, types)?),
|
Expression::Variable(term) => Some(self.compile_variable(&term.value, variables)?),
|
||||||
Expression::Literal(term) => Some(self.compile_literal(term, type_hint)?),
|
Expression::Literal(term) => Some(self.compile_literal(term, type_hint)?),
|
||||||
Expression::Call { function, args } => {
|
Expression::Call { function, args } => {
|
||||||
self.compile_call(function, args, variables, types)?
|
self.compile_call(function, args, variables, types)?
|
||||||
|
@ -367,16 +507,21 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
func_name: &str,
|
func_name: &str,
|
||||||
args: &[Box<Expression>],
|
args: &[Box<Expression>],
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
types: &mut VariableTypes<'ctx>,
|
types: &mut TypeStorage<'ctx>,
|
||||||
) -> Result<Option<BasicValueEnum<'ctx>>> {
|
) -> Result<Option<(BasicValueEnum<'ctx>, TypeExp)>> {
|
||||||
|
info!("compiling fn call: func_name={}", func_name);
|
||||||
let function = self.module.get_function(func_name).expect("should exist");
|
let function = self.module.get_function(func_name).expect("should exist");
|
||||||
|
let func_info = self
|
||||||
|
.functions
|
||||||
|
.get(func_name)
|
||||||
|
.cloned()
|
||||||
|
.expect("should exist");
|
||||||
|
|
||||||
let mut value_args: Vec<BasicMetadataValueEnum> = Vec::with_capacity(args.len());
|
let mut value_args: Vec<BasicMetadataValueEnum> = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for arg in args {
|
for (arg, arg_type) in args.iter().zip(func_info.0.iter()) {
|
||||||
let type_enum = self.find_expr_type(arg, types);
|
let (res, _res_type) = self
|
||||||
let res = self
|
.compile_expression(arg, variables, types, Some(arg_type.clone()))?
|
||||||
.compile_expression(arg, variables, types, type_enum)?
|
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
value_args.push(res.into());
|
value_args.push(res.into());
|
||||||
}
|
}
|
||||||
|
@ -387,7 +532,10 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
.try_as_basic_value();
|
.try_as_basic_value();
|
||||||
|
|
||||||
Ok(match result {
|
Ok(match result {
|
||||||
Either::Left(val) => Some(val),
|
Either::Left(val) => Some((
|
||||||
|
val,
|
||||||
|
func_info.1.expect("should have ret type info if returns"),
|
||||||
|
)),
|
||||||
Either::Right(_) => None,
|
Either::Right(_) => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -398,18 +546,20 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
op: &OpCode,
|
op: &OpCode,
|
||||||
rhs: &Expression,
|
rhs: &Expression,
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
types: &mut VariableTypes<'ctx>,
|
types: &mut TypeStorage<'ctx>,
|
||||||
type_hint: Option<BasicTypeEnum<'ctx>>,
|
type_hint: Option<TypeExp>,
|
||||||
) -> Result<BasicValueEnum<'ctx>> {
|
) -> Result<(BasicValueEnum<'ctx>, TypeExp)> {
|
||||||
let lhs = self
|
let (lhs, lhs_type) = self
|
||||||
.compile_expression(lhs, variables, types, type_hint)?
|
.compile_expression(lhs, variables, types, type_hint.clone())?
|
||||||
.expect("should have result")
|
.expect("should have result");
|
||||||
.into_int_value();
|
let (rhs, _rhs_type) = self
|
||||||
let rhs = self
|
|
||||||
.compile_expression(rhs, variables, types, type_hint)?
|
.compile_expression(rhs, variables, types, type_hint)?
|
||||||
.expect("should have result")
|
.expect("should have result");
|
||||||
.into_int_value();
|
|
||||||
|
|
||||||
|
let lhs = lhs.into_int_value();
|
||||||
|
let rhs = rhs.into_int_value();
|
||||||
|
|
||||||
|
let mut bool_result = false;
|
||||||
let result = match op {
|
let result = match op {
|
||||||
OpCode::Add => self.builder.build_int_add(lhs, rhs, "add"),
|
OpCode::Add => self.builder.build_int_add(lhs, rhs, "add"),
|
||||||
OpCode::Sub => self.builder.build_int_sub(lhs, rhs, "sub"),
|
OpCode::Sub => self.builder.build_int_sub(lhs, rhs, "sub"),
|
||||||
|
@ -418,41 +568,72 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
OpCode::Rem => self.builder.build_int_signed_rem(lhs, rhs, "rem"),
|
OpCode::Rem => self.builder.build_int_signed_rem(lhs, rhs, "rem"),
|
||||||
OpCode::And => self.builder.build_and(lhs, rhs, "and"),
|
OpCode::And => self.builder.build_and(lhs, rhs, "and"),
|
||||||
OpCode::Or => self.builder.build_or(lhs, rhs, "or"),
|
OpCode::Or => self.builder.build_or(lhs, rhs, "or"),
|
||||||
OpCode::Eq => self
|
OpCode::Eq => {
|
||||||
.builder
|
bool_result = true;
|
||||||
.build_int_compare(IntPredicate::EQ, lhs, rhs, "eq"),
|
self.builder
|
||||||
OpCode::Ne => self
|
.build_int_compare(IntPredicate::EQ, lhs, rhs, "eq")
|
||||||
.builder
|
}
|
||||||
.build_int_compare(IntPredicate::NE, lhs, rhs, "eq"),
|
OpCode::Ne => {
|
||||||
|
bool_result = true;
|
||||||
|
self.builder
|
||||||
|
.build_int_compare(IntPredicate::NE, lhs, rhs, "eq")
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result.as_basic_value_enum())
|
let mut res_type = lhs_type;
|
||||||
|
|
||||||
|
if bool_result {
|
||||||
|
res_type = TypeExp::Integer {
|
||||||
|
bits: 1,
|
||||||
|
signed: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((result.as_basic_value_enum(), res_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_literal(
|
pub fn compile_literal(
|
||||||
&self,
|
&self,
|
||||||
term: &LiteralValue,
|
term: &LiteralValue,
|
||||||
type_hint: Option<BasicTypeEnum<'ctx>>,
|
type_hint: Option<TypeExp>,
|
||||||
) -> Result<BasicValueEnum<'ctx>> {
|
) -> Result<(BasicValueEnum<'ctx>, TypeExp)> {
|
||||||
let value = match term {
|
let value = match term {
|
||||||
LiteralValue::String => todo!(),
|
LiteralValue::String(_s) => {
|
||||||
LiteralValue::Integer {
|
todo!()
|
||||||
bits,
|
/*
|
||||||
signed: _,
|
self
|
||||||
value,
|
.context
|
||||||
} => {
|
.const_string(s.as_bytes(), true)
|
||||||
if let Some(type_hint) = type_hint {
|
.as_basic_value_enum() */
|
||||||
type_hint
|
}
|
||||||
.into_int_type()
|
LiteralValue::Boolean(v) => (
|
||||||
.const_int(value.parse().unwrap(), false)
|
|
||||||
.as_basic_value_enum()
|
|
||||||
} else {
|
|
||||||
let bits = bits.unwrap_or(32);
|
|
||||||
|
|
||||||
self.context
|
self.context
|
||||||
.custom_width_int_type(bits)
|
.bool_type()
|
||||||
.const_int(value.parse().unwrap(), false)
|
.const_int((*v).into(), false)
|
||||||
.as_basic_value_enum()
|
.as_basic_value_enum(),
|
||||||
|
TypeExp::Boolean,
|
||||||
|
),
|
||||||
|
LiteralValue::Integer(v) => {
|
||||||
|
if let Some(type_hint) = type_hint {
|
||||||
|
(
|
||||||
|
self.get_llvm_type(&type_hint)?
|
||||||
|
.into_int_type()
|
||||||
|
.const_int(v.parse().unwrap(), false)
|
||||||
|
.as_basic_value_enum(),
|
||||||
|
type_hint,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let type_exp = TypeExp::Integer {
|
||||||
|
bits: 32,
|
||||||
|
signed: true,
|
||||||
|
};
|
||||||
|
(
|
||||||
|
self.get_llvm_type(&type_exp)?
|
||||||
|
.into_int_type()
|
||||||
|
.const_int(v.parse().unwrap(), false)
|
||||||
|
.as_basic_value_enum(),
|
||||||
|
type_exp,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -464,9 +645,8 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
&self,
|
&self,
|
||||||
variable: &str,
|
variable: &str,
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
types: &mut VariableTypes<'ctx>,
|
) -> Result<(BasicValueEnum<'ctx>, TypeExp)> {
|
||||||
) -> Result<BasicValueEnum<'ctx>> {
|
let var = variables.get(variable).expect("value").clone();
|
||||||
let var = *variables.get(variable).expect("value");
|
Ok((var.value, var.type_exp))
|
||||||
Ok(var.0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast,
|
ast::{self, Spanned},
|
||||||
tokens::Token,
|
tokens::Token,
|
||||||
lexer::LexicalError,
|
lexer::LexicalError,
|
||||||
};
|
};
|
||||||
|
@ -18,13 +18,20 @@ extern {
|
||||||
"if" => Token::KeywordIf,
|
"if" => Token::KeywordIf,
|
||||||
"else" => Token::KeywordElse,
|
"else" => Token::KeywordElse,
|
||||||
"identifier" => Token::Identifier(<String>),
|
"identifier" => Token::Identifier(<String>),
|
||||||
"int" => Token::Integer(<String>),
|
"int literal" => Token::Integer(<String>),
|
||||||
|
"string literal" => Token::String(<String>),
|
||||||
|
"bool literal" => Token::Boolean(<bool>),
|
||||||
"return" => Token::KeywordReturn,
|
"return" => Token::KeywordReturn,
|
||||||
"fn" => Token::KeywordFn,
|
"fn" => Token::KeywordFn,
|
||||||
|
"ptr" => Token::KeywordPtr,
|
||||||
"(" => Token::LeftParen,
|
"(" => Token::LeftParen,
|
||||||
")" => Token::RightParen,
|
")" => Token::RightParen,
|
||||||
"{" => Token::LeftBracket,
|
"{" => Token::LeftBracket,
|
||||||
"}" => Token::RightBracket,
|
"}" => Token::RightBracket,
|
||||||
|
"[" => Token::LeftSquareBracket,
|
||||||
|
"]" => Token::RightSquareBracket,
|
||||||
|
"<" => Token::LessThanSign,
|
||||||
|
">" => Token::MoreThanSign,
|
||||||
"=" => Token::Assign,
|
"=" => Token::Assign,
|
||||||
";" => Token::Semicolon,
|
";" => Token::Semicolon,
|
||||||
":" => Token::Colon,
|
":" => Token::Colon,
|
||||||
|
@ -39,6 +46,17 @@ extern {
|
||||||
"||" => Token::OperatorOr,
|
"||" => Token::OperatorOr,
|
||||||
"==" => Token::OperatorEq,
|
"==" => Token::OperatorEq,
|
||||||
"!=" => Token::OperatorNe,
|
"!=" => Token::OperatorNe,
|
||||||
|
|
||||||
|
"bool" => Token::KeywordBool,
|
||||||
|
|
||||||
|
"i8" => Token::Inti8,
|
||||||
|
"i16" => Token::Inti16,
|
||||||
|
"i32" => Token::Inti32,
|
||||||
|
"i64" => Token::Inti64,
|
||||||
|
"u8" => Token::Intu8,
|
||||||
|
"u16" => Token::Intu16,
|
||||||
|
"u32" => Token::Intu32,
|
||||||
|
"u64" => Token::Intu64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,16 +85,17 @@ Statements: Vec<ast::Statement> = {
|
||||||
Statement: ast::Statement = {
|
Statement: ast::Statement = {
|
||||||
BasicStatement,
|
BasicStatement,
|
||||||
<f:Function> => ast::Statement::Function(f),
|
<f:Function> => ast::Statement::Function(f),
|
||||||
|
<s:Struct> => ast::Statement::Struct(s),
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeInfo: String = {
|
TypeInfo: ast::TypeExp = {
|
||||||
":" <i:"identifier"> => i
|
":" <i:LangType> => i
|
||||||
};
|
};
|
||||||
|
|
||||||
// statements not including function definitions
|
// statements not including function definitions
|
||||||
BasicStatement: ast::Statement = {
|
BasicStatement: ast::Statement = {
|
||||||
"let" <i:"identifier"> <t:TypeInfo?> "=" <e:Expr> ";" => ast::Statement::Let { name: i, value: e, type_name: t},
|
<lo:@L> "let" <i:"identifier"> <t:TypeInfo?> "=" <e:Expr> ";" <hi:@R> => ast::Statement::Let { name: i, value: e, type_name: t, span: (lo, hi) },
|
||||||
<i:"identifier"> "=" <e:Expr> ";" => ast::Statement::Mutate { name: i, value: e},
|
<lo:@L> <i:"identifier"> "=" <e:Expr> ";" <hi:@R> => ast::Statement::Mutate { name: i, value: e, span: (lo, hi) },
|
||||||
"if" <cond:Expr> "{" <s:Statements> "}" <e:ElseExpr?> => ast::Statement::If { condition: cond, body: s, else_body: e},
|
"if" <cond:Expr> "{" <s:Statements> "}" <e:ElseExpr?> => ast::Statement::If { condition: cond, body: s, else_body: e},
|
||||||
"return" <e:Expr?> ";" => ast::Statement::Return(e),
|
"return" <e:Expr?> ";" => ast::Statement::Return(e),
|
||||||
};
|
};
|
||||||
|
@ -118,25 +137,67 @@ Expr4 = Tier<Level3_Op, Term>;
|
||||||
|
|
||||||
// Terms: variables, literals, calls
|
// Terms: variables, literals, calls
|
||||||
Term: Box<ast::Expression> = {
|
Term: Box<ast::Expression> = {
|
||||||
<i:"identifier"> => Box::new(ast::Expression::Variable(i)),
|
<lo:@L> <i:"identifier"> <hi:@R> => Box::new(ast::Expression::Variable(Spanned::new(i, (lo, hi)))),
|
||||||
<n:Number> => Box::new(ast::Expression::Literal(n)),
|
<n:Number> => Box::new(ast::Expression::Literal(n)),
|
||||||
|
<n:StringLit> => Box::new(ast::Expression::Literal(n)),
|
||||||
|
<n:BoolLiteral> => Box::new(ast::Expression::Literal(n)),
|
||||||
<i:"identifier"> "(" <values:Comma<Term>> ")" => Box::new(ast::Expression::Call { function: i, args: values}),
|
<i:"identifier"> "(" <values:Comma<Term>> ")" => Box::new(ast::Expression::Call { function: i, args: values}),
|
||||||
"(" <Term> ")"
|
"(" <Term> ")"
|
||||||
};
|
};
|
||||||
|
|
||||||
Number: ast::LiteralValue = <n:"int"> => ast::LiteralValue::Integer { bits: None, signed: true, value: n.to_string()};
|
Number: ast::LiteralValue = <n:"int literal"> => ast::LiteralValue::Integer(n);
|
||||||
|
|
||||||
|
StringLit: ast::LiteralValue = <n:"string literal"> => ast::LiteralValue::String(n[1..(n.len()-1)].to_string());
|
||||||
|
|
||||||
|
BoolLiteral: ast::LiteralValue = <n:"bool literal"> => ast::LiteralValue::Boolean(n);
|
||||||
|
|
||||||
|
ArrayLen: u32 = {
|
||||||
|
";" <i:"int literal"> => i.parse().unwrap(),
|
||||||
|
}
|
||||||
|
|
||||||
|
LangType: ast::TypeExp = {
|
||||||
|
"ptr" "<" <target:LangType> ">" => ast::TypeExp::Pointer { target: Box::new(target) },
|
||||||
|
"[" <of:LangType> <len:ArrayLen?> "]" => ast::TypeExp::Array { of: Box::new(of), len },
|
||||||
|
"i8" => ast::TypeExp::Integer { bits: 8, signed: true },
|
||||||
|
"i16" => ast::TypeExp::Integer { bits: 16, signed: true },
|
||||||
|
"i32" => ast::TypeExp::Integer { bits: 32, signed: true },
|
||||||
|
"i64" => ast::TypeExp::Integer { bits: 64, signed: true },
|
||||||
|
"u8" => ast::TypeExp::Integer { bits: 8, signed: false },
|
||||||
|
"u16" => ast::TypeExp::Integer { bits: 16, signed: false },
|
||||||
|
"u32" => ast::TypeExp::Integer { bits: 32, signed: false },
|
||||||
|
"u64" => ast::TypeExp::Integer { bits: 64, signed: false },
|
||||||
|
"bool" => ast::TypeExp::Boolean,
|
||||||
|
<id:"identifier"> => ast::TypeExp::Other { id },
|
||||||
|
};
|
||||||
|
|
||||||
// Function handling
|
// Function handling
|
||||||
Param: ast::Parameter = {
|
Param: ast::Parameter = {
|
||||||
<"identifier"> ":" <"identifier"> => ast::Parameter::new(<>)
|
<"identifier"> ":" <LangType> => ast::Parameter::new(<>)
|
||||||
};
|
};
|
||||||
|
|
||||||
Params = Comma<Param>;
|
Params = Comma<Param>;
|
||||||
|
|
||||||
FunctionReturn: String = {
|
FunctionReturn: ast::TypeExp = {
|
||||||
"->" <i:"identifier"> => i.to_string(),
|
"->" <i:LangType> => i,
|
||||||
}
|
}
|
||||||
|
|
||||||
Function: ast::Function = {
|
Function: ast::Function = {
|
||||||
"fn" <i:"identifier"> "(" <a:Params> ")" <r:FunctionReturn?> "{" <s:Statements> "}" => ast::Function::new(i, a, s, r)
|
"fn" <i:"identifier"> "(" <a:Params> ")" <r:FunctionReturn?> "{" <s:Statements> "}" => ast::Function::new(i, a, s, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Structures
|
||||||
|
|
||||||
|
StructField: ast::StructField = {
|
||||||
|
<"identifier"> ":" <LangType> => ast::StructField::new(<>)
|
||||||
|
};
|
||||||
|
|
||||||
|
StructFields = Comma<StructField>;
|
||||||
|
|
||||||
|
Struct: ast::Struct = {
|
||||||
|
"struct" <i:"identifier"> "{" <fields:StructFields> "}" => {
|
||||||
|
ast::Struct {
|
||||||
|
name: i,
|
||||||
|
fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
16
src/lexer.rs
16
src/lexer.rs
|
@ -1,12 +1,22 @@
|
||||||
|
use std::{fmt::Display, ops::Range};
|
||||||
|
|
||||||
use logos::{Logos, SpannedIter};
|
use logos::{Logos, SpannedIter};
|
||||||
|
|
||||||
use crate::tokens::Token;
|
use crate::tokens::Token;
|
||||||
|
|
||||||
pub type Spanned<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
|
pub type Spanned<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum LexicalError {
|
pub enum LexicalError {
|
||||||
InvalidToken,
|
InvalidToken(Range<usize>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for LexicalError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
LexicalError::InvalidToken(span) => write!(f, "lexical error at: {:?}", span),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Lexer<'input> {
|
pub struct Lexer<'input> {
|
||||||
|
@ -29,7 +39,7 @@ impl<'input> Iterator for Lexer<'input> {
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.token_stream.next().map(|(token, span)| match token {
|
self.token_stream.next().map(|(token, span)| match token {
|
||||||
Ok(token) => Ok((span.start, token, span.end)),
|
Ok(token) => Ok((span.start, token, span.end)),
|
||||||
Err(()) => Err(LexicalError::InvalidToken),
|
Err(()) => Err(LexicalError::InvalidToken(span)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
36
src/main.rs
36
src/main.rs
|
@ -7,7 +7,7 @@ use inkwell::{context::Context, execution_engine::JitFunction, OptimizationLevel
|
||||||
use lalrpop_util::lalrpop_mod;
|
use lalrpop_util::lalrpop_mod;
|
||||||
use std::{fs, path::PathBuf, println};
|
use std::{fs, path::PathBuf, println};
|
||||||
|
|
||||||
use crate::{ast::Program, lexer::Lexer};
|
use crate::{ast::Program, check::Check, lexer::Lexer};
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod check;
|
pub mod check;
|
||||||
|
@ -36,6 +36,11 @@ enum Commands {
|
||||||
/// The input file.
|
/// The input file.
|
||||||
input: PathBuf,
|
input: PathBuf,
|
||||||
},
|
},
|
||||||
|
/// Prints the code AST.
|
||||||
|
Ast {
|
||||||
|
/// The input file.
|
||||||
|
input: PathBuf,
|
||||||
|
},
|
||||||
/// Compile the edlang source file.
|
/// Compile the edlang source file.
|
||||||
Compile {
|
Compile {
|
||||||
/// The input file.
|
/// The input file.
|
||||||
|
@ -60,7 +65,6 @@ enum Commands {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
fn check_program(program: &ProgramData, ast: &ast::Program) -> bool {
|
fn check_program(program: &ProgramData, ast: &ast::Program) -> bool {
|
||||||
let errors = check::check(program, ast);
|
let errors = check::check(program, ast);
|
||||||
|
|
||||||
|
@ -84,7 +88,6 @@ fn check_program(program: &ProgramData, ast: &ast::Program) -> bool {
|
||||||
|
|
||||||
error_count == 0
|
error_count == 0
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
@ -93,14 +96,19 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
Commands::Check { input } => {
|
Commands::Check { input } => {
|
||||||
let code = fs::read_to_string(input)?;
|
let code = fs::read_to_string(&input)?;
|
||||||
let lexer = Lexer::new(code.as_str());
|
let lexer = Lexer::new(code.as_str());
|
||||||
let parser = grammar::ProgramParser::new();
|
let parser = grammar::ProgramParser::new();
|
||||||
let ast = parser.parse(lexer).unwrap();
|
let ast = parser.parse(lexer)?;
|
||||||
|
let program = ProgramData::new(&input, &code);
|
||||||
//let str_path = input.to_string_lossy();
|
check_program(&program, &ast);
|
||||||
//let program = ProgramData::new(&str_path, &code);
|
}
|
||||||
//check_program(&program, &ast);
|
Commands::Ast { input } => {
|
||||||
|
let code = fs::read_to_string(&input)?;
|
||||||
|
let lexer = Lexer::new(code.as_str());
|
||||||
|
let parser = grammar::ProgramParser::new();
|
||||||
|
let ast = parser.parse(lexer)?;
|
||||||
|
println!("{ast:#?}");
|
||||||
}
|
}
|
||||||
Commands::Compile {
|
Commands::Compile {
|
||||||
input,
|
input,
|
||||||
|
@ -111,17 +119,17 @@ fn main() -> Result<()> {
|
||||||
let code = fs::read_to_string(&input)?;
|
let code = fs::read_to_string(&input)?;
|
||||||
let lexer = Lexer::new(code.as_str());
|
let lexer = Lexer::new(code.as_str());
|
||||||
let parser = grammar::ProgramParser::new();
|
let parser = grammar::ProgramParser::new();
|
||||||
let ast: Program = parser.parse(lexer).unwrap();
|
let ast: Program = parser.parse(lexer)?;
|
||||||
|
|
||||||
let program = ProgramData::new(&input, &code);
|
let program = ProgramData::new(&input, &code);
|
||||||
|
|
||||||
let file_name = input.file_name().unwrap().to_string_lossy();
|
let file_name = input.file_name().unwrap().to_string_lossy();
|
||||||
|
|
||||||
//if !check_program(&program, &ast) {
|
if !check_program(&program, &ast) {
|
||||||
// return Ok(());
|
return Ok(());
|
||||||
//}
|
}
|
||||||
|
|
||||||
println!("{:#?}", ast);
|
// println!("{:#?}", ast);
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let mut codegen = codegen::CodeGen::new(&context, &file_name, program, ast)?;
|
let mut codegen = codegen::CodeGen::new(&context, &file_name, program, ast)?;
|
||||||
codegen.compile_ast()?;
|
codegen.compile_ast()?;
|
||||||
|
|
|
@ -15,6 +15,8 @@ pub enum Token {
|
||||||
KeywordReturn,
|
KeywordReturn,
|
||||||
#[token("struct")]
|
#[token("struct")]
|
||||||
KeywordStruct,
|
KeywordStruct,
|
||||||
|
#[token("ptr")]
|
||||||
|
KeywordPtr,
|
||||||
#[token("if")]
|
#[token("if")]
|
||||||
KeywordIf,
|
KeywordIf,
|
||||||
#[token("else")]
|
#[token("else")]
|
||||||
|
@ -24,6 +26,34 @@ pub enum Token {
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
#[regex(r"\d+", |lex| lex.slice().parse().ok())]
|
#[regex(r"\d+", |lex| lex.slice().parse().ok())]
|
||||||
Integer(String),
|
Integer(String),
|
||||||
|
#[regex(r#""(?:[^"]|\\")*""#, |lex| lex.slice().to_string())]
|
||||||
|
String(String),
|
||||||
|
#[regex(r"(true|false)", |lex| lex.slice().parse().ok())]
|
||||||
|
Boolean(bool),
|
||||||
|
|
||||||
|
#[token("bool")]
|
||||||
|
KeywordBool,
|
||||||
|
#[token("i8")]
|
||||||
|
Inti8,
|
||||||
|
#[token("i16")]
|
||||||
|
Inti16,
|
||||||
|
#[token("i32")]
|
||||||
|
Inti32,
|
||||||
|
#[token("i64")]
|
||||||
|
Inti64,
|
||||||
|
#[token("u8")]
|
||||||
|
Intu8,
|
||||||
|
#[token("u16")]
|
||||||
|
Intu16,
|
||||||
|
#[token("u32")]
|
||||||
|
Intu32,
|
||||||
|
#[token("u64")]
|
||||||
|
Intu64,
|
||||||
|
|
||||||
|
#[token("f32")]
|
||||||
|
Float32,
|
||||||
|
#[token("f64")]
|
||||||
|
Float64,
|
||||||
|
|
||||||
#[token("(")]
|
#[token("(")]
|
||||||
LeftParen,
|
LeftParen,
|
||||||
|
@ -33,6 +63,10 @@ pub enum Token {
|
||||||
LeftBracket,
|
LeftBracket,
|
||||||
#[token("}")]
|
#[token("}")]
|
||||||
RightBracket,
|
RightBracket,
|
||||||
|
#[token("[")]
|
||||||
|
LeftSquareBracket,
|
||||||
|
#[token("]")]
|
||||||
|
RightSquareBracket,
|
||||||
#[token("=")]
|
#[token("=")]
|
||||||
Assign,
|
Assign,
|
||||||
#[token(";")]
|
#[token(";")]
|
||||||
|
@ -43,6 +77,10 @@ pub enum Token {
|
||||||
Arrow,
|
Arrow,
|
||||||
#[token(",")]
|
#[token(",")]
|
||||||
Coma,
|
Coma,
|
||||||
|
#[token("<")]
|
||||||
|
LessThanSign,
|
||||||
|
#[token(">")]
|
||||||
|
MoreThanSign,
|
||||||
|
|
||||||
#[token("+")]
|
#[token("+")]
|
||||||
OperatorAdd,
|
OperatorAdd,
|
||||||
|
|
Loading…
Reference in a new issue