finished var and func numbering
This commit is contained in:
parent
6175f976a3
commit
4e13bb2ffc
|
@ -1,6 +1,10 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::{err::*, semantic::typecheck, Parser, Statement, Tokenizer};
|
||||
use crate::{
|
||||
err::*,
|
||||
semantic::{self},
|
||||
Parser, Statement, Tokenizer,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Module {
|
||||
|
@ -26,7 +30,7 @@ impl Module {
|
|||
pub fn from_string(file_name: String, source: String) -> Self {
|
||||
let tokens = Tokenizer::new(source.chars()).filter(|t| t.0.is_meaningful());
|
||||
let statements = Parser::new(tokens);
|
||||
let program = typecheck(statements.collect());
|
||||
let program = semantic::Analyzer::typecheck(statements.collect());
|
||||
let mut errors = vec![];
|
||||
Self {
|
||||
file_name,
|
||||
|
|
58
src/ir.rs
58
src/ir.rs
|
@ -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)]
|
||||
pub enum IR {
|
||||
BinOp { op: BinaryOp, type_: Type },
|
||||
UnOp { op: UnaryOp, type_: Type },
|
||||
Imm(Immediate),
|
||||
NewLocal { uid: usize, type_: Type },
|
||||
AssignLocal { uid: usize },
|
||||
AccessLocal { uid: usize },
|
||||
NewGlobal { uid: usize, type_: Type },
|
||||
AssignGlobal { uid: usize },
|
||||
AccessGlobal { uid: usize },
|
||||
StartFunc { uid: usize },
|
||||
NewParam { uid: usize, type_: Type },
|
||||
NewLocal { uid: uid, type_: Type },
|
||||
AssignLocal { uid: uid },
|
||||
GetLocal { uid: uid },
|
||||
NewGlobal { uid: uid, type_: Type },
|
||||
AssignGlobal { uid: uid },
|
||||
GetGlobal { uid: uid },
|
||||
StartFunc { uid: uid },
|
||||
NewParam { uid: uid, type_: Type },
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -62,10 +62,36 @@ fn test_expression(expr: &str) {
|
|||
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<()> {
|
||||
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 {
|
||||
println!("{s:?}");
|
||||
prints(s);
|
||||
}
|
||||
*/
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ pub enum Immediate {
|
|||
#[derive(Clone)]
|
||||
pub enum ExpressionKind {
|
||||
Immediate(Immediate),
|
||||
Identifier(String),
|
||||
Identifier(String, VarKind),
|
||||
Binary {
|
||||
op: BinaryOp,
|
||||
left: Box<Expression>,
|
||||
|
@ -37,21 +37,22 @@ pub enum ExpressionKind {
|
|||
child: Box<Expression>,
|
||||
},
|
||||
Parenthesis(Box<Expression>),
|
||||
Function {
|
||||
FunctionDef {
|
||||
params: Vec<Parameter>,
|
||||
returns_str: Option<String>,
|
||||
returns_actual: Type,
|
||||
body: Vec<Statement>,
|
||||
id: uid,
|
||||
},
|
||||
Struct(Vec<Parameter>),
|
||||
FunctionCall {
|
||||
callee: Box<Expression>,
|
||||
args: Vec<Expression>,
|
||||
},
|
||||
StructDef(Vec<Parameter>),
|
||||
StructLiteral {
|
||||
name: String,
|
||||
args: Vec<(String, Expression)>,
|
||||
},
|
||||
Call {
|
||||
callee: Box<Expression>,
|
||||
args: Vec<Expression>,
|
||||
},
|
||||
Field {
|
||||
namespace: Box<Expression>,
|
||||
field: Box<Expression>,
|
||||
|
@ -97,19 +98,21 @@ impl std::fmt::Debug for ExpressionKind {
|
|||
e::Unary { op: token, child } => {
|
||||
write!(f, "({token:?} {child:?})")
|
||||
},
|
||||
e::Identifier(i) => write!(f, "{i}"),
|
||||
e::Call { callee, args } => write!(f, "({callee:?} call {args:?})"),
|
||||
e::Identifier(i, _) => write!(f, "{i}"),
|
||||
e::FunctionCall { callee, args } => {
|
||||
write!(f, "({callee:?} call {args:?})")
|
||||
},
|
||||
e::Field { namespace, field } => {
|
||||
write!(f, "({namespace:?} . {field:?})")
|
||||
},
|
||||
e::Function {
|
||||
e::FunctionDef {
|
||||
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:?} }}"),
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +220,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
}
|
||||
let Token(_, span2) = self.eat(t::RightParen).span(&span)?;
|
||||
current = Expression::new(
|
||||
e::Call {
|
||||
e::FunctionCall {
|
||||
callee: current.into(),
|
||||
args,
|
||||
},
|
||||
|
@ -326,11 +329,12 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
.block()
|
||||
.trace_span(span, "while parsing function body")?;
|
||||
span = span + span2;
|
||||
e::Function {
|
||||
e::FunctionDef {
|
||||
params,
|
||||
returns_str,
|
||||
returns_actual: Type::Ambiguous,
|
||||
body,
|
||||
id: 0,
|
||||
}
|
||||
},
|
||||
// Struct definition
|
||||
|
@ -361,7 +365,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
}
|
||||
}
|
||||
self.eat(t::RightBrace)?;
|
||||
e::Struct(params)
|
||||
e::StructDef(params)
|
||||
},
|
||||
// Struct literal
|
||||
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) => {
|
||||
self.skip(1);
|
||||
e::Identifier(i)
|
||||
e::Identifier(i, VarKind::Undefined)
|
||||
},
|
||||
// Parenthetical
|
||||
t::LeftParen => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::semantic::Type;
|
||||
use crate::semantic::{Type, VarKind};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -10,10 +10,12 @@ pub enum StatementKind {
|
|||
type_actual: Type,
|
||||
value: Expression,
|
||||
mutable: bool,
|
||||
varkind: VarKind,
|
||||
},
|
||||
Assignment {
|
||||
name: String,
|
||||
value: Expression,
|
||||
varkind: VarKind,
|
||||
},
|
||||
If {
|
||||
predicate: Expression,
|
||||
|
@ -68,13 +70,14 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
.expression(0)
|
||||
.trace_span(span, "while parsing declaration")?;
|
||||
span = span + value.span;
|
||||
let no_semicolon = if let ExpressionKind::Function { .. } = value.kind {
|
||||
true
|
||||
} else if let ExpressionKind::Struct(_) = value.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let no_semicolon =
|
||||
if let ExpressionKind::FunctionDef { .. } = value.kind {
|
||||
true
|
||||
} else if let ExpressionKind::StructDef(_) = value.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let s = Statement {
|
||||
kind: s::Declaration {
|
||||
name,
|
||||
|
@ -82,6 +85,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
type_actual: Type::Ambiguous,
|
||||
value,
|
||||
mutable,
|
||||
varkind: VarKind::Undefined,
|
||||
},
|
||||
span,
|
||||
};
|
||||
|
@ -99,7 +103,11 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
|||
.trace_span(span, "while parsing assignment")?;
|
||||
Statement {
|
||||
span,
|
||||
kind: s::Assignment { name, value },
|
||||
kind: s::Assignment {
|
||||
name,
|
||||
value,
|
||||
varkind: VarKind::Undefined,
|
||||
},
|
||||
}
|
||||
},
|
||||
// If
|
||||
|
|
356
src/semantic/analyzer.rs
Normal file
356
src/semantic/analyzer.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -1,39 +1,138 @@
|
|||
mod analyzer;
|
||||
mod primitives;
|
||||
mod types;
|
||||
|
||||
use crate::err::*;
|
||||
use crate::{Parameter, err::*};
|
||||
pub use analyzer::*;
|
||||
pub use primitives::*;
|
||||
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)]
|
||||
pub enum Symbol {
|
||||
Var(String, Type, bool),
|
||||
Type(String, Type),
|
||||
pub enum Definition {
|
||||
Symbol(Symbol),
|
||||
BlockStart,
|
||||
FuncStart,
|
||||
}
|
||||
|
||||
fn next(array: &mut [uid]) -> uid {
|
||||
let current = array.last_mut().unwrap();
|
||||
let ret = *current;
|
||||
*current += 1;
|
||||
ret
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SymbolTable {
|
||||
syms: Vec<Symbol>,
|
||||
syms: Vec<Definition>,
|
||||
nesting: usize,
|
||||
local_varno: Vec<uid>,
|
||||
global_varno: Vec<uid>,
|
||||
funcno: Vec<uid>,
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
fn define_var(&mut self, name: String, type_: Type, mutable: bool) {
|
||||
self.syms.push(Symbol::Var(name, type_, mutable));
|
||||
pub fn new() -> Self {
|
||||
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) {
|
||||
self.syms.push(Symbol::Type(name, type_));
|
||||
fn define_symbol(
|
||||
&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) {
|
||||
self.syms.push(Symbol::FuncStart);
|
||||
self.nesting += 1;
|
||||
self.local_varno.push(0);
|
||||
self.syms.push(Definition::FuncStart);
|
||||
}
|
||||
|
||||
fn end_func(&mut self) {
|
||||
self.nesting -= 1;
|
||||
self.local_varno.pop();
|
||||
while !self.syms.is_empty() {
|
||||
if let Some(Symbol::FuncStart) = self.syms.pop() {
|
||||
if let Some(Definition::FuncStart) = self.syms.pop() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -41,34 +140,50 @@ impl SymbolTable {
|
|||
}
|
||||
|
||||
fn start_block(&mut self) {
|
||||
self.syms.push(Symbol::BlockStart);
|
||||
self.syms.push(Definition::BlockStart);
|
||||
}
|
||||
|
||||
fn end_block(&mut self) {
|
||||
while !self.syms.is_empty() {
|
||||
if let Some(Symbol::BlockStart) = self.syms.pop() {
|
||||
if let Some(Definition::BlockStart) = self.syms.pop() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
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() {
|
||||
if let Symbol::Var(name2, type_, mutable) = s {
|
||||
if name == name2 {
|
||||
return Ok((type_.clone(), *mutable));
|
||||
}
|
||||
}
|
||||
match s {
|
||||
Definition::Symbol(sym)
|
||||
// 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> {
|
||||
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 {
|
||||
return Ok(t.clone());
|
||||
return Ok(Type::Struct(params.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
semantic::SymbolTable, BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
|
||||
semantic::{Symbol, SymbolTable},
|
||||
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
|
||||
StatementKind, UnaryOp,
|
||||
};
|
||||
|
||||
|
@ -12,6 +13,7 @@ pub enum Type {
|
|||
Nothing,
|
||||
Prim(Primitive),
|
||||
Struct(Vec<Parameter>),
|
||||
StructDef(Vec<Parameter>),
|
||||
Function {
|
||||
params: Vec<Type>,
|
||||
returns: Box<Type>,
|
||||
|
@ -54,7 +56,7 @@ impl Type {
|
|||
(t::Prim(a), t::Prim(b)) => {
|
||||
let p = Primitive::binary_op(*a, op, *b)?;
|
||||
Ok(t::Prim(p))
|
||||
}
|
||||
},
|
||||
_ => 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 Type::*;
|
||||
let e = || {
|
||||
error().reason(format!(
|
||||
"Could not coerce type '{actual:?}' into '{expect:?}'"
|
||||
))
|
||||
};
|
||||
match (expect, actual) {
|
||||
(Ambiguous, Ambiguous) => None,
|
||||
(Ambiguous, Prim(p::integer_ambiguous)) => Some(Prim(p::integer)),
|
||||
(Ambiguous, Prim(p::real_ambiguous)) => Some(Prim(p::real)),
|
||||
(Ambiguous, t) => Some(t.clone()),
|
||||
(Ambiguous, Ambiguous) => e(),
|
||||
(Ambiguous, Prim(p::integer_ambiguous)) => Ok(Prim(p::integer)),
|
||||
(Ambiguous, Prim(p::real_ambiguous)) => Ok(Prim(p::real)),
|
||||
(Ambiguous, t) => Ok(t.clone()),
|
||||
(Prim(p1), Prim(p2)) => {
|
||||
let (p1, p2) = Primitive::coerce_ambiguous(*p1, *p2);
|
||||
if p1 != p2 {
|
||||
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,
|
||||
if p1 != p2 { e() } else { Ok(Type::Prim(p1)) }
|
||||
},
|
||||
})
|
||||
_ => 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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue