This commit is contained in:
Edgar 2023-06-12 12:46:23 +02:00
parent 024a06defa
commit 64dd61be3a
No known key found for this signature in database
GPG key ID: 70ADAE8F35904387
6 changed files with 189 additions and 406 deletions

246
out
View file

@ -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,
},
),
},
),
],
}

View file

@ -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 }
} }
} }

View file

@ -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,

View file

@ -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| &param.type_exp) .map(|param| &param.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)

View file

@ -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

View file

@ -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),