Splitting up semantics into lots of phases
This commit is contained in:
parent
22c78a9945
commit
552496c0f9
15
demo.hal
15
demo.hal
|
@ -1,2 +1,13 @@
|
|||
b := 10.0 / 5.0;
|
||||
c := b;
|
||||
c :: C{r: 10.0};
|
||||
|
||||
A :: struct {
|
||||
b: B,
|
||||
}
|
||||
|
||||
B :: struct {
|
||||
c: C,
|
||||
}
|
||||
|
||||
C :: struct {
|
||||
r: real,
|
||||
}
|
||||
|
|
|
@ -99,10 +99,13 @@ fn typecheck(input: &'static str) -> Vec<Statement> {
|
|||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
for s in parse(include_str!("../demo.hal")) {
|
||||
/*
|
||||
for s in typecheck(include_str!("../demo.hal")) {
|
||||
println!("{s:#?}");
|
||||
println!("------------------");
|
||||
}
|
||||
*/
|
||||
typecheck(include_str!("../demo.hal"));
|
||||
//let module = frontend::Module::from_file("./demo.hal")?;
|
||||
//module.write_to("test");
|
||||
Ok(())
|
||||
|
|
|
@ -20,6 +20,7 @@ pub enum Immediate {
|
|||
Integer(String, Base),
|
||||
Real(String),
|
||||
String(String),
|
||||
Glyph(char),
|
||||
Boolean(bool),
|
||||
}
|
||||
|
||||
|
@ -27,6 +28,7 @@ impl std::fmt::Display for Immediate {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Immediate::Integer(i, b) => write!(f, "{i} ({b:?})"),
|
||||
Immediate::Glyph(c) => write!(f, "{c}"),
|
||||
Immediate::Real(r) => write!(f, "{r}"),
|
||||
Immediate::String(s) => write!(f, "{s}"),
|
||||
Immediate::Boolean(b) => write!(f, "{b}"),
|
||||
|
@ -53,7 +55,7 @@ pub enum ExpressionKind {
|
|||
returns_str: Option<String>,
|
||||
returns_actual: Type,
|
||||
body: Vec<Statement>,
|
||||
id: UID,
|
||||
id: usize,
|
||||
},
|
||||
FunctionCall {
|
||||
callee: Box<Expression>,
|
||||
|
@ -61,7 +63,7 @@ pub enum ExpressionKind {
|
|||
is_reference: bool,
|
||||
id: UID,
|
||||
},
|
||||
StructDef(Vec<Parameter>),
|
||||
StructDef(Vec<Parameter>, usize),
|
||||
StructLiteral {
|
||||
name: String,
|
||||
args: Vec<(String, Expression)>,
|
||||
|
@ -118,7 +120,7 @@ impl std::fmt::Debug for ExpressionKind {
|
|||
} => {
|
||||
write!(f, "(fn({params:?}) -> {returns_actual:?})")
|
||||
},
|
||||
e::StructDef(params) => write!(f, "struct {{ {params:?} }}"),
|
||||
e::StructDef(params, _) => write!(f, "struct {{ {params:?} }}"),
|
||||
e::StructLiteral { name, args } => write!(f, "{name} {{ {args:?} }}"),
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +136,6 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
pub fn expression(&mut self, precedence: Precedence) -> Result<Expression> {
|
||||
use ExpressionKind as e;
|
||||
use TokenKind as t;
|
||||
let mut type_ = Type::Ambiguous;
|
||||
let next = self.peek(0)?;
|
||||
// Unary prefix expression
|
||||
let mut current = if let Ok(operator) = UnaryOp::try_from(&next.0) {
|
||||
|
@ -272,33 +273,31 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
use ExpressionKind as e;
|
||||
use Immediate as im;
|
||||
use TokenKind as t;
|
||||
let mut type_ = Type::Ambiguous;
|
||||
let next = self.peek(0)?;
|
||||
let mut span = next.1;
|
||||
let kind = match next.0 {
|
||||
t::IntegerLiteral(i, b) => {
|
||||
self.skip(1);
|
||||
type_ = Type::Prim(Primitive::integer_ambiguous);
|
||||
e::Immediate(im::Integer(i, b))
|
||||
},
|
||||
t::FloatLiteral(f) => {
|
||||
self.skip(1);
|
||||
type_ = Type::Prim(Primitive::real_ambiguous);
|
||||
e::Immediate(im::Real(f))
|
||||
},
|
||||
t::StringLiteral(s) => {
|
||||
self.skip(1);
|
||||
type_ = Type::Prim(Primitive::string);
|
||||
e::Immediate(im::String(s))
|
||||
},
|
||||
t::GlyphLiteral(c) => {
|
||||
self.skip(1);
|
||||
e::Immediate(im::Glyph(c))
|
||||
},
|
||||
t::True => {
|
||||
self.skip(1);
|
||||
type_ = Type::Prim(Primitive::boolean);
|
||||
e::Immediate(im::Boolean(true))
|
||||
},
|
||||
t::False => {
|
||||
self.skip(1);
|
||||
type_ = Type::Prim(Primitive::boolean);
|
||||
e::Immediate(im::Boolean(false))
|
||||
},
|
||||
// Function definition
|
||||
|
@ -355,7 +354,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
returns_str,
|
||||
returns_actual: Type::Ambiguous,
|
||||
body,
|
||||
id: "".into(),
|
||||
id: usize::MAX,
|
||||
}
|
||||
},
|
||||
// Struct definition
|
||||
|
@ -386,7 +385,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
}
|
||||
}
|
||||
self.eat(t::RightBrace)?;
|
||||
e::StructDef(params)
|
||||
e::StructDef(params, usize::MAX)
|
||||
},
|
||||
// Struct literal
|
||||
t::Identifier(name) if self.look(1, t::LeftBrace).is_ok() => {
|
||||
|
@ -435,7 +434,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
.reason(format!("Expected expression, found {}", next.0));
|
||||
},
|
||||
};
|
||||
Ok(Expression::new(kind, span, type_))
|
||||
Ok(Expression::new(kind, span, Type::Ambiguous))
|
||||
}
|
||||
|
||||
fn identifier(&mut self) -> Result<(String, Span)> {
|
||||
|
|
|
@ -74,7 +74,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
let no_semicolon =
|
||||
if let ExpressionKind::FunctionDef { .. } = value.kind {
|
||||
true
|
||||
} else if let ExpressionKind::StructDef(_) = value.kind {
|
||||
} else if let ExpressionKind::StructDef(_, _) = value.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -1,85 +1,199 @@
|
|||
use crate::{
|
||||
Expression, ExpressionKind, Statement, err::*, semantic::SymbolTable,
|
||||
Expression, ExpressionKind, Immediate, Statement, StatementKind,
|
||||
err::*,
|
||||
semantic::{Primitive, SymbolTable},
|
||||
};
|
||||
|
||||
use super::Type;
|
||||
|
||||
pub struct Analyzer {
|
||||
table: SymbolTable,
|
||||
pub table: SymbolTable,
|
||||
}
|
||||
|
||||
impl Analyzer {
|
||||
pub fn typecheck(statements: Vec<Statement>) -> Vec<Statement> {
|
||||
/*
|
||||
pub fn typecheck(mut statements: Vec<Statement>) -> Vec<Statement> {
|
||||
let mut this = Self {
|
||||
table: SymbolTable::new(),
|
||||
};
|
||||
this.block(statements)
|
||||
*/
|
||||
for s in &mut statements {
|
||||
*s = *this.naming_pass_stmt(s.clone().into()).unwrap();
|
||||
}
|
||||
println!("-----TABLE------");
|
||||
println!("{:#?}", this.table.table);
|
||||
println!("-----FUNCS------");
|
||||
println!("{:#?}", this.table.functions);
|
||||
println!("-----STRUCTS----");
|
||||
println!("{:#?}", this.table.structs);
|
||||
statements
|
||||
}
|
||||
|
||||
// Bottom up type inference
|
||||
fn propogate_up(
|
||||
pub fn naming_pass_stmt(
|
||||
&mut self,
|
||||
mut stmt: Box<Statement>,
|
||||
) -> Result<Box<Statement>> {
|
||||
use StatementKind as s;
|
||||
stmt.kind = match stmt.kind {
|
||||
s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
mut type_actual,
|
||||
mut value,
|
||||
mutable,
|
||||
mut uid,
|
||||
} => {
|
||||
value = *self.naming_pass_expr(value.into())?;
|
||||
if let Some(ref type_str) = type_str {
|
||||
type_actual = self.table.reference_ident(type_str).type_;
|
||||
} else {
|
||||
type_actual = value.type_.clone();
|
||||
}
|
||||
let symbol =
|
||||
self
|
||||
.table
|
||||
.define_ident(&name, type_actual.clone(), mutable)?;
|
||||
uid = symbol.uid;
|
||||
s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
type_actual,
|
||||
value,
|
||||
mutable,
|
||||
uid,
|
||||
}
|
||||
},
|
||||
s::Assignment {
|
||||
name,
|
||||
mut value,
|
||||
mut uid,
|
||||
} => {
|
||||
uid = self.table.reference_ident(&name).uid;
|
||||
value = *self.naming_pass_expr(value.into())?;
|
||||
// Hint ident is rhs type and mutable
|
||||
self.table.modify_ident(
|
||||
uid.clone(),
|
||||
None,
|
||||
Some(value.type_.clone()),
|
||||
Some(true),
|
||||
false,
|
||||
)?;
|
||||
s::Assignment { name, value, uid }
|
||||
},
|
||||
s::If {
|
||||
mut predicate,
|
||||
mut block,
|
||||
mut else_,
|
||||
} => {
|
||||
predicate = *self.naming_pass_expr(predicate.into())?;
|
||||
self.table.start_block();
|
||||
for s in &mut block {
|
||||
*s = *self.naming_pass_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_block();
|
||||
else_ = if let Some(else_) = else_ {
|
||||
Some(self.naming_pass_stmt(else_)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
s::If {
|
||||
predicate,
|
||||
block,
|
||||
else_,
|
||||
}
|
||||
},
|
||||
s::While {
|
||||
mut predicate,
|
||||
mut block,
|
||||
} => {
|
||||
predicate = *self.naming_pass_expr(predicate.into())?;
|
||||
self.table.start_block();
|
||||
for s in &mut block {
|
||||
*s = *self.naming_pass_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_block();
|
||||
s::While { predicate, block }
|
||||
},
|
||||
s::Print(mut expression) => {
|
||||
expression = *self.naming_pass_expr(expression.into())?;
|
||||
s::Print(expression)
|
||||
},
|
||||
s::Expression(mut expression) => {
|
||||
expression = *self.naming_pass_expr(expression.into())?;
|
||||
s::Expression(expression)
|
||||
},
|
||||
s::Block(mut block) => {
|
||||
self.table.start_block();
|
||||
for s in &mut block {
|
||||
*s = *self.naming_pass_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_block();
|
||||
s::Block(block)
|
||||
},
|
||||
s::Return(mut expression) => {
|
||||
if let Some(e) = expression {
|
||||
expression = Some(*self.naming_pass_expr(e.into())?);
|
||||
}
|
||||
s::Return(expression)
|
||||
},
|
||||
s::Error(diagnostic) => s::Error(diagnostic),
|
||||
};
|
||||
Ok(stmt)
|
||||
}
|
||||
|
||||
pub fn naming_pass_expr(
|
||||
&mut self,
|
||||
mut expr: Box<Expression>,
|
||||
) -> Result<Box<Expression>> {
|
||||
use ExpressionKind as e;
|
||||
expr.kind = match expr.kind {
|
||||
e::Immediate(imm) => e::Immediate(imm),
|
||||
e::Identifier(id, mut mangle) => {
|
||||
let symbol = self.table.lookup(&id).span(&expr.span)?;
|
||||
mangle = symbol.uid;
|
||||
expr.type_ = symbol.type_;
|
||||
e::Identifier(id, mangle)
|
||||
e::Immediate(immediate) => {
|
||||
expr.type_ = Type::Prim(match &immediate {
|
||||
Immediate::Integer(_, _) => Primitive::integer_ambiguous,
|
||||
Immediate::Real(_) => Primitive::real_ambiguous,
|
||||
Immediate::String(_) => Primitive::string,
|
||||
Immediate::Glyph(_) => Primitive::glyph,
|
||||
Immediate::Boolean(_) => Primitive::boolean,
|
||||
});
|
||||
e::Immediate(immediate)
|
||||
},
|
||||
e::Identifier(name, mut mangle) => {
|
||||
let sym = self.table.reference_ident(&name);
|
||||
mangle = sym.uid;
|
||||
expr.type_ = sym.type_;
|
||||
e::Identifier(name, mangle)
|
||||
},
|
||||
e::Binary {
|
||||
op,
|
||||
mut left,
|
||||
mut right,
|
||||
} => {
|
||||
left = self.propogate_up(left)?;
|
||||
right = self.propogate_up(right)?;
|
||||
expr.type_ =
|
||||
Type::binary_op(&left.type_, op, &right.type_).span(&expr.span)?;
|
||||
left = self.naming_pass_expr(left)?;
|
||||
right = self.naming_pass_expr(right)?;
|
||||
e::Binary { op, left, right }
|
||||
},
|
||||
e::Unary { op, mut child } => {
|
||||
child = self.propogate_up(child)?;
|
||||
expr.type_ = Type::unary_op(op, &child.type_).span(&expr.span)?;
|
||||
child = self.naming_pass_expr(child)?;
|
||||
e::Unary { op, child }
|
||||
},
|
||||
e::Parenthesis(mut inner) => {
|
||||
inner = self.propogate_up(inner)?;
|
||||
expr.type_ = inner.type_.clone();
|
||||
e::Parenthesis(inner)
|
||||
e::Parenthesis(mut expression) => {
|
||||
expression = self.naming_pass_expr(expression)?;
|
||||
e::Parenthesis(expression)
|
||||
},
|
||||
// Define anonymous function, do not evaluate body yet
|
||||
e::FunctionDef {
|
||||
mut params,
|
||||
params,
|
||||
returns_str,
|
||||
mut returns_actual,
|
||||
body,
|
||||
returns_actual,
|
||||
mut body,
|
||||
mut id,
|
||||
} => {
|
||||
for p in &mut params {
|
||||
p.type_actual =
|
||||
self.table.lookup_typedef(&p.type_str).span(&expr.span)?;
|
||||
}
|
||||
let param_types: Vec<_> =
|
||||
params.iter().map(|p| p.type_actual.clone()).collect();
|
||||
returns_actual = match returns_str {
|
||||
Some(ref s) => self.table.lookup_typedef(s).span(&expr.span)?,
|
||||
None => Type::Nothing,
|
||||
};
|
||||
id = self
|
||||
.table
|
||||
.start_func(param_types.clone(), returns_actual.clone());
|
||||
expr.type_ = Type::FunctionDef {
|
||||
params: param_types,
|
||||
returns: returns_actual.clone().into(),
|
||||
id: id.clone(),
|
||||
};
|
||||
.create_function(params.clone(), returns_str.clone());
|
||||
expr.type_ = Type::Function(id.clone());
|
||||
self.table.start_function();
|
||||
for s in &mut body {
|
||||
*s = *self.naming_pass_stmt(s.clone().into())?;
|
||||
}
|
||||
self.table.end_function();
|
||||
e::FunctionDef {
|
||||
params,
|
||||
returns_str,
|
||||
|
@ -91,47 +205,12 @@ impl Analyzer {
|
|||
e::FunctionCall {
|
||||
mut callee,
|
||||
mut args,
|
||||
mut is_reference,
|
||||
mut id,
|
||||
is_reference,
|
||||
id,
|
||||
} => {
|
||||
callee = self.propogate_up(callee)?;
|
||||
let (params, returns) = match callee.type_.clone() {
|
||||
Type::FunctionRef {
|
||||
id: uid,
|
||||
params,
|
||||
returns,
|
||||
} => {
|
||||
is_reference = true;
|
||||
id = uid;
|
||||
(params, returns)
|
||||
},
|
||||
Type::FunctionDef {
|
||||
id: uid,
|
||||
params,
|
||||
returns,
|
||||
} => {
|
||||
is_reference = false;
|
||||
id = uid;
|
||||
(params, returns)
|
||||
},
|
||||
_ => {
|
||||
return error()
|
||||
.reason(format!("Cannot call type '{}'", callee.type_))
|
||||
.span(&expr.span);
|
||||
},
|
||||
};
|
||||
expr.type_ = *returns;
|
||||
// Check that args are correct
|
||||
if args.len() != params.len() {
|
||||
return error().reason(format!(
|
||||
"Expected {} arguments, found {}",
|
||||
params.len(),
|
||||
args.len()
|
||||
));
|
||||
}
|
||||
for (index, a) in args.iter_mut().enumerate() {
|
||||
*a = *self.propogate_up(a.clone().into())?;
|
||||
*a = *self.propogate_down(a.clone().into(), params[index].clone())?;
|
||||
callee = self.naming_pass_expr(callee)?;
|
||||
for a in &mut args {
|
||||
*a = *self.naming_pass_expr(a.clone().into())?;
|
||||
}
|
||||
e::FunctionCall {
|
||||
callee,
|
||||
|
@ -140,405 +219,31 @@ impl Analyzer {
|
|||
id,
|
||||
}
|
||||
},
|
||||
e::StructDef(vec) => todo!(),
|
||||
e::StructLiteral { ref name, mut args } => {
|
||||
self.table.lookup_typedef(name).span(&expr.span)?;
|
||||
todo!()
|
||||
e::StructDef(params, mut sid) => {
|
||||
sid = self.table.create_struct(params.clone());
|
||||
expr.type_ = Type::Alias(Box::new(Type::Struct(sid)));
|
||||
e::StructDef(params, sid)
|
||||
},
|
||||
e::StructLiteral { name, mut args } => {
|
||||
self.table.reference_ident(&name);
|
||||
for (_, a) in &mut args {
|
||||
*a = *self.naming_pass_expr(a.clone().into())?;
|
||||
}
|
||||
e::StructLiteral { name, args }
|
||||
},
|
||||
e::Field {
|
||||
namespace,
|
||||
mut namespace,
|
||||
field,
|
||||
uid,
|
||||
} => todo!(),
|
||||
} => {
|
||||
namespace = self.naming_pass_expr(namespace)?;
|
||||
e::Field {
|
||||
namespace,
|
||||
field,
|
||||
uid,
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
// Top down type assertion
|
||||
fn propogate_down(
|
||||
&mut self,
|
||||
expr: Box<Expression>,
|
||||
expect: Type,
|
||||
) -> Result<Box<Expression>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/*
|
||||
fn block(&mut self, block: Vec<Statement>) -> Vec<Statement> {
|
||||
let mut new_block = vec![];
|
||||
for st in block {
|
||||
let span = st.span;
|
||||
let st = match self.statement(st.into()) {
|
||||
Ok(st) => *st,
|
||||
Err(e) => Statement {
|
||||
kind: StatementKind::Error(e),
|
||||
span,
|
||||
},
|
||||
};
|
||||
new_block.push(st);
|
||||
}
|
||||
new_block
|
||||
}
|
||||
|
||||
fn statement(&mut self, mut stmt: Box<Statement>) -> Result<Box<Statement>> {
|
||||
use Primitive as p;
|
||||
use StatementKind as s;
|
||||
match stmt.kind {
|
||||
// Variable declaration
|
||||
s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
value,
|
||||
mutable,
|
||||
..
|
||||
} => {
|
||||
let type_lhs = if let Some(ref s) = type_str {
|
||||
self.table.get_type(s).span(&stmt.span)?
|
||||
} else {
|
||||
Type::Ambiguous
|
||||
};
|
||||
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_)
|
||||
.reason(format!(
|
||||
"Expected type '{:?}', found type '{:?}'",
|
||||
type_lhs, value.type_
|
||||
))
|
||||
.span(&stmt.span)?;
|
||||
value.type_ = type_actual.clone();
|
||||
let varkind = self
|
||||
.table
|
||||
.define_symbol(name.clone(), type_actual.clone(), mutable)
|
||||
.span(&stmt.span)?;
|
||||
stmt.kind = s::Declaration {
|
||||
name,
|
||||
type_str,
|
||||
type_actual,
|
||||
value: *value,
|
||||
mutable,
|
||||
varkind,
|
||||
};
|
||||
},
|
||||
// Variable assignment
|
||||
s::Assignment { name, value, .. } => {
|
||||
let symbol = self.table.find_symbol(&name).span(&stmt.span)?;
|
||||
// Check that it is mutable
|
||||
if !symbol.mutable {
|
||||
return error()
|
||||
.reason(format!("Cannot assign to immutable '{}'", name))
|
||||
.span(&stmt.span);
|
||||
}
|
||||
let mut value =
|
||||
*self.expression(value.into(), Some(symbol.type_.clone()))?;
|
||||
let type_actual =
|
||||
Type::coerce(&symbol.type_, &value.type_).span(&stmt.span)?;
|
||||
value.type_ = type_actual;
|
||||
stmt.kind = s::Assignment {
|
||||
name,
|
||||
value,
|
||||
varkind: symbol.kind,
|
||||
};
|
||||
},
|
||||
s::If {
|
||||
predicate,
|
||||
block,
|
||||
else_,
|
||||
} => {
|
||||
self.table.start_block();
|
||||
let predicate =
|
||||
*self.expression(predicate.into(), Some(Type::Prim(p::boolean)))?;
|
||||
Type::coerce(&Type::Prim(p::boolean), &predicate.type_)
|
||||
.span(&predicate.span)?;
|
||||
let block = self.block(block);
|
||||
let else_ = if let Some(else_) = else_ {
|
||||
Some(self.statement(else_)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
stmt.kind = s::If {
|
||||
predicate,
|
||||
block,
|
||||
else_,
|
||||
};
|
||||
self.table.end_block();
|
||||
},
|
||||
s::While { predicate, block } => {
|
||||
self.table.start_block();
|
||||
let predicate =
|
||||
*self.expression(predicate.into(), Some(Type::Prim(p::boolean)))?;
|
||||
Type::coerce(&Type::Prim(p::boolean), &predicate.type_)
|
||||
.span(&predicate.span)?;
|
||||
let block = self.block(block);
|
||||
stmt.kind = s::While { predicate, block };
|
||||
self.table.end_block();
|
||||
},
|
||||
s::Print(e) => {
|
||||
let mut e = *self.expression(e.into(), None)?;
|
||||
e.type_ = Type::coerce(&Type::Prim(p::integer), &e.type_)?;
|
||||
stmt.kind = s::Print(e);
|
||||
},
|
||||
s::Expression(e) => {
|
||||
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) => {
|
||||
self.table.start_block();
|
||||
let block = self.block(block);
|
||||
stmt.kind = s::Block(block);
|
||||
self.table.end_block();
|
||||
},
|
||||
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 mut e = self.expression(e.into(), Some(return_type.clone()))?;
|
||||
e.type_ = Type::coerce(&return_type, &e.type_).span(&e.span)?;
|
||||
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)
|
||||
}
|
||||
|
||||
fn expression(
|
||||
&mut self,
|
||||
mut expr: Box<Expression>,
|
||||
type_hint: Option<Type>,
|
||||
) -> Result<Box<Expression>> {
|
||||
// TODO implement type hinting
|
||||
use ExpressionKind as e;
|
||||
use Immediate as i;
|
||||
use Primitive as p;
|
||||
let type_ = match expr.kind {
|
||||
e::Immediate(ref i) => Type::Prim(match i {
|
||||
i::Integer(_, _) => p::integer_ambiguous,
|
||||
i::Real(_) => p::real_ambiguous,
|
||||
i::String(_) => p::string,
|
||||
i::Boolean(_) => p::boolean,
|
||||
}),
|
||||
e::Identifier(ref i, ref mut kind) => {
|
||||
let symbol = self.table.find_symbol(i)?;
|
||||
*kind = symbol.kind;
|
||||
self.table.find_symbol(i)?.type_
|
||||
},
|
||||
e::Binary { op, left, right } => {
|
||||
let left = self.expression(left, type_hint.clone())?;
|
||||
let right = self.expression(right, type_hint.clone())?;
|
||||
let type_ = Type::binary_op(&left.type_, op, &right.type_)?;
|
||||
expr.kind = e::Binary { left, right, op };
|
||||
type_
|
||||
},
|
||||
e::Unary { op, child } => {
|
||||
let child = self.expression(child, type_hint.clone())?;
|
||||
let type_ = Type::unary_op(op, &child.type_)?;
|
||||
expr.kind = e::Unary { child, op };
|
||||
type_
|
||||
},
|
||||
e::Parenthesis(inner) => {
|
||||
let inner = self.expression(inner, type_hint.clone())?;
|
||||
let type_ = inner.type_.clone();
|
||||
expr.kind = e::Parenthesis(inner);
|
||||
type_
|
||||
},
|
||||
e::FunctionDef {
|
||||
mut params,
|
||||
returns_str,
|
||||
mut returns_actual,
|
||||
body,
|
||||
id: _,
|
||||
} => {
|
||||
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 {
|
||||
p.type_actual = self.table.get_type(&p.type_str).span(&expr.span)?;
|
||||
self
|
||||
.table
|
||||
.define_param(p.name.clone(), p.type_actual.clone())?;
|
||||
}
|
||||
let body = self.block(body);
|
||||
self.table.end_func();
|
||||
expr.kind = e::FunctionDef {
|
||||
params: params.clone(),
|
||||
returns_str,
|
||||
returns_actual: returns_actual.clone(),
|
||||
body,
|
||||
id,
|
||||
};
|
||||
Type::FunctionDef {
|
||||
params: params.into_iter().map(|p| p.type_actual).collect(),
|
||||
returns: returns_actual.into(),
|
||||
id,
|
||||
}
|
||||
},
|
||||
e::FunctionCall { callee, mut args } => {
|
||||
let callee = self.expression(callee, None)?;
|
||||
// Check that this is actually a function
|
||||
// TODO allow function references to be called
|
||||
let Type::FunctionDef {
|
||||
ref params,
|
||||
ref returns,
|
||||
..
|
||||
} = callee.type_
|
||||
else {
|
||||
return error()
|
||||
.reason(format!("Cannot call type {:?}", callee.type_))
|
||||
.span(&callee.span);
|
||||
};
|
||||
// Check for correct number of args
|
||||
if params.len() != args.len() {
|
||||
return error()
|
||||
.reason(format!(
|
||||
"Wrong number of arguments, function expects {}, found {}",
|
||||
params.len(),
|
||||
args.len()
|
||||
))
|
||||
.span(&callee.span);
|
||||
}
|
||||
// Check for correct arg types
|
||||
for (expect, actual) in params.iter().zip(args.iter_mut()) {
|
||||
*actual =
|
||||
*self.expression(actual.clone().into(), Some(expect.clone()))?;
|
||||
let coerced_type = Type::coerce(expect, &actual.type_);
|
||||
if let Ok(t) = coerced_type {
|
||||
actual.type_ = t;
|
||||
} else {
|
||||
return error()
|
||||
.reason(format!(
|
||||
"Expected type {expect:?}, found {:?}",
|
||||
actual.type_
|
||||
))
|
||||
.span(&actual.span);
|
||||
}
|
||||
}
|
||||
let returns = *returns.clone();
|
||||
expr.kind = e::FunctionCall { callee, args };
|
||||
returns
|
||||
},
|
||||
e::StructDef(mut params) => {
|
||||
for p in &mut params {
|
||||
p.type_actual = self.table.get_type(&p.type_str).span(&expr.span)?;
|
||||
}
|
||||
expr.kind = e::StructDef(params.clone());
|
||||
Type::StructDef(params)
|
||||
},
|
||||
e::StructLiteral { name, args } => {
|
||||
let type_ = self.table.get_type(&name).span(&expr.span)?;
|
||||
let Type::Struct(params) = type_ else {
|
||||
return error().reason(format!(
|
||||
"Cannot construct type {:?} as struct literal",
|
||||
type_
|
||||
));
|
||||
};
|
||||
if args.len() != params.len() {
|
||||
return error().reason(format!(
|
||||
"Incorrect number of parameters for struct '{}'; expected {}, \
|
||||
found {}",
|
||||
name,
|
||||
params.len(),
|
||||
args.len()
|
||||
));
|
||||
}
|
||||
// TODO out of order params
|
||||
let mut new_args = vec![];
|
||||
for (
|
||||
(argname, argexpr),
|
||||
Parameter {
|
||||
name: pname,
|
||||
type_actual: ptype,
|
||||
..
|
||||
},
|
||||
) in args.iter().zip(params.iter())
|
||||
{
|
||||
if argname != pname {
|
||||
return error()
|
||||
.reason(format!(
|
||||
"In struct literal, expected parameter '{pname}', found \
|
||||
'{argname}'"
|
||||
))
|
||||
.span(&argexpr.span);
|
||||
}
|
||||
let argspan = argexpr.span;
|
||||
let mut arg = *self
|
||||
.expression(argexpr.clone().into(), Some(ptype.clone()))
|
||||
.trace_span(expr.span, "while parsing struct literal")?;
|
||||
let coerced_type = Type::coerce(ptype, &arg.type_);
|
||||
if let Ok(t) = coerced_type {
|
||||
arg.type_ = t;
|
||||
} else {
|
||||
return error()
|
||||
.reason(format!(
|
||||
"In struct literal, expected type '{ptype:?}', found '{:?}",
|
||||
arg.type_,
|
||||
))
|
||||
.span(&argspan);
|
||||
}
|
||||
new_args.push((argname.clone(), arg));
|
||||
}
|
||||
expr.kind = e::StructLiteral {
|
||||
name,
|
||||
args: new_args,
|
||||
};
|
||||
Type::Struct(params)
|
||||
},
|
||||
e::Field { namespace, field } => {
|
||||
let namespace = self.expression(namespace, None)?;
|
||||
// Check that namespace is struct
|
||||
// TODO: fields in other types
|
||||
let Type::Struct(ref params) = namespace.type_ else {
|
||||
return error()
|
||||
.reason(format!("Type {:?} does not have fields", namespace.type_))
|
||||
.span(&namespace.span);
|
||||
};
|
||||
// Check that field is identifier
|
||||
// TODO: tuple fields?
|
||||
let e::Identifier(ref name, _) = field.kind else {
|
||||
return error()
|
||||
.reason("Field must be an identifier")
|
||||
.span(&field.span);
|
||||
};
|
||||
let mut type_ = None;
|
||||
for p in params {
|
||||
if &p.name == name {
|
||||
type_ = Some(p.type_actual.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
let type_ = type_
|
||||
.reason(format!(
|
||||
"Type {:?} does not contain field {}",
|
||||
namespace, name
|
||||
))
|
||||
.span(&field.span)?;
|
||||
expr.kind = e::Field { namespace, field };
|
||||
type_
|
||||
},
|
||||
};
|
||||
/*
|
||||
if let Some(expect) = &type_hint {
|
||||
if let Type::Ambiguous = expect {
|
||||
} else {
|
||||
expr.type_ = Type::coerce(expect, &expr.type_)?;
|
||||
}
|
||||
}
|
||||
*/
|
||||
expr.type_ = type_;
|
||||
Ok(expr)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
25
src/semantic/bottom_up.rs
Normal file
25
src/semantic/bottom_up.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use super::Analyzer;
|
||||
use crate::{
|
||||
Expression, ExpressionKind, Immediate, Statement, StatementKind,
|
||||
err::*,
|
||||
semantic::{Primitive, SymbolTable},
|
||||
};
|
||||
|
||||
use super::Type;
|
||||
|
||||
impl Analyzer {
|
||||
pub fn stmt_bottom_up(
|
||||
&mut self,
|
||||
mut stmt: Box<Statement>,
|
||||
) -> Result<Box<Statement>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// Bottom up type inference
|
||||
pub fn expr_bottom_up(
|
||||
&mut self,
|
||||
mut expr: Box<Expression>,
|
||||
) -> Result<Box<Expression>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
mod analyzer;
|
||||
mod bottom_up;
|
||||
mod primitives;
|
||||
mod top_down;
|
||||
mod types;
|
||||
|
||||
use crate::err::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{Parameter, err::*};
|
||||
pub use analyzer::*;
|
||||
pub use primitives::*;
|
||||
pub use types::*;
|
||||
|
@ -15,6 +19,20 @@ pub struct Symbol {
|
|||
name: String,
|
||||
type_: Type,
|
||||
uid: UID,
|
||||
initialized: bool,
|
||||
mutable: Option<bool>,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn assert_is_type(&self) -> Result<Type> {
|
||||
if let Type::Alias(ref t) = self.type_ {
|
||||
return Ok(*t.clone());
|
||||
}
|
||||
error().reason(format!(
|
||||
"The name '{}' refers to a value, not a type",
|
||||
self.name
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -26,121 +44,253 @@ enum Definition {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SymbolTable {
|
||||
defs: Vec<Definition>,
|
||||
pub structs: Vec<StructureDef>,
|
||||
pub functions: Vec<FunctionDef>,
|
||||
scope: Vec<Definition>,
|
||||
pub table: HashMap<UID, Symbol>,
|
||||
mangle_num: usize,
|
||||
nesting: usize,
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
pub fn new() -> Self {
|
||||
// Setup builtin symbols
|
||||
let prims = primitive_symbols();
|
||||
let mut scope = vec![];
|
||||
let mut table = HashMap::new();
|
||||
for p in prims {
|
||||
scope.push(Definition::Ident(p.clone()));
|
||||
table.insert(p.uid.clone(), p);
|
||||
}
|
||||
let nothing_symbol = Symbol {
|
||||
name: "Nothing".to_string(),
|
||||
type_: Type::Alias(Box::new(Type::Nothing)),
|
||||
uid: nothing_mangle(),
|
||||
initialized: true,
|
||||
mutable: Some(false),
|
||||
};
|
||||
scope.push(Definition::Ident(nothing_symbol.clone()));
|
||||
table.insert(nothing_symbol.uid.clone(), nothing_symbol);
|
||||
Self {
|
||||
defs: vec![],
|
||||
structs: vec![],
|
||||
functions: vec![],
|
||||
scope,
|
||||
table,
|
||||
mangle_num: 0,
|
||||
nesting: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_block(&mut self) {
|
||||
self.scope.push(Definition::BlockStart);
|
||||
}
|
||||
|
||||
pub fn end_block(&mut self) {
|
||||
while !self.scope.is_empty() {
|
||||
if let Some(Definition::BlockStart) = self.scope.pop() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
unreachable!("Cannot end global scope")
|
||||
}
|
||||
|
||||
pub fn start_function(&mut self) {
|
||||
self.nesting += 1;
|
||||
self.scope.push(Definition::FuncStart);
|
||||
}
|
||||
|
||||
pub fn end_function(&mut self) {
|
||||
while !self.scope.is_empty() {
|
||||
if let Some(Definition::FuncStart) = self.scope.pop() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
unreachable!("Cannot end global scope")
|
||||
}
|
||||
|
||||
fn generate_uid(&mut self, name: &str) -> UID {
|
||||
let uid = format!("${name}${}", self.mangle_num);
|
||||
let uid = format!("${}${name}", self.mangle_num);
|
||||
self.mangle_num += 1;
|
||||
uid
|
||||
}
|
||||
|
||||
pub fn start_func(&mut self, params: Vec<Type>, returns: Type) -> UID {
|
||||
self.nesting += 1;
|
||||
let uid = self.generate_uid("func");
|
||||
let type_ = Type::FunctionDef {
|
||||
params,
|
||||
returns: returns.into(),
|
||||
id: uid.clone(),
|
||||
pub fn create_struct(&mut self, params: Vec<Parameter>) -> SID {
|
||||
let sid = self.structs.len();
|
||||
let mut new_params = vec![];
|
||||
for p in params {
|
||||
let s = self.reference_ident(&p.type_str);
|
||||
new_params.push((p.name, s.uid));
|
||||
}
|
||||
self.structs.push(StructureDef(new_params));
|
||||
sid
|
||||
}
|
||||
|
||||
pub fn create_function(
|
||||
&mut self,
|
||||
params: Vec<Parameter>,
|
||||
returns: Option<String>,
|
||||
) -> FID {
|
||||
let fid = self.functions.len();
|
||||
let mut new_params = vec![];
|
||||
for p in params {
|
||||
let s = self.reference_ident(&p.type_str);
|
||||
new_params.push(s.uid);
|
||||
}
|
||||
let returns = if let Some(s) = returns {
|
||||
Some(self.reference_ident(&s).uid)
|
||||
} else {
|
||||
Some(nothing_mangle())
|
||||
};
|
||||
self.define("<anonymous function>", type_.clone());
|
||||
self.defs.push(Definition::FuncStart);
|
||||
uid
|
||||
self.functions.push(FunctionDef {
|
||||
params: new_params,
|
||||
returns,
|
||||
});
|
||||
fid
|
||||
}
|
||||
|
||||
pub fn end_func(&mut self) {
|
||||
while !self.defs.is_empty() {
|
||||
if let Some(Definition::FuncStart) = self.defs.pop() {
|
||||
return;
|
||||
pub fn modify_ident(
|
||||
&mut self,
|
||||
uid: UID,
|
||||
name: Option<String>,
|
||||
type_: Option<Type>,
|
||||
mutable: Option<bool>,
|
||||
init: bool,
|
||||
) -> Result<()> {
|
||||
{
|
||||
let symbol = self.table.get_mut(&uid).unwrap();
|
||||
if let Some(ref type_) = type_ {
|
||||
symbol.type_ = symbol.type_.clone().deduce(type_)?;
|
||||
}
|
||||
if let Some(m) = mutable {
|
||||
symbol.mutable = Some(m);
|
||||
}
|
||||
}
|
||||
unreachable!("Cannot end global scope")
|
||||
}
|
||||
|
||||
pub fn start_block(&mut self) {
|
||||
while !self.defs.is_empty() {
|
||||
if let Some(Definition::BlockStart) = self.defs.pop() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
unreachable!("Cannot end global scope")
|
||||
}
|
||||
|
||||
pub fn define(&mut self, name: &str, type_: Type) -> UID {
|
||||
let uid = self.generate_uid(name);
|
||||
self.defs.push(Definition::Ident(Symbol {
|
||||
name: name.to_string(),
|
||||
type_,
|
||||
uid: uid.clone(),
|
||||
}));
|
||||
uid
|
||||
}
|
||||
|
||||
pub fn define_func(&mut self, name: &str, uid: UID) {
|
||||
for def in &mut self.defs {
|
||||
match def {
|
||||
Definition::Ident(symbol) if symbol.uid == uid => {
|
||||
symbol.name = name.to_string();
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
panic!("Tried to name nonexistant function");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_typedef(&self, name: &str) -> Result<Type> {
|
||||
let mut nesting = self.nesting;
|
||||
for def in &self.defs {
|
||||
for def in &mut self.scope {
|
||||
match def {
|
||||
Definition::Ident(symbol)
|
||||
if (symbol.name == name)
|
||||
if (symbol.uid == uid)
|
||||
&& (nesting == 0 || nesting == self.nesting) =>
|
||||
{
|
||||
if let Type::StructDef(vec) = &symbol.type_ {
|
||||
return Ok(Type::Struct(
|
||||
vec.iter().map(|p| p.type_actual.clone()).collect(),
|
||||
));
|
||||
if let Some(ref name) = name {
|
||||
symbol.name = name.clone();
|
||||
}
|
||||
if let Some(ref type_) = type_ {
|
||||
symbol.type_ = symbol.type_.clone().deduce(type_)?;
|
||||
}
|
||||
if let Some(m) = mutable {
|
||||
symbol.mutable = Some(m);
|
||||
}
|
||||
symbol.initialized &= init;
|
||||
return Ok(());
|
||||
},
|
||||
Definition::FuncStart => nesting -= 1,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
if let Some(p) = Primitive::from_string(name) {
|
||||
return Ok(Type::Prim(p));
|
||||
}
|
||||
error().reason(format!("Cannot find type definition '{}'", name))
|
||||
panic!("Symbol {uid} does not exist")
|
||||
}
|
||||
|
||||
pub fn lookup(&self, name: &str) -> Result<Symbol> {
|
||||
pub fn define_ident(
|
||||
&mut self,
|
||||
name: &str,
|
||||
type_: Type,
|
||||
mutable: bool,
|
||||
) -> Result<Symbol> {
|
||||
if let Ok(s) = self.lookup_this_scope(name) {
|
||||
// Re-definition error
|
||||
// TODO support name shadowing
|
||||
if s.initialized {
|
||||
return error()
|
||||
.reason(format!("Multiple definitions of '{name}' in this scope"));
|
||||
}
|
||||
// Referenced before init, modify existing entry
|
||||
else {
|
||||
self.modify_ident(
|
||||
s.uid.clone(),
|
||||
None,
|
||||
Some(type_),
|
||||
Some(mutable),
|
||||
true,
|
||||
)?;
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
// First initialization in this scope
|
||||
let uid = self.generate_uid(name);
|
||||
let sym = Symbol {
|
||||
name: name.into(),
|
||||
type_,
|
||||
uid: uid.clone(),
|
||||
initialized: true,
|
||||
mutable: Some(mutable),
|
||||
};
|
||||
self.scope.push(Definition::Ident(sym.clone()));
|
||||
self.table.insert(uid, sym.clone());
|
||||
Ok(sym)
|
||||
}
|
||||
|
||||
pub fn reference_ident(&mut self, name: &str) -> Symbol {
|
||||
match self.lookup_available_scope(name) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
let uid = self.generate_uid(name);
|
||||
let sym = Symbol {
|
||||
name: name.into(),
|
||||
type_: Type::Ambiguous,
|
||||
uid: uid.clone(),
|
||||
initialized: false,
|
||||
mutable: None,
|
||||
};
|
||||
self.scope.push(Definition::Ident(sym.clone()));
|
||||
self.table.insert(uid, sym.clone());
|
||||
sym
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_this_scope(&self, name: &str) -> Result<Symbol> {
|
||||
self.lookup_scope(name, false)
|
||||
}
|
||||
|
||||
pub fn lookup_available_scope(&self, name: &str) -> Result<Symbol> {
|
||||
self.lookup_scope(name, true)
|
||||
}
|
||||
|
||||
fn lookup_scope(&self, name: &str, with_global: bool) -> Result<Symbol> {
|
||||
let mut nesting = self.nesting;
|
||||
for def in &self.defs {
|
||||
for def in &self.scope {
|
||||
match def {
|
||||
Definition::Ident(symbol)
|
||||
if (symbol.name == name)
|
||||
&& (nesting == 0 || nesting == self.nesting) =>
|
||||
&& ((nesting == 0 && with_global) || nesting == self.nesting) =>
|
||||
{
|
||||
if let Type::StructDef(_) = symbol.type_ {
|
||||
continue;
|
||||
}
|
||||
return Ok(symbol.clone());
|
||||
},
|
||||
Definition::FuncStart => nesting -= 1,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
error().reason(format!("Cannot find the definition of '{}'", name))
|
||||
error().reason(format!(
|
||||
"Cannot find the definition of '{}' in the current scope",
|
||||
name
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// I think this works? Box<T> pattern matching weirdness
|
||||
// going on. If this is ever even used
|
||||
fn unwrap_aliases(mut t: Type) -> Type {
|
||||
loop {
|
||||
if let Type::Alias(ref t1) = t {
|
||||
if let Type::Alias(t2) = &**t1 {
|
||||
t = *t2.clone();
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{BinaryOp, UnaryOp};
|
||||
|
||||
use crate::err::*;
|
||||
use crate::semantic::{Symbol, Type, UID};
|
||||
|
||||
macro_rules! primitives {
|
||||
( $($i:ident),* ) => {
|
||||
|
@ -19,6 +20,16 @@ macro_rules! primitives {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mangle(&self) -> UID {
|
||||
match self {
|
||||
Primitive::integer_ambiguous => "$$integer_amgibuous".into(),
|
||||
Primitive::real_ambiguous => "$$real_amgibuous".into(),
|
||||
$(
|
||||
Primitive::$i => format!("$${}", stringify!{$i}),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for Primitive {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
@ -29,6 +40,17 @@ macro_rules! primitives {
|
|||
}
|
||||
}
|
||||
}
|
||||
pub fn primitive_symbols() -> Vec<Symbol> {
|
||||
vec![
|
||||
$(Symbol {
|
||||
name: stringify!{$i}.into(),
|
||||
type_: Type::Alias(Box::new(Type::Prim(Primitive::$i))),
|
||||
uid: format!("$${}", stringify!{$i}),
|
||||
initialized: true,
|
||||
mutable: Some(false),
|
||||
},)*
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -114,7 +136,7 @@ impl Primitive {
|
|||
integer_ambiguous,
|
||||
i8 | i16 | i32 | i64 | integer | w8 | w16 | w32 | w64 | whole,
|
||||
) => into,
|
||||
(real_ambiguous, r32 | r64) => into,
|
||||
(real_ambiguous, r32 | r64 | real) => into,
|
||||
_ => *self,
|
||||
};
|
||||
}
|
||||
|
|
24
src/semantic/top_down.rs
Normal file
24
src/semantic/top_down.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use super::{Analyzer, Type};
|
||||
use crate::{
|
||||
Expression, ExpressionKind, Immediate, Statement, StatementKind,
|
||||
err::*,
|
||||
semantic::{Primitive, SymbolTable},
|
||||
};
|
||||
|
||||
impl Analyzer {
|
||||
pub fn stmt_top_down(
|
||||
&mut self,
|
||||
mut stmt: Box<Statement>,
|
||||
expect: &Type,
|
||||
) -> Result<Box<Statement>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn expr_top_down(
|
||||
&mut self,
|
||||
mut expr: Box<Expression>,
|
||||
expect: &Type,
|
||||
) -> Result<Box<Expression>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -1,28 +1,34 @@
|
|||
use crate::{
|
||||
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
|
||||
StatementKind, UnaryOp,
|
||||
};
|
||||
use crate::{BinaryOp, UnaryOp};
|
||||
|
||||
use super::{UID, primitives::*};
|
||||
use super::{Symbol, UID, primitives::*};
|
||||
use crate::err::*;
|
||||
|
||||
/// Struct ID
|
||||
pub type SID = usize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StructureDef(pub Vec<(String, UID)>);
|
||||
|
||||
pub type FID = usize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionDef {
|
||||
pub params: Vec<UID>,
|
||||
pub returns: Option<UID>,
|
||||
}
|
||||
|
||||
pub fn nothing_mangle() -> UID {
|
||||
"$$nothing".into()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Type {
|
||||
Ambiguous,
|
||||
Nothing,
|
||||
Alias(Box<Type>),
|
||||
Prim(Primitive),
|
||||
Struct(Vec<Type>),
|
||||
StructDef(Vec<Parameter>),
|
||||
FunctionRef {
|
||||
params: Vec<Type>,
|
||||
returns: Box<Type>,
|
||||
id: UID,
|
||||
},
|
||||
FunctionDef {
|
||||
params: Vec<Type>,
|
||||
returns: Box<Type>,
|
||||
id: UID,
|
||||
},
|
||||
Struct(SID),
|
||||
Function(FID),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Type {
|
||||
|
@ -32,15 +38,8 @@ impl std::fmt::Display for Type {
|
|||
Type::Nothing => write!(f, "nothing"),
|
||||
Type::Prim(primitive) => write!(f, "{primitive}"),
|
||||
Type::Struct(vec) => write!(f, "struct {vec:?}"),
|
||||
Type::StructDef(_) => write!(f, "struct definition"),
|
||||
Type::FunctionRef {
|
||||
params, returns, ..
|
||||
} => {
|
||||
write!(f, "({params:?}) -> {returns}")
|
||||
},
|
||||
Type::FunctionDef {
|
||||
params, returns, ..
|
||||
} => write!(f, "({params:?}) -> {returns}"),
|
||||
Type::Alias(t) => write!(f, "type alias ({t})"),
|
||||
Type::Function(fid) => write!(f, "func {fid:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +50,9 @@ impl PartialEq for Type {
|
|||
match (self, other) {
|
||||
(Ambiguous, Ambiguous) => true,
|
||||
(Prim(p1), Prim(p2)) if p1 == p2 => true,
|
||||
(Struct(p1), Struct(p2)) => p1.iter().eq(p2.iter()),
|
||||
(FunctionRef { id: id1, .. }, FunctionRef { id: id2, .. }) => id1 == id2,
|
||||
(FunctionDef { id: id1, .. }, FunctionDef { id: id2, .. }) => id1 == id2,
|
||||
(Struct(p1), Struct(p2)) => p1 == p2,
|
||||
(Function(id1), Function(id2)) => id1 == id2,
|
||||
(Alias(t1), Alias(t2)) => t1 == t2,
|
||||
(Nothing, Nothing) => true,
|
||||
_ => false,
|
||||
}
|
||||
|
@ -85,17 +84,19 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn coerce(&mut self, expect: &Type) {
|
||||
pub fn deduce(self, hint: &Type) -> Result<Self> {
|
||||
use Type::*;
|
||||
*self = match (expect, self.clone()) {
|
||||
(Ambiguous, Ambiguous) => Ambiguous,
|
||||
(Ambiguous, t) => t.clone(),
|
||||
match (hint, self) {
|
||||
(Ambiguous, t) => Ok(t),
|
||||
(t, Ambiguous) => Ok(t.clone()),
|
||||
(Prim(mut p1), Prim(p2)) => {
|
||||
p1.coerce(p2);
|
||||
Prim(p1)
|
||||
Ok(Prim(p1))
|
||||
},
|
||||
(t1, t2) if t1 == &t2 => t1.clone(),
|
||||
_ => Ambiguous,
|
||||
};
|
||||
(t1, t2) if t1 == &t2 => Ok(t1.clone()),
|
||||
(t1, t2) => {
|
||||
error().reason(format!("Cannot coerce type '{t2}' into '{t1}'"))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue