This commit is contained in:
Edgar 2023-06-09 12:33:11 +02:00
parent bd93a7b313
commit 53e3e4dcd5
No known key found for this signature in database
GPG key ID: 70ADAE8F35904387
3 changed files with 329 additions and 29 deletions

246
out Normal file
View file

@ -0,0 +1,246 @@
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

@ -110,7 +110,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_inference(&mut ast); type_analysis::type_inference2(&mut ast);
println!("{ast:#?}"); println!("{ast:#?}");
} }
Commands::Compile { Commands::Compile {

View file

@ -10,14 +10,7 @@ struct Storage {
functions: HashMap<String, Function>, functions: HashMap<String, Function>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] // this works, but need to find a way to store the found info + handle literal integer types (or not?)
enum TypeGuess {
/// The guess comes from a strong source: call args, return type, let binding.
Strong,
///
Weak,
}
pub fn type_inference2(ast: &mut ast::Program) { pub fn type_inference2(ast: &mut ast::Program) {
let mut storage = Storage::default(); let mut storage = Storage::default();
@ -42,6 +35,8 @@ pub fn type_inference2(ast: &mut ast::Program) {
} }
} }
dbg!(&storage);
for function in storage.functions.values() { for function in storage.functions.values() {
let mut scope_vars: HashMap<String, Option<TypeExp>> = HashMap::new(); let mut scope_vars: HashMap<String, Option<TypeExp>> = HashMap::new();
@ -49,7 +44,9 @@ pub fn type_inference2(ast: &mut ast::Program) {
scope_vars.insert(arg.ident.clone(), Some(arg.type_exp.clone())); scope_vars.insert(arg.ident.clone(), Some(arg.type_exp.clone()));
} }
type_inference_scope(&function.body, &scope_vars, function, &storage); let (new_scope_vars, _) =
type_inference_scope(&function.body, &scope_vars, function, &storage);
dbg!(new_scope_vars);
} }
} }
@ -73,7 +70,7 @@ fn type_inference_scope(
} => { } => {
new_vars.insert(name.clone()); new_vars.insert(name.clone());
let exp_type = type_inference_expression(value, &scope_vars, storage); let exp_type = type_inference_expression(value, &mut scope_vars, storage, None);
if value_type.is_none() { if value_type.is_none() {
scope_vars.insert(name.clone(), exp_type); scope_vars.insert(name.clone(), exp_type);
@ -94,7 +91,7 @@ fn type_inference_scope(
panic!("undeclared variable"); panic!("undeclared variable");
} }
let exp_type = type_inference_expression(value, &scope_vars, storage); 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();
if var.is_none() { if var.is_none() {
@ -108,9 +105,45 @@ fn type_inference_scope(
body, body,
else_body, else_body,
} => { } => {
let cond_type = type_inference_expression(condition, &scope_vars, storage); type_inference_expression(
}, condition,
Statement::Return(_) => todo!(), &mut scope_vars,
storage,
Some(TypeExp::Boolean),
);
let (new_scope_vars, new_vars) =
type_inference_scope(body, &scope_vars, func, storage);
for (k, v) in new_scope_vars.into_iter() {
// not a new var within the scope (shadowing), so type info is valid
if scope_vars.contains_key(&k) && !new_vars.contains(&k) {
scope_vars.insert(k, v);
}
}
if let Some(body) = else_body {
let (new_scope_vars, new_vars) =
type_inference_scope(body, &scope_vars, func, storage);
for (k, v) in new_scope_vars.into_iter() {
// not a new var within the scope (shadowing), so type info is valid
if scope_vars.contains_key(&k) && !new_vars.contains(&k) {
scope_vars.insert(k, v);
}
}
}
}
Statement::Return(exp) => {
if let Some(exp) = exp {
type_inference_expression(
exp,
&mut scope_vars,
storage,
func.return_type.clone(),
);
}
}
Statement::Function(_) => unreachable!(), Statement::Function(_) => unreachable!(),
Statement::Struct(_) => unreachable!(), Statement::Struct(_) => unreachable!(),
} }
@ -121,8 +154,9 @@ fn type_inference_scope(
fn type_inference_expression( fn type_inference_expression(
exp: &Expression, exp: &Expression,
scope_vars: &HashMap<String, Option<TypeExp>>, scope_vars: &mut HashMap<String, Option<TypeExp>>,
storage: &Storage, storage: &Storage,
expected_type: Option<TypeExp>,
) -> Option<TypeExp> { ) -> Option<TypeExp> {
match exp { match exp {
Expression::Literal(lit) => { Expression::Literal(lit) => {
@ -148,24 +182,44 @@ fn type_inference_expression(
Expression::Variable { Expression::Variable {
name, name,
value_type: _, value_type: _,
} => scope_vars.get(&name.value).cloned().flatten(), } => {
let var = scope_vars.get(&name.value).cloned().flatten();
if expected_type.is_some() {
if var.is_none() {
scope_vars.insert(name.value.clone(), expected_type.clone());
expected_type
} else if expected_type.is_some() {
assert_eq!(var, expected_type, "type mismatch with variables");
expected_type
} else {
var
}
} else {
var
}
}
Expression::Call { Expression::Call {
function, function,
args: _, args,
value_type: _, value_type: _,
} => storage } => {
.functions let func = storage.functions.get(function).cloned().unwrap();
.get(function)
.map(|x| &x.return_type) for (i, arg) in args.iter().enumerate() {
.cloned() let arg_type = func.params[i].type_exp.clone();
.flatten(), // result is ignored, but need these to infer call arg types
type_inference_expression(arg, scope_vars, storage, Some(arg_type));
}
func.return_type
}
Expression::BinaryOp(lhs, op, rhs) => match op { Expression::BinaryOp(lhs, op, rhs) => match op {
ast::OpCode::Eq | ast::OpCode::Ne => { ast::OpCode::Eq | ast::OpCode::Ne => Some(TypeExp::Boolean),
Some(TypeExp::Boolean)
},
_ => { _ => {
let lhs_type = type_inference_expression(lhs, scope_vars, storage); let lhs_type =
let rhs_type = type_inference_expression(rhs, scope_vars, storage); type_inference_expression(lhs, scope_vars, storage, expected_type.clone());
let rhs_type = type_inference_expression(rhs, scope_vars, storage, expected_type);
if lhs_type.is_some() && rhs_type.is_some() { if lhs_type.is_some() && rhs_type.is_some() {
assert_eq!(lhs_type, rhs_type, "types should match"); assert_eq!(lhs_type, rhs_type, "types should match");