finished var and func numbering

This commit is contained in:
Logan 2024-10-24 14:40:41 -05:00
parent 6175f976a3
commit 4e13bb2ffc
10 changed files with 632 additions and 444 deletions

3
demo.hal Normal file
View file

@ -0,0 +1,3 @@
bar :: (a: integer) {
local := a;
}

View file

@ -1,2 +0,0 @@
a : i32 = 10;
b := 20 + a;

View file

@ -1,6 +1,10 @@
use std::path::Path; use std::path::Path;
use crate::{err::*, semantic::typecheck, Parser, Statement, Tokenizer}; use crate::{
err::*,
semantic::{self},
Parser, Statement, Tokenizer,
};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Module { pub struct Module {
@ -26,7 +30,7 @@ impl Module {
pub fn from_string(file_name: String, source: String) -> Self { pub fn from_string(file_name: String, source: String) -> Self {
let tokens = Tokenizer::new(source.chars()).filter(|t| t.0.is_meaningful()); let tokens = Tokenizer::new(source.chars()).filter(|t| t.0.is_meaningful());
let statements = Parser::new(tokens); let statements = Parser::new(tokens);
let program = typecheck(statements.collect()); let program = semantic::Analyzer::typecheck(statements.collect());
let mut errors = vec![]; let mut errors = vec![];
Self { Self {
file_name, file_name,

View file

@ -1,17 +1,57 @@
use crate::{semantic::Type, BinaryOp, Immediate, UnaryOp}; use crate::{
BinaryOp, Expression, ExpressionKind, Immediate, UnaryOp,
semantic::{Type, VarKind, uid},
};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum IR { pub enum IR {
BinOp { op: BinaryOp, type_: Type }, BinOp { op: BinaryOp, type_: Type },
UnOp { op: UnaryOp, type_: Type }, UnOp { op: UnaryOp, type_: Type },
Imm(Immediate), Imm(Immediate),
NewLocal { uid: usize, type_: Type }, NewLocal { uid: uid, type_: Type },
AssignLocal { uid: usize }, AssignLocal { uid: uid },
AccessLocal { uid: usize }, GetLocal { uid: uid },
NewGlobal { uid: usize, type_: Type }, NewGlobal { uid: uid, type_: Type },
AssignGlobal { uid: usize }, AssignGlobal { uid: uid },
AccessGlobal { uid: usize }, GetGlobal { uid: uid },
StartFunc { uid: usize }, StartFunc { uid: uid },
NewParam { uid: usize, type_: Type }, NewParam { uid: uid, type_: Type },
EndFunc, EndFunc,
} }
pub struct Compiler {
ir: Vec<IR>,
}
impl Compiler {
fn expression(&mut self, expression: Expression) {
use ExpressionKind::*;
match expression.kind {
Immediate(immediate) => {
self.ir.push(IR::Imm(immediate));
},
Identifier(name, var_kind) => match var_kind {
VarKind::Global(uid) => self.ir.push(IR::GetGlobal { uid }),
VarKind::Local(uid) | VarKind::Param(uid) => {
self.ir.push(IR::GetLocal { uid })
},
VarKind::Function(_) => todo!(),
VarKind::Undefined => todo!(),
},
Binary { op, left, right } => todo!(),
Unary { op, child } => todo!(),
Parenthesis(expression) => todo!(),
FunctionDef {
params,
returns_str,
returns_actual,
body,
id,
} => todo!(),
FunctionCall { callee, args } => todo!(),
StructDef(vec) => todo!(),
StructLiteral { name, args } => todo!(),
Field { namespace, field } => todo!(),
}
}
}

View file

@ -62,10 +62,36 @@ fn test_expression(expr: &str) {
println!("{:?}", parser.expression(0).unwrap()); println!("{:?}", parser.expression(0).unwrap());
} }
fn prints(st: &Statement) {
use StatementKind as s;
println!("{st:?}");
match &st.kind {
s::Declaration {
value:
Expression {
kind: ExpressionKind::FunctionDef { body: block, .. },
..
},
..
}
| s::If { block, .. }
| s::While { block, .. }
| s::Block(block) => {
for s in block {
prints(s);
}
},
_ => {},
};
}
fn main() -> Result<()> { fn main() -> Result<()> {
let module = frontend::Module::from_file("./demo.lang")?; test_expression("asdf.asdf()");
/*
let module = frontend::Module::from_file("./demo.hal")?;
for s in &module.program { for s in &module.program {
println!("{s:?}"); prints(s);
} }
*/
Ok(()) Ok(())
} }

View file

@ -26,7 +26,7 @@ pub enum Immediate {
#[derive(Clone)] #[derive(Clone)]
pub enum ExpressionKind { pub enum ExpressionKind {
Immediate(Immediate), Immediate(Immediate),
Identifier(String), Identifier(String, VarKind),
Binary { Binary {
op: BinaryOp, op: BinaryOp,
left: Box<Expression>, left: Box<Expression>,
@ -37,21 +37,22 @@ pub enum ExpressionKind {
child: Box<Expression>, child: Box<Expression>,
}, },
Parenthesis(Box<Expression>), Parenthesis(Box<Expression>),
Function { FunctionDef {
params: Vec<Parameter>, params: Vec<Parameter>,
returns_str: Option<String>, returns_str: Option<String>,
returns_actual: Type, returns_actual: Type,
body: Vec<Statement>, body: Vec<Statement>,
id: uid,
}, },
Struct(Vec<Parameter>), FunctionCall {
callee: Box<Expression>,
args: Vec<Expression>,
},
StructDef(Vec<Parameter>),
StructLiteral { StructLiteral {
name: String, name: String,
args: Vec<(String, Expression)>, args: Vec<(String, Expression)>,
}, },
Call {
callee: Box<Expression>,
args: Vec<Expression>,
},
Field { Field {
namespace: Box<Expression>, namespace: Box<Expression>,
field: Box<Expression>, field: Box<Expression>,
@ -97,19 +98,21 @@ impl std::fmt::Debug for ExpressionKind {
e::Unary { op: token, child } => { e::Unary { op: token, child } => {
write!(f, "({token:?} {child:?})") write!(f, "({token:?} {child:?})")
}, },
e::Identifier(i) => write!(f, "{i}"), e::Identifier(i, _) => write!(f, "{i}"),
e::Call { callee, args } => write!(f, "({callee:?} call {args:?})"), e::FunctionCall { callee, args } => {
write!(f, "({callee:?} call {args:?})")
},
e::Field { namespace, field } => { e::Field { namespace, field } => {
write!(f, "({namespace:?} . {field:?})") write!(f, "({namespace:?} . {field:?})")
}, },
e::Function { e::FunctionDef {
params, params,
returns_actual, returns_actual,
.. ..
} => { } => {
write!(f, "(fn({params:?}) -> {returns_actual:?})") write!(f, "(fn({params:?}) -> {returns_actual:?})")
}, },
e::Struct(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:?} }}"),
} }
} }
@ -217,7 +220,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
} }
let Token(_, span2) = self.eat(t::RightParen).span(&span)?; let Token(_, span2) = self.eat(t::RightParen).span(&span)?;
current = Expression::new( current = Expression::new(
e::Call { e::FunctionCall {
callee: current.into(), callee: current.into(),
args, args,
}, },
@ -326,11 +329,12 @@ impl<I: Iterator<Item = Token>> Parser<I> {
.block() .block()
.trace_span(span, "while parsing function body")?; .trace_span(span, "while parsing function body")?;
span = span + span2; span = span + span2;
e::Function { e::FunctionDef {
params, params,
returns_str, returns_str,
returns_actual: Type::Ambiguous, returns_actual: Type::Ambiguous,
body, body,
id: 0,
} }
}, },
// Struct definition // Struct definition
@ -361,7 +365,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
} }
} }
self.eat(t::RightBrace)?; self.eat(t::RightBrace)?;
e::Struct(params) e::StructDef(params)
}, },
// 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() => {
@ -390,7 +394,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
}, },
t::Identifier(i) => { t::Identifier(i) => {
self.skip(1); self.skip(1);
e::Identifier(i) e::Identifier(i, VarKind::Undefined)
}, },
// Parenthetical // Parenthetical
t::LeftParen => { t::LeftParen => {

View file

@ -1,4 +1,4 @@
use crate::semantic::Type; use crate::semantic::{Type, VarKind};
use super::*; use super::*;
@ -10,10 +10,12 @@ pub enum StatementKind {
type_actual: Type, type_actual: Type,
value: Expression, value: Expression,
mutable: bool, mutable: bool,
varkind: VarKind,
}, },
Assignment { Assignment {
name: String, name: String,
value: Expression, value: Expression,
varkind: VarKind,
}, },
If { If {
predicate: Expression, predicate: Expression,
@ -68,13 +70,14 @@ impl<I: Iterator<Item = Token>> Parser<I> {
.expression(0) .expression(0)
.trace_span(span, "while parsing declaration")?; .trace_span(span, "while parsing declaration")?;
span = span + value.span; span = span + value.span;
let no_semicolon = if let ExpressionKind::Function { .. } = value.kind { let no_semicolon =
true if let ExpressionKind::FunctionDef { .. } = value.kind {
} else if let ExpressionKind::Struct(_) = value.kind { true
true } else if let ExpressionKind::StructDef(_) = value.kind {
} else { true
false } else {
}; false
};
let s = Statement { let s = Statement {
kind: s::Declaration { kind: s::Declaration {
name, name,
@ -82,6 +85,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
type_actual: Type::Ambiguous, type_actual: Type::Ambiguous,
value, value,
mutable, mutable,
varkind: VarKind::Undefined,
}, },
span, span,
}; };
@ -99,7 +103,11 @@ impl<I: Iterator<Item = Token>> Parser<I> {
.trace_span(span, "while parsing assignment")?; .trace_span(span, "while parsing assignment")?;
Statement { Statement {
span, span,
kind: s::Assignment { name, value }, kind: s::Assignment {
name,
value,
varkind: VarKind::Undefined,
},
} }
}, },
// If // If

356
src/semantic/analyzer.rs Normal file
View file

@ -0,0 +1,356 @@
use crate::{
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
StatementKind, UnaryOp,
semantic::{Symbol, SymbolTable, Type, VarKind},
};
use super::primitives::*;
use crate::err::*;
pub struct Analyzer {
table: SymbolTable,
}
impl Analyzer {
pub fn typecheck(statements: Vec<Statement>) -> Vec<Statement> {
let mut this = Self {
table: SymbolTable::new(),
};
this.block(statements)
}
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 mut value = self.expression(value.into())?;
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())?;
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())?;
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())?;
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) => {
stmt.kind = s::Print(*self.expression(e.into())?);
},
s::Expression(e) => {
stmt.kind = s::Expression(*self.expression(e.into())?);
},
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),
}
Ok(stmt)
}
fn expression(
&mut self,
mut expr: Box<Expression>,
) -> Result<Box<Expression>> {
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)?;
let right = self.expression(right)?;
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)?;
let type_ = Type::unary_op(op, &child.type_)?;
expr.kind = e::Unary { child, op };
type_
},
e::Parenthesis(inner) => {
let inner = self.expression(inner)?;
let type_ = inner.type_.clone();
expr.kind = e::Parenthesis(inner);
type_
},
e::FunctionDef {
mut params,
returns_str,
mut returns_actual,
body,
id,
} => {
self.table.start_func();
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())?;
}
returns_actual = match &returns_str {
Some(s) => self.table.get_type(s).span(&expr.span)?,
None => Type::Nothing,
};
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::Function {
params: params.into_iter().map(|p| p.type_actual).collect(),
returns: returns_actual.into(),
}
},
e::FunctionCall { callee, mut args } => {
let callee = self.expression(callee)?;
// Check that this is actually a function
let Type::Function {
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())?;
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())
.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)?;
// 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_
},
};
expr.type_ = type_;
Ok(expr)
}
}

View file

@ -1,39 +1,138 @@
mod analyzer;
mod primitives; mod primitives;
mod types; mod types;
use crate::err::*; use crate::{Parameter, err::*};
pub use analyzer::*;
pub use primitives::*; pub use primitives::*;
pub use types::*; pub use types::*;
#[allow(non_camel_case_types)]
pub type uid = u32;
// Variable and function numbering
#[derive(Clone, Copy, Debug)]
pub enum VarKind {
Global(uid),
Local(uid),
Param(uid),
Function(uid),
Undefined,
}
impl VarKind {
pub fn unwrap(self) -> uid {
match self {
VarKind::Global(i)
| VarKind::Local(i)
| VarKind::Param(i)
| VarKind::Function(i) => i,
VarKind::Undefined => unreachable!("Failed unwrapping uid"),
}
}
}
#[derive(Clone, Debug)]
pub struct Symbol {
pub name: String,
pub type_: Type,
pub mutable: bool,
pub kind: VarKind,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Symbol { pub enum Definition {
Var(String, Type, bool), Symbol(Symbol),
Type(String, Type),
BlockStart, BlockStart,
FuncStart, FuncStart,
} }
fn next(array: &mut [uid]) -> uid {
let current = array.last_mut().unwrap();
let ret = *current;
*current += 1;
ret
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SymbolTable { pub struct SymbolTable {
syms: Vec<Symbol>, syms: Vec<Definition>,
nesting: usize,
local_varno: Vec<uid>,
global_varno: Vec<uid>,
funcno: Vec<uid>,
} }
impl SymbolTable { impl SymbolTable {
fn define_var(&mut self, name: String, type_: Type, mutable: bool) { pub fn new() -> Self {
self.syms.push(Symbol::Var(name, type_, mutable)); Self {
syms: vec![],
nesting: 0,
global_varno: vec![0],
local_varno: vec![0],
funcno: vec![0],
}
} }
fn define_type(&mut self, name: String, type_: Type) { fn define_symbol(
self.syms.push(Symbol::Type(name, type_)); &mut self,
name: String,
type_: Type,
mutable: bool,
) -> Result<VarKind> {
let kind = match type_ {
Type::Prim(_) | Type::Struct(_) => {
if self.nesting == 0 {
VarKind::Global(next(&mut self.global_varno))
} else {
VarKind::Local(next(&mut self.local_varno))
}
},
Type::StructDef(_) => {
if mutable {
return error().reason("Struct definition must be immutable");
}
VarKind::Undefined
},
Type::Function { .. } => {
if !mutable {
VarKind::Function(next(&mut self.funcno))
} else {
return error().reason("Function declaration must be immutable");
}
},
_ => VarKind::Undefined,
};
self.syms.push(Definition::Symbol(Symbol {
name,
type_,
mutable,
kind,
}));
Ok(kind)
}
fn define_param(&mut self, name: String, type_: Type) -> Result<VarKind> {
let kind = VarKind::Param(next(&mut self.local_varno));
self.syms.push(Definition::Symbol(Symbol {
name,
type_,
mutable: false,
kind,
}));
Ok(kind)
} }
fn start_func(&mut self) { fn start_func(&mut self) {
self.syms.push(Symbol::FuncStart); self.nesting += 1;
self.local_varno.push(0);
self.syms.push(Definition::FuncStart);
} }
fn end_func(&mut self) { fn end_func(&mut self) {
self.nesting -= 1;
self.local_varno.pop();
while !self.syms.is_empty() { while !self.syms.is_empty() {
if let Some(Symbol::FuncStart) = self.syms.pop() { if let Some(Definition::FuncStart) = self.syms.pop() {
return; return;
} }
} }
@ -41,34 +140,50 @@ impl SymbolTable {
} }
fn start_block(&mut self) { fn start_block(&mut self) {
self.syms.push(Symbol::BlockStart); self.syms.push(Definition::BlockStart);
} }
fn end_block(&mut self) { fn end_block(&mut self) {
while !self.syms.is_empty() { while !self.syms.is_empty() {
if let Some(Symbol::BlockStart) = self.syms.pop() { if let Some(Definition::BlockStart) = self.syms.pop() {
return; return;
} }
} }
unreachable!("Tried to exit global scope in symbol table") unreachable!("Tried to exit global scope in symbol table")
} }
fn get_var(&self, name: &str) -> Result<(Type, bool)> { fn find_symbol(&self, find_name: &str) -> Result<Symbol> {
let mut nesting = self.nesting;
println!("Looking for {find_name}, scope = {nesting}");
for s in self.syms.iter().rev() { for s in self.syms.iter().rev() {
if let Symbol::Var(name2, type_, mutable) = s { match s {
if name == name2 { Definition::Symbol(sym)
return Ok((type_.clone(), *mutable)); // Only search function local and global scope
} if nesting == self.nesting || nesting == 0 => {
} println!("{}, {:?}, {nesting}", sym.name, sym.type_);
if find_name == sym.name {
return Ok(sym.clone());
}
},
Definition::FuncStart => {
nesting -= 1;
},
_ => {},
};
} }
error().reason(format!("Identifier {name} is not defined")) error().reason(format!("Symbol '{find_name}' is not defined"))
} }
fn get_type(&self, name: &str) -> Result<Type> { fn get_type(&self, name: &str) -> Result<Type> {
for s in self.syms.iter().rev() { for s in self.syms.iter().rev() {
if let Symbol::Type(name2, t) = s { if let Definition::Symbol(Symbol {
name: name2,
type_: Type::StructDef(params),
..
}) = s
{
if name == name2 { if name == name2 {
return Ok(t.clone()); return Ok(Type::Struct(params.clone()));
} }
} }
} }

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
semantic::SymbolTable, BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement, semantic::{Symbol, SymbolTable},
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
StatementKind, UnaryOp, StatementKind, UnaryOp,
}; };
@ -12,6 +13,7 @@ pub enum Type {
Nothing, Nothing,
Prim(Primitive), Prim(Primitive),
Struct(Vec<Parameter>), Struct(Vec<Parameter>),
StructDef(Vec<Parameter>),
Function { Function {
params: Vec<Type>, params: Vec<Type>,
returns: Box<Type>, returns: Box<Type>,
@ -54,7 +56,7 @@ impl Type {
(t::Prim(a), t::Prim(b)) => { (t::Prim(a), t::Prim(b)) => {
let p = Primitive::binary_op(*a, op, *b)?; let p = Primitive::binary_op(*a, op, *b)?;
Ok(t::Prim(p)) Ok(t::Prim(p))
} },
_ => e, _ => e,
} }
} }
@ -69,392 +71,24 @@ impl Type {
} }
} }
fn coerce(expect: &Type, actual: &Type) -> Option<Type> { pub fn coerce(expect: &Type, actual: &Type) -> Result<Type> {
use Primitive as p; use Primitive as p;
use Type::*; use Type::*;
let e = || {
error().reason(format!(
"Could not coerce type '{actual:?}' into '{expect:?}'"
))
};
match (expect, actual) { match (expect, actual) {
(Ambiguous, Ambiguous) => None, (Ambiguous, Ambiguous) => e(),
(Ambiguous, Prim(p::integer_ambiguous)) => Some(Prim(p::integer)), (Ambiguous, Prim(p::integer_ambiguous)) => Ok(Prim(p::integer)),
(Ambiguous, Prim(p::real_ambiguous)) => Some(Prim(p::real)), (Ambiguous, Prim(p::real_ambiguous)) => Ok(Prim(p::real)),
(Ambiguous, t) => Some(t.clone()), (Ambiguous, t) => Ok(t.clone()),
(Prim(p1), Prim(p2)) => { (Prim(p1), Prim(p2)) => {
let (p1, p2) = Primitive::coerce_ambiguous(*p1, *p2); let (p1, p2) = Primitive::coerce_ambiguous(*p1, *p2);
if p1 != p2 { if p1 != p2 { e() } else { Ok(Type::Prim(p1)) }
None
} else {
Some(Type::Prim(p1))
}
}
_ => None,
}
}
}
pub fn typecheck(program: Vec<Statement>) -> Vec<Statement> {
use StatementKind as s;
let mut table = SymbolTable { syms: vec![] };
let mut ret = vec![];
for s in program {
let span = s.span;
ret.push(match statement(s.into(), &mut table) {
Ok(s) => *s,
Err(e) => Statement {
kind: s::Error(e),
span,
}, },
}) _ => e(),
}
} }
ret
}
fn statement(mut stmt: Box<Statement>, table: &mut SymbolTable) -> Result<Box<Statement>> {
use Primitive as p;
use StatementKind as s;
match stmt.kind {
s::Declaration {
name,
type_str,
value,
mutable,
..
} => {
let type_expect = match type_str {
Some(ref s) => table.get_type(s).span(&stmt.span)?,
None => Type::Ambiguous,
};
let value = expression(value.into(), table)?;
let type_actual = Type::coerce(&type_expect, &value.type_).reason(format!(
"Expected type '{:?}', found type '{:?}'",
type_expect, value.type_
))?;
// Check that structs are const
if let Type::Struct(_) = type_actual {
if mutable {
return error()
.reason("Struct declarations must be immutable")
.span(&stmt.span);
}
table.define_type(name.clone(), type_actual.clone());
}
// Check that functions are const
else if let Type::Function { .. } = type_actual {
if mutable {
return error()
.reason("Function declarations must be immutable")
.span(&stmt.span);
}
table.define_var(name.clone(), type_actual.clone(), false);
} else {
table.define_var(name.clone(), type_actual.clone(), mutable);
}
stmt.kind = s::Declaration {
name,
type_str,
type_actual,
value: *value,
mutable,
};
}
s::Assignment { name, value } => {
let (type_, mutable) = table.get_var(&name).span(&stmt.span)?;
// Check that it is mutable
if !mutable {
return error()
.reason(format!("Cannot assign to immutable '{}'", name))
.span(&stmt.span);
}
let value = *expression(value.into(), table)?;
// Check for correct type
if type_ != value.type_ {
return error().reason(format!(
"Attempted to assign '{:?}' to '{type_:?}'",
value.type_
));
}
stmt.kind = s::Assignment { name, value };
}
s::If {
predicate,
block,
else_,
} => {
table.start_block();
let predicate = *expression(predicate.into(), table)?;
if predicate.type_ != Type::Prim(p::boolean) {
return error()
.reason(format!(
"Predicate of if statement must be a boolean, found {:?}",
predicate.type_
))
.span(&predicate.span);
}
let mut new_block = vec![];
for stmt in block {
new_block.push(*statement(stmt.into(), table)?);
}
let else_ = if let Some(else_) = else_ {
Some(statement(else_, table)?)
} else {
None
};
stmt.kind = s::If {
predicate,
block: new_block,
else_,
};
table.end_block();
}
s::While { predicate, block } => {
table.start_block();
let predicate = *expression(predicate.into(), table)?;
if predicate.type_ != Type::Prim(p::boolean) {
return error()
.reason(format!(
"Predicate of while statement must be a boolean, found {:?}",
predicate.type_
))
.span(&predicate.span);
}
let mut new_block = vec![];
for stmt in block {
new_block.push(*statement(stmt.into(), table)?);
}
stmt.kind = s::While {
predicate,
block: new_block,
};
table.end_block();
}
s::Print(e) => {
stmt.kind = s::Print(*expression(e.into(), table)?);
}
s::Expression(mut e) => {
use ExpressionKind as e;
let is_func = if let e::Function { params, .. } = &mut e.kind {
// TODO: start/end function instead
table.start_block();
for p in params {
p.type_actual = table.get_type(&p.type_str)?;
table.define_var(p.name.clone(), p.type_actual.clone(), false);
}
true
} else {
false
};
stmt.kind = s::Expression(*expression(e.into(), table)?);
if is_func {
table.end_block();
}
}
s::Block(block) => {
table.start_block();
let mut new_block = vec![];
for stmt in block {
new_block.push(*statement(stmt.into(), table)?);
}
stmt.kind = s::Block(new_block);
table.end_block();
}
s::Error(e) => return Err(e),
}
Ok(stmt)
}
fn expression(mut expr: Box<Expression>, table: &SymbolTable) -> Result<Box<Expression>> {
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) => table.get_var(i)?.0,
e::Binary { op, left, right } => {
let left = expression(left, table)?;
let right = expression(right, table)?;
let type_ = Type::binary_op(&left.type_, op, &right.type_)?;
expr.kind = e::Binary { left, right, op };
type_
}
e::Unary { op, child } => {
let child = expression(child, table)?;
let type_ = Type::unary_op(op, &child.type_)?;
expr.kind = e::Unary { child, op };
type_
}
e::Parenthesis(inner) => {
let inner = expression(inner, table)?;
let type_ = inner.type_.clone();
expr.kind = e::Parenthesis(inner);
type_
}
e::Function {
mut params,
returns_str,
mut returns_actual,
body,
} => {
for p in &mut params {
p.type_actual = table.get_type(&p.type_str).span(&expr.span)?;
}
returns_actual = match &returns_str {
Some(s) => table.get_type(s).span(&expr.span)?,
None => Type::Nothing,
};
expr.kind = e::Function {
params: params.clone(),
returns_str,
returns_actual: returns_actual.clone(),
body,
};
Type::Function {
params: params.into_iter().map(|p| p.type_actual).collect(),
returns: returns_actual.into(),
}
}
e::Call { callee, mut args } => {
let callee = expression(callee, table)?;
// Check that this is actually a function
let Type::Function {
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 = *expression(actual.clone().into(), table)?;
let coerced_type = Type::coerce(expect, &actual.type_);
println!("{:?}, {:?}, {coerced_type:?}", expect, actual.type_);
if let Some(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::Call { callee, args };
returns
}
e::Struct(mut params) => {
for p in &mut params {
p.type_actual = table.get_type(&p.type_str).span(&expr.span)?;
}
expr.kind = e::Struct(params.clone());
Type::Struct(params)
}
e::StructLiteral { name, args } => {
let type_ = 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 = *expression(argexpr.clone().into(), table)
.trace_span(expr.span, "while parsing struct literal")?;
let coerced_type = Type::coerce(ptype, &arg.type_);
if let Some(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 = expression(namespace, table)?;
// 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_
}
};
expr.type_ = type_;
Ok(expr)
} }