finished variable and function hoisting
This commit is contained in:
parent
4e13bb2ffc
commit
7e110a9560
|
@ -1,17 +1,17 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
Parser, Tokenizer,
|
||||||
err::*,
|
err::*,
|
||||||
|
ir::{Compiler, IR},
|
||||||
semantic::{self},
|
semantic::{self},
|
||||||
Parser, Statement, Tokenizer,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
file_name: String,
|
file_name: String,
|
||||||
source: String,
|
source: String,
|
||||||
pub program: Vec<Statement>,
|
pub program: Vec<IR>,
|
||||||
errors: Vec<Diagnostic>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
|
@ -31,20 +31,12 @@ impl Module {
|
||||||
let tokens = Tokenizer::new(source.chars()).filter(|t| t.0.is_meaningful());
|
let tokens = Tokenizer::new(source.chars()).filter(|t| t.0.is_meaningful());
|
||||||
let statements = Parser::new(tokens);
|
let statements = Parser::new(tokens);
|
||||||
let program = semantic::Analyzer::typecheck(statements.collect());
|
let program = semantic::Analyzer::typecheck(statements.collect());
|
||||||
let mut errors = vec![];
|
let mut compiler = Compiler::new();
|
||||||
|
compiler.compile(program);
|
||||||
Self {
|
Self {
|
||||||
file_name,
|
file_name,
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
program,
|
program: compiler.ir,
|
||||||
errors,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errors(&self) -> &[Diagnostic] {
|
|
||||||
&self.errors
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ok(&self) -> bool {
|
|
||||||
self.errors.len() == 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
234
src/ir.rs
234
src/ir.rs
|
@ -1,5 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
BinaryOp, Expression, ExpressionKind, Immediate, UnaryOp,
|
BinaryOp, Expression, ExpressionKind, Immediate, Statement, StatementKind,
|
||||||
|
UnaryOp,
|
||||||
semantic::{Type, VarKind, uid},
|
semantic::{Type, VarKind, uid},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,43 +16,246 @@ pub enum IR {
|
||||||
AssignGlobal { uid: uid },
|
AssignGlobal { uid: uid },
|
||||||
GetGlobal { uid: uid },
|
GetGlobal { uid: uid },
|
||||||
StartFunc { uid: uid },
|
StartFunc { uid: uid },
|
||||||
NewParam { uid: uid, type_: Type },
|
|
||||||
EndFunc,
|
EndFunc,
|
||||||
|
ReturnType { type_: Type },
|
||||||
|
NewParam { uid: uid, type_: Type },
|
||||||
|
Return,
|
||||||
|
Call { uid: uid },
|
||||||
|
Drop,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for IR {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
use IR::*;
|
||||||
|
match self {
|
||||||
|
BinOp { op, type_ } => write!(f, "{op} ({type_})"),
|
||||||
|
UnOp { op, type_ } => write!(f, "{op}, {type_}"),
|
||||||
|
Imm(immediate) => write!(f, "push {immediate}"),
|
||||||
|
NewLocal { uid, type_ } => write!(f, "local ${uid} = {type_}"),
|
||||||
|
AssignLocal { uid } => write!(f, "pop local ${uid}"),
|
||||||
|
GetLocal { uid } => write!(f, "push local ${uid}"),
|
||||||
|
NewGlobal { uid, type_ } => write!(f, "global ${uid} = {type_}"),
|
||||||
|
AssignGlobal { uid } => write!(f, "pop global ${uid}"),
|
||||||
|
GetGlobal { uid } => write!(f, "push global ${uid}"),
|
||||||
|
StartFunc { uid } => write!(f, "<function id=${uid}>"),
|
||||||
|
EndFunc => write!(f, "</function>"),
|
||||||
|
NewParam { uid, type_ } => write!(f, "param ${uid} = {type_}"),
|
||||||
|
Return => write!(f, "return"),
|
||||||
|
Call { uid } => write!(f, "call ${uid}"),
|
||||||
|
Drop => write!(f, "pop"),
|
||||||
|
ReturnType { type_ } => write!(f, "result {type_}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Compiler {
|
pub struct Compiler {
|
||||||
ir: Vec<IR>,
|
pub ir: Vec<IR>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compiler {
|
impl Compiler {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { ir: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(&mut self, block: Vec<Statement>) {
|
||||||
|
for s in block {
|
||||||
|
self.statement(s);
|
||||||
|
}
|
||||||
|
self.ir = self.hoist_functions();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statement(&mut self, statement: Statement) {
|
||||||
|
use StatementKind::*;
|
||||||
|
match statement.kind {
|
||||||
|
Declaration {
|
||||||
|
type_actual,
|
||||||
|
value,
|
||||||
|
varkind,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
match varkind {
|
||||||
|
VarKind::Global(uid) => self.ir.push(IR::NewGlobal {
|
||||||
|
uid,
|
||||||
|
type_: type_actual,
|
||||||
|
}),
|
||||||
|
VarKind::Local(uid) => self.ir.push(IR::NewLocal {
|
||||||
|
uid,
|
||||||
|
type_: type_actual,
|
||||||
|
}),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
self.expression(value);
|
||||||
|
match varkind {
|
||||||
|
VarKind::Global(uid) => self.ir.push(IR::AssignGlobal { uid }),
|
||||||
|
VarKind::Local(uid) => self.ir.push(IR::AssignLocal { uid }),
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Assignment {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
varkind,
|
||||||
|
} => {
|
||||||
|
self.expression(value);
|
||||||
|
match varkind {
|
||||||
|
VarKind::Global(uid) => self.ir.push(IR::AssignGlobal { uid }),
|
||||||
|
VarKind::Local(uid) => self.ir.push(IR::AssignLocal { uid }),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
If {
|
||||||
|
predicate,
|
||||||
|
block,
|
||||||
|
else_,
|
||||||
|
} => todo!(),
|
||||||
|
While { predicate, block } => todo!(),
|
||||||
|
Print(expression) => todo!(),
|
||||||
|
Expression(expression) => {
|
||||||
|
if expression.type_ == Type::Nothing {
|
||||||
|
self.expression(expression);
|
||||||
|
} else {
|
||||||
|
self.expression(expression);
|
||||||
|
self.ir.push(IR::Drop);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Block(statements) => {
|
||||||
|
for s in statements {
|
||||||
|
self.statement(s);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Error(diagnostic) => {
|
||||||
|
panic!("{}", diagnostic);
|
||||||
|
},
|
||||||
|
Return(expression) => {
|
||||||
|
if let Some(e) = expression {
|
||||||
|
self.expression(e);
|
||||||
|
}
|
||||||
|
self.ir.push(IR::Return);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expression(&mut self, expression: Expression) {
|
fn expression(&mut self, expression: Expression) {
|
||||||
use ExpressionKind::*;
|
use ExpressionKind::*;
|
||||||
match expression.kind {
|
match expression.kind {
|
||||||
Immediate(immediate) => {
|
Immediate(immediate) => {
|
||||||
self.ir.push(IR::Imm(immediate));
|
self.ir.push(IR::Imm(immediate));
|
||||||
},
|
},
|
||||||
Identifier(name, var_kind) => match var_kind {
|
Identifier(_, var_kind) => match var_kind {
|
||||||
VarKind::Global(uid) => self.ir.push(IR::GetGlobal { uid }),
|
VarKind::Global(uid) => self.ir.push(IR::GetGlobal { uid }),
|
||||||
VarKind::Local(uid) | VarKind::Param(uid) => {
|
VarKind::Local(uid) => self.ir.push(IR::GetLocal { uid }),
|
||||||
self.ir.push(IR::GetLocal { uid })
|
VarKind::Function(_) => {},
|
||||||
|
VarKind::Undefined => panic!("Undefined var not caught by typecheck"),
|
||||||
},
|
},
|
||||||
VarKind::Function(_) => todo!(),
|
Binary {
|
||||||
VarKind::Undefined => todo!(),
|
op,
|
||||||
|
mut left,
|
||||||
|
mut right,
|
||||||
|
} => {
|
||||||
|
left.type_ = Type::coerce(&expression.type_, &left.type_).unwrap();
|
||||||
|
right.type_ = Type::coerce(&expression.type_, &right.type_).unwrap();
|
||||||
|
assert!(&left.type_ == &right.type_);
|
||||||
|
self.expression(*left.clone());
|
||||||
|
self.expression(*right);
|
||||||
|
self.ir.push(IR::BinOp {
|
||||||
|
op,
|
||||||
|
type_: expression.type_,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Unary { op, mut child } => {
|
||||||
|
child.type_ = Type::coerce(&expression.type_, &child.type_).unwrap();
|
||||||
|
self.expression(*child);
|
||||||
|
self.ir.push(IR::UnOp {
|
||||||
|
op,
|
||||||
|
type_: expression.type_,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Parenthesis(mut e) => {
|
||||||
|
e.type_ = Type::coerce(&expression.type_, &e.type_).unwrap();
|
||||||
|
self.expression(*e);
|
||||||
},
|
},
|
||||||
Binary { op, left, right } => todo!(),
|
|
||||||
Unary { op, child } => todo!(),
|
|
||||||
Parenthesis(expression) => todo!(),
|
|
||||||
FunctionDef {
|
FunctionDef {
|
||||||
params,
|
params,
|
||||||
returns_str,
|
|
||||||
returns_actual,
|
returns_actual,
|
||||||
body,
|
body,
|
||||||
id,
|
id,
|
||||||
} => todo!(),
|
..
|
||||||
FunctionCall { callee, args } => todo!(),
|
} => {
|
||||||
StructDef(vec) => todo!(),
|
self.ir.push(IR::StartFunc { uid: id });
|
||||||
|
for (i, p) in params.iter().enumerate() {
|
||||||
|
self.ir.push(IR::NewParam {
|
||||||
|
uid: i as uid,
|
||||||
|
type_: p.type_actual.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
self.ir.push(IR::ReturnType {
|
||||||
|
type_: returns_actual,
|
||||||
|
});
|
||||||
|
for s in body {
|
||||||
|
self.statement(s);
|
||||||
|
}
|
||||||
|
self.ir.push(IR::EndFunc);
|
||||||
|
},
|
||||||
|
FunctionCall { callee, args } => {
|
||||||
|
let Type::FunctionDef { id, .. } = callee.type_ else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
for arg in args {
|
||||||
|
self.expression(arg);
|
||||||
|
}
|
||||||
|
self.ir.push(IR::Call { uid: id });
|
||||||
|
},
|
||||||
|
StructDef(_) => {},
|
||||||
StructLiteral { name, args } => todo!(),
|
StructLiteral { name, args } => todo!(),
|
||||||
Field { namespace, field } => todo!(),
|
Field { namespace, field } => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hoist_functions(&self) -> Vec<IR> {
|
||||||
|
let mut functions = vec![(vec![], vec![])];
|
||||||
|
let mut result = vec![];
|
||||||
|
for index in 0..self.ir.len() {
|
||||||
|
let ir = self.ir.get(index).unwrap();
|
||||||
|
match ir {
|
||||||
|
IR::StartFunc { .. } => {
|
||||||
|
functions.push((vec![], vec![]));
|
||||||
|
},
|
||||||
|
IR::EndFunc => {
|
||||||
|
let (inits, instr) = functions.pop().unwrap();
|
||||||
|
for ir in inits {
|
||||||
|
result.push(ir);
|
||||||
|
}
|
||||||
|
for ir in instr {
|
||||||
|
result.push(ir);
|
||||||
|
}
|
||||||
|
result.push(IR::EndFunc);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
// Push instruction to correct stack
|
||||||
|
let (inits, instr) = functions.last_mut().unwrap();
|
||||||
|
match ir {
|
||||||
|
IR::NewLocal { .. }
|
||||||
|
| IR::NewGlobal { .. }
|
||||||
|
| IR::NewParam { .. }
|
||||||
|
| IR::StartFunc { .. } => {
|
||||||
|
inits.push(ir.clone());
|
||||||
|
},
|
||||||
|
_ => instr.push(ir.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Initialize globals
|
||||||
|
let (inits, instr) = functions.pop().unwrap();
|
||||||
|
for ir in inits {
|
||||||
|
result.push(ir);
|
||||||
|
}
|
||||||
|
// The main function (index 0)
|
||||||
|
result.push(IR::StartFunc { uid: 0 });
|
||||||
|
for ir in instr {
|
||||||
|
result.push(ir);
|
||||||
|
}
|
||||||
|
result.push(IR::EndFunc);
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,12 +86,10 @@ fn prints(st: &Statement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
test_expression("asdf.asdf()");
|
//test_expression("a.b.c()");
|
||||||
/*
|
|
||||||
let module = frontend::Module::from_file("./demo.hal")?;
|
let module = frontend::Module::from_file("./demo.hal")?;
|
||||||
for s in &module.program {
|
for s in &module.program {
|
||||||
prints(s);
|
println!("{s}");
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,17 @@ pub enum Immediate {
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Immediate {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Immediate::Integer(i) => write!(f, "{i}"),
|
||||||
|
Immediate::Real(r) => write!(f, "{r}"),
|
||||||
|
Immediate::String(s) => write!(f, "{s}"),
|
||||||
|
Immediate::Boolean(b) => write!(f, "{b}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ExpressionKind {
|
pub enum ExpressionKind {
|
||||||
Immediate(Immediate),
|
Immediate(Immediate),
|
||||||
|
@ -99,7 +110,7 @@ impl std::fmt::Debug for ExpressionKind {
|
||||||
write!(f, "({token:?} {child:?})")
|
write!(f, "({token:?} {child:?})")
|
||||||
},
|
},
|
||||||
e::Identifier(i, _) => write!(f, "{i}"),
|
e::Identifier(i, _) => write!(f, "{i}"),
|
||||||
e::FunctionCall { callee, args } => {
|
e::FunctionCall { callee, args, .. } => {
|
||||||
write!(f, "({callee:?} call {args:?})")
|
write!(f, "({callee:?} call {args:?})")
|
||||||
},
|
},
|
||||||
e::Field { namespace, field } => {
|
e::Field { namespace, field } => {
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub enum StatementKind {
|
||||||
Print(Expression),
|
Print(Expression),
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
Block(Vec<Statement>),
|
Block(Vec<Statement>),
|
||||||
|
Return(Option<Expression>),
|
||||||
Error(Diagnostic),
|
Error(Diagnostic),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +162,19 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// Return
|
||||||
|
(Token(t::Return, span2), _) => {
|
||||||
|
span = span + span2;
|
||||||
|
self.skip(1);
|
||||||
|
let expr = self.expression(0).ok();
|
||||||
|
if let Some(expr) = &expr {
|
||||||
|
span = span + expr.span;
|
||||||
|
}
|
||||||
|
Statement {
|
||||||
|
span,
|
||||||
|
kind: s::Return(expr),
|
||||||
|
}
|
||||||
|
},
|
||||||
// Expression
|
// Expression
|
||||||
(Token(_, span2), _) => {
|
(Token(_, span2), _) => {
|
||||||
span = span + span2;
|
span = span + span2;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
|
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
|
||||||
StatementKind, UnaryOp,
|
StatementKind, UnaryOp,
|
||||||
|
@ -52,7 +54,12 @@ impl Analyzer {
|
||||||
} else {
|
} else {
|
||||||
Type::Ambiguous
|
Type::Ambiguous
|
||||||
};
|
};
|
||||||
let mut value = self.expression(value.into())?;
|
let type_hint = if let Type::Ambiguous = type_lhs {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(type_lhs.clone())
|
||||||
|
};
|
||||||
|
let mut value = self.expression(value.into(), type_hint)?;
|
||||||
let type_actual = Type::coerce(&type_lhs, &value.type_)
|
let type_actual = Type::coerce(&type_lhs, &value.type_)
|
||||||
.reason(format!(
|
.reason(format!(
|
||||||
"Expected type '{:?}', found type '{:?}'",
|
"Expected type '{:?}', found type '{:?}'",
|
||||||
|
@ -82,7 +89,8 @@ impl Analyzer {
|
||||||
.reason(format!("Cannot assign to immutable '{}'", name))
|
.reason(format!("Cannot assign to immutable '{}'", name))
|
||||||
.span(&stmt.span);
|
.span(&stmt.span);
|
||||||
}
|
}
|
||||||
let mut value = *self.expression(value.into())?;
|
let mut value =
|
||||||
|
*self.expression(value.into(), Some(symbol.type_.clone()))?;
|
||||||
let type_actual =
|
let type_actual =
|
||||||
Type::coerce(&symbol.type_, &value.type_).span(&stmt.span)?;
|
Type::coerce(&symbol.type_, &value.type_).span(&stmt.span)?;
|
||||||
value.type_ = type_actual;
|
value.type_ = type_actual;
|
||||||
|
@ -98,7 +106,8 @@ impl Analyzer {
|
||||||
else_,
|
else_,
|
||||||
} => {
|
} => {
|
||||||
self.table.start_block();
|
self.table.start_block();
|
||||||
let predicate = *self.expression(predicate.into())?;
|
let predicate =
|
||||||
|
*self.expression(predicate.into(), Some(Type::Prim(p::boolean)))?;
|
||||||
Type::coerce(&Type::Prim(p::boolean), &predicate.type_)
|
Type::coerce(&Type::Prim(p::boolean), &predicate.type_)
|
||||||
.span(&predicate.span)?;
|
.span(&predicate.span)?;
|
||||||
let block = self.block(block);
|
let block = self.block(block);
|
||||||
|
@ -116,7 +125,8 @@ impl Analyzer {
|
||||||
},
|
},
|
||||||
s::While { predicate, block } => {
|
s::While { predicate, block } => {
|
||||||
self.table.start_block();
|
self.table.start_block();
|
||||||
let predicate = *self.expression(predicate.into())?;
|
let predicate =
|
||||||
|
*self.expression(predicate.into(), Some(Type::Prim(p::boolean)))?;
|
||||||
Type::coerce(&Type::Prim(p::boolean), &predicate.type_)
|
Type::coerce(&Type::Prim(p::boolean), &predicate.type_)
|
||||||
.span(&predicate.span)?;
|
.span(&predicate.span)?;
|
||||||
let block = self.block(block);
|
let block = self.block(block);
|
||||||
|
@ -124,10 +134,13 @@ impl Analyzer {
|
||||||
self.table.end_block();
|
self.table.end_block();
|
||||||
},
|
},
|
||||||
s::Print(e) => {
|
s::Print(e) => {
|
||||||
stmt.kind = s::Print(*self.expression(e.into())?);
|
stmt.kind = s::Print(*self.expression(e.into(), None)?);
|
||||||
},
|
},
|
||||||
s::Expression(e) => {
|
s::Expression(e) => {
|
||||||
stmt.kind = s::Expression(*self.expression(e.into())?);
|
let mut expr = *self.expression(e.into(), None)?;
|
||||||
|
expr.type_ =
|
||||||
|
Type::coerce(&Type::Ambiguous, &expr.type_).span(&expr.span)?;
|
||||||
|
stmt.kind = s::Expression(expr);
|
||||||
},
|
},
|
||||||
s::Block(block) => {
|
s::Block(block) => {
|
||||||
self.table.start_block();
|
self.table.start_block();
|
||||||
|
@ -136,6 +149,20 @@ impl Analyzer {
|
||||||
self.table.end_block();
|
self.table.end_block();
|
||||||
},
|
},
|
||||||
s::Error(e) => return Err(e),
|
s::Error(e) => return Err(e),
|
||||||
|
s::Return(mut expression) => {
|
||||||
|
let return_type = self.table.get_return_type().span(&stmt.span)?;
|
||||||
|
let type_ = match expression {
|
||||||
|
Some(e) => {
|
||||||
|
let e = self.expression(e.into(), Some(return_type.clone()))?;
|
||||||
|
let type_ = e.type_.clone();
|
||||||
|
expression = Some(*e);
|
||||||
|
type_
|
||||||
|
},
|
||||||
|
None => Type::Nothing,
|
||||||
|
};
|
||||||
|
Type::coerce(&return_type, &type_).span(&stmt.span)?;
|
||||||
|
stmt.kind = s::Return(expression);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
Ok(stmt)
|
Ok(stmt)
|
||||||
}
|
}
|
||||||
|
@ -143,7 +170,9 @@ impl Analyzer {
|
||||||
fn expression(
|
fn expression(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut expr: Box<Expression>,
|
mut expr: Box<Expression>,
|
||||||
|
type_hint: Option<Type>,
|
||||||
) -> Result<Box<Expression>> {
|
) -> Result<Box<Expression>> {
|
||||||
|
// TODO implement type hinting
|
||||||
use ExpressionKind as e;
|
use ExpressionKind as e;
|
||||||
use Immediate as i;
|
use Immediate as i;
|
||||||
use Primitive as p;
|
use Primitive as p;
|
||||||
|
@ -160,20 +189,20 @@ impl Analyzer {
|
||||||
self.table.find_symbol(i)?.type_
|
self.table.find_symbol(i)?.type_
|
||||||
},
|
},
|
||||||
e::Binary { op, left, right } => {
|
e::Binary { op, left, right } => {
|
||||||
let left = self.expression(left)?;
|
let left = self.expression(left, type_hint.clone())?;
|
||||||
let right = self.expression(right)?;
|
let right = self.expression(right, type_hint.clone())?;
|
||||||
let type_ = Type::binary_op(&left.type_, op, &right.type_)?;
|
let type_ = Type::binary_op(&left.type_, op, &right.type_)?;
|
||||||
expr.kind = e::Binary { left, right, op };
|
expr.kind = e::Binary { left, right, op };
|
||||||
type_
|
type_
|
||||||
},
|
},
|
||||||
e::Unary { op, child } => {
|
e::Unary { op, child } => {
|
||||||
let child = self.expression(child)?;
|
let child = self.expression(child, type_hint.clone())?;
|
||||||
let type_ = Type::unary_op(op, &child.type_)?;
|
let type_ = Type::unary_op(op, &child.type_)?;
|
||||||
expr.kind = e::Unary { child, op };
|
expr.kind = e::Unary { child, op };
|
||||||
type_
|
type_
|
||||||
},
|
},
|
||||||
e::Parenthesis(inner) => {
|
e::Parenthesis(inner) => {
|
||||||
let inner = self.expression(inner)?;
|
let inner = self.expression(inner, type_hint.clone())?;
|
||||||
let type_ = inner.type_.clone();
|
let type_ = inner.type_.clone();
|
||||||
expr.kind = e::Parenthesis(inner);
|
expr.kind = e::Parenthesis(inner);
|
||||||
type_
|
type_
|
||||||
|
@ -183,19 +212,19 @@ impl Analyzer {
|
||||||
returns_str,
|
returns_str,
|
||||||
mut returns_actual,
|
mut returns_actual,
|
||||||
body,
|
body,
|
||||||
id,
|
id: _,
|
||||||
} => {
|
} => {
|
||||||
self.table.start_func();
|
returns_actual = match &returns_str {
|
||||||
|
Some(s) => self.table.get_type(s).span(&expr.span)?,
|
||||||
|
None => Type::Nothing,
|
||||||
|
};
|
||||||
|
let id = self.table.start_func(returns_actual.clone());
|
||||||
for p in &mut params {
|
for p in &mut params {
|
||||||
p.type_actual = self.table.get_type(&p.type_str).span(&expr.span)?;
|
p.type_actual = self.table.get_type(&p.type_str).span(&expr.span)?;
|
||||||
self
|
self
|
||||||
.table
|
.table
|
||||||
.define_param(p.name.clone(), p.type_actual.clone())?;
|
.define_param(p.name.clone(), p.type_actual.clone())?;
|
||||||
}
|
}
|
||||||
returns_actual = match &returns_str {
|
|
||||||
Some(s) => self.table.get_type(s).span(&expr.span)?,
|
|
||||||
None => Type::Nothing,
|
|
||||||
};
|
|
||||||
let body = self.block(body);
|
let body = self.block(body);
|
||||||
self.table.end_func();
|
self.table.end_func();
|
||||||
expr.kind = e::FunctionDef {
|
expr.kind = e::FunctionDef {
|
||||||
|
@ -205,17 +234,20 @@ impl Analyzer {
|
||||||
body,
|
body,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
Type::Function {
|
Type::FunctionDef {
|
||||||
params: params.into_iter().map(|p| p.type_actual).collect(),
|
params: params.into_iter().map(|p| p.type_actual).collect(),
|
||||||
returns: returns_actual.into(),
|
returns: returns_actual.into(),
|
||||||
|
id,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
e::FunctionCall { callee, mut args } => {
|
e::FunctionCall { callee, mut args } => {
|
||||||
let callee = self.expression(callee)?;
|
let callee = self.expression(callee, None)?;
|
||||||
// Check that this is actually a function
|
// Check that this is actually a function
|
||||||
let Type::Function {
|
// TODO allow function references to be called
|
||||||
|
let Type::FunctionDef {
|
||||||
ref params,
|
ref params,
|
||||||
ref returns,
|
ref returns,
|
||||||
|
..
|
||||||
} = callee.type_
|
} = callee.type_
|
||||||
else {
|
else {
|
||||||
return error()
|
return error()
|
||||||
|
@ -234,7 +266,8 @@ impl Analyzer {
|
||||||
}
|
}
|
||||||
// Check for correct arg types
|
// Check for correct arg types
|
||||||
for (expect, actual) in params.iter().zip(args.iter_mut()) {
|
for (expect, actual) in params.iter().zip(args.iter_mut()) {
|
||||||
*actual = *self.expression(actual.clone().into())?;
|
*actual =
|
||||||
|
*self.expression(actual.clone().into(), Some(expect.clone()))?;
|
||||||
let coerced_type = Type::coerce(expect, &actual.type_);
|
let coerced_type = Type::coerce(expect, &actual.type_);
|
||||||
if let Ok(t) = coerced_type {
|
if let Ok(t) = coerced_type {
|
||||||
actual.type_ = t;
|
actual.type_ = t;
|
||||||
|
@ -296,7 +329,7 @@ impl Analyzer {
|
||||||
}
|
}
|
||||||
let argspan = argexpr.span;
|
let argspan = argexpr.span;
|
||||||
let mut arg = *self
|
let mut arg = *self
|
||||||
.expression(argexpr.clone().into())
|
.expression(argexpr.clone().into(), Some(ptype.clone()))
|
||||||
.trace_span(expr.span, "while parsing struct literal")?;
|
.trace_span(expr.span, "while parsing struct literal")?;
|
||||||
let coerced_type = Type::coerce(ptype, &arg.type_);
|
let coerced_type = Type::coerce(ptype, &arg.type_);
|
||||||
if let Ok(t) = coerced_type {
|
if let Ok(t) = coerced_type {
|
||||||
|
@ -318,7 +351,7 @@ impl Analyzer {
|
||||||
Type::Struct(params)
|
Type::Struct(params)
|
||||||
},
|
},
|
||||||
e::Field { namespace, field } => {
|
e::Field { namespace, field } => {
|
||||||
let namespace = self.expression(namespace)?;
|
let namespace = self.expression(namespace, None)?;
|
||||||
// Check that namespace is struct
|
// Check that namespace is struct
|
||||||
// TODO: fields in other types
|
// TODO: fields in other types
|
||||||
let Type::Struct(ref params) = namespace.type_ else {
|
let Type::Struct(ref params) = namespace.type_ else {
|
||||||
|
@ -350,6 +383,14 @@ impl Analyzer {
|
||||||
type_
|
type_
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
/*
|
||||||
|
if let Some(expect) = &type_hint {
|
||||||
|
if let Type::Ambiguous = expect {
|
||||||
|
} else {
|
||||||
|
expr.type_ = Type::coerce(expect, &expr.type_)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
expr.type_ = type_;
|
expr.type_ = type_;
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ pub type uid = u32;
|
||||||
pub enum VarKind {
|
pub enum VarKind {
|
||||||
Global(uid),
|
Global(uid),
|
||||||
Local(uid),
|
Local(uid),
|
||||||
Param(uid),
|
|
||||||
Function(uid),
|
Function(uid),
|
||||||
Undefined,
|
Undefined,
|
||||||
}
|
}
|
||||||
|
@ -22,10 +21,7 @@ pub enum VarKind {
|
||||||
impl VarKind {
|
impl VarKind {
|
||||||
pub fn unwrap(self) -> uid {
|
pub fn unwrap(self) -> uid {
|
||||||
match self {
|
match self {
|
||||||
VarKind::Global(i)
|
VarKind::Global(i) | VarKind::Local(i) | VarKind::Function(i) => i,
|
||||||
| VarKind::Local(i)
|
|
||||||
| VarKind::Param(i)
|
|
||||||
| VarKind::Function(i) => i,
|
|
||||||
VarKind::Undefined => unreachable!("Failed unwrapping uid"),
|
VarKind::Undefined => unreachable!("Failed unwrapping uid"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +39,7 @@ pub struct Symbol {
|
||||||
pub enum Definition {
|
pub enum Definition {
|
||||||
Symbol(Symbol),
|
Symbol(Symbol),
|
||||||
BlockStart,
|
BlockStart,
|
||||||
FuncStart,
|
FuncStart(Type),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(array: &mut [uid]) -> uid {
|
fn next(array: &mut [uid]) -> uid {
|
||||||
|
@ -59,7 +55,7 @@ pub struct SymbolTable {
|
||||||
nesting: usize,
|
nesting: usize,
|
||||||
local_varno: Vec<uid>,
|
local_varno: Vec<uid>,
|
||||||
global_varno: Vec<uid>,
|
global_varno: Vec<uid>,
|
||||||
funcno: Vec<uid>,
|
funcno: uid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolTable {
|
impl SymbolTable {
|
||||||
|
@ -69,7 +65,7 @@ impl SymbolTable {
|
||||||
nesting: 0,
|
nesting: 0,
|
||||||
global_varno: vec![0],
|
global_varno: vec![0],
|
||||||
local_varno: vec![0],
|
local_varno: vec![0],
|
||||||
funcno: vec![0],
|
funcno: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +76,7 @@ impl SymbolTable {
|
||||||
mutable: bool,
|
mutable: bool,
|
||||||
) -> Result<VarKind> {
|
) -> Result<VarKind> {
|
||||||
let kind = match type_ {
|
let kind = match type_ {
|
||||||
Type::Prim(_) | Type::Struct(_) => {
|
Type::Prim(_) | Type::Struct(_) | Type::FunctionRef { .. } => {
|
||||||
if self.nesting == 0 {
|
if self.nesting == 0 {
|
||||||
VarKind::Global(next(&mut self.global_varno))
|
VarKind::Global(next(&mut self.global_varno))
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,9 +89,9 @@ impl SymbolTable {
|
||||||
}
|
}
|
||||||
VarKind::Undefined
|
VarKind::Undefined
|
||||||
},
|
},
|
||||||
Type::Function { .. } => {
|
Type::FunctionDef { id, .. } => {
|
||||||
if !mutable {
|
if !mutable {
|
||||||
VarKind::Function(next(&mut self.funcno))
|
VarKind::Function(id)
|
||||||
} else {
|
} else {
|
||||||
return error().reason("Function declaration must be immutable");
|
return error().reason("Function declaration must be immutable");
|
||||||
}
|
}
|
||||||
|
@ -112,7 +108,7 @@ impl SymbolTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_param(&mut self, name: String, type_: Type) -> Result<VarKind> {
|
fn define_param(&mut self, name: String, type_: Type) -> Result<VarKind> {
|
||||||
let kind = VarKind::Param(next(&mut self.local_varno));
|
let kind = VarKind::Local(next(&mut self.local_varno));
|
||||||
self.syms.push(Definition::Symbol(Symbol {
|
self.syms.push(Definition::Symbol(Symbol {
|
||||||
name,
|
name,
|
||||||
type_,
|
type_,
|
||||||
|
@ -122,17 +118,29 @@ impl SymbolTable {
|
||||||
Ok(kind)
|
Ok(kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_func(&mut self) {
|
fn start_func(&mut self, returns: Type) -> uid {
|
||||||
self.nesting += 1;
|
self.nesting += 1;
|
||||||
self.local_varno.push(0);
|
self.local_varno.push(0);
|
||||||
self.syms.push(Definition::FuncStart);
|
self.syms.push(Definition::FuncStart(returns));
|
||||||
|
let old = self.funcno;
|
||||||
|
self.funcno += 1;
|
||||||
|
old
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_return_type(&mut self) -> Result<Type> {
|
||||||
|
for def in &self.syms {
|
||||||
|
if let Definition::FuncStart(t) = def {
|
||||||
|
return Ok(t.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error().reason("Return outside of function")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_func(&mut self) {
|
fn end_func(&mut self) {
|
||||||
self.nesting -= 1;
|
self.nesting -= 1;
|
||||||
self.local_varno.pop();
|
self.local_varno.pop();
|
||||||
while !self.syms.is_empty() {
|
while !self.syms.is_empty() {
|
||||||
if let Some(Definition::FuncStart) = self.syms.pop() {
|
if let Some(Definition::FuncStart(_)) = self.syms.pop() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,18 +162,17 @@ impl SymbolTable {
|
||||||
|
|
||||||
fn find_symbol(&self, find_name: &str) -> Result<Symbol> {
|
fn find_symbol(&self, find_name: &str) -> Result<Symbol> {
|
||||||
let mut nesting = self.nesting;
|
let mut nesting = self.nesting;
|
||||||
println!("Looking for {find_name}, scope = {nesting}");
|
|
||||||
for s in self.syms.iter().rev() {
|
for s in self.syms.iter().rev() {
|
||||||
match s {
|
match s {
|
||||||
Definition::Symbol(sym)
|
Definition::Symbol(sym)
|
||||||
// Only search function local and global scope
|
// Only search function local and global scope
|
||||||
if nesting == self.nesting || nesting == 0 => {
|
if nesting == self.nesting || nesting == 0 => {
|
||||||
println!("{}, {:?}, {nesting}", sym.name, sym.type_);
|
// Convert function definition to function reference
|
||||||
if find_name == sym.name {
|
if find_name == sym.name {
|
||||||
return Ok(sym.clone());
|
return Ok(sym.clone());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Definition::FuncStart => {
|
Definition::FuncStart(_) => {
|
||||||
nesting -= 1;
|
nesting -= 1;
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
semantic::{Symbol, SymbolTable},
|
|
||||||
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
|
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
|
||||||
StatementKind, UnaryOp,
|
StatementKind, UnaryOp,
|
||||||
|
semantic::{Symbol, SymbolTable},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::primitives::*;
|
use super::{primitives::*, uid};
|
||||||
use crate::err::*;
|
use crate::err::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -14,10 +14,35 @@ pub enum Type {
|
||||||
Prim(Primitive),
|
Prim(Primitive),
|
||||||
Struct(Vec<Parameter>),
|
Struct(Vec<Parameter>),
|
||||||
StructDef(Vec<Parameter>),
|
StructDef(Vec<Parameter>),
|
||||||
Function {
|
FunctionRef {
|
||||||
params: Vec<Type>,
|
params: Vec<Type>,
|
||||||
returns: Box<Type>,
|
returns: Box<Type>,
|
||||||
},
|
},
|
||||||
|
FunctionDef {
|
||||||
|
params: Vec<Type>,
|
||||||
|
returns: Box<Type>,
|
||||||
|
id: uid,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Type {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Type::Ambiguous => write!(f, "ambiguous"),
|
||||||
|
Type::Nothing => write!(f, "nothing"),
|
||||||
|
Type::Prim(primitive) => write!(f, "{primitive}"),
|
||||||
|
Type::Struct(vec) => write!(f, "struct {vec:?}"),
|
||||||
|
Type::StructDef(vec) => write!(f, "struct definition"),
|
||||||
|
Type::FunctionRef { params, returns } => {
|
||||||
|
write!(f, "({params:?}) -> {returns}")
|
||||||
|
},
|
||||||
|
Type::FunctionDef {
|
||||||
|
params,
|
||||||
|
returns,
|
||||||
|
id,
|
||||||
|
} => write!(f, "({params:?}) -> {returns}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Type {
|
impl PartialEq for Type {
|
||||||
|
@ -31,15 +56,16 @@ impl PartialEq for Type {
|
||||||
.map(|p| p.type_actual.clone())
|
.map(|p| p.type_actual.clone())
|
||||||
.eq(p2.iter().map(|p| p.type_actual.clone())),
|
.eq(p2.iter().map(|p| p.type_actual.clone())),
|
||||||
(
|
(
|
||||||
Function {
|
FunctionRef {
|
||||||
params: p1,
|
params: p1,
|
||||||
returns: r1,
|
returns: r1,
|
||||||
},
|
},
|
||||||
Function {
|
FunctionRef {
|
||||||
params: p2,
|
params: p2,
|
||||||
returns: r2,
|
returns: r2,
|
||||||
},
|
},
|
||||||
) => p1.iter().eq(p2.iter()) && r1 == r2,
|
) => p1.iter().eq(p2.iter()) && r1 == r2,
|
||||||
|
(FunctionDef { id: id1, .. }, FunctionDef { id: id2, .. }) => id1 == id2,
|
||||||
(Nothing, Nothing) => true,
|
(Nothing, Nothing) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -88,6 +114,7 @@ impl Type {
|
||||||
let (p1, p2) = Primitive::coerce_ambiguous(*p1, *p2);
|
let (p1, p2) = Primitive::coerce_ambiguous(*p1, *p2);
|
||||||
if p1 != p2 { e() } else { Ok(Type::Prim(p1)) }
|
if p1 != p2 { e() } else { Ok(Type::Prim(p1)) }
|
||||||
},
|
},
|
||||||
|
(t1, t2) if t1 == t2 => Ok(t1.clone()),
|
||||||
_ => e(),
|
_ => e(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue