rework some errors and stuff

This commit is contained in:
tezlm 2023-10-05 09:58:56 -07:00
parent 2392087c4b
commit 298e69611f
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
7 changed files with 363 additions and 150 deletions

View file

@ -9,7 +9,7 @@ optimizations
use std::io::Write;
use crate::data::{BinaryOp, Expr, Func, Literal, Pattern, PrefixOp, Statement, Type};
use crate::data::{BinaryOp, ExprData, Func, Literal, Pattern, PrefixOp, Statement, Type, Expr};
use crate::generator::OutputFormat;
use crate::parser::Context;
use crate::Error;
@ -59,18 +59,11 @@ impl<'a> Compiler<'a> {
let mut gctx = GenContext::new();
let mut alloc = Allocator::new();
get_locals(
&Expr::Block(crate::data::Block(stmts.to_vec())),
&Expr::new(ExprData::Block(crate::data::Block(stmts.to_vec())), (0, 1), &ctx)?,
&mut alloc,
&mut ctx,
&mut gctx,
);
// get_strings(&Expr::Block(crate::data::Block(stmts.to_vec())), &mut gctx);
// if !gctx.strings.is_empty() {
// writeln!(self.output, "(memory 1)")?;
// for (s, offset) in &mut gctx.strings {
// writeln!(self.output, "(data $strings (i32.const {}) \"{}\")", offset, s.replace("\n", "\\n").replace("\"", "\\\""))?;
// }
// }
for stmt in stmts {
match stmt {
@ -78,8 +71,9 @@ impl<'a> Compiler<'a> {
Statement::Let(..) | Statement::TailExpr(..) | Statement::Expr(..) => {
return Err(Error::OtherError("incorrect top level statement".into()))
}
};
}
}
writeln!(self.output, ")")?;
Ok(())
}
@ -98,11 +92,11 @@ impl<'a> Compiler<'a> {
write!(self.output, " (export \"{}\")", func.name)?;
}
let expr = Expr::Block(func.block.clone());
let expr = Expr::new(ExprData::Block(func.block.clone()), (0, 1), ctx)?;
let mut ctx = ctx.clone();
for (name, ty) in &func.params {
ctx.locals.insert(name.clone(), ty.clone());
ctx.register(name, ty.clone());
write!(self.output, " (param ${} {})", name, ty.string())?;
}
@ -129,6 +123,7 @@ impl<'a> Compiler<'a> {
for (name, expr) in &exprs {
let ty = match expr.infer(&ctx).unwrap() {
Type::Integer => "i32",
Type::Boolean => "i32",
Type::Float => "f64",
// Type::String => "i32",
_ => todo!(),
@ -153,8 +148,8 @@ impl<'a> Compiler<'a> {
parent_gctx: &GenContext,
ctx: &Context,
) -> Result<(), Error> {
match expr {
Expr::Literal(lit) => match lit {
match &expr.data {
ExprData::Literal(lit) => match lit {
Literal::Integer(int) => writeln!(self.output, "i32.const {int}")?,
Literal::Float(f) => writeln!(self.output, "f64.const {f}")?,
Literal::Boolean(b) => {
@ -163,10 +158,10 @@ impl<'a> Compiler<'a> {
// Literal::String(s) => writeln!(self.output, "i32.const {}", parent_gctx.strings.iter().find_map(|(s2, off)| (s == s2).then_some(off)).unwrap())?,
_ => todo!(),
},
Expr::Variable(name) => {
ExprData::Variable(name) => {
writeln!(self.output, "local.get ${name}")?;
}
Expr::Binary(op, a, b) => {
ExprData::Binary(op, a, b) => {
self.gen_expr(a, parent_gctx, ctx)?;
self.gen_expr(b, parent_gctx, ctx)?;
@ -189,14 +184,16 @@ impl<'a> Compiler<'a> {
_ => todo!(),
}
}
Expr::Unary(op, e) => {
if let Expr::Literal(lit) = e.as_ref() {
match lit {
Literal::Integer(int) => writeln!(self.output, "i32.const {}", -int)?,
Literal::Float(f) => writeln!(self.output, "f64.const {}", -f)?,
_ => todo!(),
ExprData::Unary(op, e) => {
if op == &PrefixOp::Minus {
if let ExprData::Literal(lit) = &e.as_ref().data {
match lit {
Literal::Integer(int) => writeln!(self.output, "i32.const {}", -int)?,
Literal::Float(f) => writeln!(self.output, "f64.const {}", -f)?,
_ => todo!(),
}
return Ok(());
}
return Ok(());
}
self.gen_expr(e, parent_gctx, ctx)?;
@ -217,11 +214,11 @@ impl<'a> Compiler<'a> {
}
}
// FIXME: awful code until i fix patching up parser and lexer
Expr::Match(cond, arms) => {
self.gen_expr(cond, parent_gctx, ctx)?;
ExprData::Match(mat) => {
self.gen_expr(&mat.expr, parent_gctx, ctx)?;
writeln!(self.output, "local.set $match")?;
for (idx, (pat, expr)) in arms.iter().enumerate() {
for (idx, (pat, expr)) in mat.arms.iter().enumerate() {
match pat {
Pattern::Literal(lit) => {
match lit {
@ -244,16 +241,16 @@ impl<'a> Compiler<'a> {
writeln!(self.output, "(if (result i32) (then")?;
self.gen_expr(expr, parent_gctx, ctx)?;
if idx == arms.len() - 1 {
if idx == mat.arms.len() - 1 {
// TODO: verify its actually unreachable earlier on
writeln!(self.output, ") (else unreachable")?;
} else {
writeln!(self.output, ") (else")?;
}
}
writeln!(self.output, "{}", ")".repeat(arms.len() * 2))?;
writeln!(self.output, "{}", ")".repeat(mat.arms.len() * 2))?;
}
Expr::Block(b) => {
ExprData::Block(b) => {
let mut ctx = ctx.clone();
for (_i, stmt) in b.0.iter().enumerate() {
match stmt {
@ -262,20 +259,18 @@ impl<'a> Compiler<'a> {
}
Statement::Expr(expr) => {
self.gen_expr(expr, parent_gctx, &ctx)?;
if !expr.infer(&ctx).unwrap().is_empty() {
if !expr.ty.is_empty() {
writeln!(self.output, "drop")?;
}
}
Statement::Let(name, expr) => {
let ty = expr.infer(&ctx).unwrap();
ctx.locals.insert(name.clone(), ty);
// exprs.push((name.clone(), expr.clone()));
ctx.register(name, expr.ty.clone());
}
_ => {}
}
}
}
Expr::Call(func, args) => {
ExprData::Call(func, args) => {
expr.infer(ctx)?;
for expr in args {
self.gen_expr(expr, parent_gctx, ctx)?;
@ -316,16 +311,15 @@ impl GenContext {
}
fn get_locals(expr: &Expr, alloc: &mut Allocator, ctx: &mut Context, gctx: &mut GenContext) {
match expr {
Expr::Block(b) => {
match &expr.data {
ExprData::Block(b) => {
for stmt in &b.0 {
match stmt {
Statement::Let(name, expr) => {
// FIXME: block scoped variables (name collisions)
get_locals(expr, alloc, ctx, gctx);
let ty = expr.infer(ctx).unwrap();
ctx.locals.insert(name.clone(), ty);
ctx.register(name, expr.ty.clone());
gctx.exprs.push((alloc.alloc_ident(name), expr.clone()));
}
Statement::TailExpr(expr) => get_locals(expr, alloc, ctx, gctx),
@ -333,18 +327,17 @@ fn get_locals(expr: &Expr, alloc: &mut Allocator, ctx: &mut Context, gctx: &mut
Statement::Func(Func {
name, ret, params, ..
}) => {
ctx.funcs
.insert(name.clone(), (params.clone(), ret.clone()));
ctx.register(name, Type::Function(params.clone(), Box::new(ret.clone())))
}
}
}
}
Expr::Unary(_, expr) => get_locals(expr, alloc, ctx, gctx),
Expr::Binary(_, a, b) => {
ExprData::Unary(_, expr) => get_locals(expr, alloc, ctx, gctx),
ExprData::Binary(_, a, b) => {
get_locals(a, alloc, ctx, gctx);
get_locals(b, alloc, ctx, gctx);
}
Expr::Literal(Literal::String(s)) => {
ExprData::Literal(Literal::String(s)) => {
let offset = gctx
.strings
.last()
@ -352,18 +345,18 @@ fn get_locals(expr: &Expr, alloc: &mut Allocator, ctx: &mut Context, gctx: &mut
.unwrap_or(8);
gctx.strings.push((s.clone(), offset));
}
Expr::Match(pat, arms) => {
get_locals(pat, alloc, ctx, gctx);
for (_, arm) in arms {
ExprData::Match(mat) => {
get_locals(&mat.expr, alloc, ctx, gctx);
for (_, arm) in &mat.arms {
get_locals(arm, alloc, ctx, gctx);
}
}
Expr::Call(_, exprs) => {
ExprData::Call(_, exprs) => {
for expr in exprs {
get_locals(expr, alloc, ctx, gctx);
}
}
Expr::Variable(..) | Expr::Literal(..) => (),
ExprData::Variable(..) | ExprData::Literal(..) => (),
}
}

View file

@ -20,7 +20,7 @@ pub enum BinaryOp {
// Set,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PrefixOp {
Minus,
LogicNot,
@ -57,16 +57,36 @@ pub struct Func {
pub struct Block(pub Vec<Statement>);
#[derive(Debug, Clone)]
pub enum Expr {
pub enum ExprData {
Literal(Literal),
Variable(String),
Binary(BinaryOp, Box<Expr>, Box<Expr>),
Unary(PrefixOp, Box<Expr>),
Match(Box<Expr>, Vec<(Pattern, Expr)>),
Match(Match),
Call(String, Vec<Expr>),
Block(Block),
}
#[derive(Debug, Clone)]
pub struct Expr {
pub span: (usize, usize),
pub ty: Type,
pub data: ExprData,
}
#[derive(Debug, Clone)]
pub struct Match {
pub expr: Box<Expr>,
pub arms: Vec<(Pattern, Expr)>,
pub span: (usize, usize),
pub kind: MatchKind,
}
#[derive(Debug, Clone)]
pub enum MatchKind {
Match, If,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Pattern {
Wildcard,
@ -92,7 +112,7 @@ pub enum Type {
// Struct(HashMap<String, Type>),
// Enum(HashMap<String, Option<Type>>),
// Newtype(HashMap<String, Type>),
// Function(Vec<Type10 >, Box<Type>),
Function(Vec<(String, Type)>, Box<Type>),
Tuple(Vec<Type>),
}
@ -166,6 +186,13 @@ impl Type {
self == &Type::Tuple(vec![])
}
pub fn is_callable(&self) -> bool {
match self {
Type::Function(..) => true,
_ => false,
}
}
// FIXME: use actual types
pub fn string(&self) -> String {
match self {

View file

@ -1,13 +1,16 @@
use miette::{Diagnostic, NamedSource, SourceSpan};
use thiserror::Error;
use crate::data::{Type, Pattern, BinaryOp, PrefixOp};
#[derive(Debug, Error, Diagnostic)]
#[error("some kind of error")]
pub enum Error {
SyntaxError(SyntaxError),
TypeError(TypeError),
TypeErrorOld(String),
ReferenceError(String),
ReferenceError(ReferenceError),
ReferenceErrorOld(String),
IoError(std::io::Error),
OtherError(String),
}
@ -51,25 +54,69 @@ pub struct SyntaxError {
#[error("type error!")]
#[diagnostic()]
pub enum TypeError {
#[diagnostic(code(error::types::unary_bad_input))]
Unary {
#[help]
help: String,
#[label("i do not know what this is")]
#[label("this is a {expr_ty:?}")]
expr: SourceSpan,
expr_ty: Type,
#[label("i do not know what this is")]
operator: SourceSpan,
// TODO: unary operators besides only prefix
operator: PrefixOp,
#[label("you can't use {operator:?} on this type")]
operator_label: SourceSpan,
},
#[diagnostic(code(error::types::binary_bad_inputs))]
Binary {
#[help]
help: String,
#[label("i do not know what this is")]
expr: SourceSpan,
#[label("this is a {lhs_ty:?}")]
lhs: SourceSpan,
#[label("i do not know what this is")]
operator: SourceSpan,
#[label("and this is a {rhs_ty:?}")]
rhs: SourceSpan,
lhs_ty: Type,
rhs_ty: Type,
operator: BinaryOp,
#[label("you can't use {operator:?} on these types")]
operator_label: SourceSpan,
},
#[diagnostic(code(error::types::match_different_types))]
MatchPattern {
#[help]
help: String,
#[label("you are comparing a {expr_label:?}")]
expr_span: SourceSpan,
#[label("but this pattern matches a {pattern_label:?}")]
pattern_span: SourceSpan,
expr_label: Type,
pattern_label: Pattern,
#[label("in this match")]
matcher: SourceSpan,
},
#[diagnostic(code(error::types::if_not_bool))]
IfPattern {
#[help]
help: String,
#[label("this is a {expr_label:?}")]
expr_span: SourceSpan,
expr_label: Type,
#[label("in this if")]
// funny name
iffer: SourceSpan,
},
UnknownType {
#[help]
@ -80,6 +127,30 @@ pub enum TypeError {
},
}
#[derive(Debug, Error, Diagnostic)]
#[error("")]
#[diagnostic()]
pub struct ReferenceWrapper {
#[source_code]
pub src: NamedSource,
#[related]
pub reference: Vec<ReferenceError>,
}
#[derive(Debug, Error, Diagnostic)]
#[error("reference error!")]
#[diagnostic()]
pub struct ReferenceError {
#[label("this {kind} doesn't exist")]
pub unknown: SourceSpan,
pub kind: String,
#[help]
pub help: String,
}
impl Error {
pub fn syn(pos: (usize, usize), help: impl Into<String>) -> Error {
Error::SyntaxError(SyntaxError {
@ -88,7 +159,7 @@ impl Error {
})
}
pub fn ty(what: &'static str) -> Error {
pub fn ty_old(what: &'static str) -> Error {
Error::TypeErrorOld(what.to_string())
}
}

View file

@ -9,7 +9,7 @@ use std::{fs::OpenOptions, path::PathBuf};
use clap::Parser;
use lang::{
compiler::Compiler,
error::{SyntaxWrapper, TypeWrapper},
error::{SyntaxWrapper, TypeWrapper, ReferenceWrapper},
generator::OutputFormat,
lexer, parser,
};
@ -38,7 +38,7 @@ enum Cli {
},
// future work:
//
// /// Start a read-print-eval loop
// /// Start a read-eval-print-loop
// Repl,
//
// /// Check/lint the source code without running it
@ -123,10 +123,11 @@ fn main() {
}
};
let ctx = parser::Context::new();
let mut parser = parser::Parser::new(tokens);
let mut statements = vec![];
loop {
match parser.parse_statement_option() {
match parser.parse_statement_option(&ctx) {
Ok(None) => break,
Ok(Some(tree)) => statements.push(tree),
Err(error) => {
@ -149,6 +150,15 @@ fn main() {
reporter.render_report(&mut s, &ty).unwrap();
eprintln!("{}", s);
}
lang::Error::ReferenceError(r) => {
let mut s = String::new();
let ty = ReferenceWrapper {
src: NamedSource::new(file.to_string_lossy(), source),
reference: vec![r],
};
reporter.render_report(&mut s, &ty).unwrap();
eprintln!("{}", s);
}
_ => eprintln!("error: {:?}", error),
}
return;

View file

@ -1,6 +1,6 @@
use std::collections::HashMap;
use crate::data::{BinaryOp, Block, Expr, Func, Literal, Pattern, PrefixOp, Statement, Type};
use crate::data::{BinaryOp, Block, Expr, ExprData, Func, Literal, Pattern, PrefixOp, Statement, Type, Match, MatchKind};
use crate::lexer::{Symbol, Token, TokenContent};
use crate::Error;
@ -9,10 +9,30 @@ pub struct Parser {
pos: usize,
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Context {
pub locals: HashMap<String, Type>,
pub funcs: HashMap<String, (Vec<(String, Type)>, Type)>,
data: HashMap<String, Type>,
}
impl Context {
pub fn new() -> Context {
Context {
data: HashMap::new(),
}
}
pub fn register(&mut self, name: &str, ty: Type) {
self.data.insert(name.to_string(), ty);
}
pub fn lookup(&self, name: &str) -> Option<&Type> {
self.data.get(name)
}
// TODO: implement this more cheaply?
pub fn clone(&self) -> Context {
Context { data: self.data.clone() }
}
}
impl Parser {
@ -61,14 +81,14 @@ impl Parser {
result
}
pub fn parse_statement_option(&mut self) -> Result<Option<Statement>, Error> {
pub fn parse_statement_option(&mut self, ctx: &Context) -> Result<Option<Statement>, Error> {
match self.peek_tok() {
Some(_) => Some(self.parse_statement()).transpose(),
Some(_) => Some(self.parse_statement(ctx)).transpose(),
None => Ok(None),
}
}
fn parse_statement(&mut self) -> Result<Statement, Error> {
fn parse_statement(&mut self, ctx: &Context) -> Result<Statement, Error> {
let Some(tok) = self.peek_tok() else {
return Err(self.err_eof("statement"));
};
@ -81,7 +101,7 @@ impl Parser {
self.parse_type()?;
}
self.eat(TokenContent::Symbol(Symbol::Set))?;
let expr = self.parse_expr(0)?;
let expr = self.parse_expr(&ctx, 0)?;
self.eat(TokenContent::Symbol(Symbol::Semicolon))?;
Statement::Let(name, expr)
}
@ -118,7 +138,7 @@ impl Parser {
Type::empty()
};
self.eat(TokenContent::OpenBrace)?;
let block = self.parse_block()?;
let block = self.parse_block(&ctx)?;
self.eat(TokenContent::CloseBrace)?;
Statement::Func(Func {
name,
@ -129,7 +149,7 @@ impl Parser {
})
}
_ => {
let expr = self.parse_expr(0)?;
let expr = self.parse_expr(&ctx, 0)?;
if self
.peek_tok()
.is_some_and(|tk| tk.token == TokenContent::Symbol(Symbol::Semicolon))
@ -195,7 +215,8 @@ impl Parser {
result
}
fn parse_block(&mut self) -> Result<Block, Error> {
fn parse_block(&mut self, ctx: &Context) -> Result<Block, Error> {
let mut ctx = ctx.clone();
let mut statements = vec![];
loop {
if self
@ -204,13 +225,18 @@ impl Parser {
{
break;
}
match self.parse_statement()? {
stmt @ Statement::TailExpr(..) => {
let stmt = self.parse_statement(&ctx)?;
match &stmt {
Statement::TailExpr(..) => {
statements.push(stmt);
break;
}
stmt => statements.push(stmt),
Statement::Let(name, expr) => {
ctx.register(name, expr.ty.clone());
}
_ => (),
}
statements.push(stmt);
match self.peek_tok() {
Some(Token {
token: TokenContent::CloseBrace,
@ -222,7 +248,7 @@ impl Parser {
Ok(Block(statements))
}
fn parse_expr(&mut self, binding: u8) -> Result<Expr, Error> {
fn parse_expr(&mut self, ctx: &Context, binding: u8) -> Result<Expr, Error> {
let Some(tok) = self.next_tok() else {
return Err(self.err_eof("expression"));
};
@ -230,9 +256,17 @@ impl Parser {
let mut expr = match &tok.token {
TokenContent::Number { radix: _, text } => {
if text.contains('.') {
Expr::Literal(Literal::Float(text.parse().unwrap()))
Expr {
span: tok.span,
ty: Type::Float,
data: ExprData::Literal(Literal::Float(text.parse().unwrap())),
}
} else {
Expr::Literal(Literal::Integer(text.parse().unwrap()))
Expr {
span: tok.span,
ty: Type::Integer,
data: ExprData::Literal(Literal::Integer(text.parse().unwrap())),
}
}
}
TokenContent::Ident(ident) => {
@ -243,25 +277,41 @@ impl Parser {
.peek_tok()
.is_some_and(|tok| tok.token == TokenContent::CloseParan)
{
params.push(self.parse_expr(0)?);
params.push(self.parse_expr(ctx, 0)?);
if self.eat(TokenContent::Symbol(Symbol::Comma)).is_err() {
break;
}
}
self.eat(TokenContent::CloseParan)?;
Expr::Call(ident, params)
Expr::new(ExprData::Call(ident, params), tok.span, ctx)?
} else {
Expr::Variable(ident)
Expr::new(ExprData::Variable(ident), tok.span, ctx)?
}
}
TokenContent::False => Expr::Literal(Literal::Boolean(false)),
TokenContent::True => Expr::Literal(Literal::Boolean(true)),
TokenContent::String(s) => Expr::Literal(Literal::String(s.to_string())),
TokenContent::Char(ch) => Expr::Literal(Literal::Char(*ch)),
TokenContent::False => Expr {
span: tok.span,
ty: Type::Boolean,
data: ExprData::Literal(Literal::Boolean(false))
},
TokenContent::True => Expr {
span: tok.span,
ty: Type::Boolean,
data: ExprData::Literal(Literal::Boolean(true))
},
TokenContent::String(s) => Expr {
span: tok.span,
ty: Type::String,
data: ExprData::Literal(Literal::String(s.to_string()))
},
TokenContent::Char(ch) => Expr {
span: tok.span,
ty: Type::Char,
data: ExprData::Literal(Literal::Char(*ch))
},
TokenContent::If => {
let cond = self.parse_expr(0)?;
let cond = self.parse_expr(ctx, 0)?;
self.eat(TokenContent::OpenBrace)?;
let block = self.parse_block()?;
let block = self.parse_block(ctx)?;
self.eat(TokenContent::CloseBrace)?;
let otherwise = if self
.peek_tok()
@ -274,14 +324,14 @@ impl Parser {
..
}) => {
self.eat(TokenContent::OpenBrace)?;
let b = Some(self.parse_block()?);
let b = Some(self.parse_block(ctx)?);
self.eat(TokenContent::CloseBrace)?;
b
}
Some(Token {
token: TokenContent::If,
..
}) => Some(Block(vec![Statement::TailExpr(self.parse_expr(0)?)])),
}) => Some(Block(vec![Statement::TailExpr(self.parse_expr(ctx, 0)?)])),
Some(tk) => {
return Err(
self.err_syn(tk.span, "this should be followed by an if or block")
@ -292,14 +342,20 @@ impl Parser {
} else {
None
};
let mut map = vec![(Pattern::Literal(Literal::Boolean(true)), Expr::Block(block))];
let mut map = vec![(Pattern::Literal(Literal::Boolean(true)), Expr::new(ExprData::Block(block), (0, 1), ctx)?)];
if let Some(otherwise) = otherwise {
map.push((
Pattern::Literal(Literal::Boolean(false)),
Expr::Block(otherwise),
Expr::new(ExprData::Block(otherwise), (0, 1), ctx)?,
));
}
Expr::Match(Box::new(cond), map)
let mat = Match {
expr: Box::new(cond),
arms: map,
span: tok.span,
kind: MatchKind::If,
};
Expr::new(ExprData::Match(mat), tok.span, ctx)?
}
TokenContent::Symbol(_) => {
let Some(op) = PrefixOp::from_token(&tok.token) else {
@ -308,17 +364,17 @@ impl Parser {
"this should be changed into a valid operator or removed outright",
));
};
let expr = self.parse_expr(1)?;
Expr::Unary(op, Box::new(expr))
let expr = self.parse_expr(ctx, 1)?;
Expr::new(ExprData::Unary(op, Box::new(expr)), tok.span, ctx)?
}
TokenContent::Match => {
let expr = self.parse_expr(0)?;
let expr = self.parse_expr(ctx, 0)?;
let mut arms = vec![];
self.eat(TokenContent::OpenBrace)?;
loop {
let pat = self.parse_pattern()?;
self.eat(TokenContent::Symbol(Symbol::FatArrow))?;
let expr = self.parse_expr(0)?;
let expr = self.parse_expr(ctx, 0)?;
arms.push((pat, expr));
if self.eat(TokenContent::Symbol(Symbol::Comma)).is_err() {
@ -334,15 +390,21 @@ impl Parser {
}
}
self.eat(TokenContent::CloseBrace)?;
Expr::Match(Box::new(expr), arms)
let mat = Match {
expr: Box::new(expr),
arms,
span: tok.span,
kind: MatchKind::Match,
};
Expr::new(ExprData::Match(mat), tok.span, ctx)?
}
TokenContent::OpenBrace => {
let b = Expr::Block(self.parse_block()?);
let b = ExprData::Block(self.parse_block(ctx)?);
self.eat(TokenContent::CloseBrace)?;
b
todo!()
}
TokenContent::OpenParan => {
let expr = self.parse_expr(0)?;
let expr = self.parse_expr(ctx, 0)?;
self.eat(TokenContent::CloseParan)?;
expr
}
@ -356,9 +418,10 @@ impl Parser {
if bind_left < binding {
break;
}
let span = next.span;
self.next_tok();
let rhs = self.parse_expr(bind_right)?;
expr = Expr::Binary(op, Box::new(expr), Box::new(rhs));
let rhs = self.parse_expr(ctx, bind_right)?;
expr = Expr::new(ExprData::Binary(op, Box::new(expr), Box::new(rhs)), span, ctx)?;
}
Ok(expr)
}

View file

@ -2,55 +2,108 @@
// fn infer();
// }
use std::collections::HashMap;
use crate::{
data::{BinaryOp, Block, Expr, Literal, Pattern, PrefixOp, Statement, Type},
data::{BinaryOp, Block, Expr, ExprData, Literal, Pattern, PrefixOp, Statement, Type, MatchKind},
parser::Context,
Error,
Error, error::{TypeError, ReferenceError},
};
impl Expr {
pub fn new(data: ExprData, span: (usize, usize), ctx: &Context) -> Result<Expr, Error> {
Ok(Expr {
span,
ty: data.infer(ctx, span)?,
data
})
}
pub fn infer(&self, ctx: &Context) -> Result<Type, Error> {
self.data.infer(ctx, self.span)
}
}
impl ExprData {
pub fn infer(&self, ctx: &Context, span: (usize, usize)) -> Result<Type, Error> {
match self {
Self::Literal(lit) => lit.infer(),
Self::Binary(op, lhs, rhs) => Ok(op.infer(lhs.infer(ctx)?, rhs.infer(ctx)?)?),
Self::Unary(op, expr) => Ok(op.infer(expr.infer(ctx)?)?),
Self::Variable(name) => match ctx.locals.get(name) {
Some(ty) => Ok(ty.clone()),
None => Err(Error::ReferenceError(format!(
"cannot find variable {name}"
))),
ExprData::Literal(lit) => lit.infer(),
ExprData::Binary(op, lhs, rhs) => {
match op.infer(&lhs.ty, &rhs.ty) {
Ok(ty) => Ok(ty),
Err(_) => return Err(Error::TypeError(TypeError::Binary {
help: "try changing some things".into(),
lhs: lhs.span.into(),
rhs: rhs.span.into(),
lhs_ty: lhs.ty.clone(),
rhs_ty: rhs.ty.clone(),
operator: op.clone(),
operator_label: span.into(),
}))
}
}
ExprData::Unary(op, expr) => {
match op.infer(&expr.ty) {
Ok(ty) => Ok(ty),
Err(_) => return Err(Error::TypeError(TypeError::Unary {
help: "try changing some things".into(),
expr: expr.span.into(),
expr_ty: expr.ty.clone(),
operator: op.clone(),
operator_label: span.into(),
}))
}
},
Self::Match(item, arms) => {
ExprData::Variable(name) => match ctx.lookup(&name) {
Some(ty) => Ok(ty.clone()),
None => Err(Error::ReferenceError(ReferenceError {
unknown: span.into(),
kind: "variable".into(),
help: "create it, check scope of variables, etc".into(),
})),
},
ExprData::Match(mat) => {
let mut match_ty = None;
let item_ty = item.infer(ctx)?;
for (pat, expr) in arms {
for (pat, expr) in &mat.arms {
let ty = expr.infer(ctx)?;
if !pat.matches_type(&item_ty)? {
return Err(Error::ty("cannot compare different type"));
if !pat.matches_type(&mat.expr.ty)? {
let err = match mat.kind {
MatchKind::Match => {
TypeError::MatchPattern {
help: "these are different types".into(),
expr_span: mat.expr.span.into(),
pattern_span: (0, 1).into(),
expr_label: mat.expr.ty.clone(),
pattern_label: pat.clone(),
matcher: span.into(),
}
},
MatchKind::If => {
TypeError::IfPattern {
help: "this is not a boolean".into(),
expr_span: mat.expr.span.into(),
expr_label: mat.expr.ty.clone(),
iffer: mat.span.into(),
}
},
};
return Err(Error::TypeError(err));
}
if match_ty.is_some_and(|mty| mty != ty) {
return Err(Error::ty("branch returns different type"));
return Err(Error::ty_old("branch returns different type"));
}
match_ty = Some(ty);
}
// TODO: exhaustiveness checks
let Some(match_ty) = match_ty else {
// TODO: infallible types? `enum Nope {}`
return Err(Error::ty("match has no branches to infer"));
};
Ok(match_ty)
Ok(match_ty.unwrap_or_else(Type::empty))
}
Self::Block(block) => block.infer(ctx),
Self::Call(name, args) => match ctx.funcs.get(name) {
Some((params, ret)) => {
ExprData::Block(block) => block.infer(ctx),
ExprData::Call(name, args) => match ctx.lookup(&name) {
Some(Type::Function(params, ret)) => {
if args.len() < params.len() {
return Err(Error::ty("missing parameters"));
return Err(Error::ty_old("missing parameters"));
}
if args.len() > params.len() {
return Err(Error::ty("too many parameters"));
return Err(Error::ty_old("too many parameters"));
}
for (arg, (name, ty)) in args.iter().zip(params) {
@ -60,18 +113,23 @@ impl Expr {
}
}
Ok(ret.clone())
Ok((**ret).clone())
}
None => Err(Error::ReferenceError(format!(
"cannot find variable {name}"
))),
Some(bad) => {
return Err(Error::TypeErrorOld(format!("{name} is {bad:?} which is not a function you can call")));
}
None => Err(Error::ReferenceError(ReferenceError {
unknown: span.into(),
kind: "function".into(),
help: "create it, check scope of functions, etc".into(),
})),
},
}
}
}
impl BinaryOp {
pub fn infer(&self, a: Type, b: Type) -> Result<Type, Error> {
pub fn infer(&self, a: &Type, b: &Type) -> Result<Type, Error> {
use BinaryOp as B;
use Type as T;
@ -103,7 +161,7 @@ impl BinaryOp {
}
impl PrefixOp {
pub fn infer(&self, a: Type) -> Result<Type, Error> {
pub fn infer(&self, a: &Type) -> Result<Type, Error> {
use PrefixOp as U;
use Type as T;
@ -131,7 +189,7 @@ impl Block {
Statement::TailExpr(expr) => ty = expr.infer(&ctx)?,
Statement::Let(name, expr) => {
let var_ty = expr.infer(&ctx)?;
ctx.locals.insert(name.clone(), var_ty);
ctx.register(name, var_ty);
ty = Type::empty();
}
_ => (),
@ -161,12 +219,3 @@ impl Pattern {
}
}
}
impl Context {
pub fn new() -> Context {
Context {
locals: HashMap::new(),
funcs: HashMap::new(),
}
}
}

View file

@ -1,3 +1,3 @@
fn main() {
let a = 10 +
let x = -true;
}