mirror of
https://github.com/edg-l/edlang.git
synced 2024-11-23 00:18:24 +00:00
require fully typed integers for now
This commit is contained in:
parent
f3cc72e7ce
commit
8c212d948d
15
programs/ifelse.ed
Normal file
15
programs/ifelse.ed
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
fn works(x: i64) -> i64 {
|
||||||
|
let z = 0i64;
|
||||||
|
if 2i64 == x {
|
||||||
|
z = x * 2i64;
|
||||||
|
} else {
|
||||||
|
z = x * 3i64;
|
||||||
|
}
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> i64 {
|
||||||
|
let y = 2i64;
|
||||||
|
let z = y;
|
||||||
|
return works(z);
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ fn test(x: Hello) {
|
||||||
|
|
||||||
fn works(x: i64) -> i64 {
|
fn works(x: i64) -> i64 {
|
||||||
let z = 0i64;
|
let z = 0i64;
|
||||||
if 2 == x {
|
if 2i64 == x {
|
||||||
z = x * 2i64;
|
z = x * 2i64;
|
||||||
} else {
|
} else {
|
||||||
z = x * 3i64;
|
z = x * 3i64;
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Spanned<T> {
|
pub struct Spanned<T> {
|
||||||
pub span: (usize, usize),
|
pub span: (usize, usize),
|
||||||
|
@ -64,12 +66,10 @@ pub enum Expression {
|
||||||
Literal(LiteralValue),
|
Literal(LiteralValue),
|
||||||
Variable {
|
Variable {
|
||||||
name: Spanned<String>,
|
name: Spanned<String>,
|
||||||
value_type: Option<TypeExp>,
|
|
||||||
},
|
},
|
||||||
Call {
|
Call {
|
||||||
function: String,
|
function: String,
|
||||||
args: Vec<Box<Self>>,
|
args: Vec<Box<Self>>,
|
||||||
value_type: Option<TypeExp>,
|
|
||||||
},
|
},
|
||||||
BinaryOp(Box<Self>, OpCode, Box<Self>),
|
BinaryOp(Box<Self>, OpCode, Box<Self>),
|
||||||
}
|
}
|
||||||
|
@ -86,16 +86,17 @@ impl Parameter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Function {
|
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 scope_type_info: HashMap<String, Vec<TypeExp>>,
|
||||||
pub return_type: Option<TypeExp>,
|
pub return_type: Option<TypeExp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub const fn new(
|
pub fn new(
|
||||||
name: String,
|
name: String,
|
||||||
params: Vec<Parameter>,
|
params: Vec<Parameter>,
|
||||||
body: Vec<Statement>,
|
body: Vec<Statement>,
|
||||||
|
@ -106,6 +107,7 @@ impl Function {
|
||||||
params,
|
params,
|
||||||
body,
|
body,
|
||||||
return_type,
|
return_type,
|
||||||
|
scope_type_info: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,14 +115,14 @@ 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: String,
|
||||||
pub type_exp: TypeExp,
|
pub field_type: TypeExp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructField {
|
impl StructField {
|
||||||
pub const fn new(ident: String, type_name: TypeExp) -> Self {
|
pub const fn new(ident: String, type_name: TypeExp) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ident,
|
ident,
|
||||||
type_exp: type_name,
|
field_type: type_name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +133,7 @@ pub struct Struct {
|
||||||
pub fields: Vec<StructField>,
|
pub fields: Vec<StructField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Let {
|
Let {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -142,13 +144,14 @@ pub enum Statement {
|
||||||
Mutate {
|
Mutate {
|
||||||
name: String,
|
name: String,
|
||||||
value: Box<Expression>,
|
value: Box<Expression>,
|
||||||
value_type: Option<TypeExp>,
|
|
||||||
span: (usize, usize),
|
span: (usize, usize),
|
||||||
},
|
},
|
||||||
If {
|
If {
|
||||||
condition: Box<Expression>,
|
condition: Box<Expression>,
|
||||||
body: Vec<Statement>,
|
body: Vec<Statement>,
|
||||||
|
scope_type_info: HashMap<String, Vec<TypeExp>>,
|
||||||
else_body: Option<Vec<Statement>>,
|
else_body: Option<Vec<Statement>>,
|
||||||
|
else_body_scope_type_info: HashMap<String, Vec<TypeExp>>,
|
||||||
},
|
},
|
||||||
Return(Option<Box<Expression>>),
|
Return(Option<Box<Expression>>),
|
||||||
Function(Function),
|
Function(Function),
|
||||||
|
|
217
src/codegen.rs
217
src/codegen.rs
|
@ -37,10 +37,10 @@ pub struct CodeGen<'ctx> {
|
||||||
context: &'ctx Context,
|
context: &'ctx Context,
|
||||||
pub module: Module<'ctx>,
|
pub module: Module<'ctx>,
|
||||||
builder: Builder<'ctx>,
|
builder: Builder<'ctx>,
|
||||||
types: TypeStorage<'ctx>,
|
//types: TypeStorage<'ctx>,
|
||||||
struct_types: StructTypeStorage<'ctx>,
|
struct_types: StructTypeStorage<'ctx>,
|
||||||
// function to return type
|
// function to return type
|
||||||
functions: HashMap<String, (Vec<TypeExp>, Option<TypeExp>)>,
|
functions: HashMap<String, Function>,
|
||||||
_program: ProgramData,
|
_program: ProgramData,
|
||||||
ast: ast::Program,
|
ast: ast::Program,
|
||||||
}
|
}
|
||||||
|
@ -48,12 +48,12 @@ pub struct CodeGen<'ctx> {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Variable<'ctx> {
|
pub struct Variable<'ctx> {
|
||||||
pub value: BasicValueEnum<'ctx>,
|
pub value: BasicValueEnum<'ctx>,
|
||||||
|
pub type_counter: usize,
|
||||||
pub phi_counter: usize,
|
pub phi_counter: usize,
|
||||||
pub type_exp: TypeExp,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Variables<'ctx> = HashMap<String, Variable<'ctx>>;
|
pub type Variables<'ctx> = HashMap<String, Variable<'ctx>>;
|
||||||
pub type TypeStorage<'ctx> = HashMap<TypeExp, BasicTypeEnum<'ctx>>;
|
// pub type TypeStorage<'ctx> = HashMap<TypeExp, BasicTypeEnum<'ctx>>;
|
||||||
|
|
||||||
/// Holds the struct type and maps fields to types and the location within the struct.
|
/// Holds the struct type and maps fields to types and the location within the struct.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -79,7 +79,6 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
builder: context.create_builder(),
|
builder: context.create_builder(),
|
||||||
_program,
|
_program,
|
||||||
ast,
|
ast,
|
||||||
types: HashMap::new(),
|
|
||||||
struct_types: HashMap::new(),
|
struct_types: HashMap::new(),
|
||||||
functions: HashMap::new(),
|
functions: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
@ -88,9 +87,8 @@ 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 = HashMap::new();
|
||||||
let mut func_info = HashMap::new();
|
// let mut types: TypeStorage<'ctx> = HashMap::new();
|
||||||
let mut types: TypeStorage<'ctx> = HashMap::new();
|
|
||||||
let mut struct_types: StructTypeStorage<'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.
|
||||||
|
@ -102,13 +100,11 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
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() {
|
||||||
if !types.contains_key(&field.type_exp) {
|
// todo: this doesnt handle out of order structs well
|
||||||
types.insert(field.type_exp.clone(), self.get_llvm_type(&field.type_exp)?);
|
let ty = self.get_llvm_type(&field.field_type)?;
|
||||||
}
|
|
||||||
let ty = self.get_llvm_type(&field.type_exp)?;
|
|
||||||
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.type_exp.clone()));
|
fields.insert(field.ident.clone(), (i, field.field_type.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ty = self.context.struct_type(&field_types, false);
|
let ty = self.context.struct_type(&field_types, false);
|
||||||
|
@ -123,38 +119,16 @@ 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 {
|
||||||
functions.push(function);
|
functions.insert(function.name.clone(), function.clone());
|
||||||
let (args, ret_type) = self.compile_function_signature(function)?;
|
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.functions = functions;
|
||||||
|
|
||||||
self.types = types;
|
|
||||||
self.functions = func_info;
|
|
||||||
|
|
||||||
info!("functions:\n{:#?}", self.functions);
|
info!("functions:\n{:#?}", self.functions);
|
||||||
|
|
||||||
// implement them.
|
// implement them.
|
||||||
for function in functions {
|
for (_, function) in &self.functions {
|
||||||
self.compile_function(function)?;
|
self.compile_function(function)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,9 +143,6 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_llvm_type(&self, id: &TypeExp) -> 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 {
|
||||||
TypeExp::Integer { bits, signed: _ } => self
|
TypeExp::Integer { bits, signed: _ } => self
|
||||||
.context
|
.context
|
||||||
|
@ -194,13 +165,9 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
.as_basic_type_enum(),
|
.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, function: &Function) -> Result<()> {
|
||||||
&self,
|
|
||||||
function: &Function,
|
|
||||||
) -> Result<(Vec<TypeExp>, Option<TypeExp>)> {
|
|
||||||
let args_types: Vec<BasicTypeEnum<'ctx>> = function
|
let args_types: Vec<BasicTypeEnum<'ctx>> = function
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -221,14 +188,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
|
|
||||||
self.module.add_function(&function.name, fn_type, None);
|
self.module.add_function(&function.name, fn_type, None);
|
||||||
|
|
||||||
Ok((
|
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<()> {
|
||||||
|
@ -238,7 +198,6 @@ 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: TypeStorage = self.types.clone();
|
|
||||||
|
|
||||||
for (i, param) in function.params.iter().enumerate() {
|
for (i, param) in function.params.iter().enumerate() {
|
||||||
let id = ¶m.ident;
|
let id = ¶m.ident;
|
||||||
|
@ -250,7 +209,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
Variable {
|
Variable {
|
||||||
value: param_value,
|
value: param_value,
|
||||||
phi_counter: 0,
|
phi_counter: 0,
|
||||||
type_exp: param.type_exp.clone(),
|
type_counter: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -261,7 +220,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
if let Statement::Return(_) = statement {
|
if let Statement::Return(_) = statement {
|
||||||
has_return = true
|
has_return = true
|
||||||
}
|
}
|
||||||
self.compile_statement(func, statement, &mut variables, &mut types)?;
|
self.compile_statement(func, statement, &mut variables, &function.scope_type_info)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_return {
|
if !has_return {
|
||||||
|
@ -277,7 +236,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
statement: &Statement,
|
statement: &Statement,
|
||||||
// value, assignments
|
// value, assignments
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
types: &mut TypeStorage<'ctx>,
|
scope_info: &HashMap<String, Vec<TypeExp>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match statement {
|
match statement {
|
||||||
// Variable assignment
|
// Variable assignment
|
||||||
|
@ -287,41 +246,32 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
value_type: _,
|
value_type: _,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let (value, value_type) = self
|
let value = self
|
||||||
.compile_expression(value, variables, types)?
|
.compile_expression(value, variables, scope_info)?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
|
|
||||||
if !types.contains_key(&value_type) {
|
|
||||||
let ty = self.get_llvm_type(&value_type)?;
|
|
||||||
types.insert(value_type.clone(), ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("adding variable: name={}, ty={:?}", name, value_type);
|
|
||||||
|
|
||||||
variables.insert(
|
variables.insert(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
Variable {
|
Variable {
|
||||||
value,
|
value,
|
||||||
phi_counter: 0,
|
phi_counter: 0,
|
||||||
type_exp: value_type,
|
type_counter: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Statement::Mutate { name, value, .. } => {
|
Statement::Mutate { name, value, .. } => {
|
||||||
let (value, value_type) = self
|
let value = self
|
||||||
.compile_expression(value, variables, types)?
|
.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).expect("variable should exist");
|
||||||
var.phi_counter += 1;
|
var.phi_counter += 1;
|
||||||
var.value = value;
|
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 (value, _value_type) = self
|
let value = self
|
||||||
.compile_expression(ret, variables, types)?
|
.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 {
|
||||||
|
@ -332,9 +282,11 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
condition,
|
condition,
|
||||||
body,
|
body,
|
||||||
else_body,
|
else_body,
|
||||||
|
scope_type_info,
|
||||||
|
else_body_scope_type_info,
|
||||||
} => {
|
} => {
|
||||||
let (condition, _cond_type) = self
|
let condition = self
|
||||||
.compile_expression(condition, variables, types)?
|
.compile_expression(condition, variables, scope_info)?
|
||||||
.expect("should produce a value");
|
.expect("should produce a value");
|
||||||
|
|
||||||
let mut if_block = self.context.append_basic_block(function_value, "if");
|
let mut if_block = self.context.append_basic_block(function_value, "if");
|
||||||
|
@ -354,7 +306,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(function_value, s, &mut variables_if, types)?;
|
self.compile_statement(function_value, s, &mut variables_if, scope_type_info)?;
|
||||||
}
|
}
|
||||||
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
|
||||||
|
@ -364,7 +316,12 @@ 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(function_value, s, &mut variables_else, types)?;
|
self.compile_statement(
|
||||||
|
function_value,
|
||||||
|
s,
|
||||||
|
&mut variables_else,
|
||||||
|
else_body_scope_type_info,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
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
|
||||||
|
@ -381,7 +338,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
.builder
|
.builder
|
||||||
.build_phi(old_var.value.get_type(), &format!("{name}_phi"));
|
.build_phi(old_var.value.get_type(), &format!("{name}_phi"));
|
||||||
phi.add_incoming(&[(&new_var.value, if_block)]);
|
phi.add_incoming(&[(&new_var.value, if_block)]);
|
||||||
processed_vars.insert(name, (phi, new_var.type_exp));
|
processed_vars.insert(name, phi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -391,7 +348,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
if variables.contains_key(&name) {
|
if variables.contains_key(&name) {
|
||||||
let old_var = variables.get(&name).unwrap();
|
let old_var = variables.get(&name).unwrap();
|
||||||
if new_var.phi_counter > old_var.phi_counter {
|
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(&[(&new_var.value, else_block)]);
|
phi.add_incoming(&[(&new_var.value, else_block)]);
|
||||||
} else {
|
} else {
|
||||||
let phi = self.builder.build_phi(
|
let phi = self.builder.build_phi(
|
||||||
|
@ -399,22 +356,25 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
&format!("{name}_phi"),
|
&format!("{name}_phi"),
|
||||||
);
|
);
|
||||||
phi.add_incoming(&[(&old_var.value, else_block)]);
|
phi.add_incoming(&[(&old_var.value, else_block)]);
|
||||||
processed_vars.insert(name, (phi, new_var.type_exp));
|
processed_vars.insert(name, phi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, (phi, type_exp)) in processed_vars {
|
for (name, phi) in processed_vars {
|
||||||
|
/*
|
||||||
variables.insert(
|
variables.insert(
|
||||||
name,
|
name,
|
||||||
Variable {
|
Variable {
|
||||||
value: phi.as_basic_value(),
|
value: phi.as_basic_value(),
|
||||||
phi_counter: 0,
|
phi_counter: 0,
|
||||||
type_exp,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
|
let mut var = variables.get_mut(&name).unwrap();
|
||||||
|
var.value = phi.as_basic_value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Function(_) => unreachable!(),
|
Statement::Function(_) => unreachable!(),
|
||||||
|
@ -428,21 +388,16 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
&self,
|
&self,
|
||||||
expr: &Expression,
|
expr: &Expression,
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
types: &mut TypeStorage<'ctx>,
|
scope_info: &HashMap<String, Vec<TypeExp>>,
|
||||||
) -> Result<Option<(BasicValueEnum<'ctx>, TypeExp)>> {
|
) -> Result<Option<BasicValueEnum<'ctx>>> {
|
||||||
Ok(match expr {
|
Ok(match expr {
|
||||||
Expression::Variable {
|
Expression::Variable { name } => Some(self.compile_variable(&name.value, variables)?),
|
||||||
name,
|
|
||||||
value_type: _,
|
|
||||||
} => Some(self.compile_variable(&name.value, variables)?),
|
|
||||||
Expression::Literal(term) => Some(self.compile_literal(term)?),
|
Expression::Literal(term) => Some(self.compile_literal(term)?),
|
||||||
Expression::Call {
|
Expression::Call { function, args } => {
|
||||||
function,
|
self.compile_call(function, args, variables, scope_info)?
|
||||||
args,
|
}
|
||||||
value_type,
|
|
||||||
} => self.compile_call(function, args, variables, types, value_type.clone())?,
|
|
||||||
Expression::BinaryOp(lhs, op, rhs) => {
|
Expression::BinaryOp(lhs, op, rhs) => {
|
||||||
Some(self.compile_binary_op(lhs, op, rhs, variables, types)?)
|
Some(self.compile_binary_op(lhs, op, rhs, variables, scope_info)?)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -452,17 +407,16 @@ 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 TypeStorage<'ctx>,
|
scope_info: &HashMap<String, Vec<TypeExp>>,
|
||||||
value_type: Option<TypeExp>,
|
) -> Result<Option<BasicValueEnum<'ctx>>> {
|
||||||
) -> Result<Option<(BasicValueEnum<'ctx>, TypeExp)>> {
|
|
||||||
info!("compiling fn call: func_name={}", func_name);
|
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 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.iter() {
|
for arg in args.iter() {
|
||||||
let (res, _res_type) = self
|
let res = self
|
||||||
.compile_expression(arg, variables, types)?
|
.compile_expression(arg, variables, scope_info)?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
value_args.push(res.into());
|
value_args.push(res.into());
|
||||||
}
|
}
|
||||||
|
@ -473,7 +427,7 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
.try_as_basic_value();
|
.try_as_basic_value();
|
||||||
|
|
||||||
Ok(match result {
|
Ok(match result {
|
||||||
Either::Left(val) => Some((val, value_type.unwrap())),
|
Either::Left(val) => Some(val),
|
||||||
Either::Right(_) => None,
|
Either::Right(_) => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -484,20 +438,18 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
op: &OpCode,
|
op: &OpCode,
|
||||||
rhs: &Expression,
|
rhs: &Expression,
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
types: &mut TypeStorage<'ctx>,
|
scope_info: &HashMap<String, Vec<TypeExp>>,
|
||||||
) -> Result<(BasicValueEnum<'ctx>, TypeExp)> {
|
) -> Result<BasicValueEnum<'ctx>> {
|
||||||
let (lhs, lhs_type) = self
|
let lhs = self
|
||||||
.compile_expression(lhs, variables, types)?
|
.compile_expression(lhs, variables, scope_info)?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
let (rhs, rhs_type) = self
|
let rhs = self
|
||||||
.compile_expression(rhs, variables, types)?
|
.compile_expression(rhs, variables, scope_info)?
|
||||||
.expect("should have result");
|
.expect("should have result");
|
||||||
|
|
||||||
assert_eq!(lhs_type, rhs_type);
|
|
||||||
let lhs = lhs.into_int_value();
|
let lhs = lhs.into_int_value();
|
||||||
let rhs = rhs.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"),
|
||||||
|
@ -506,31 +458,18 @@ 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 => {
|
OpCode::Eq => self
|
||||||
bool_result = true;
|
.builder
|
||||||
self.builder
|
.build_int_compare(IntPredicate::EQ, lhs, rhs, "eq"),
|
||||||
.build_int_compare(IntPredicate::EQ, lhs, rhs, "eq")
|
OpCode::Ne => self
|
||||||
}
|
.builder
|
||||||
OpCode::Ne => {
|
.build_int_compare(IntPredicate::NE, lhs, rhs, "eq"),
|
||||||
bool_result = true;
|
|
||||||
self.builder
|
|
||||||
.build_int_compare(IntPredicate::NE, lhs, rhs, "eq")
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res_type = lhs_type;
|
Ok(result.as_basic_value_enum())
|
||||||
|
|
||||||
if bool_result {
|
|
||||||
res_type = TypeExp::Integer {
|
|
||||||
bits: 1,
|
|
||||||
signed: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((result.as_basic_value_enum(), res_type))
|
pub fn compile_literal(&self, term: &LiteralValue) -> Result<BasicValueEnum<'ctx>> {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compile_literal(&self, term: &LiteralValue) -> Result<(BasicValueEnum<'ctx>, TypeExp)> {
|
|
||||||
let value = match term {
|
let value = match term {
|
||||||
LiteralValue::String(_s) => {
|
LiteralValue::String(_s) => {
|
||||||
todo!()
|
todo!()
|
||||||
|
@ -540,13 +479,11 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
.const_string(s.as_bytes(), true)
|
.const_string(s.as_bytes(), true)
|
||||||
.as_basic_value_enum() */
|
.as_basic_value_enum() */
|
||||||
}
|
}
|
||||||
LiteralValue::Boolean(v) => (
|
LiteralValue::Boolean(v) => self
|
||||||
self.context
|
.context
|
||||||
.bool_type()
|
.bool_type()
|
||||||
.const_int((*v).into(), false)
|
.const_int((*v).into(), false)
|
||||||
.as_basic_value_enum(),
|
.as_basic_value_enum(),
|
||||||
TypeExp::Boolean,
|
|
||||||
),
|
|
||||||
LiteralValue::Integer {
|
LiteralValue::Integer {
|
||||||
value,
|
value,
|
||||||
bits,
|
bits,
|
||||||
|
@ -554,13 +491,11 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
} => {
|
} => {
|
||||||
let bits = *bits;
|
let bits = *bits;
|
||||||
let signed = *signed;
|
let signed = *signed;
|
||||||
(
|
|
||||||
self.context
|
self.context
|
||||||
.custom_width_int_type(bits)
|
.custom_width_int_type(bits)
|
||||||
.const_int(value.parse().unwrap(), false)
|
.const_int(value.parse().unwrap(), false)
|
||||||
.as_basic_value_enum(),
|
.as_basic_value_enum()
|
||||||
TypeExp::Integer { bits, signed },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -571,8 +506,8 @@ impl<'ctx> CodeGen<'ctx> {
|
||||||
&self,
|
&self,
|
||||||
variable: &str,
|
variable: &str,
|
||||||
variables: &mut Variables<'ctx>,
|
variables: &mut Variables<'ctx>,
|
||||||
) -> Result<(BasicValueEnum<'ctx>, TypeExp)> {
|
) -> Result<BasicValueEnum<'ctx>> {
|
||||||
let var = variables.get(variable).expect("value").clone();
|
let var = variables.get(variable).expect("value").clone();
|
||||||
Ok((var.value, var.type_exp))
|
Ok(var.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::str::FromStr;
|
use std::collections::HashMap;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{self, Spanned},
|
ast::{self, Spanned},
|
||||||
tokens::Token,
|
tokens::Token,
|
||||||
|
@ -99,9 +99,15 @@ BasicStatement: 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) },
|
ast::Statement::Let { name: i, value: e, value_type: t, span: (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), value_type: None },
|
ast::Statement::Mutate { name: i, value: e, span: (lo, hi) },
|
||||||
"if" <cond:Expr> "{" <s:Statements> "}" <e:ElseExpr?> =>
|
"if" <cond:Expr> "{" <s:Statements> "}" <e:ElseExpr?> =>
|
||||||
ast::Statement::If { condition: cond, body: s, else_body: e},
|
ast::Statement::If {
|
||||||
|
condition: cond,
|
||||||
|
body: s,
|
||||||
|
else_body: e,
|
||||||
|
scope_type_info: Default::default(),
|
||||||
|
else_body_scope_type_info: Default::default(),
|
||||||
|
},
|
||||||
"return" <e:Expr?> ";" => ast::Statement::Return(e),
|
"return" <e:Expr?> ";" => ast::Statement::Return(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -143,13 +149,12 @@ Expr4 = Tier<Level3_Op, Term>;
|
||||||
// Terms: variables, literals, calls
|
// Terms: variables, literals, calls
|
||||||
Term: Box<ast::Expression> = {
|
Term: Box<ast::Expression> = {
|
||||||
<lo:@L> <i:"identifier"> <hi:@R> => Box::new(ast::Expression::Variable {
|
<lo:@L> <i:"identifier"> <hi:@R> => Box::new(ast::Expression::Variable {
|
||||||
name: Spanned::new(i, (lo, hi)),
|
name: Spanned::new(i, (lo, hi))
|
||||||
value_type: None
|
|
||||||
}),
|
}),
|
||||||
<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:StringLit> => Box::new(ast::Expression::Literal(n)),
|
||||||
<n:BoolLiteral> => 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, value_type: None }),
|
<i:"identifier"> "(" <values:Comma<Term>> ")" => Box::new(ast::Expression::Call { function: i, args: values }),
|
||||||
"(" <Term> ")"
|
"(" <Term> ")"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ fn main() -> Result<()> {
|
||||||
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 mut ast = parser.parse(lexer)?;
|
let mut ast = parser.parse(lexer)?;
|
||||||
type_analysis::type_inference2(&mut ast);
|
type_analysis::type_inference(&mut ast);
|
||||||
let program = ProgramData::new(&input, &code);
|
let program = ProgramData::new(&input, &code);
|
||||||
check_program(&program, &ast);
|
check_program(&program, &ast);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ fn main() -> Result<()> {
|
||||||
let parser = grammar::ProgramParser::new();
|
let parser = grammar::ProgramParser::new();
|
||||||
match parser.parse(lexer) {
|
match parser.parse(lexer) {
|
||||||
Ok(mut ast) => {
|
Ok(mut ast) => {
|
||||||
type_analysis::type_inference2(&mut ast);
|
type_analysis::type_inference(&mut ast);
|
||||||
println!("{ast:#?}");
|
println!("{ast:#?}");
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -130,7 +130,7 @@ fn main() -> Result<()> {
|
||||||
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 mut ast: Program = parser.parse(lexer)?;
|
let mut ast: Program = parser.parse(lexer)?;
|
||||||
type_analysis::type_inference2(&mut ast);
|
type_analysis::type_inference(&mut ast);
|
||||||
|
|
||||||
let program = ProgramData::new(&input, &code);
|
let program = ProgramData::new(&input, &code);
|
||||||
|
|
||||||
|
|
|
@ -8,19 +8,15 @@ struct Storage {
|
||||||
functions: HashMap<String, Function>,
|
functions: HashMap<String, Function>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// problem with scopes,
|
||||||
To briefly summarize the union-find algorithm, given the set of all types in a proof,
|
// let x = 2;
|
||||||
it allows one to group them together into equivalence classes by means of a union procedure and to
|
// let x = 2i64;
|
||||||
pick a representative for each such class using a find procedure. Emphasizing the word procedure in
|
|
||||||
the sense of side effect, we're clearly leaving the realm of logic in order to prepare an effective algorithm.
|
type ScopeMap = HashMap<String, Vec<Option<TypeExp>>>;
|
||||||
The representative of a u n i o n ( a , b ) {\mathtt {union}}(a,b) is determined such that, if both a and b are
|
|
||||||
type variables then the representative is arbitrarily one of them, but while uniting a variable and a term, the
|
|
||||||
term becomes the representative. Assuming an implementation of union-find at hand, one can formulate the unification of two monotypes as follows:
|
|
||||||
*/
|
|
||||||
|
|
||||||
// this works, but need to find a way to store the found info + handle literal integer types (or not?)
|
// this works, but need to find a way to store the found info + handle literal integer types (or not?)
|
||||||
// maybe use scope ids
|
// maybe use scope ids
|
||||||
pub fn type_inference2(ast: &mut ast::Program) {
|
pub fn type_inference(ast: &mut ast::Program) {
|
||||||
let mut storage = Storage::default();
|
let mut storage = Storage::default();
|
||||||
|
|
||||||
// gather global constructs first
|
// gather global constructs first
|
||||||
|
@ -30,7 +26,7 @@ pub fn type_inference2(ast: &mut ast::Program) {
|
||||||
let fields = st
|
let fields = st
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| (x.ident.clone(), x.type_exp.clone()))
|
.map(|x| (x.ident.clone(), x.field_type.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
storage.structs.insert(st.name.clone(), fields);
|
storage.structs.insert(st.name.clone(), fields);
|
||||||
}
|
}
|
||||||
|
@ -46,26 +42,33 @@ pub fn type_inference2(ast: &mut ast::Program) {
|
||||||
|
|
||||||
dbg!(&storage);
|
dbg!(&storage);
|
||||||
|
|
||||||
for function in storage.functions.values() {
|
for statement in ast.statements.iter_mut() {
|
||||||
let mut scope_vars: HashMap<String, Option<TypeExp>> = HashMap::new();
|
if let Statement::Function(function) = statement {
|
||||||
|
let mut scope_vars: ScopeMap = HashMap::new();
|
||||||
|
|
||||||
for arg in &function.params {
|
for arg in &function.params {
|
||||||
scope_vars.insert(arg.ident.clone(), Some(arg.type_exp.clone()));
|
scope_vars.insert(arg.ident.clone(), vec![Some(arg.type_exp.clone())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let func_info = function.clone();
|
||||||
let (new_scope_vars, _) =
|
let (new_scope_vars, _) =
|
||||||
type_inference_scope(&function.body, &scope_vars, function, &storage);
|
type_inference_scope(&mut function.body, &scope_vars, &func_info, &storage);
|
||||||
dbg!(new_scope_vars);
|
// todo: check all vars have type info?
|
||||||
|
function.scope_type_info = new_scope_vars
|
||||||
|
.into_iter()
|
||||||
|
.map(|(a, b)| (a, b.into_iter().map(Option::unwrap).collect()))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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: &[ast::Statement],
|
statements: &mut [ast::Statement],
|
||||||
scope_vars: &HashMap<String, Option<TypeExp>>,
|
scope_vars: &ScopeMap,
|
||||||
func: &Function,
|
func: &Function,
|
||||||
storage: &Storage,
|
storage: &Storage,
|
||||||
) -> (HashMap<String, Option<TypeExp>>, HashSet<String>) {
|
) -> (ScopeMap, HashSet<String>) {
|
||||||
let mut scope_vars = scope_vars.clone();
|
let mut scope_vars = scope_vars.clone();
|
||||||
let mut new_vars: HashSet<String> = HashSet::new();
|
let mut new_vars: HashSet<String> = HashSet::new();
|
||||||
|
|
||||||
|
@ -81,19 +84,24 @@ fn type_inference_scope(
|
||||||
|
|
||||||
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) {
|
||||||
|
scope_vars.insert(name.clone(), vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let var = scope_vars.get_mut(name).unwrap();
|
||||||
|
|
||||||
if value_type.is_none() {
|
if value_type.is_none() {
|
||||||
scope_vars.insert(name.clone(), 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 {
|
||||||
panic!("let type mismatch: {:?} != {:?}", value_type, exp_type);
|
panic!("let type mismatch: {:?} != {:?}", value_type, exp_type);
|
||||||
}
|
}
|
||||||
scope_vars.insert(name.clone(), value_type.clone());
|
var.push(value_type.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Mutate {
|
Statement::Mutate {
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
value_type: _,
|
|
||||||
span: _,
|
span: _,
|
||||||
} => {
|
} => {
|
||||||
if !scope_vars.contains_key(name) {
|
if !scope_vars.contains_key(name) {
|
||||||
|
@ -101,7 +109,7 @@ fn type_inference_scope(
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
let var = scope_vars.get_mut(name).unwrap().last_mut().unwrap();
|
||||||
|
|
||||||
if var.is_none() {
|
if var.is_none() {
|
||||||
*var = exp_type;
|
*var = exp_type;
|
||||||
|
@ -113,6 +121,8 @@ fn type_inference_scope(
|
||||||
condition,
|
condition,
|
||||||
body,
|
body,
|
||||||
else_body,
|
else_body,
|
||||||
|
scope_type_info,
|
||||||
|
else_body_scope_type_info,
|
||||||
} => {
|
} => {
|
||||||
type_inference_expression(
|
type_inference_expression(
|
||||||
condition,
|
condition,
|
||||||
|
@ -124,23 +134,33 @@ fn type_inference_scope(
|
||||||
let (new_scope_vars, new_vars) =
|
let (new_scope_vars, new_vars) =
|
||||||
type_inference_scope(body, &scope_vars, func, storage);
|
type_inference_scope(body, &scope_vars, func, storage);
|
||||||
|
|
||||||
for (k, v) in new_scope_vars.into_iter() {
|
for (k, v) in new_scope_vars.iter() {
|
||||||
// not a new var within the scope (shadowing), so type info is valid
|
// not a new var within the scope (shadowing), so type info is valid
|
||||||
if scope_vars.contains_key(&k) && !new_vars.contains(&k) {
|
if scope_vars.contains_key(k) && !new_vars.contains(k) {
|
||||||
scope_vars.insert(k, v);
|
scope_vars.insert(k.clone(), v.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*scope_type_info = new_scope_vars
|
||||||
|
.into_iter()
|
||||||
|
.map(|(a, b)| (a, b.into_iter().map(Option::unwrap).collect()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
if let Some(body) = else_body {
|
if let Some(body) = else_body {
|
||||||
let (new_scope_vars, new_vars) =
|
let (new_scope_vars, new_vars) =
|
||||||
type_inference_scope(body, &scope_vars, func, storage);
|
type_inference_scope(body, &scope_vars, func, storage);
|
||||||
|
|
||||||
for (k, v) in new_scope_vars.into_iter() {
|
for (k, v) in new_scope_vars.iter() {
|
||||||
// not a new var within the scope (shadowing), so type info is valid
|
// not a new var within the scope (shadowing), so type info is valid
|
||||||
if scope_vars.contains_key(&k) && !new_vars.contains(&k) {
|
if scope_vars.contains_key(k) && !new_vars.contains(k) {
|
||||||
scope_vars.insert(k, v);
|
scope_vars.insert(k.clone(), v.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*else_body_scope_type_info = new_scope_vars
|
||||||
|
.into_iter()
|
||||||
|
.map(|(a, b)| (a, b.into_iter().map(Option::unwrap).collect()))
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Return(exp) => {
|
Statement::Return(exp) => {
|
||||||
|
@ -163,7 +183,7 @@ fn type_inference_scope(
|
||||||
|
|
||||||
fn type_inference_expression(
|
fn type_inference_expression(
|
||||||
exp: &Expression,
|
exp: &Expression,
|
||||||
scope_vars: &mut HashMap<String, Option<TypeExp>>,
|
scope_vars: &mut ScopeMap,
|
||||||
storage: &Storage,
|
storage: &Storage,
|
||||||
expected_type: Option<TypeExp>,
|
expected_type: Option<TypeExp>,
|
||||||
) -> Option<TypeExp> {
|
) -> Option<TypeExp> {
|
||||||
|
@ -182,31 +202,28 @@ fn type_inference_expression(
|
||||||
ast::LiteralValue::Boolean(_) => Some(TypeExp::Boolean),
|
ast::LiteralValue::Boolean(_) => Some(TypeExp::Boolean),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Variable {
|
Expression::Variable { name } => {
|
||||||
name,
|
let var = scope_vars
|
||||||
value_type: _,
|
.get_mut(&name.value)
|
||||||
} => {
|
.expect("to exist")
|
||||||
let var = scope_vars.get(&name.value).cloned().flatten();
|
.last_mut()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if expected_type.is_some() {
|
if expected_type.is_some() {
|
||||||
if var.is_none() {
|
if var.is_none() {
|
||||||
scope_vars.insert(name.value.clone(), expected_type.clone());
|
*var = expected_type.clone();
|
||||||
expected_type
|
expected_type
|
||||||
} else if expected_type.is_some() {
|
} else if expected_type.is_some() {
|
||||||
assert_eq!(var, expected_type, "type mismatch with variables");
|
assert_eq!(*var, expected_type, "type mismatch with variables");
|
||||||
expected_type
|
expected_type
|
||||||
} else {
|
} else {
|
||||||
var
|
var.clone()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var
|
var.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Call {
|
Expression::Call { function, args } => {
|
||||||
function,
|
|
||||||
args,
|
|
||||||
value_type: _,
|
|
||||||
} => {
|
|
||||||
let func = storage.functions.get(function).cloned().unwrap();
|
let func = storage.functions.get(function).cloned().unwrap();
|
||||||
|
|
||||||
for (i, arg) in args.iter().enumerate() {
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
|
Loading…
Reference in a new issue