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 :: C{r: 10.0};
|
||||||
c := b;
|
|
||||||
|
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<()> {
|
fn main() -> Result<()> {
|
||||||
for s in parse(include_str!("../demo.hal")) {
|
/*
|
||||||
|
for s in typecheck(include_str!("../demo.hal")) {
|
||||||
println!("{s:#?}");
|
println!("{s:#?}");
|
||||||
println!("------------------");
|
println!("------------------");
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
typecheck(include_str!("../demo.hal"));
|
||||||
//let module = frontend::Module::from_file("./demo.hal")?;
|
//let module = frontend::Module::from_file("./demo.hal")?;
|
||||||
//module.write_to("test");
|
//module.write_to("test");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub enum Immediate {
|
||||||
Integer(String, Base),
|
Integer(String, Base),
|
||||||
Real(String),
|
Real(String),
|
||||||
String(String),
|
String(String),
|
||||||
|
Glyph(char),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ impl std::fmt::Display for Immediate {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Immediate::Integer(i, b) => write!(f, "{i} ({b:?})"),
|
Immediate::Integer(i, b) => write!(f, "{i} ({b:?})"),
|
||||||
|
Immediate::Glyph(c) => write!(f, "{c}"),
|
||||||
Immediate::Real(r) => write!(f, "{r}"),
|
Immediate::Real(r) => write!(f, "{r}"),
|
||||||
Immediate::String(s) => write!(f, "{s}"),
|
Immediate::String(s) => write!(f, "{s}"),
|
||||||
Immediate::Boolean(b) => write!(f, "{b}"),
|
Immediate::Boolean(b) => write!(f, "{b}"),
|
||||||
|
@ -53,7 +55,7 @@ pub enum ExpressionKind {
|
||||||
returns_str: Option<String>,
|
returns_str: Option<String>,
|
||||||
returns_actual: Type,
|
returns_actual: Type,
|
||||||
body: Vec<Statement>,
|
body: Vec<Statement>,
|
||||||
id: UID,
|
id: usize,
|
||||||
},
|
},
|
||||||
FunctionCall {
|
FunctionCall {
|
||||||
callee: Box<Expression>,
|
callee: Box<Expression>,
|
||||||
|
@ -61,7 +63,7 @@ pub enum ExpressionKind {
|
||||||
is_reference: bool,
|
is_reference: bool,
|
||||||
id: UID,
|
id: UID,
|
||||||
},
|
},
|
||||||
StructDef(Vec<Parameter>),
|
StructDef(Vec<Parameter>, usize),
|
||||||
StructLiteral {
|
StructLiteral {
|
||||||
name: String,
|
name: String,
|
||||||
args: Vec<(String, Expression)>,
|
args: Vec<(String, Expression)>,
|
||||||
|
@ -118,7 +120,7 @@ impl std::fmt::Debug for ExpressionKind {
|
||||||
} => {
|
} => {
|
||||||
write!(f, "(fn({params:?}) -> {returns_actual:?})")
|
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:?} }}"),
|
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> {
|
pub fn expression(&mut self, precedence: Precedence) -> Result<Expression> {
|
||||||
use ExpressionKind as e;
|
use ExpressionKind as e;
|
||||||
use TokenKind as t;
|
use TokenKind as t;
|
||||||
let mut type_ = Type::Ambiguous;
|
|
||||||
let next = self.peek(0)?;
|
let next = self.peek(0)?;
|
||||||
// Unary prefix expression
|
// Unary prefix expression
|
||||||
let mut current = if let Ok(operator) = UnaryOp::try_from(&next.0) {
|
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 ExpressionKind as e;
|
||||||
use Immediate as im;
|
use Immediate as im;
|
||||||
use TokenKind as t;
|
use TokenKind as t;
|
||||||
let mut type_ = Type::Ambiguous;
|
|
||||||
let next = self.peek(0)?;
|
let next = self.peek(0)?;
|
||||||
let mut span = next.1;
|
let mut span = next.1;
|
||||||
let kind = match next.0 {
|
let kind = match next.0 {
|
||||||
t::IntegerLiteral(i, b) => {
|
t::IntegerLiteral(i, b) => {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
type_ = Type::Prim(Primitive::integer_ambiguous);
|
|
||||||
e::Immediate(im::Integer(i, b))
|
e::Immediate(im::Integer(i, b))
|
||||||
},
|
},
|
||||||
t::FloatLiteral(f) => {
|
t::FloatLiteral(f) => {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
type_ = Type::Prim(Primitive::real_ambiguous);
|
|
||||||
e::Immediate(im::Real(f))
|
e::Immediate(im::Real(f))
|
||||||
},
|
},
|
||||||
t::StringLiteral(s) => {
|
t::StringLiteral(s) => {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
type_ = Type::Prim(Primitive::string);
|
|
||||||
e::Immediate(im::String(s))
|
e::Immediate(im::String(s))
|
||||||
},
|
},
|
||||||
|
t::GlyphLiteral(c) => {
|
||||||
|
self.skip(1);
|
||||||
|
e::Immediate(im::Glyph(c))
|
||||||
|
},
|
||||||
t::True => {
|
t::True => {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
type_ = Type::Prim(Primitive::boolean);
|
|
||||||
e::Immediate(im::Boolean(true))
|
e::Immediate(im::Boolean(true))
|
||||||
},
|
},
|
||||||
t::False => {
|
t::False => {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
type_ = Type::Prim(Primitive::boolean);
|
|
||||||
e::Immediate(im::Boolean(false))
|
e::Immediate(im::Boolean(false))
|
||||||
},
|
},
|
||||||
// Function definition
|
// Function definition
|
||||||
|
@ -355,7 +354,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
returns_str,
|
returns_str,
|
||||||
returns_actual: Type::Ambiguous,
|
returns_actual: Type::Ambiguous,
|
||||||
body,
|
body,
|
||||||
id: "".into(),
|
id: usize::MAX,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Struct definition
|
// Struct definition
|
||||||
|
@ -386,7 +385,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.eat(t::RightBrace)?;
|
self.eat(t::RightBrace)?;
|
||||||
e::StructDef(params)
|
e::StructDef(params, usize::MAX)
|
||||||
},
|
},
|
||||||
// Struct literal
|
// Struct literal
|
||||||
t::Identifier(name) if self.look(1, t::LeftBrace).is_ok() => {
|
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));
|
.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)> {
|
fn identifier(&mut self) -> Result<(String, Span)> {
|
||||||
|
|
|
@ -74,7 +74,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
let no_semicolon =
|
let no_semicolon =
|
||||||
if let ExpressionKind::FunctionDef { .. } = value.kind {
|
if let ExpressionKind::FunctionDef { .. } = value.kind {
|
||||||
true
|
true
|
||||||
} else if let ExpressionKind::StructDef(_) = value.kind {
|
} else if let ExpressionKind::StructDef(_, _) = value.kind {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
@ -1,85 +1,199 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Expression, ExpressionKind, Statement, err::*, semantic::SymbolTable,
|
Expression, ExpressionKind, Immediate, Statement, StatementKind,
|
||||||
|
err::*,
|
||||||
|
semantic::{Primitive, SymbolTable},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Type;
|
use super::Type;
|
||||||
|
|
||||||
pub struct Analyzer {
|
pub struct Analyzer {
|
||||||
table: SymbolTable,
|
pub table: SymbolTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Analyzer {
|
impl Analyzer {
|
||||||
pub fn typecheck(statements: Vec<Statement>) -> Vec<Statement> {
|
pub fn typecheck(mut statements: Vec<Statement>) -> Vec<Statement> {
|
||||||
/*
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
table: SymbolTable::new(),
|
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
|
statements
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bottom up type inference
|
pub fn naming_pass_stmt(
|
||||||
fn propogate_up(
|
&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 self,
|
||||||
mut expr: Box<Expression>,
|
mut expr: Box<Expression>,
|
||||||
) -> Result<Box<Expression>> {
|
) -> Result<Box<Expression>> {
|
||||||
use ExpressionKind as e;
|
use ExpressionKind as e;
|
||||||
expr.kind = match expr.kind {
|
expr.kind = match expr.kind {
|
||||||
e::Immediate(imm) => e::Immediate(imm),
|
e::Immediate(immediate) => {
|
||||||
e::Identifier(id, mut mangle) => {
|
expr.type_ = Type::Prim(match &immediate {
|
||||||
let symbol = self.table.lookup(&id).span(&expr.span)?;
|
Immediate::Integer(_, _) => Primitive::integer_ambiguous,
|
||||||
mangle = symbol.uid;
|
Immediate::Real(_) => Primitive::real_ambiguous,
|
||||||
expr.type_ = symbol.type_;
|
Immediate::String(_) => Primitive::string,
|
||||||
e::Identifier(id, mangle)
|
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 {
|
e::Binary {
|
||||||
op,
|
op,
|
||||||
mut left,
|
mut left,
|
||||||
mut right,
|
mut right,
|
||||||
} => {
|
} => {
|
||||||
left = self.propogate_up(left)?;
|
left = self.naming_pass_expr(left)?;
|
||||||
right = self.propogate_up(right)?;
|
right = self.naming_pass_expr(right)?;
|
||||||
expr.type_ =
|
|
||||||
Type::binary_op(&left.type_, op, &right.type_).span(&expr.span)?;
|
|
||||||
e::Binary { op, left, right }
|
e::Binary { op, left, right }
|
||||||
},
|
},
|
||||||
e::Unary { op, mut child } => {
|
e::Unary { op, mut child } => {
|
||||||
child = self.propogate_up(child)?;
|
child = self.naming_pass_expr(child)?;
|
||||||
expr.type_ = Type::unary_op(op, &child.type_).span(&expr.span)?;
|
|
||||||
e::Unary { op, child }
|
e::Unary { op, child }
|
||||||
},
|
},
|
||||||
e::Parenthesis(mut inner) => {
|
e::Parenthesis(mut expression) => {
|
||||||
inner = self.propogate_up(inner)?;
|
expression = self.naming_pass_expr(expression)?;
|
||||||
expr.type_ = inner.type_.clone();
|
e::Parenthesis(expression)
|
||||||
e::Parenthesis(inner)
|
|
||||||
},
|
},
|
||||||
// Define anonymous function, do not evaluate body yet
|
|
||||||
e::FunctionDef {
|
e::FunctionDef {
|
||||||
mut params,
|
params,
|
||||||
returns_str,
|
returns_str,
|
||||||
mut returns_actual,
|
returns_actual,
|
||||||
body,
|
mut body,
|
||||||
mut id,
|
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
|
id = self
|
||||||
.table
|
.table
|
||||||
.start_func(param_types.clone(), returns_actual.clone());
|
.create_function(params.clone(), returns_str.clone());
|
||||||
expr.type_ = Type::FunctionDef {
|
expr.type_ = Type::Function(id.clone());
|
||||||
params: param_types,
|
self.table.start_function();
|
||||||
returns: returns_actual.clone().into(),
|
for s in &mut body {
|
||||||
id: id.clone(),
|
*s = *self.naming_pass_stmt(s.clone().into())?;
|
||||||
};
|
}
|
||||||
|
self.table.end_function();
|
||||||
e::FunctionDef {
|
e::FunctionDef {
|
||||||
params,
|
params,
|
||||||
returns_str,
|
returns_str,
|
||||||
|
@ -91,47 +205,12 @@ impl Analyzer {
|
||||||
e::FunctionCall {
|
e::FunctionCall {
|
||||||
mut callee,
|
mut callee,
|
||||||
mut args,
|
mut args,
|
||||||
mut is_reference,
|
is_reference,
|
||||||
mut id,
|
id,
|
||||||
} => {
|
} => {
|
||||||
callee = self.propogate_up(callee)?;
|
callee = self.naming_pass_expr(callee)?;
|
||||||
let (params, returns) = match callee.type_.clone() {
|
for a in &mut args {
|
||||||
Type::FunctionRef {
|
*a = *self.naming_pass_expr(a.clone().into())?;
|
||||||
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())?;
|
|
||||||
}
|
}
|
||||||
e::FunctionCall {
|
e::FunctionCall {
|
||||||
callee,
|
callee,
|
||||||
|
@ -140,405 +219,31 @@ impl Analyzer {
|
||||||
id,
|
id,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
e::StructDef(vec) => todo!(),
|
e::StructDef(params, mut sid) => {
|
||||||
e::StructLiteral { ref name, mut args } => {
|
sid = self.table.create_struct(params.clone());
|
||||||
self.table.lookup_typedef(name).span(&expr.span)?;
|
expr.type_ = Type::Alias(Box::new(Type::Struct(sid)));
|
||||||
todo!()
|
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 {
|
e::Field {
|
||||||
namespace,
|
mut namespace,
|
||||||
field,
|
field,
|
||||||
uid,
|
uid,
|
||||||
} => todo!(),
|
} => {
|
||||||
|
namespace = self.naming_pass_expr(namespace)?;
|
||||||
|
e::Field {
|
||||||
|
namespace,
|
||||||
|
field,
|
||||||
|
uid,
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
Ok(expr)
|
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 analyzer;
|
||||||
|
mod bottom_up;
|
||||||
mod primitives;
|
mod primitives;
|
||||||
|
mod top_down;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
use crate::err::*;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{Parameter, err::*};
|
||||||
pub use analyzer::*;
|
pub use analyzer::*;
|
||||||
pub use primitives::*;
|
pub use primitives::*;
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
@ -15,6 +19,20 @@ pub struct Symbol {
|
||||||
name: String,
|
name: String,
|
||||||
type_: Type,
|
type_: Type,
|
||||||
uid: UID,
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -26,121 +44,253 @@ enum Definition {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SymbolTable {
|
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,
|
mangle_num: usize,
|
||||||
nesting: usize,
|
nesting: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolTable {
|
impl SymbolTable {
|
||||||
pub fn new() -> Self {
|
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 {
|
Self {
|
||||||
defs: vec![],
|
structs: vec![],
|
||||||
|
functions: vec![],
|
||||||
|
scope,
|
||||||
|
table,
|
||||||
mangle_num: 0,
|
mangle_num: 0,
|
||||||
nesting: 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 {
|
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;
|
self.mangle_num += 1;
|
||||||
uid
|
uid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_func(&mut self, params: Vec<Type>, returns: Type) -> UID {
|
pub fn create_struct(&mut self, params: Vec<Parameter>) -> SID {
|
||||||
self.nesting += 1;
|
let sid = self.structs.len();
|
||||||
let uid = self.generate_uid("func");
|
let mut new_params = vec![];
|
||||||
let type_ = Type::FunctionDef {
|
for p in params {
|
||||||
params,
|
let s = self.reference_ident(&p.type_str);
|
||||||
returns: returns.into(),
|
new_params.push((p.name, s.uid));
|
||||||
id: uid.clone(),
|
}
|
||||||
|
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.functions.push(FunctionDef {
|
||||||
self.defs.push(Definition::FuncStart);
|
params: new_params,
|
||||||
uid
|
returns,
|
||||||
|
});
|
||||||
|
fid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end_func(&mut self) {
|
pub fn modify_ident(
|
||||||
while !self.defs.is_empty() {
|
&mut self,
|
||||||
if let Some(Definition::FuncStart) = self.defs.pop() {
|
uid: UID,
|
||||||
return;
|
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;
|
let mut nesting = self.nesting;
|
||||||
for def in &self.defs {
|
for def in &mut self.scope {
|
||||||
match def {
|
match def {
|
||||||
Definition::Ident(symbol)
|
Definition::Ident(symbol)
|
||||||
if (symbol.name == name)
|
if (symbol.uid == uid)
|
||||||
&& (nesting == 0 || nesting == self.nesting) =>
|
&& (nesting == 0 || nesting == self.nesting) =>
|
||||||
{
|
{
|
||||||
if let Type::StructDef(vec) = &symbol.type_ {
|
if let Some(ref name) = name {
|
||||||
return Ok(Type::Struct(
|
symbol.name = name.clone();
|
||||||
vec.iter().map(|p| p.type_actual.clone()).collect(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
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,
|
Definition::FuncStart => nesting -= 1,
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(p) = Primitive::from_string(name) {
|
panic!("Symbol {uid} does not exist")
|
||||||
return Ok(Type::Prim(p));
|
|
||||||
}
|
|
||||||
error().reason(format!("Cannot find type definition '{}'", name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
let mut nesting = self.nesting;
|
||||||
for def in &self.defs {
|
for def in &self.scope {
|
||||||
match def {
|
match def {
|
||||||
Definition::Ident(symbol)
|
Definition::Ident(symbol)
|
||||||
if (symbol.name == name)
|
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());
|
return Ok(symbol.clone());
|
||||||
},
|
},
|
||||||
Definition::FuncStart => nesting -= 1,
|
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::{BinaryOp, UnaryOp};
|
||||||
|
|
||||||
use crate::err::*;
|
use crate::err::*;
|
||||||
|
use crate::semantic::{Symbol, Type, UID};
|
||||||
|
|
||||||
macro_rules! primitives {
|
macro_rules! primitives {
|
||||||
( $($i:ident),* ) => {
|
( $($i:ident),* ) => {
|
||||||
|
@ -19,6 +20,16 @@ macro_rules! primitives {
|
||||||
_ => None,
|
_ => 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 {
|
impl std::fmt::Display for Primitive {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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,
|
integer_ambiguous,
|
||||||
i8 | i16 | i32 | i64 | integer | w8 | w16 | w32 | w64 | whole,
|
i8 | i16 | i32 | i64 | integer | w8 | w16 | w32 | w64 | whole,
|
||||||
) => into,
|
) => into,
|
||||||
(real_ambiguous, r32 | r64) => into,
|
(real_ambiguous, r32 | r64 | real) => into,
|
||||||
_ => *self,
|
_ => *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::{
|
use crate::{BinaryOp, UnaryOp};
|
||||||
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
|
|
||||||
StatementKind, UnaryOp,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{UID, primitives::*};
|
use super::{Symbol, UID, primitives::*};
|
||||||
use crate::err::*;
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Ambiguous,
|
Ambiguous,
|
||||||
Nothing,
|
Nothing,
|
||||||
|
Alias(Box<Type>),
|
||||||
Prim(Primitive),
|
Prim(Primitive),
|
||||||
Struct(Vec<Type>),
|
Struct(SID),
|
||||||
StructDef(Vec<Parameter>),
|
Function(FID),
|
||||||
FunctionRef {
|
|
||||||
params: Vec<Type>,
|
|
||||||
returns: Box<Type>,
|
|
||||||
id: UID,
|
|
||||||
},
|
|
||||||
FunctionDef {
|
|
||||||
params: Vec<Type>,
|
|
||||||
returns: Box<Type>,
|
|
||||||
id: UID,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Type {
|
impl std::fmt::Display for Type {
|
||||||
|
@ -32,15 +38,8 @@ impl std::fmt::Display for Type {
|
||||||
Type::Nothing => write!(f, "nothing"),
|
Type::Nothing => write!(f, "nothing"),
|
||||||
Type::Prim(primitive) => write!(f, "{primitive}"),
|
Type::Prim(primitive) => write!(f, "{primitive}"),
|
||||||
Type::Struct(vec) => write!(f, "struct {vec:?}"),
|
Type::Struct(vec) => write!(f, "struct {vec:?}"),
|
||||||
Type::StructDef(_) => write!(f, "struct definition"),
|
Type::Alias(t) => write!(f, "type alias ({t})"),
|
||||||
Type::FunctionRef {
|
Type::Function(fid) => write!(f, "func {fid:?}"),
|
||||||
params, returns, ..
|
|
||||||
} => {
|
|
||||||
write!(f, "({params:?}) -> {returns}")
|
|
||||||
},
|
|
||||||
Type::FunctionDef {
|
|
||||||
params, returns, ..
|
|
||||||
} => write!(f, "({params:?}) -> {returns}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,9 +50,9 @@ impl PartialEq for Type {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Ambiguous, Ambiguous) => true,
|
(Ambiguous, Ambiguous) => true,
|
||||||
(Prim(p1), Prim(p2)) if p1 == p2 => true,
|
(Prim(p1), Prim(p2)) if p1 == p2 => true,
|
||||||
(Struct(p1), Struct(p2)) => p1.iter().eq(p2.iter()),
|
(Struct(p1), Struct(p2)) => p1 == p2,
|
||||||
(FunctionRef { id: id1, .. }, FunctionRef { id: id2, .. }) => id1 == id2,
|
(Function(id1), Function(id2)) => id1 == id2,
|
||||||
(FunctionDef { id: id1, .. }, FunctionDef { id: id2, .. }) => id1 == id2,
|
(Alias(t1), Alias(t2)) => t1 == t2,
|
||||||
(Nothing, Nothing) => true,
|
(Nothing, Nothing) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -85,17 +84,19 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn coerce(&mut self, expect: &Type) {
|
pub fn deduce(self, hint: &Type) -> Result<Self> {
|
||||||
use Type::*;
|
use Type::*;
|
||||||
*self = match (expect, self.clone()) {
|
match (hint, self) {
|
||||||
(Ambiguous, Ambiguous) => Ambiguous,
|
(Ambiguous, t) => Ok(t),
|
||||||
(Ambiguous, t) => t.clone(),
|
(t, Ambiguous) => Ok(t.clone()),
|
||||||
(Prim(mut p1), Prim(p2)) => {
|
(Prim(mut p1), Prim(p2)) => {
|
||||||
p1.coerce(p2);
|
p1.coerce(p2);
|
||||||
Prim(p1)
|
Ok(Prim(p1))
|
||||||
},
|
},
|
||||||
(t1, t2) if t1 == &t2 => t1.clone(),
|
(t1, t2) if t1 == &t2 => Ok(t1.clone()),
|
||||||
_ => Ambiguous,
|
(t1, t2) => {
|
||||||
};
|
error().reason(format!("Cannot coerce type '{t2}' into '{t1}'"))
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue