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 := b;
c :: C{r: 10.0};
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<()> {
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(())

View file

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

View file

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

View file

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

View file

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