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 parser = grammar::ProgramParser::new();
let mut ast = parser.parse(lexer)?;
type_analysis::type_inference(&mut ast);
type_analysis::type_inference2(&mut ast);
println!("{ast:#?}");
}
Commands::Compile {

View file

@ -10,14 +10,7 @@ struct Storage {
functions: HashMap<String, Function>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TypeGuess {
/// The guess comes from a strong source: call args, return type, let binding.
Strong,
///
Weak,
}
// this works, but need to find a way to store the found info + handle literal integer types (or not?)
pub fn type_inference2(ast: &mut ast::Program) {
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() {
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()));
}
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());
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() {
scope_vars.insert(name.clone(), exp_type);
@ -94,7 +91,7 @@ fn type_inference_scope(
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();
if var.is_none() {
@ -108,9 +105,45 @@ fn type_inference_scope(
body,
else_body,
} => {
let cond_type = type_inference_expression(condition, &scope_vars, storage);
},
Statement::Return(_) => todo!(),
type_inference_expression(
condition,
&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::Struct(_) => unreachable!(),
}
@ -121,8 +154,9 @@ fn type_inference_scope(
fn type_inference_expression(
exp: &Expression,
scope_vars: &HashMap<String, Option<TypeExp>>,
scope_vars: &mut HashMap<String, Option<TypeExp>>,
storage: &Storage,
expected_type: Option<TypeExp>,
) -> Option<TypeExp> {
match exp {
Expression::Literal(lit) => {
@ -148,24 +182,44 @@ fn type_inference_expression(
Expression::Variable {
name,
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 {
function,
args: _,
args,
value_type: _,
} => storage
.functions
.get(function)
.map(|x| &x.return_type)
.cloned()
.flatten(),
} => {
let func = storage.functions.get(function).cloned().unwrap();
for (i, arg) in args.iter().enumerate() {
let arg_type = func.params[i].type_exp.clone();
// 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 {
ast::OpCode::Eq | ast::OpCode::Ne => {
Some(TypeExp::Boolean)
},
ast::OpCode::Eq | ast::OpCode::Ne => Some(TypeExp::Boolean),
_ => {
let lhs_type = type_inference_expression(lhs, scope_vars, storage);
let rhs_type = type_inference_expression(rhs, scope_vars, storage);
let lhs_type =
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() {
assert_eq!(lhs_type, rhs_type, "types should match");