mirror of
https://github.com/edg-l/edlang.git
synced 2024-11-23 00:18:24 +00:00
spans
This commit is contained in:
parent
024a06defa
commit
64dd61be3a
246
out
246
out
|
@ -1,246 +0,0 @@
|
||||||
Program {
|
|
||||||
statements: [
|
|
||||||
Struct(
|
|
||||||
Struct {
|
|
||||||
name: "Hello",
|
|
||||||
fields: [
|
|
||||||
StructField {
|
|
||||||
ident: "x",
|
|
||||||
type_exp: Integer {
|
|
||||||
bits: 32,
|
|
||||||
signed: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
StructField {
|
|
||||||
ident: "y",
|
|
||||||
type_exp: Integer {
|
|
||||||
bits: 32,
|
|
||||||
signed: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Function(
|
|
||||||
Function {
|
|
||||||
name: "test",
|
|
||||||
params: [
|
|
||||||
Parameter {
|
|
||||||
ident: "x",
|
|
||||||
type_exp: Other {
|
|
||||||
id: "Hello",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
body: [
|
|
||||||
Return(
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
return_type: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Function(
|
|
||||||
Function {
|
|
||||||
name: "works",
|
|
||||||
params: [
|
|
||||||
Parameter {
|
|
||||||
ident: "x",
|
|
||||||
type_exp: Integer {
|
|
||||||
bits: 64,
|
|
||||||
signed: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
body: [
|
|
||||||
Let {
|
|
||||||
name: "z",
|
|
||||||
value: Literal(
|
|
||||||
Integer {
|
|
||||||
value: "0",
|
|
||||||
bits: None,
|
|
||||||
signed: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
value_type: None,
|
|
||||||
span: (
|
|
||||||
107,
|
|
||||||
117,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
If {
|
|
||||||
condition: BinaryOp(
|
|
||||||
Literal(
|
|
||||||
Integer {
|
|
||||||
value: "2",
|
|
||||||
bits: None,
|
|
||||||
signed: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Eq,
|
|
||||||
Variable {
|
|
||||||
name: Spanned {
|
|
||||||
span: (
|
|
||||||
130,
|
|
||||||
131,
|
|
||||||
),
|
|
||||||
value: "x",
|
|
||||||
},
|
|
||||||
value_type: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
body: [
|
|
||||||
Mutate {
|
|
||||||
name: "z",
|
|
||||||
value: BinaryOp(
|
|
||||||
Variable {
|
|
||||||
name: Spanned {
|
|
||||||
span: (
|
|
||||||
146,
|
|
||||||
147,
|
|
||||||
),
|
|
||||||
value: "x",
|
|
||||||
},
|
|
||||||
value_type: None,
|
|
||||||
},
|
|
||||||
Mul,
|
|
||||||
Literal(
|
|
||||||
Integer {
|
|
||||||
value: "2",
|
|
||||||
bits: None,
|
|
||||||
signed: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
value_type: None,
|
|
||||||
span: (
|
|
||||||
142,
|
|
||||||
152,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
else_body: Some(
|
|
||||||
[
|
|
||||||
Mutate {
|
|
||||||
name: "z",
|
|
||||||
value: BinaryOp(
|
|
||||||
Variable {
|
|
||||||
name: Spanned {
|
|
||||||
span: (
|
|
||||||
178,
|
|
||||||
179,
|
|
||||||
),
|
|
||||||
value: "x",
|
|
||||||
},
|
|
||||||
value_type: None,
|
|
||||||
},
|
|
||||||
Mul,
|
|
||||||
Literal(
|
|
||||||
Integer {
|
|
||||||
value: "3",
|
|
||||||
bits: None,
|
|
||||||
signed: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
value_type: None,
|
|
||||||
span: (
|
|
||||||
174,
|
|
||||||
184,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Return(
|
|
||||||
Some(
|
|
||||||
Variable {
|
|
||||||
name: Spanned {
|
|
||||||
span: (
|
|
||||||
202,
|
|
||||||
203,
|
|
||||||
),
|
|
||||||
value: "z",
|
|
||||||
},
|
|
||||||
value_type: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
return_type: Some(
|
|
||||||
Integer {
|
|
||||||
bits: 64,
|
|
||||||
signed: true,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Function(
|
|
||||||
Function {
|
|
||||||
name: "main",
|
|
||||||
params: [],
|
|
||||||
body: [
|
|
||||||
Let {
|
|
||||||
name: "y",
|
|
||||||
value: Literal(
|
|
||||||
Integer {
|
|
||||||
value: "2",
|
|
||||||
bits: None,
|
|
||||||
signed: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
value_type: None,
|
|
||||||
span: (
|
|
||||||
231,
|
|
||||||
241,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Let {
|
|
||||||
name: "z",
|
|
||||||
value: Variable {
|
|
||||||
name: Spanned {
|
|
||||||
span: (
|
|
||||||
254,
|
|
||||||
255,
|
|
||||||
),
|
|
||||||
value: "y",
|
|
||||||
},
|
|
||||||
value_type: None,
|
|
||||||
},
|
|
||||||
value_type: None,
|
|
||||||
span: (
|
|
||||||
246,
|
|
||||||
256,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Return(
|
|
||||||
Some(
|
|
||||||
Call {
|
|
||||||
function: "works",
|
|
||||||
args: [
|
|
||||||
Variable {
|
|
||||||
name: Spanned {
|
|
||||||
span: (
|
|
||||||
274,
|
|
||||||
275,
|
|
||||||
),
|
|
||||||
value: "z",
|
|
||||||
},
|
|
||||||
value_type: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
value_type: None,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
return_type: Some(
|
|
||||||
Integer {
|
|
||||||
bits: 64,
|
|
||||||
signed: true,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
}
|
|
|
@ -43,11 +43,21 @@ impl OpCode {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum TypeExp {
|
pub enum TypeExp {
|
||||||
Integer { bits: u32, signed: bool },
|
Integer {
|
||||||
|
bits: u32,
|
||||||
|
signed: bool,
|
||||||
|
},
|
||||||
Boolean,
|
Boolean,
|
||||||
Array { of: Box<Self>, len: Option<u32> },
|
Array {
|
||||||
Pointer { target: Box<Self> },
|
of: Spanned<Box<Self>>,
|
||||||
Other { id: String },
|
len: Option<u32>,
|
||||||
|
},
|
||||||
|
Pointer {
|
||||||
|
target: Spanned<Box<Self>>,
|
||||||
|
},
|
||||||
|
Other {
|
||||||
|
id: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -65,42 +75,42 @@ pub enum LiteralValue {
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Literal(LiteralValue),
|
Literal(LiteralValue),
|
||||||
Variable {
|
Variable {
|
||||||
name: Spanned<String>,
|
name: String,
|
||||||
},
|
},
|
||||||
Call {
|
Call {
|
||||||
function: String,
|
function: Spanned<String>,
|
||||||
args: Vec<Box<Self>>,
|
args: Vec<Spanned<Box<Self>>>,
|
||||||
},
|
},
|
||||||
BinaryOp(Box<Self>, OpCode, Box<Self>),
|
BinaryOp(Spanned<Box<Self>>, OpCode, Spanned<Box<Self>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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: Spanned<String>,
|
||||||
pub type_exp: TypeExp,
|
pub type_exp: Spanned<TypeExp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parameter {
|
impl Parameter {
|
||||||
pub const fn new(ident: String, type_exp: TypeExp) -> Self {
|
pub const fn new(ident: Spanned<String>, type_exp: Spanned<TypeExp>) -> Self {
|
||||||
Self { ident, type_exp }
|
Self { ident, type_exp }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub name: String,
|
pub name: Spanned<String>,
|
||||||
pub params: Vec<Parameter>,
|
pub params: Vec<Parameter>,
|
||||||
pub body: Vec<Statement>,
|
pub body: Vec<Spanned<Statement>>,
|
||||||
pub scope_type_info: HashMap<String, Vec<TypeExp>>,
|
pub scope_type_info: HashMap<String, Vec<TypeExp>>,
|
||||||
pub return_type: Option<TypeExp>,
|
pub return_type: Option<Spanned<TypeExp>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
name: String,
|
name: Spanned<String>,
|
||||||
params: Vec<Parameter>,
|
params: Vec<Parameter>,
|
||||||
body: Vec<Statement>,
|
body: Vec<Spanned<Statement>>,
|
||||||
return_type: Option<TypeExp>,
|
return_type: Option<Spanned<TypeExp>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
@ -114,12 +124,12 @@ impl Function {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct StructField {
|
pub struct StructField {
|
||||||
pub ident: String,
|
pub ident: Spanned<String>,
|
||||||
pub field_type: TypeExp,
|
pub field_type: Spanned<TypeExp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructField {
|
impl StructField {
|
||||||
pub const fn new(ident: String, type_name: TypeExp) -> Self {
|
pub const fn new(ident: Spanned<String>, type_name: Spanned<TypeExp>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ident,
|
ident,
|
||||||
field_type: type_name,
|
field_type: type_name,
|
||||||
|
@ -129,42 +139,40 @@ impl StructField {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Struct {
|
pub struct Struct {
|
||||||
pub name: String,
|
pub name: Spanned<String>,
|
||||||
pub fields: Vec<StructField>,
|
pub fields: Vec<StructField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Let {
|
Let {
|
||||||
name: String,
|
name: Spanned<String>,
|
||||||
value: Box<Expression>,
|
value: Spanned<Box<Expression>>,
|
||||||
value_type: Option<TypeExp>,
|
value_type: Option<Spanned<TypeExp>>,
|
||||||
span: (usize, usize),
|
|
||||||
},
|
},
|
||||||
Mutate {
|
Mutate {
|
||||||
name: String,
|
name: Spanned<String>,
|
||||||
value: Box<Expression>,
|
value: Spanned<Box<Expression>>,
|
||||||
span: (usize, usize),
|
|
||||||
},
|
},
|
||||||
If {
|
If {
|
||||||
condition: Box<Expression>,
|
condition: Spanned<Box<Expression>>,
|
||||||
body: Vec<Statement>,
|
body: Vec<Spanned<Statement>>,
|
||||||
scope_type_info: HashMap<String, Vec<TypeExp>>,
|
scope_type_info: HashMap<String, Vec<TypeExp>>,
|
||||||
else_body: Option<Vec<Statement>>,
|
else_body: Option<Vec<Spanned<Statement>>>,
|
||||||
else_body_scope_type_info: HashMap<String, Vec<TypeExp>>,
|
else_body_scope_type_info: HashMap<String, Vec<TypeExp>>,
|
||||||
},
|
},
|
||||||
Return(Option<Box<Expression>>),
|
Return(Option<Spanned<Box<Expression>>>),
|
||||||
Function(Function),
|
Function(Function),
|
||||||
Struct(Struct),
|
Struct(Struct),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub statements: Vec<Statement>,
|
pub statements: Vec<Spanned<Statement>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
pub fn new(statements: Vec<Statement>) -> Self {
|
pub fn new(statements: Vec<Spanned<Statement>>) -> Self {
|
||||||
Self { statements }
|
Self { statements }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
31
src/check.rs
31
src/check.rs
|
@ -22,8 +22,8 @@ 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 {
|
match &statement.value {
|
||||||
Statement::Let { name: _, span, .. } => {
|
Statement::Let { name, .. } => {
|
||||||
// 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 {
|
||||||
|
@ -40,7 +40,7 @@ pub fn check<'a>(data: &'a ProgramData, ast: &ast::Program) -> Vec<Check<'a>> {
|
||||||
annotations: vec![SourceAnnotation {
|
annotations: vec![SourceAnnotation {
|
||||||
label: "unexpected statement",
|
label: "unexpected statement",
|
||||||
annotation_type: AnnotationType::Error,
|
annotation_type: AnnotationType::Error,
|
||||||
range: *span,
|
range: name.span,
|
||||||
}],
|
}],
|
||||||
}],
|
}],
|
||||||
opt: FormatOptions {
|
opt: FormatOptions {
|
||||||
|
@ -52,7 +52,7 @@ 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::Mutate { span, .. } => {
|
Statement::Mutate { name, .. } => {
|
||||||
// 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 {
|
||||||
|
@ -69,7 +69,7 @@ pub fn check<'a>(data: &'a ProgramData, ast: &ast::Program) -> Vec<Check<'a>> {
|
||||||
annotations: vec![SourceAnnotation {
|
annotations: vec![SourceAnnotation {
|
||||||
label: "unexpected statement",
|
label: "unexpected statement",
|
||||||
annotation_type: AnnotationType::Error,
|
annotation_type: AnnotationType::Error,
|
||||||
range: *span,
|
range: name.span,
|
||||||
}],
|
}],
|
||||||
}],
|
}],
|
||||||
opt: FormatOptions {
|
opt: FormatOptions {
|
||||||
|
@ -112,9 +112,15 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
|
||||||
let dl = DisplayList::from(snippet);
|
let dl = DisplayList::from(snippet);
|
||||||
println!("{dl}");
|
println!("{dl}");
|
||||||
}
|
}
|
||||||
ParseError::UnrecognizedEof { location, expected } => todo!(),
|
ParseError::UnrecognizedEof {
|
||||||
ParseError::UnrecognizedToken { token, expected } => todo!(),
|
location: _,
|
||||||
ParseError::ExtraToken { token } => todo!(),
|
expected: _,
|
||||||
|
} => todo!(),
|
||||||
|
ParseError::UnrecognizedToken {
|
||||||
|
token: _,
|
||||||
|
expected: _,
|
||||||
|
} => todo!(),
|
||||||
|
ParseError::ExtraToken { token: _ } => todo!(),
|
||||||
ParseError::User { error } => match error {
|
ParseError::User { error } => match error {
|
||||||
LexicalError::InvalidToken(err, range) => {
|
LexicalError::InvalidToken(err, range) => {
|
||||||
let title = format!("invalid token (lexical error): {:?}", err);
|
let title = format!("invalid token (lexical error): {:?}", err);
|
||||||
|
@ -126,7 +132,7 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
|
||||||
}),
|
}),
|
||||||
footer: vec![],
|
footer: vec![],
|
||||||
slices: vec![Slice {
|
slices: vec![Slice {
|
||||||
source: source,
|
source,
|
||||||
line_start: 1,
|
line_start: 1,
|
||||||
fold: false,
|
fold: false,
|
||||||
origin: None,
|
origin: None,
|
||||||
|
@ -149,11 +155,10 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_type_error(source: &str, err: TypeError) {
|
pub fn print_type_error(source: &str, err: TypeError) {
|
||||||
dbg!(&err);
|
|
||||||
match err {
|
match err {
|
||||||
TypeError::Mismatch {
|
TypeError::Mismatch {
|
||||||
found,
|
found: _,
|
||||||
expected,
|
expected: _,
|
||||||
span,
|
span,
|
||||||
} => {
|
} => {
|
||||||
let snippet = Snippet {
|
let snippet = Snippet {
|
||||||
|
@ -182,7 +187,7 @@ pub fn print_type_error(source: &str, err: TypeError) {
|
||||||
let dl = DisplayList::from(snippet);
|
let dl = DisplayList::from(snippet);
|
||||||
println!("{dl}");
|
println!("{dl}");
|
||||||
}
|
}
|
||||||
TypeError::UndeclaredVariable { name, span } => {
|
TypeError::UndeclaredVariable { name: _, span } => {
|
||||||
let snippet = Snippet {
|
let snippet = Snippet {
|
||||||
title: Some(Annotation {
|
title: Some(Annotation {
|
||||||
id: None,
|
id: None,
|
||||||
|
|
|
@ -16,7 +16,7 @@ use inkwell::{
|
||||||
use itertools::{Either, Itertools};
|
use itertools::{Either, Itertools};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::ast::{self, Expression, Function, LiteralValue, OpCode, Statement, TypeExp};
|
use crate::ast::{self, Expression, Function, LiteralValue, OpCode, Spanned, Statement, TypeExp};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ProgramData {
|
pub struct ProgramData {
|
||||||
|
@ -95,22 +95,25 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
|
|
||||||
// create types
|
// create types
|
||||||
for statement in &self.ast.statements {
|
for statement in &self.ast.statements {
|
||||||
if let Statement::Struct(s) = &statement {
|
if let Statement::Struct(s) = &statement.value {
|
||||||
let mut fields = HashMap::new();
|
let mut fields = HashMap::new();
|
||||||
let mut field_types = vec![];
|
let mut field_types = vec![];
|
||||||
|
|
||||||
for (i, field) in s.fields.iter().enumerate() {
|
for (i, field) in s.fields.iter().enumerate() {
|
||||||
// todo: this doesnt handle out of order structs well
|
// todo: this doesnt handle out of order structs well
|
||||||
let ty = self.get_llvm_type(&field.field_type)?;
|
let ty = self.get_llvm_type(&field.field_type.value)?;
|
||||||
field_types.push(ty);
|
field_types.push(ty);
|
||||||
// todo: ensure alignment and padding here
|
// todo: ensure alignment and padding here
|
||||||
fields.insert(field.ident.clone(), (i, field.field_type.clone()));
|
fields.insert(
|
||||||
|
field.ident.value.clone(),
|
||||||
|
(i, field.field_type.value.clone()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ty = self.context.struct_type(&field_types, false);
|
let ty = self.context.struct_type(&field_types, false);
|
||||||
|
|
||||||
let struct_type = StructTypeInfo { fields, ty };
|
let struct_type = StructTypeInfo { fields, ty };
|
||||||
struct_types.insert(s.name.clone(), struct_type);
|
struct_types.insert(s.name.value.clone(), struct_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +121,8 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
|
|
||||||
// create the llvm functions first.
|
// create the llvm functions first.
|
||||||
for statement in &self.ast.statements {
|
for statement in &self.ast.statements {
|
||||||
if let Statement::Function(function) = &statement {
|
if let Statement::Function(function) = &statement.value {
|
||||||
functions.insert(function.name.clone(), function.clone());
|
functions.insert(function.name.value.clone(), function.clone());
|
||||||
self.compile_function_signature(function)?;
|
self.compile_function_signature(function)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,11 +153,11 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
.as_basic_type_enum(),
|
.as_basic_type_enum(),
|
||||||
TypeExp::Boolean => self.context.bool_type().as_basic_type_enum(),
|
TypeExp::Boolean => self.context.bool_type().as_basic_type_enum(),
|
||||||
TypeExp::Array { of, len } => {
|
TypeExp::Array { of, len } => {
|
||||||
let ty = self.get_llvm_type(of)?;
|
let ty = self.get_llvm_type(&of.value)?;
|
||||||
ty.array_type(len.unwrap()).as_basic_type_enum()
|
ty.array_type(len.unwrap()).as_basic_type_enum()
|
||||||
}
|
}
|
||||||
TypeExp::Pointer { target } => {
|
TypeExp::Pointer { target } => {
|
||||||
let ty = self.get_llvm_type(target)?;
|
let ty = self.get_llvm_type(&target.value)?;
|
||||||
ty.ptr_type(Default::default()).as_basic_type_enum()
|
ty.ptr_type(Default::default()).as_basic_type_enum()
|
||||||
}
|
}
|
||||||
TypeExp::Other { id } => self
|
TypeExp::Other { id } => self
|
||||||
|
@ -172,7 +175,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|param| ¶m.type_exp)
|
.map(|param| ¶m.type_exp)
|
||||||
.map(|t| self.get_llvm_type(t))
|
.map(|t| self.get_llvm_type(&t.value))
|
||||||
.try_collect()?;
|
.try_collect()?;
|
||||||
|
|
||||||
let args_types: Vec<BasicMetadataTypeEnum<'ctx>> =
|
let args_types: Vec<BasicMetadataTypeEnum<'ctx>> =
|
||||||
|
@ -180,19 +183,20 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
|
|
||||||
let fn_type = match &function.return_type {
|
let fn_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.value)?;
|
||||||
return_type.fn_type(&args_types, false)
|
return_type.fn_type(&args_types, false)
|
||||||
}
|
}
|
||||||
None => self.context.void_type().fn_type(&args_types, false),
|
None => self.context.void_type().fn_type(&args_types, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.module.add_function(&function.name, fn_type, None);
|
self.module
|
||||||
|
.add_function(&function.name.value, fn_type, None);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_function(&self, function: &Function) -> Result<()> {
|
fn compile_function(&self, function: &Function) -> Result<()> {
|
||||||
let func = self.module.get_function(&function.name).unwrap();
|
let func = self.module.get_function(&function.name.value).unwrap();
|
||||||
let entry_block = self.context.append_basic_block(func, "entry");
|
let entry_block = self.context.append_basic_block(func, "entry");
|
||||||
|
|
||||||
self.builder.position_at_end(entry_block);
|
self.builder.position_at_end(entry_block);
|
||||||
|
@ -205,7 +209,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
.get_nth_param(i.try_into().unwrap())
|
.get_nth_param(i.try_into().unwrap())
|
||||||
.expect("parameter");
|
.expect("parameter");
|
||||||
variables.insert(
|
variables.insert(
|
||||||
id.clone(),
|
id.value.clone(),
|
||||||
Variable {
|
Variable {
|
||||||
value: param_value,
|
value: param_value,
|
||||||
phi_counter: 0,
|
phi_counter: 0,
|
||||||
|
@ -217,7 +221,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
let mut has_return = false;
|
let mut has_return = false;
|
||||||
|
|
||||||
for statement in &function.body {
|
for statement in &function.body {
|
||||||
if let Statement::Return(_) = statement {
|
if let Statement::Return(_) = statement.value {
|
||||||
has_return = true
|
has_return = true
|
||||||
}
|
}
|
||||||
self.compile_statement(func, statement, &mut variables, &function.scope_type_info)?;
|
self.compile_statement(func, statement, &mut variables, &function.scope_type_info)?;
|
||||||
|
@ -233,12 +237,12 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
fn compile_statement(
|
fn compile_statement(
|
||||||
&self,
|
&self,
|
||||||
function_value: FunctionValue,
|
function_value: FunctionValue,
|
||||||
statement: &Statement,
|
statement: &Spanned<Statement>,
|
||||||
// value, assignments
|
// value, assignments
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
scope_info: &HashMap<String, Vec<TypeExp>>,
|
scope_info: &HashMap<String, Vec<TypeExp>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match statement {
|
match &statement.value {
|
||||||
// Variable assignment
|
// Variable assignment
|
||||||
Statement::Let {
|
Statement::Let {
|
||||||
name,
|
name,
|
||||||
|
@ -247,11 +251,11 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let value = self
|
let value = self
|
||||||
.compile_expression(value, variables, scope_info)?
|
.compile_expression(&value, variables, scope_info)?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
|
|
||||||
variables.insert(
|
variables.insert(
|
||||||
name.clone(),
|
name.value.clone(),
|
||||||
Variable {
|
Variable {
|
||||||
value,
|
value,
|
||||||
phi_counter: 0,
|
phi_counter: 0,
|
||||||
|
@ -261,17 +265,19 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
}
|
}
|
||||||
Statement::Mutate { name, value, .. } => {
|
Statement::Mutate { name, value, .. } => {
|
||||||
let value = self
|
let value = self
|
||||||
.compile_expression(value, variables, scope_info)?
|
.compile_expression(&value, variables, scope_info)?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
|
|
||||||
let var = variables.get_mut(name).expect("variable should exist");
|
let var = variables
|
||||||
|
.get_mut(&name.value)
|
||||||
|
.expect("variable should exist");
|
||||||
var.phi_counter += 1;
|
var.phi_counter += 1;
|
||||||
var.value = value;
|
var.value = value;
|
||||||
}
|
}
|
||||||
Statement::Return(ret) => {
|
Statement::Return(ret) => {
|
||||||
if let Some(ret) = ret {
|
if let Some(ret) = ret {
|
||||||
let value = self
|
let value = self
|
||||||
.compile_expression(ret, variables, scope_info)?
|
.compile_expression(&ret, variables, scope_info)?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
self.builder.build_return(Some(&value));
|
self.builder.build_return(Some(&value));
|
||||||
} else {
|
} else {
|
||||||
|
@ -386,12 +392,12 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
|
|
||||||
pub fn compile_expression(
|
pub fn compile_expression(
|
||||||
&self,
|
&self,
|
||||||
expr: &Expression,
|
expr: &Spanned<Box<Expression>>,
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
scope_info: &HashMap<String, Vec<TypeExp>>,
|
scope_info: &HashMap<String, Vec<TypeExp>>,
|
||||||
) -> Result<Option<BasicValueEnum<'ctx>>> {
|
) -> Result<Option<BasicValueEnum<'ctx>>> {
|
||||||
Ok(match expr {
|
Ok(match &*expr.value {
|
||||||
Expression::Variable { name } => Some(self.compile_variable(&name.value, variables)?),
|
Expression::Variable { name } => Some(self.compile_variable(&name, variables)?),
|
||||||
Expression::Literal(term) => Some(self.compile_literal(term)?),
|
Expression::Literal(term) => Some(self.compile_literal(term)?),
|
||||||
Expression::Call { function, args } => {
|
Expression::Call { function, args } => {
|
||||||
self.compile_call(function, args, variables, scope_info)?
|
self.compile_call(function, args, variables, scope_info)?
|
||||||
|
@ -404,13 +410,16 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
|
|
||||||
pub fn compile_call(
|
pub fn compile_call(
|
||||||
&self,
|
&self,
|
||||||
func_name: &str,
|
func_name: &Spanned<String>,
|
||||||
args: &[Box<Expression>],
|
args: &[Spanned<Box<Expression>>],
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
scope_info: &HashMap<String, Vec<TypeExp>>,
|
scope_info: &HashMap<String, Vec<TypeExp>>,
|
||||||
) -> Result<Option<BasicValueEnum<'ctx>>> {
|
) -> Result<Option<BasicValueEnum<'ctx>>> {
|
||||||
info!("compiling fn call: func_name={}", func_name);
|
info!("compiling fn call: func_name={}", func_name.value);
|
||||||
let function = self.module.get_function(func_name).expect("should exist");
|
let function = self
|
||||||
|
.module
|
||||||
|
.get_function(&func_name.value)
|
||||||
|
.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());
|
||||||
|
|
||||||
|
@ -423,7 +432,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.builder
|
.builder
|
||||||
.build_call(function, &value_args, &format!("{func_name}_call"))
|
.build_call(function, &value_args, &format!("{}_call", func_name.value))
|
||||||
.try_as_basic_value();
|
.try_as_basic_value();
|
||||||
|
|
||||||
Ok(match result {
|
Ok(match result {
|
||||||
|
@ -434,9 +443,9 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
|
|
||||||
pub fn compile_binary_op(
|
pub fn compile_binary_op(
|
||||||
&self,
|
&self,
|
||||||
lhs: &Expression,
|
lhs: &Spanned<Box<Expression>>,
|
||||||
op: &OpCode,
|
op: &OpCode,
|
||||||
rhs: &Expression,
|
rhs: &Spanned<Box<Expression>>,
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
scope_info: &HashMap<String, Vec<TypeExp>>,
|
scope_info: &HashMap<String, Vec<TypeExp>>,
|
||||||
) -> Result<BasicValueEnum<'ctx>> {
|
) -> Result<BasicValueEnum<'ctx>> {
|
||||||
|
@ -489,10 +498,9 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
LiteralValue::Integer {
|
LiteralValue::Integer {
|
||||||
value,
|
value,
|
||||||
bits,
|
bits,
|
||||||
signed,
|
signed: _,
|
||||||
} => {
|
} => {
|
||||||
let bits = *bits;
|
let bits = *bits;
|
||||||
let signed = *signed;
|
|
||||||
|
|
||||||
self.context
|
self.context
|
||||||
.custom_width_int_type(bits)
|
.custom_width_int_type(bits)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{self, Spanned},
|
ast::{self, Spanned},
|
||||||
tokens::Token,
|
tokens::Token,
|
||||||
|
@ -76,42 +75,46 @@ pub Program: ast::Program = {
|
||||||
Statements => ast::Program::new(<>)
|
Statements => ast::Program::new(<>)
|
||||||
}
|
}
|
||||||
|
|
||||||
Statements: Vec<ast::Statement> = {
|
Statements: Vec<Spanned<ast::Statement>> = {
|
||||||
Statement => vec![<>],
|
<Statement> => vec![<>],
|
||||||
<mut s:Statements> <n:Statement> => {
|
<mut s:Statements> <n:Statement> => {
|
||||||
s.push(n);
|
s.push(n);
|
||||||
s
|
s
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Statement: ast::Statement = {
|
Statement: Spanned<ast::Statement> = {
|
||||||
BasicStatement,
|
BasicStatement,
|
||||||
<f:Function> => ast::Statement::Function(f),
|
<lo:@L> <f:Function> <hi:@R> => Spanned::new(ast::Statement::Function(f), (lo, hi)),
|
||||||
<s:Struct> => ast::Statement::Struct(s),
|
<lo:@L> <s:Struct> <hi:@R> => Spanned::new(ast::Statement::Struct(s), (lo, hi)),
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeInfo: ast::TypeExp = {
|
TypeInfo: Spanned<ast::TypeExp> = {
|
||||||
":" <i:LangType> => i
|
":" <i:LangType> => i
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Identifier: Spanned<String> = {
|
||||||
|
<lo:@L> <i:"identifier"> <hi:@R> => Spanned::new(i, (lo, hi))
|
||||||
|
}
|
||||||
|
|
||||||
// statements not including function definitions
|
// statements not including function definitions
|
||||||
BasicStatement: ast::Statement = {
|
BasicStatement: Spanned<ast::Statement> = {
|
||||||
<lo:@L> "let" <i:"identifier"> <t:TypeInfo?> "=" <e:Expr> ";" <hi:@R> =>
|
<lo:@L> "let" <i:Identifier> <t:TypeInfo?> "=" <e:Expr> ";" <hi:@R> =>
|
||||||
ast::Statement::Let { name: i, value: e, value_type: t, span: (lo, hi) },
|
Spanned::new(ast::Statement::Let { name: i, value: e, value_type: t }, (lo, hi)),
|
||||||
<lo:@L> <i:"identifier"> "=" <e:Expr> ";" <hi:@R> =>
|
<lo:@L> <i:Identifier> "=" <e:Expr> ";" <hi:@R> =>
|
||||||
ast::Statement::Mutate { name: i, value: e, span: (lo, hi) },
|
Spanned::new(ast::Statement::Mutate { name: i, value: e }, (lo, hi)),
|
||||||
"if" <cond:Expr> "{" <s:Statements> "}" <e:ElseExpr?> =>
|
<lo:@L> "if" <cond:Expr> "{" <s:Statements> "}" <e:ElseExpr?> <hi:@R> =>
|
||||||
ast::Statement::If {
|
Spanned::new(ast::Statement::If {
|
||||||
condition: cond,
|
condition: cond,
|
||||||
body: s,
|
body: s,
|
||||||
else_body: e,
|
else_body: e,
|
||||||
scope_type_info: Default::default(),
|
scope_type_info: Default::default(),
|
||||||
else_body_scope_type_info: Default::default(),
|
else_body_scope_type_info: Default::default(),
|
||||||
},
|
}, (lo, hi)),
|
||||||
"return" <e:Expr?> ";" => ast::Statement::Return(e),
|
<lo:@L> "return" <e:Expr?> ";" <hi:@R> => Spanned::new(ast::Statement::Return(e), (lo, hi)),
|
||||||
};
|
};
|
||||||
|
|
||||||
ElseExpr: Vec<ast::Statement> = {
|
ElseExpr: Vec<Spanned<ast::Statement>> = {
|
||||||
"else" "{" <s:Statements> "}" => s
|
"else" "{" <s:Statements> "}" => s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,8 +139,8 @@ Level3_Op: ast::OpCode = {
|
||||||
"%" => ast::OpCode::Rem,
|
"%" => ast::OpCode::Rem,
|
||||||
}
|
}
|
||||||
|
|
||||||
Tier<Op,NextTier>: Box<ast::Expression> = {
|
Tier<Op,NextTier>: Spanned<Box<ast::Expression>> = {
|
||||||
<t:Tier<Op,NextTier>> <o:Op> <n:NextTier> => Box::new(ast::Expression::BinaryOp(t, o, n)),
|
<lo:@L> <t:Tier<Op,NextTier>> <o:Op> <n:NextTier> <hi:@R> => Spanned::new(Box::new(ast::Expression::BinaryOp(t, o, n)), (lo, hi)),
|
||||||
NextTier
|
NextTier
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -147,14 +150,14 @@ Expr3 = Tier<Level2_Op, Expr4>;
|
||||||
Expr4 = Tier<Level3_Op, Term>;
|
Expr4 = Tier<Level3_Op, Term>;
|
||||||
|
|
||||||
// Terms: variables, literals, calls
|
// Terms: variables, literals, calls
|
||||||
Term: Box<ast::Expression> = {
|
Term: Spanned<Box<ast::Expression>> = {
|
||||||
<lo:@L> <i:"identifier"> <hi:@R> => Box::new(ast::Expression::Variable {
|
<lo:@L> <i:"identifier"> <hi:@R> => Spanned::new(Box::new(ast::Expression::Variable {
|
||||||
name: Spanned::new(i, (lo, hi))
|
name: i
|
||||||
}),
|
}), (lo, hi)),
|
||||||
<n:Number> => Box::new(ast::Expression::Literal(n)),
|
<lo:@L> <n:Number> <hi:@R> => Spanned::new(Box::new(ast::Expression::Literal(n)), (lo, hi)),
|
||||||
<n:StringLit> => Box::new(ast::Expression::Literal(n)),
|
<lo:@L> <n:StringLit> <hi:@R> => Spanned::new(Box::new(ast::Expression::Literal(n)), (lo, hi)),
|
||||||
<n:BoolLiteral> => Box::new(ast::Expression::Literal(n)),
|
<lo:@L> <n:BoolLiteral> <hi:@R> => Spanned::new(Box::new(ast::Expression::Literal(n)), (lo, hi)),
|
||||||
<i:"identifier"> "(" <values:Comma<Term>> ")" => Box::new(ast::Expression::Call { function: i, args: values }),
|
<lo:@L> <i:Identifier> "(" <values:Comma<Term>> ")" <hi:@R> => Spanned::new(Box::new(ast::Expression::Call { function: i, args: values }), (lo, hi)),
|
||||||
"(" <Term> ")"
|
"(" <Term> ")"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -214,46 +217,46 @@ ArrayLen: u32 = {
|
||||||
";" <i:"int literal"> => i.parse().unwrap(),
|
";" <i:"int literal"> => i.parse().unwrap(),
|
||||||
}
|
}
|
||||||
|
|
||||||
LangType: ast::TypeExp = {
|
LangType: Spanned<ast::TypeExp> = {
|
||||||
"ptr" "<" <target:LangType> ">" => ast::TypeExp::Pointer { target: Box::new(target) },
|
<lo:@L> "ptr" "<" <target:LangType> ">" <hi:@R> => Spanned::new(ast::TypeExp::Pointer { target: Spanned::new(Box::new(target.value), target.span) }, (lo, hi)),
|
||||||
"[" <of:LangType> <len:ArrayLen?> "]" => ast::TypeExp::Array { of: Box::new(of), len },
|
<lo:@L> "[" <of:LangType> <len:ArrayLen?> "]" <hi:@R> => Spanned::new(ast::TypeExp::Array { of: Spanned::new(Box::new(of.value), of.span), len }, (lo, hi)),
|
||||||
"i8" => ast::TypeExp::Integer { bits: 8, signed: true },
|
<lo:@L> "i8" <hi:@R> => Spanned::new(ast::TypeExp::Integer { bits: 8, signed: true }, (lo, hi)),
|
||||||
"i16" => ast::TypeExp::Integer { bits: 16, signed: true },
|
<lo:@L> "i16" <hi:@R> => Spanned::new(ast::TypeExp::Integer { bits: 16, signed: true }, (lo, hi)),
|
||||||
"i32" => ast::TypeExp::Integer { bits: 32, signed: true },
|
<lo:@L> "i32" <hi:@R> => Spanned::new(ast::TypeExp::Integer { bits: 32, signed: true }, (lo, hi)),
|
||||||
"i64" => ast::TypeExp::Integer { bits: 64, signed: true },
|
<lo:@L> "i64" <hi:@R> => Spanned::new(ast::TypeExp::Integer { bits: 64, signed: true }, (lo, hi)),
|
||||||
"u8" => ast::TypeExp::Integer { bits: 8, signed: false },
|
<lo:@L> "u8" <hi:@R> => Spanned::new(ast::TypeExp::Integer { bits: 8, signed: false }, (lo, hi)),
|
||||||
"u16" => ast::TypeExp::Integer { bits: 16, signed: false },
|
<lo:@L> "u16" <hi:@R> => Spanned::new(ast::TypeExp::Integer { bits: 16, signed: false }, (lo, hi)),
|
||||||
"u32" => ast::TypeExp::Integer { bits: 32, signed: false },
|
<lo:@L> "u32" <hi:@R> => Spanned::new(ast::TypeExp::Integer { bits: 32, signed: false }, (lo, hi)),
|
||||||
"u64" => ast::TypeExp::Integer { bits: 64, signed: false },
|
<lo:@L> "u64" <hi:@R> => Spanned::new(ast::TypeExp::Integer { bits: 64, signed: false }, (lo, hi)),
|
||||||
"bool" => ast::TypeExp::Boolean,
|
<lo:@L> "bool" <hi:@R> => Spanned::new(ast::TypeExp::Boolean, (lo, hi)),
|
||||||
<id:"identifier"> => ast::TypeExp::Other { id },
|
<lo:@L> <id:"identifier"> <hi:@R> => Spanned::new(ast::TypeExp::Other { id }, (lo, hi)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function handling
|
// Function handling
|
||||||
Param: ast::Parameter = {
|
Param: ast::Parameter = {
|
||||||
<"identifier"> ":" <LangType> => ast::Parameter::new(<>)
|
<Identifier> ":" <LangType> => ast::Parameter::new(<>)
|
||||||
};
|
};
|
||||||
|
|
||||||
Params = Comma<Param>;
|
Params = Comma<Param>;
|
||||||
|
|
||||||
FunctionReturn: ast::TypeExp = {
|
FunctionReturn: Spanned<ast::TypeExp> = {
|
||||||
"->" <i:LangType> => i,
|
"->" <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
|
// Structures
|
||||||
|
|
||||||
StructField: ast::StructField = {
|
StructField: ast::StructField = {
|
||||||
<"identifier"> ":" <LangType> => ast::StructField::new(<>)
|
<Identifier> ":" <LangType> => ast::StructField::new(<>)
|
||||||
};
|
};
|
||||||
|
|
||||||
StructFields = Comma<StructField>;
|
StructFields = Comma<StructField>;
|
||||||
|
|
||||||
Struct: ast::Struct = {
|
Struct: ast::Struct = {
|
||||||
"struct" <i:"identifier"> "{" <fields:StructFields> "}" => {
|
"struct" <i:Identifier> "{" <fields:StructFields> "}" => {
|
||||||
ast::Struct {
|
ast::Struct {
|
||||||
name: i,
|
name: i,
|
||||||
fields
|
fields
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::ast::{self, Expression, Function, Statement, TypeExp};
|
use crate::ast::{self, Expression, Function, Spanned, Statement, TypeExp};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TypeError {
|
pub enum TypeError {
|
||||||
|
@ -30,19 +30,19 @@ pub fn type_check(ast: &mut ast::Program) -> Result<(), TypeError> {
|
||||||
|
|
||||||
// gather global constructs first
|
// gather global constructs first
|
||||||
for statement in ast.statements.iter_mut() {
|
for statement in ast.statements.iter_mut() {
|
||||||
match statement {
|
match &mut statement.value {
|
||||||
Statement::Struct(st) => {
|
Statement::Struct(st) => {
|
||||||
let fields = st
|
let fields = st
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| (x.ident.clone(), x.field_type.clone()))
|
.map(|x| (x.ident.value.clone(), x.field_type.value.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
storage.structs.insert(st.name.clone(), fields);
|
storage.structs.insert(st.name.value.clone(), fields);
|
||||||
}
|
}
|
||||||
Statement::Function(function) => {
|
Statement::Function(function) => {
|
||||||
storage
|
storage
|
||||||
.functions
|
.functions
|
||||||
.insert(function.name.clone(), function.clone());
|
.insert(function.name.value.clone(), function.clone());
|
||||||
}
|
}
|
||||||
// todo: find globals here too
|
// todo: find globals here too
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -50,11 +50,14 @@ pub fn type_check(ast: &mut ast::Program) -> Result<(), TypeError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for statement in ast.statements.iter_mut() {
|
for statement in ast.statements.iter_mut() {
|
||||||
if let Statement::Function(function) = statement {
|
if let Statement::Function(function) = &mut statement.value {
|
||||||
let mut scope_vars: ScopeMap = HashMap::new();
|
let mut scope_vars: ScopeMap = HashMap::new();
|
||||||
|
|
||||||
for arg in &function.params {
|
for arg in &function.params {
|
||||||
scope_vars.insert(arg.ident.clone(), vec![Some(arg.type_exp.clone())]);
|
scope_vars.insert(
|
||||||
|
arg.ident.value.clone(),
|
||||||
|
vec![Some(arg.type_exp.value.clone())],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let func_info = function.clone();
|
let func_info = function.clone();
|
||||||
|
@ -72,7 +75,7 @@ pub fn type_check(ast: &mut ast::Program) -> Result<(), TypeError> {
|
||||||
|
|
||||||
/// Finds variable types in the scope, returns newly created variables to handle shadowing
|
/// Finds variable types in the scope, returns newly created variables to handle shadowing
|
||||||
fn type_inference_scope(
|
fn type_inference_scope(
|
||||||
statements: &mut [ast::Statement],
|
statements: &mut [Spanned<ast::Statement>],
|
||||||
scope_vars: &ScopeMap,
|
scope_vars: &ScopeMap,
|
||||||
func: &Function,
|
func: &Function,
|
||||||
storage: &Storage,
|
storage: &Storage,
|
||||||
|
@ -81,43 +84,45 @@ fn type_inference_scope(
|
||||||
let mut new_vars: HashSet<String> = HashSet::new();
|
let mut new_vars: HashSet<String> = HashSet::new();
|
||||||
|
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
match statement {
|
match &mut statement.value {
|
||||||
Statement::Let {
|
Statement::Let {
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
value_type,
|
value_type,
|
||||||
span,
|
|
||||||
} => {
|
} => {
|
||||||
new_vars.insert(name.clone());
|
new_vars.insert(name.value.clone());
|
||||||
|
|
||||||
let exp_type = type_inference_expression(value, &mut scope_vars, storage, None)?;
|
let exp_type = type_inference_expression(&value, &mut scope_vars, storage, None)?;
|
||||||
|
|
||||||
if !scope_vars.contains_key(name) {
|
if !scope_vars.contains_key(&name.value) {
|
||||||
scope_vars.insert(name.clone(), vec![]);
|
scope_vars.insert(name.value.clone(), vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let var = scope_vars.get_mut(name).unwrap();
|
let var = scope_vars.get_mut(&name.value).unwrap();
|
||||||
|
|
||||||
if value_type.is_none() {
|
if value_type.is_none() {
|
||||||
var.push(exp_type);
|
var.push(exp_type);
|
||||||
} else {
|
} else {
|
||||||
if exp_type.is_some() && &exp_type != value_type {
|
if exp_type.is_some() && exp_type != value_type.clone().map(|x| x.value) {
|
||||||
Err(TypeError::Mismatch {
|
Err(TypeError::Mismatch {
|
||||||
found: exp_type.clone().unwrap(),
|
found: exp_type.clone().unwrap(),
|
||||||
expected: value_type.clone().unwrap(),
|
expected: value_type.clone().map(|x| x.value).unwrap(),
|
||||||
span: *span,
|
span: statement.span,
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
var.push(value_type.clone());
|
var.push(value_type.clone().map(|x| x.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Mutate { name, value, span } => {
|
Statement::Mutate { name, value } => {
|
||||||
if !scope_vars.contains_key(name) {
|
if !scope_vars.contains_key(&name.value) {
|
||||||
panic!("undeclared variable");
|
Err(TypeError::UndeclaredVariable {
|
||||||
|
name: name.value.clone(),
|
||||||
|
span: name.span,
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let exp_type = type_inference_expression(value, &mut scope_vars, storage, None)?;
|
let exp_type = type_inference_expression(&value, &mut scope_vars, storage, None)?;
|
||||||
let var = scope_vars.get_mut(name).unwrap().last_mut().unwrap();
|
let var = scope_vars.get_mut(&name.value).unwrap().last_mut().unwrap();
|
||||||
|
|
||||||
if var.is_none() {
|
if var.is_none() {
|
||||||
*var = exp_type;
|
*var = exp_type;
|
||||||
|
@ -125,7 +130,7 @@ fn type_inference_scope(
|
||||||
Err(TypeError::Mismatch {
|
Err(TypeError::Mismatch {
|
||||||
found: exp_type.clone().unwrap(),
|
found: exp_type.clone().unwrap(),
|
||||||
expected: var.clone().unwrap(),
|
expected: var.clone().unwrap(),
|
||||||
span: *span,
|
span: statement.span,
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +142,7 @@ fn type_inference_scope(
|
||||||
else_body_scope_type_info,
|
else_body_scope_type_info,
|
||||||
} => {
|
} => {
|
||||||
type_inference_expression(
|
type_inference_expression(
|
||||||
condition,
|
&condition,
|
||||||
&mut scope_vars,
|
&mut scope_vars,
|
||||||
storage,
|
storage,
|
||||||
Some(TypeExp::Boolean),
|
Some(TypeExp::Boolean),
|
||||||
|
@ -181,7 +186,7 @@ fn type_inference_scope(
|
||||||
exp,
|
exp,
|
||||||
&mut scope_vars,
|
&mut scope_vars,
|
||||||
storage,
|
storage,
|
||||||
func.return_type.clone(),
|
func.return_type.clone().map(|x| x.value),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,12 +199,12 @@ fn type_inference_scope(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_inference_expression(
|
fn type_inference_expression(
|
||||||
exp: &Expression,
|
exp: &Spanned<Box<Expression>>,
|
||||||
scope_vars: &mut ScopeMap,
|
scope_vars: &mut ScopeMap,
|
||||||
storage: &Storage,
|
storage: &Storage,
|
||||||
expected_type: Option<TypeExp>,
|
expected_type: Option<TypeExp>,
|
||||||
) -> Result<Option<TypeExp>, TypeError> {
|
) -> Result<Option<TypeExp>, TypeError> {
|
||||||
Ok(match exp {
|
Ok(match &*exp.value {
|
||||||
Expression::Literal(lit) => {
|
Expression::Literal(lit) => {
|
||||||
match lit {
|
match lit {
|
||||||
ast::LiteralValue::String(_) => None, // todo
|
ast::LiteralValue::String(_) => None, // todo
|
||||||
|
@ -216,7 +221,7 @@ fn type_inference_expression(
|
||||||
}
|
}
|
||||||
Expression::Variable { name } => {
|
Expression::Variable { name } => {
|
||||||
let var = scope_vars
|
let var = scope_vars
|
||||||
.get_mut(&name.value)
|
.get_mut(name)
|
||||||
.expect("to exist")
|
.expect("to exist")
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -230,7 +235,7 @@ fn type_inference_expression(
|
||||||
Err(TypeError::Mismatch {
|
Err(TypeError::Mismatch {
|
||||||
found: expected_type.clone().unwrap(),
|
found: expected_type.clone().unwrap(),
|
||||||
expected: var.clone().unwrap(),
|
expected: var.clone().unwrap(),
|
||||||
span: name.span,
|
span: exp.span,
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
expected_type
|
expected_type
|
||||||
|
@ -242,15 +247,15 @@ fn type_inference_expression(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Call { function, args } => {
|
Expression::Call { function, args } => {
|
||||||
let func = storage.functions.get(function).cloned().unwrap();
|
let func = storage.functions.get(&function.value).cloned().unwrap();
|
||||||
|
|
||||||
for (i, arg) in args.iter().enumerate() {
|
for (i, arg) in args.iter().enumerate() {
|
||||||
let arg_type = func.params[i].type_exp.clone();
|
let arg_type = func.params[i].type_exp.clone();
|
||||||
// result is ignored, but need these to infer call arg types
|
// result is ignored, but need these to infer call arg types
|
||||||
type_inference_expression(arg, scope_vars, storage, Some(arg_type))?;
|
type_inference_expression(arg, scope_vars, storage, Some(arg_type.value))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
func.return_type
|
func.return_type.map(|x| x.value)
|
||||||
}
|
}
|
||||||
Expression::BinaryOp(lhs, op, rhs) => match op {
|
Expression::BinaryOp(lhs, op, rhs) => match op {
|
||||||
ast::OpCode::Eq | ast::OpCode::Ne => Some(TypeExp::Boolean),
|
ast::OpCode::Eq | ast::OpCode::Ne => Some(TypeExp::Boolean),
|
||||||
|
|
Loading…
Reference in a new issue