Splitting up semantics into lots of phases

This commit is contained in:
Logan 2024-10-29 22:33:38 -05:00
parent 22c78a9945
commit 552496c0f9
10 changed files with 543 additions and 603 deletions

View file

@ -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,
}

View file

@ -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(())

View file

@ -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)> {

View file

@ -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

View file

@ -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
View 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!()
}
}

View file

@ -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;
}
} }
} }

View file

@ -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
View 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!()
}
}

View file

@ -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}'"))
},
}
} }
} }