finish porting to new parser

This commit is contained in:
2021-11-01 00:10:40 +01:00
parent 607c3c7a28
commit 02b8ff860b
7 changed files with 1184 additions and 1726 deletions

View File

@@ -1,152 +1,146 @@
#[derive(Debug, Clone, Copy)] use crate::Span;
pub struct Position(pub usize);
#[derive(Debug)] #[derive(Debug)]
pub struct Script<'a> { pub struct Script {
pub imports: Vec<Import<'a>>, pub imports: Vec<Import>,
pub global_vars: Vec<GlobalVar<'a>>, pub global_vars: Vec<GlobalVar>,
pub functions: Vec<Function<'a>>, pub functions: Vec<Function>,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum TopLevelItem<'a> { pub enum TopLevelItem {
Import(Import<'a>), Import(Import),
GlobalVar(GlobalVar<'a>), GlobalVar(GlobalVar),
Function(Function<'a>), Function(Function),
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Import<'a> { pub struct Import {
pub position: Position, pub span: Span,
pub import: &'a str, pub import: String,
pub type_: ImportType<'a>, pub type_: ImportType,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum ImportType<'a> { pub enum ImportType {
Memory(u32), Memory(u32),
Variable { Variable {
name: &'a str, name: String,
type_: Type, type_: Type,
mutable: bool, mutable: bool,
}, },
// Function { name: &'a str, params: Vec<Type>, result: Option<Type> } // Function { name: String, params: Vec<Type>, result: Option<Type> }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct GlobalVar<'a> { pub struct GlobalVar {
pub position: Position, pub span: Span,
pub name: &'a str, pub name: String,
pub type_: Type, pub type_: Type,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Function<'a> { pub struct Function {
pub position: Position, pub span: Span,
pub export: bool, pub export: bool,
pub name: &'a str, pub name: String,
pub params: Vec<(&'a str, Type)>, pub params: Vec<(String, Type)>,
pub type_: Option<Type>, pub type_: Option<Type>,
pub body: Block<'a>, pub body: Block,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Block<'a> { pub struct Block {
pub statements: Vec<Statement<'a>>, pub statements: Vec<Expression>,
pub final_expression: Option<Expression<'a>>, pub final_expression: Option<Box<Expression>>,
} }
impl<'a> Block<'a> { impl Block {
pub fn type_(&self) -> Option<Type> { pub fn type_(&self) -> Option<Type> {
self.final_expression.as_ref().and_then(|e| e.type_) self.final_expression.as_ref().and_then(|e| e.type_)
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub enum Statement<'a> { pub struct MemoryLocation {
LocalVariable(LocalVariable<'a>), pub span: Span,
Poke {
position: Position,
mem_location: MemoryLocation<'a>,
value: Expression<'a>,
},
Expression(Expression<'a>),
}
#[derive(Debug)]
pub struct MemoryLocation<'a> {
pub position: Position,
pub size: MemSize, pub size: MemSize,
pub left: Expression<'a>, pub left: Box<Expression>,
pub right: Expression<'a>, pub right: Box<Expression>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct LocalVariable<'a> { pub struct Expression {
pub position: Position,
pub name: &'a str,
pub type_: Option<Type>, pub type_: Option<Type>,
pub value: Option<Expression<'a>>, pub expr: Expr,
pub defer: bool pub span: Span,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Expression<'a> { pub enum Expr {
pub type_: Option<Type>,
pub expr: Expr<'a>,
}
impl<'a> From<Expr<'a>> for Expression<'a> {
fn from(expr: Expr<'a>) -> Expression<'a> {
Expression { type_: None, expr }
}
}
#[derive(Debug)]
pub enum Expr<'a> {
I32Const(i32), I32Const(i32),
F32Const(f32), F32Const(f32),
Variable { Variable(String),
position: Position, Let {
name: &'a str, name: String,
type_: Option<Type>,
value: Option<Box<Expression>>,
defer: bool,
},
Poke {
mem_location: MemoryLocation,
value: Box<Expression>,
}, },
Loop { Loop {
position: Position, label: String,
label: &'a str, block: Box<Block>,
block: Box<Block<'a>>,
}, },
BranchIf { BranchIf {
position: Position, condition: Box<Expression>,
condition: Box<Expression<'a>>, label: String,
label: &'a str, },
UnaryOp {
op: UnaryOp,
value: Box<Expression>,
}, },
BinOp { BinOp {
position: Position,
op: BinOp, op: BinOp,
left: Box<Expression<'a>>, left: Box<Expression>,
right: Box<Expression<'a>>, right: Box<Expression>,
}, },
LocalTee { LocalTee {
position: Position, name: String,
name: &'a str, value: Box<Expression>,
value: Box<Expression<'a>>,
}, },
Cast { Cast {
position: Position, value: Box<Expression>,
value: Box<Expression<'a>>,
type_: Type, type_: Type,
}, },
FuncCall { FuncCall {
position: Position, name: String,
name: &'a str, params: Vec<Expression>,
params: Vec<Expression<'a>>
}, },
Select { Select {
position: Position, condition: Box<Expression>,
condition: Box<Expression<'a>>, if_true: Box<Expression>,
if_true: Box<Expression<'a>>, if_false: Box<Expression>,
if_false: Box<Expression<'a>> },
Error,
} }
impl Expr {
pub fn with_span(self, span: Span) -> Expression {
Expression {
type_: None,
expr: self,
span: span,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum UnaryOp {
Negate,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]

View File

@@ -8,22 +8,7 @@ pub fn fold_script(script: &mut ast::Script) {
fn fold_block(block: &mut ast::Block) { fn fold_block(block: &mut ast::Block) {
for stmt in &mut block.statements { for stmt in &mut block.statements {
match stmt { fold_expr(stmt);
ast::Statement::LocalVariable(lv) => {
if let Some(ref mut expr) = lv.value {
fold_expr(expr);
}
}
ast::Statement::Expression(expr) => fold_expr(expr),
ast::Statement::Poke {
mem_location,
value,
..
} => {
fold_mem_location(mem_location);
fold_expr(value);
}
}
} }
if let Some(ref mut expr) = block.final_expression { if let Some(ref mut expr) = block.final_expression {
fold_expr(expr); fold_expr(expr);
@@ -38,6 +23,29 @@ fn fold_mem_location(mem_location: &mut ast::MemoryLocation) {
fn fold_expr(expr: &mut ast::Expression) { fn fold_expr(expr: &mut ast::Expression) {
use ast::BinOp::*; use ast::BinOp::*;
match expr.expr { match expr.expr {
ast::Expr::Let { ref mut value, .. } => {
if let Some(ref mut expr) = value {
fold_expr(expr);
}
}
ast::Expr::Poke {
ref mut mem_location,
ref mut value,
..
} => {
fold_mem_location(mem_location);
fold_expr(value);
}
ast::Expr::UnaryOp { op, ref mut value } => {
fold_expr(value);
let result = match (op, &value.expr) {
(ast::UnaryOp::Negate, ast::Expr::I32Const(value)) => Some(ast::Expr::I32Const(-*value)),
_ => None
};
if let Some(result) = result {
expr.expr = result;
}
}
ast::Expr::BinOp { ast::Expr::BinOp {
ref mut left, ref mut left,
op, op,
@@ -105,7 +113,7 @@ fn fold_expr(expr: &mut ast::Expression) {
} => fold_expr(condition), } => fold_expr(condition),
ast::Expr::Cast { ref mut value, .. } => fold_expr(value), ast::Expr::Cast { ref mut value, .. } => fold_expr(value),
ast::Expr::FuncCall { ast::Expr::FuncCall {
name, ref name,
ref mut params, ref mut params,
.. ..
} => { } => {
@@ -114,15 +122,21 @@ fn fold_expr(expr: &mut ast::Expression) {
} }
use ast::Expr::*; use ast::Expr::*;
let params: Vec<_> = params.iter().map(|e| &e.expr).collect(); let params: Vec<_> = params.iter().map(|e| &e.expr).collect();
expr.expr = match (name, params.as_slice()) { expr.expr = match (name.as_str(), params.as_slice()) {
("sqrt", [F32Const(v)]) if *v >= 0.0 => F32Const(v.sqrt()), ("sqrt", [F32Const(v)]) if *v >= 0.0 => F32Const(v.sqrt()),
_ => return, _ => return,
}; };
} }
ast::Expr::Select { ref mut condition, ref mut if_true, ref mut if_false, .. } => { ast::Expr::Select {
ref mut condition,
ref mut if_true,
ref mut if_false,
..
} => {
fold_expr(condition); fold_expr(condition);
fold_expr(if_true); fold_expr(if_true);
fold_expr(if_false); fold_expr(if_false);
} }
ast::Expr::Error => unreachable!()
} }
} }

View File

@@ -35,7 +35,7 @@ pub fn emit(script: &ast::Script) -> Vec<u8> {
Some(&import.import[(dot_index + 1)..]), Some(&import.import[(dot_index + 1)..]),
) )
} else { } else {
(import.import, None) (import.import.as_str(), None)
}; };
let type_: EntityType = match import.type_ { let type_: EntityType = match import.type_ {
ast::ImportType::Memory(min_size) => MemoryType { ast::ImportType::Memory(min_size) => MemoryType {
@@ -46,7 +46,7 @@ pub fn emit(script: &ast::Script) -> Vec<u8> {
.into(), .into(),
ast::ImportType::Variable { ast::ImportType::Variable {
type_, type_,
name, ref name,
mutable, mutable,
} => { } => {
globals.insert(name, globals.len() as u32); globals.insert(name, globals.len() as u32);
@@ -72,7 +72,7 @@ pub fn emit(script: &ast::Script) -> Vec<u8> {
let type_ = *function_types.get(&function_type_key(func)).unwrap(); let type_ = *function_types.get(&function_type_key(func)).unwrap();
functions.function(type_ as u32); functions.function(type_ as u32);
if func.export { if func.export {
exports.export(func.name, Export::Function(index as u32)); exports.export(&func.name, Export::Function(index as u32));
} }
code.function(&emit_function(func, &globals)); code.function(&emit_function(func, &globals));
@@ -109,9 +109,9 @@ fn function_type_key(func: &ast::Function) -> FunctionTypeKey {
struct FunctionContext<'a> { struct FunctionContext<'a> {
function: &'a mut Function, function: &'a mut Function,
globals: &'a HashMap<&'a str, u32>, globals: &'a HashMap<&'a str, u32>,
locals: &'a HashMap<&'a str, u32>, locals: &'a HashMap<String, u32>,
labels: Vec<String>, labels: Vec<String>,
deferred_inits: HashMap<&'a str, &'a ast::Expression<'a>>, deferred_inits: HashMap<&'a str, &'a ast::Expression>,
} }
fn emit_function(func: &ast::Function, globals: &HashMap<&str, u32>) -> Function { fn emit_function(func: &ast::Function, globals: &HashMap<&str, u32>) -> Function {
@@ -121,7 +121,7 @@ fn emit_function(func: &ast::Function, globals: &HashMap<&str, u32>) -> Function
let mut function = Function::new_with_locals_types(locals.iter().map(|(_, t)| map_type(*t))); let mut function = Function::new_with_locals_types(locals.iter().map(|(_, t)| map_type(*t)));
let locals: HashMap<&str, u32> = locals let locals: HashMap<String, u32> = locals
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(index, (name, _))| (name, index as u32)) .map(|(index, (name, _))| (name, index as u32))
@@ -144,17 +144,24 @@ fn emit_function(func: &ast::Function, globals: &HashMap<&str, u32>) -> Function
function function
} }
fn collect_locals<'a>(block: &ast::Block<'a>, locals: &mut Vec<(&'a str, ast::Type)>) { fn collect_locals<'a>(block: &ast::Block, locals: &mut Vec<(String, ast::Type)>) {
for stmt in &block.statements { for stmt in &block.statements {
match stmt { collect_locals_expr(stmt, locals);
ast::Statement::LocalVariable(v) => { }
locals.push((v.name, v.type_.unwrap())); if let Some(ref expr) = block.final_expression {
if let Some(ref value) = v.value { collect_locals_expr(expr, locals);
}
}
fn collect_locals_expr<'a>(expr: &ast::Expression, locals: &mut Vec<(String, ast::Type)>) {
match &expr.expr {
ast::Expr::Let {name, type_, value, ..} => {
locals.push((name.clone(), type_.unwrap()));
if let Some(ref value) = value {
collect_locals_expr(value, locals); collect_locals_expr(value, locals);
} }
} }
ast::Statement::Expression(e) => collect_locals_expr(e, locals), ast::Expr::Poke {
ast::Statement::Poke {
mem_location, mem_location,
value, value,
.. ..
@@ -162,16 +169,8 @@ fn collect_locals<'a>(block: &ast::Block<'a>, locals: &mut Vec<(&'a str, ast::Ty
collect_locals_expr(&mem_location.left, locals); collect_locals_expr(&mem_location.left, locals);
collect_locals_expr(value, locals); collect_locals_expr(value, locals);
} }
}
}
if let Some(ref expr) = block.final_expression {
collect_locals_expr(expr, locals);
}
}
fn collect_locals_expr<'a>(expr: &ast::Expression<'a>, locals: &mut Vec<(&'a str, ast::Type)>) {
match &expr.expr {
ast::Expr::Variable { .. } | ast::Expr::I32Const(_) | ast::Expr::F32Const(_) => (), ast::Expr::Variable { .. } | ast::Expr::I32Const(_) | ast::Expr::F32Const(_) => (),
ast::Expr::UnaryOp { value, .. } => collect_locals_expr(value, locals),
ast::Expr::BinOp { left, right, .. } => { ast::Expr::BinOp { left, right, .. } => {
collect_locals_expr(left, locals); collect_locals_expr(left, locals);
collect_locals_expr(right, locals); collect_locals_expr(right, locals);
@@ -195,30 +194,36 @@ fn collect_locals_expr<'a>(expr: &ast::Expression<'a>, locals: &mut Vec<(&'a str
collect_locals_expr(if_true, locals); collect_locals_expr(if_true, locals);
collect_locals_expr(if_false, locals); collect_locals_expr(if_false, locals);
} }
ast::Expr::Error => unreachable!()
} }
} }
fn emit_block<'a>(ctx: &mut FunctionContext<'a>, block: &'a ast::Block) { fn emit_block<'a>(ctx: &mut FunctionContext<'a>, block: &'a ast::Block) {
for stmt in &block.statements { for stmt in &block.statements {
match stmt { emit_expression(ctx, stmt);
ast::Statement::Expression(e) => { if stmt.type_.is_some() {
emit_expression(ctx, e);
if e.type_.is_some() {
ctx.function.instruction(&Instruction::Drop); ctx.function.instruction(&Instruction::Drop);
} }
} }
ast::Statement::LocalVariable(v) => { if let Some(ref expr) = block.final_expression {
if let Some(ref val) = v.value { emit_expression(ctx, expr);
if v.defer { }
ctx.deferred_inits.insert(v.name, val); }
fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression) {
match &expr.expr {
ast::Expr::Let { value, name, defer, ..} => {
if let Some(ref val) = value {
if *defer {
ctx.deferred_inits.insert(name, val);
} else { } else {
emit_expression(ctx, val); emit_expression(ctx, val);
ctx.function ctx.function
.instruction(&Instruction::LocalSet(*ctx.locals.get(v.name).unwrap())); .instruction(&Instruction::LocalSet(*ctx.locals.get(name).unwrap()));
} }
} }
} }
ast::Statement::Poke { ast::Expr::Poke {
mem_location, mem_location,
value, value,
.. ..
@@ -243,15 +248,19 @@ fn emit_block<'a>(ctx: &mut FunctionContext<'a>, block: &'a ast::Block) {
}), }),
}); });
} }
ast::Expr::UnaryOp { op, value } => {
use ast::UnaryOp::*;
use ast::Type::*;
match (value.type_.unwrap(), op) {
(I32, Negate) => {
// TODO: try to improve this uglyness
ctx.function.instruction(&Instruction::I32Const(0));
emit_expression(ctx, value);
ctx.function.instruction(&Instruction::I32Sub);
} }
_ => unreachable!()
};
} }
if let Some(ref expr) = block.final_expression {
emit_expression(ctx, expr);
}
}
fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression) {
match &expr.expr {
ast::Expr::BinOp { ast::Expr::BinOp {
left, op, right, .. left, op, right, ..
} => { } => {
@@ -300,7 +309,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|(_, l)| l == label) .find(|(_, l)| *l == label)
.unwrap() .unwrap()
.0; .0;
ctx.function.instruction(&Instruction::BrIf(depth as u32)); ctx.function.instruction(&Instruction::BrIf(depth as u32));
@@ -313,7 +322,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
} }
ast::Expr::LocalTee { name, value, .. } => { ast::Expr::LocalTee { name, value, .. } => {
emit_expression(ctx, value); emit_expression(ctx, value);
let index = ctx.locals.get(*name).unwrap(); let index = ctx.locals.get(name).unwrap();
ctx.function.instruction(&Instruction::LocalTee(*index)); ctx.function.instruction(&Instruction::LocalTee(*index));
} }
ast::Expr::Loop { label, block, .. } => { ast::Expr::Loop { label, block, .. } => {
@@ -324,15 +333,15 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
ctx.labels.pop(); ctx.labels.pop();
ctx.function.instruction(&Instruction::End); ctx.function.instruction(&Instruction::End);
} }
ast::Expr::Variable { name, .. } => { ast::Expr::Variable(name) => {
if let Some(index) = ctx.locals.get(*name) { if let Some(index) = ctx.locals.get(name) {
if let Some(expr) = ctx.deferred_inits.remove(*name) { if let Some(expr) = ctx.deferred_inits.remove(name.as_str()) {
emit_expression(ctx, expr); emit_expression(ctx, expr);
ctx.function.instruction(&Instruction::LocalTee(*index)); ctx.function.instruction(&Instruction::LocalTee(*index));
} else { } else {
ctx.function.instruction(&Instruction::LocalGet(*index)); ctx.function.instruction(&Instruction::LocalGet(*index));
} }
} else if let Some(index) = ctx.globals.get(*name) { } else if let Some(index) = ctx.globals.get(name.as_str()) {
ctx.function.instruction(&Instruction::GlobalGet(*index)); ctx.function.instruction(&Instruction::GlobalGet(*index));
} else { } else {
unreachable!() unreachable!()
@@ -371,6 +380,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
emit_expression(ctx, condition); emit_expression(ctx, condition);
ctx.function.instruction(&Instruction::Select); ctx.function.instruction(&Instruction::Select);
} }
ast::Expr::Error => unreachable!()
} }
} }

View File

@@ -6,9 +6,10 @@ mod ast;
mod constfold; mod constfold;
mod emit; mod emit;
mod parser; mod parser;
mod parser2;
mod typecheck; mod typecheck;
type Span = std::ops::Range<usize>;
fn main() -> Result<()> { fn main() -> Result<()> {
let mut filename = PathBuf::from( let mut filename = PathBuf::from(
std::env::args() std::env::args()
@@ -18,28 +19,14 @@ fn main() -> Result<()> {
let mut input = String::new(); let mut input = String::new();
File::open(&filename)?.read_to_string(&mut input)?; File::open(&filename)?.read_to_string(&mut input)?;
if let Err(_) = parser2::parse(&input) { let mut script = match parser::parse(&input) {
bail!("Parse failed");
}
let mut script = match parser::parse(input.as_str()) {
Ok(script) => script, Ok(script) => script,
Err(err) => { Err(_) => bail!("Parse failed")
bail!(
"parse error: {}",
nom::error::convert_error(input.as_str(), err)
);
}
}; };
constfold::fold_script(&mut script); constfold::fold_script(&mut script);
if let Err(err) = typecheck::tc_script(&mut script) { if let Err(_) = typecheck::tc_script(&mut script, &input) {
let line = input[..(input.len() - err.position.0)] bail!("Parse failed");
.chars()
.filter(|c| *c == '\n')
.count()
+ 1;
bail!("{} in line {}", err.message, line);
} }
let wasm = emit::emit(&script); let wasm = emit::emit(&script);

File diff suppressed because it is too large Load Diff

View File

@@ -1,830 +0,0 @@
use ariadne::{Color, Fmt, Label, Report, ReportKind, Source};
use chumsky::{prelude::*, stream::Stream};
use std::fmt;
pub type Span = std::ops::Range<usize>;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum Token {
Import,
Export,
Fn,
Let,
Memory,
Global,
Mut,
Loop,
BranchIf,
Defer,
As,
Select,
Ident(String),
Str(String),
Int(i32),
Float(String),
Op(String),
Ctrl(char),
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Token::Import => write!(f, "import"),
Token::Export => write!(f, "export"),
Token::Fn => write!(f, "fn"),
Token::Let => write!(f, "let"),
Token::Memory => write!(f, "memory"),
Token::Global => write!(f, "global"),
Token::Mut => write!(f, "mut"),
Token::Loop => write!(f, "loop"),
Token::BranchIf => write!(f, "branch_if"),
Token::Defer => write!(f, "defer"),
Token::As => write!(f, "as"),
Token::Select => write!(f, "select"),
Token::Ident(s) => write!(f, "{}", s),
Token::Str(s) => write!(f, "{:?}", s),
Token::Int(v) => write!(f, "{}", v),
Token::Float(v) => write!(f, "{}", v),
Token::Op(s) => write!(f, "{}", s),
Token::Ctrl(c) => write!(f, "{}", c),
}
}
}
pub fn parse(source: &str) -> Result<(), ()> {
let tokens = match lexer().parse(source) {
Ok(tokens) => tokens,
Err(errors) => {
report_errors(
errors
.into_iter()
.map(|e| e.map(|c| c.to_string()))
.collect(),
source,
);
return Err(());
}
};
let source_len = source.chars().count();
let script = match script_parser().parse(Stream::from_iter(
source_len..source_len + 1,
tokens.into_iter(),
)) {
Ok(script) => script,
Err(errors) => {
report_errors(
errors
.into_iter()
.map(|e| e.map(|t| t.to_string()))
.collect(),
source,
);
return Err(());
}
};
dbg!(script);
Ok(())
}
fn report_errors(errors: Vec<Simple<String>>, source: &str) {
for error in errors {
let report = Report::build(ReportKind::Error, (), error.span().start());
let report = match error.reason() {
chumsky::error::SimpleReason::Unclosed { span, delimiter } => report
.with_message(format!(
"Unclosed delimiter {}",
delimiter.fg(Color::Yellow)
))
.with_label(
Label::new(span.clone())
.with_message(format!(
"Unclosed delimiter {}",
delimiter.fg(Color::Yellow)
))
.with_color(Color::Yellow),
)
.with_label(
Label::new(error.span())
.with_message(format!(
"Must be closed before this {}",
error
.found()
.unwrap_or(&"end of file".to_string())
.fg(Color::Red)
))
.with_color(Color::Red),
),
chumsky::error::SimpleReason::Unexpected => report
.with_message(format!(
"{}, expected one of {}",
if error.found().is_some() {
"Unexpected token in input"
} else {
"Unexpted end of input"
},
if error.expected().len() == 0 {
"end of input".to_string()
} else {
error
.expected()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ")
}
))
.with_label(
Label::new(error.span())
.with_message(format!(
"Unexpected token {}",
error
.found()
.unwrap_or(&"end of file".to_string())
.fg(Color::Red)
))
.with_color(Color::Red),
),
chumsky::error::SimpleReason::Custom(msg) => report.with_message(msg).with_label(
Label::new(error.span())
.with_message(format!("{}", msg.fg(Color::Red)))
.with_color(Color::Red),
),
};
report.finish().eprint(Source::from(source)).unwrap();
}
}
fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
let float = text::int(10)
.chain::<char, _, _>(just('.').chain(text::digits(10)))
.collect::<String>()
.map(Token::Float);
let int = text::int(10).map(|s: String| Token::Int(s.parse().unwrap()));
let str_ = just('"')
.ignore_then(filter(|c| *c != '"').repeated())
.then_ignore(just('"'))
.collect::<String>()
.map(Token::Str);
let op = one_of("+-*/%&^|<=>".chars())
.repeated()
.at_least(1)
.or(just(':').chain(just('=')))
.collect::<String>()
.map(Token::Op);
let ctrl = one_of("(){};,:?!".chars()).map(Token::Ctrl);
let ident = text::ident().map(|ident: String| match ident.as_str() {
"import" => Token::Import,
"export" => Token::Export,
"fn" => Token::Fn,
"let" => Token::Let,
"memory" => Token::Memory,
"global" => Token::Global,
"mut" => Token::Mut,
"loop" => Token::Loop,
"branch_if" => Token::BranchIf,
"defer" => Token::Defer,
"as" => Token::As,
"select" => Token::Select,
_ => Token::Ident(ident),
});
let single_line =
seq::<_, _, Simple<char>>("//".chars()).then_ignore(take_until(text::newline()));
let multi_line =
seq::<_, _, Simple<char>>("/*".chars()).then_ignore(take_until(seq("*/".chars())));
let comment = single_line.or(multi_line);
let token = float
.or(int)
.or(str_)
.or(op)
.or(ctrl)
.or(ident)
.recover_with(skip_then_retry_until([]));
token
.map_with_span(|tok, span| (tok, span))
.padded()
.padded_by(comment.padded().repeated())
.repeated()
}
mod ast {
use super::Span;
#[derive(Debug)]
pub struct Script {
pub imports: Vec<Import>,
pub global_vars: Vec<GlobalVar>,
pub functions: Vec<Function>,
}
#[derive(Debug)]
pub enum TopLevelItem {
Import(Import),
GlobalVar(GlobalVar),
Function(Function),
}
#[derive(Debug)]
pub struct Import {
pub span: Span,
pub import: String,
pub type_: ImportType,
}
#[derive(Debug)]
pub enum ImportType {
Memory(u32),
Variable {
name: String,
type_: Type,
mutable: bool,
},
// Function { name: String, params: Vec<Type>, result: Option<Type> }
}
#[derive(Debug)]
pub struct GlobalVar {
pub span: Span,
pub name: String,
pub type_: Type,
}
#[derive(Debug)]
pub struct Function {
pub span: Span,
pub export: bool,
pub name: String,
pub params: Vec<(String, Type)>,
pub type_: Option<Type>,
pub body: Block,
}
#[derive(Debug)]
pub struct Block {
pub statements: Vec<Expression>,
pub final_expression: Option<Box<Expression>>,
}
impl Block {
pub fn type_(&self) -> Option<Type> {
self.final_expression.as_ref().and_then(|e| e.type_)
}
}
#[derive(Debug)]
pub struct MemoryLocation {
pub span: Span,
pub size: MemSize,
pub left: Box<Expression>,
pub right: Box<Expression>,
}
#[derive(Debug)]
pub struct LocalVariable {
pub span: Span,
pub name: String,
pub type_: Option<Type>,
pub value: Option<Expression>,
pub defer: bool,
}
#[derive(Debug)]
pub struct Expression {
pub type_: Option<Type>,
pub expr: Expr,
pub span: Span,
}
#[derive(Debug)]
pub enum Expr {
I32Const(i32),
F32Const(f32),
Variable(String),
Let {
name: String,
type_: Option<Type>,
value: Option<Box<Expression>>,
defer: bool,
},
Poke {
mem_location: MemoryLocation,
value: Box<Expression>,
},
Loop {
label: String,
block: Box<Block>,
},
BranchIf {
condition: Box<Expression>,
label: String,
},
UnaryOp {
op: UnaryOp,
value: Box<Expression>,
},
BinOp {
op: BinOp,
left: Box<Expression>,
right: Box<Expression>,
},
LocalTee {
name: String,
value: Box<Expression>,
},
Cast {
value: Box<Expression>,
type_: Type,
},
FuncCall {
name: String,
params: Vec<Expression>,
},
Select {
condition: Box<Expression>,
if_true: Box<Expression>,
if_false: Box<Expression>,
},
Error,
}
impl Expr {
pub fn with_span(self, span: Span) -> Expression {
Expression {
type_: None,
expr: self,
span: span,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum UnaryOp {
Negate,
}
#[derive(Debug, Clone, Copy)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Rem,
And,
Or,
Xor,
Eq,
Ne,
Gt,
Ge,
Lt,
Le,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemSize {
Byte,
Word,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum Type {
I32,
I64,
F32,
F64,
}
}
fn map_token<O>(
f: impl Fn(&Token) -> Option<O> + 'static + Clone,
) -> impl Parser<Token, O, Error = Simple<Token>> + Clone {
filter_map(move |span, tok: Token| {
if let Some(output) = f(&tok) {
Ok(output)
} else {
Err(Simple::expected_input_found(span, Vec::new(), Some(tok)))
}
})
}
fn block_parser() -> impl Parser<Token, ast::Block, Error = Simple<Token>> + Clone {
recursive(|block| {
let mut block_expression = None;
let expression = recursive(|expression| {
let val = map_token(|tok| match tok {
Token::Int(v) => Some(ast::Expr::I32Const(*v)),
Token::Float(v) => Some(ast::Expr::F32Const(v.parse().unwrap())),
_ => None,
})
.labelled("value");
let variable = filter_map(|span, tok| match tok {
Token::Ident(id) => Ok(ast::Expr::Variable(id)),
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))),
})
.labelled("variable");
let ident = filter_map(|span, tok| match tok {
Token::Ident(id) => Ok(id),
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))),
})
.labelled("identifier");
let local_tee = ident
.then(just(Token::Op(":=".to_string())).ignore_then(expression.clone()))
.map(|(name, expr)| ast::Expr::LocalTee {
name,
value: Box::new(expr),
}).boxed();
let loop_expr = just(Token::Loop)
.ignore_then(ident)
.then(
block
.clone()
.delimited_by(Token::Ctrl('{'), Token::Ctrl('}')),
)
.map(|(label, block)| ast::Expr::Loop {
label,
block: Box::new(block),
});
let block_expr = loop_expr.boxed();
block_expression = Some(block_expr.clone());
let branch_if = just(Token::BranchIf)
.ignore_then(expression.clone())
.then_ignore(just(Token::Ctrl(':')))
.then(ident)
.map(|(condition, label)| ast::Expr::BranchIf {
condition: Box::new(condition),
label,
}).boxed();
let let_ = just(Token::Let)
.ignore_then(just(Token::Defer).or_not())
.then(ident.clone())
.then(just(Token::Ctrl(':')).ignore_then(type_parser()).or_not())
.then(
just(Token::Op("=".to_string()))
.ignore_then(expression.clone())
.or_not(),
)
.map(|(((defer, name), type_), value)| ast::Expr::Let {
name,
type_,
value: value.map(Box::new),
defer: defer.is_some(),
}).boxed();
let tee = ident
.clone()
.then_ignore(just(Token::Op(":=".to_string())))
.then(expression.clone())
.map(|(name, value)| ast::Expr::LocalTee {
name,
value: Box::new(value),
}).boxed();
let select = just(Token::Select)
.ignore_then(
expression
.clone()
.then_ignore(just(Token::Ctrl(',')))
.then(expression.clone())
.then_ignore(just(Token::Ctrl(',')))
.then(expression.clone())
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
)
.map(|((condition, if_true), if_false)| ast::Expr::Select {
condition: Box::new(condition),
if_true: Box::new(if_true),
if_false: Box::new(if_false),
}).boxed();
let function_call = ident
.clone()
.then(
expression
.clone()
.separated_by(just(Token::Ctrl(',')))
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
)
.map(|(name, params)| ast::Expr::FuncCall { name, params }).boxed();
let atom = val
.or(tee)
.or(function_call)
.or(variable)
.or(local_tee)
.or(block_expr)
.or(branch_if)
.or(let_)
.or(select)
.map_with_span(|expr, span| expr.with_span(span))
.or(expression
.clone()
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')))
.recover_with(nested_delimiters(
Token::Ctrl('('),
Token::Ctrl(')'),
[(Token::Ctrl('{'), Token::Ctrl('}'))],
|span| ast::Expr::Error.with_span(span),
)).boxed();
let unary_op = just(Token::Op("-".to_string()))
.to(ast::UnaryOp::Negate)
.map_with_span(|op, span| (op, span))
.repeated()
.then(atom)
.map(|(ops, value)| {
ops.into_iter().rev().fold(value, |acc, (op, span)| {
let span = span.start..acc.span.end;
ast::Expr::UnaryOp {
op,
value: Box::new(acc),
}
.with_span(span)
})
}).boxed();
let op_cast = unary_op
.clone()
.then(
just(Token::As)
.ignore_then(type_parser())
.map_with_span(|type_, span| (type_, span))
.repeated(),
)
.foldl(|value, (type_, span)| {
ast::Expr::Cast {
value: Box::new(value),
type_,
}
.with_span(span)
}).boxed();
let mem_size = just(Token::Ctrl('?'))
.to(ast::MemSize::Byte)
.or(just(Token::Ctrl('!')).to(ast::MemSize::Word));
let memory_op = op_cast
.clone()
.then(
mem_size
.then(op_cast.clone())
.then_ignore(just(Token::Op("=".to_string())))
.then(expression.clone())
.repeated(),
)
.foldl(|left, ((size, right), value)| {
let span = left.span.start..value.span.end;
ast::Expr::Poke {
mem_location: ast::MemoryLocation {
span: span.clone(),
left: Box::new(left),
size,
right: Box::new(right),
},
value: Box::new(value),
}
.with_span(span)
}).boxed();
let op_product = memory_op
.clone()
.then(
just(Token::Op("*".to_string()))
.to(ast::BinOp::Mul)
.or(just(Token::Op("/".to_string())).to(ast::BinOp::Div))
.or(just(Token::Op("%".to_string())).to(ast::BinOp::Rem))
.then(memory_op.clone())
.repeated(),
)
.foldl(|left, (op, right)| {
let span = left.span.start..right.span.end;
ast::Expr::BinOp {
op,
left: Box::new(left),
right: Box::new(right),
}
.with_span(span)
}).boxed();
let op_sum = op_product
.clone()
.then(
just(Token::Op("+".to_string()))
.to(ast::BinOp::Add)
.or(just(Token::Op("-".to_string())).to(ast::BinOp::Sub))
.then(op_product.clone())
.repeated(),
)
.foldl(|left, (op, right)| {
let span = left.span.start..right.span.end;
ast::Expr::BinOp {
op,
left: Box::new(left),
right: Box::new(right),
}
.with_span(span)
}).boxed();
let op_cmp = op_sum
.clone()
.then(
just(Token::Op("==".to_string()))
.to(ast::BinOp::Eq)
.or(just(Token::Op("!=".to_string())).to(ast::BinOp::Ne))
.or(just(Token::Op("<".to_string())).to(ast::BinOp::Lt))
.or(just(Token::Op("<=".to_string())).to(ast::BinOp::Le))
.or(just(Token::Op(">".to_string())).to(ast::BinOp::Gt))
.or(just(Token::Op(">=".to_string())).to(ast::BinOp::Ge))
.then(op_sum.clone())
.repeated(),
)
.foldl(|left, (op, right)| {
let span = left.span.start..right.span.end;
ast::Expr::BinOp {
op,
left: Box::new(left),
right: Box::new(right),
}
.with_span(span)
}).boxed();
let op_bit = op_cmp
.clone()
.then(
just(Token::Op("&".to_string()))
.to(ast::BinOp::And)
.or(just(Token::Op("|".to_string())).to(ast::BinOp::Or))
.or(just(Token::Op("^".to_string())).to(ast::BinOp::Xor))
.then(op_cmp.clone())
.repeated(),
)
.foldl(|left, (op, right)| {
let span = left.span.start..right.span.end;
ast::Expr::BinOp {
op,
left: Box::new(left),
right: Box::new(right),
}
.with_span(span)
}).boxed();
op_bit
});
let block_expression = block_expression.unwrap();
expression
.clone()
.then_ignore(just(Token::Ctrl(';')))
.or(block_expression.map_with_span(|expr, span| expr.with_span(span)))
.repeated()
.then(expression.clone().or_not())
.map(|(statements, final_expression)| ast::Block {
statements,
final_expression: final_expression.map(|e| Box::new(e)),
})
})
}
fn type_parser() -> impl Parser<Token, ast::Type, Error = Simple<Token>> + Clone {
filter_map(|span, tok| match tok {
Token::Ident(id) if id == "i32" => Ok(ast::Type::I32),
Token::Ident(id) if id == "i64" => Ok(ast::Type::I64),
Token::Ident(id) if id == "f32" => Ok(ast::Type::F32),
Token::Ident(id) if id == "f64" => Ok(ast::Type::F64),
_ => Err(Simple::expected_input_found(
span,
vec![
Token::Ident("i32".into()),
Token::Ident("i64".into()),
Token::Ident("f32".into()),
Token::Ident("f64".into()),
],
Some(tok),
)),
})
}
fn top_level_item_parser() -> impl Parser<Token, ast::TopLevelItem, Error = Simple<Token>> + Clone {
let integer = map_token(|tok| match tok {
Token::Int(v) => Some(*v),
_ => None,
});
let string = map_token(|tok| match tok {
Token::Str(s) => Some(s.clone()),
_ => None,
});
let identifier = map_token(|tok| match tok {
Token::Ident(id) => Some(id.clone()),
_ => None,
});
let import_memory = just(Token::Memory)
.ignore_then(integer.delimited_by(Token::Ctrl('('), Token::Ctrl(')')))
.map(|min_size| ast::ImportType::Memory(min_size as u32));
let import_global = just(Token::Global)
.ignore_then(just(Token::Mut).or_not())
.then(identifier.clone())
.then_ignore(just(Token::Ctrl(':')))
.then(type_parser())
.map(|((mut_opt, name), type_)| ast::ImportType::Variable {
mutable: mut_opt.is_some(),
name,
type_,
});
let import = just(Token::Import)
.ignore_then(string)
.then(import_memory.or(import_global))
.then_ignore(just(Token::Ctrl(';')))
.map_with_span(|(import, type_), span| {
ast::TopLevelItem::Import(ast::Import {
span,
import,
type_,
})
});
let parameter = identifier
.clone()
.then_ignore(just(Token::Ctrl(':')))
.then(type_parser());
let function = just(Token::Export)
.or_not()
.then_ignore(just(Token::Fn))
.then(identifier.clone())
.then(
parameter
.separated_by(just(Token::Ctrl(',')))
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
)
.then(
just(Token::Op("->".to_string()))
.ignore_then(type_parser())
.or_not(),
)
.then(block_parser().delimited_by(Token::Ctrl('{'), Token::Ctrl('}')))
.map_with_span(|((((export, name), params), type_), body), span| {
ast::TopLevelItem::Function(ast::Function {
span,
params,
export: export.is_some(),
name,
type_,
body,
})
});
let global = just(Token::Global)
.ignore_then(identifier.clone())
.then_ignore(just(Token::Ctrl(':')))
.then(type_parser())
.then_ignore(just(Token::Ctrl(';')))
.map_with_span(|(name, type_), span| {
ast::TopLevelItem::GlobalVar(ast::GlobalVar { name, type_, span })
});
import.or(function).or(global)
}
fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + Clone {
top_level_item_parser()
.repeated()
.then_ignore(end())
.map(|items| {
let mut script = ast::Script {
imports: Vec::new(),
global_vars: Vec::new(),
functions: Vec::new(),
};
for item in items {
match item {
ast::TopLevelItem::Import(i) => script.imports.push(i),
ast::TopLevelItem::GlobalVar(v) => script.global_vars.push(v),
ast::TopLevelItem::Function(f) => script.functions.push(f),
}
}
script
})
}

View File

@@ -1,34 +1,49 @@
use ariadne::{Color, Label, Report, ReportKind, Source};
use std::collections::HashMap; use std::collections::HashMap;
use crate::ast; use crate::ast;
use crate::Span;
use ast::Type::*; use ast::Type::*;
#[derive(Debug)] type Result<T> = std::result::Result<T, ()>;
pub struct Error {
pub position: ast::Position, struct Var {
pub message: String, span: Span,
type_: ast::Type,
} }
type Vars = HashMap<String, Var>;
type Result<T> = std::result::Result<T, Error>; pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
type Vars<'a> = HashMap<&'a str, ast::Type>;
pub fn tc_script(script: &mut ast::Script) -> Result<()> {
let mut context = Context { let mut context = Context {
source,
global_vars: HashMap::new(), global_vars: HashMap::new(),
local_vars: HashMap::new(), local_vars: HashMap::new(),
block_stack: Vec::new(),
}; };
let mut result = Ok(());
for import in &script.imports { for import in &script.imports {
match import.type_ { match import.type_ {
ast::ImportType::Variable { name, type_, .. } => { ast::ImportType::Variable {
if context.global_vars.contains_key(name) { ref name, type_, ..
return Err(Error { } => {
position: import.position, if let Some(Var { span, .. }) = context.global_vars.get(name) {
message: "Duplicate global variable".into(), result = report_duplicate_definition(
}); "Global already defined",
&import.span,
span,
source,
);
} else {
context.global_vars.insert(
name.clone(),
Var {
type_,
span: import.span.clone(),
},
);
} }
context.global_vars.insert(name, type_);
} }
// ast::ImportType::Function { .. } => todo!(), // ast::ImportType::Function { .. } => todo!(),
ast::ImportType::Memory(..) => (), ast::ImportType::Memory(..) => (),
@@ -36,120 +51,261 @@ pub fn tc_script(script: &mut ast::Script) -> Result<()> {
} }
for v in &script.global_vars { for v in &script.global_vars {
if context.global_vars.contains_key(v.name) { if let Some(Var { span, .. }) = context.global_vars.get(&v.name) {
return Err(Error { result = report_duplicate_definition("Global already defined", &v.span, span, source);
position: v.position, } else {
message: "Duplicate global variable".into(), context.global_vars.insert(
}); v.name.clone(),
Var {
type_: v.type_,
span: v.span.clone(),
},
);
} }
context.global_vars.insert(v.name, v.type_);
} }
for f in &mut script.functions { for f in &mut script.functions {
context.local_vars.clear(); context.local_vars.clear();
for (name, type_) in &f.params { for (name, type_) in &f.params {
if context.local_vars.contains_key(name) || context.global_vars.contains_key(name) { if let Some(Var { span, .. }) = context
return Err(Error { .local_vars
position: f.position, .get(name)
message: format!("Variable already defined '{}'", name), .or_else(|| context.global_vars.get(name))
}); {
result =
report_duplicate_definition("Variable already defined", &f.span, span, source);
} else {
context.local_vars.insert(
name.clone(),
Var {
type_: *type_,
span: f.span.clone(),
},
);
} }
context.local_vars.insert(name, *type_);
} }
tc_block(&mut context, &mut f.body)?; tc_block(&mut context, &mut f.body)?;
} }
Ok(()) result
} }
struct Context<'a> { struct Context<'a> {
global_vars: Vars<'a>, source: &'a str,
local_vars: Vars<'a>, global_vars: Vars,
local_vars: Vars,
block_stack: Vec<String>,
} }
fn tc_block<'a>(context: &mut Context<'a>, block: &mut ast::Block<'a>) -> Result<()> { fn tc_block(context: &mut Context, block: &mut ast::Block) -> Result<()> {
let mut result = Ok(());
for stmt in &mut block.statements { for stmt in &mut block.statements {
match *stmt { if tc_expression(context, stmt).is_err() {
ast::Statement::Expression(ref mut expr) => tc_expression(context, expr)?, result = Err(());
ast::Statement::LocalVariable(ref mut lv) => { }
if let Some(ref mut value) = lv.value { }
if let Some(ref mut expr) = block.final_expression {
tc_expression(context, expr)?;
}
result
}
fn report_duplicate_definition(
msg: &str,
span: &Span,
prev_span: &Span,
source: &str,
) -> Result<()> {
Report::build(ReportKind::Error, (), span.start)
.with_message(msg)
.with_label(
Label::new(span.clone())
.with_message(msg)
.with_color(Color::Red),
)
.with_label(
Label::new(prev_span.clone())
.with_message("Previous definition was here")
.with_color(Color::Yellow),
)
.finish()
.eprint(Source::from(source))
.unwrap();
Err(())
}
fn type_mismatch(
type1: ast::Type,
span1: &Span,
type2: Option<ast::Type>,
span2: &Span,
source: &str,
) -> Result<()> {
Report::build(ReportKind::Error, (), span2.start)
.with_message("Type mismatch")
.with_label(
Label::new(span1.clone())
.with_message(format!("Expected type {:?}...", type1))
.with_color(Color::Yellow),
)
.with_label(
Label::new(span2.clone())
.with_message(format!(
"...but found type {}",
type2
.map(|t| format!("{:?}", t))
.unwrap_or("void".to_string())
))
.with_color(Color::Red),
)
.finish()
.eprint(Source::from(source))
.unwrap();
Err(())
}
fn expected_type(span: &Span, source: &str) -> Result<()> {
Report::build(ReportKind::Error, (), span.start)
.with_message("Expected value but found expression of type void")
.with_label(
Label::new(span.clone())
.with_message("Expected value but found expression of type void")
.with_color(Color::Red),
)
.finish()
.eprint(Source::from(source))
.unwrap();
Err(())
}
fn unknown_variable(span: &Span, source: &str) -> Result<()> {
Report::build(ReportKind::Error, (), span.start)
.with_message("Unknown variable")
.with_label(
Label::new(span.clone())
.with_message("Unknown variable")
.with_color(Color::Red),
)
.finish()
.eprint(Source::from(source))
.unwrap();
Err(())
}
fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()> {
expr.type_ = match expr.expr {
ast::Expr::Let {
ref mut value,
ref mut type_,
ref name,
..
} => {
if let Some(ref mut value) = value {
tc_expression(context, value)?; tc_expression(context, value)?;
if lv.type_.is_none() { if let Some(type_) = type_ {
lv.type_ = value.type_; if Some(*type_) != value.type_ {
} else if lv.type_ != value.type_ { return type_mismatch(
return Err(Error { *type_,
position: lv.position, &expr.span,
message: "Mismatched types".into(), value.type_,
}); &value.span,
context.source,
);
} }
} } else if value.type_.is_none() {
if let Some(type_) = lv.type_ { return expected_type(&value.span, context.source);
if context.local_vars.contains_key(lv.name)
|| context.global_vars.contains_key(lv.name)
{
return Err(Error {
position: lv.position,
message: format!("Variable '{}' already defined", lv.name),
});
}
context.local_vars.insert(lv.name, type_);
} else { } else {
return Err(Error { *type_ = value.type_;
position: lv.position,
message: "Missing type".into(),
});
} }
} }
ast::Statement::Poke { if let Some(type_) = type_ {
position, if let Some(Var { span, .. }) = context
.local_vars
.get(name)
.or_else(|| context.global_vars.get(name))
{
return report_duplicate_definition(
"Variable already defined",
&expr.span,
span,
context.source,
);
}
context.local_vars.insert(
name.clone(),
Var {
type_: *type_,
span: expr.span.clone(),
},
);
} else {
Report::build(ReportKind::Error, (), expr.span.start)
.with_message("Type missing")
.with_label(
Label::new(expr.span.clone())
.with_message("Type missing")
.with_color(Color::Red),
)
.finish()
.eprint(Source::from(context.source))
.unwrap();
return Err(());
}
None
}
ast::Expr::Poke {
ref mut mem_location, ref mut mem_location,
ref mut value, ref mut value,
} => { } => {
tc_mem_location(context, mem_location)?; tc_mem_location(context, mem_location)?;
tc_expression(context, value)?; tc_expression(context, value)?;
if value.type_ != Some(I32) { if value.type_ != Some(I32) {
return Err(Error { return type_mismatch(I32, &expr.span, value.type_, &value.span, context.source);
position,
message: "Type mismatch".into(),
});
} }
None
} }
}
}
if let Some(ref mut expr) = block.final_expression {
tc_expression(context, expr)?;
}
Ok(())
}
fn tc_expression<'a>(context: &mut Context<'a>, expr: &mut ast::Expression<'a>) -> Result<()> {
expr.type_ = match expr.expr {
ast::Expr::I32Const(_) => Some(ast::Type::I32), ast::Expr::I32Const(_) => Some(ast::Type::I32),
ast::Expr::F32Const(_) => Some(ast::Type::F32), ast::Expr::F32Const(_) => Some(ast::Type::F32),
ast::Expr::UnaryOp {
op: _,
ref mut value,
} => {
tc_expression(context, value)?;
todo!();
}
ast::Expr::BinOp { ast::Expr::BinOp {
position,
op, op,
ref mut left, ref mut left,
ref mut right, ref mut right,
} => { } => {
tc_expression(context, left)?; tc_expression(context, left)?;
tc_expression(context, right)?; tc_expression(context, right)?;
if left.type_.is_none() || left.type_ != right.type_ { if let Some(type_) = left.type_ {
return Err(Error { if left.type_ != right.type_ {
position, return type_mismatch(
message: "Type mismatch".into(), type_,
}); &left.span,
right.type_,
&right.span,
context.source,
);
}
} else {
return expected_type(&left.span, context.source);
} }
use ast::BinOp::*; use ast::BinOp::*;
match op { match op {
Add | Sub | Mul | Div => left.type_, Add | Sub | Mul | Div => left.type_,
Rem | And | Or | Xor => { Rem | And | Or | Xor => {
if left.type_ != Some(I32) { if left.type_ != Some(I32) {
return Err(Error { return type_mismatch(
position, I32,
message: "Unsupported type".into(), &left.span,
}); left.type_,
&left.span,
context.source,
);
} else { } else {
left.type_ left.type_
} }
@@ -157,117 +313,136 @@ fn tc_expression<'a>(context: &mut Context<'a>, expr: &mut ast::Expression<'a>)
Eq | Ne | Lt | Le | Gt | Ge => Some(I32), Eq | Ne | Lt | Le | Gt | Ge => Some(I32),
} }
} }
ast::Expr::Variable { position, name } => { ast::Expr::Variable(ref name) => {
if let Some(&type_) = context if let Some(&Var { type_, .. }) = context
.global_vars .global_vars
.get(name) .get(name)
.or_else(|| context.local_vars.get(name)) .or_else(|| context.local_vars.get(name))
{ {
Some(type_) Some(type_)
} else { } else {
return Err(Error { return unknown_variable(&expr.span, context.source);
position,
message: format!("Variable '{}' not found", name),
});
} }
} }
ast::Expr::LocalTee { ast::Expr::LocalTee {
position, ref name,
name,
ref mut value, ref mut value,
} => { } => {
tc_expression(context, value)?; tc_expression(context, value)?;
if let Some(&type_) = context.local_vars.get(name) { if let Some(&Var {
type_, ref span, ..
}) = context.local_vars.get(name)
{
if value.type_ != Some(type_) { if value.type_ != Some(type_) {
return Err(Error { return type_mismatch(type_, span, value.type_, &value.span, context.source);
position,
message: "Type mismatch".into(),
});
} }
Some(type_) Some(type_)
} else { } else {
return Err(Error { return unknown_variable(&expr.span, context.source);
position,
message: format!("No local variable '{}' found", name),
});
} }
} }
ast::Expr::Loop { ast::Expr::Loop {
position: _, ref label,
label: _,
ref mut block, ref mut block,
} => { } => {
context.block_stack.push(label.clone());
tc_block(context, block)?; tc_block(context, block)?;
context.block_stack.pop();
block.final_expression.as_ref().and_then(|e| e.type_) block.final_expression.as_ref().and_then(|e| e.type_)
} }
ast::Expr::BranchIf { ast::Expr::BranchIf {
position,
ref mut condition, ref mut condition,
label: _, ref label,
} => { } => {
tc_expression(context, condition)?; tc_expression(context, condition)?;
if condition.type_ != Some(I32) { if condition.type_ != Some(I32) {
return Err(Error { return type_mismatch(
position, I32,
message: "Condition has to be i32".into(), &expr.span,
}); condition.type_,
&condition.span,
context.source,
);
}
if !context.block_stack.contains(label) {
Report::build(ReportKind::Error, (), expr.span.start)
.with_message("Label not found")
.with_label(
Label::new(expr.span.clone())
.with_message("Label not found")
.with_color(Color::Red),
)
.finish()
.eprint(Source::from(context.source))
.unwrap();
return Err(());
} }
None None
} }
ast::Expr::Cast { ast::Expr::Cast {
position,
ref mut value, ref mut value,
type_, type_,
} => { } => {
tc_expression(context, value)?; tc_expression(context, value)?;
if value.type_.is_none() { if value.type_.is_none() {
return Err(Error { return expected_type(&expr.span, context.source);
position,
message: "Cannot cast void".into(),
});
} }
Some(type_) Some(type_)
} }
ast::Expr::FuncCall { ast::Expr::FuncCall {
position, ref name,
name,
ref mut params, ref mut params,
} => { } => {
if let Some((ptypes, rtype)) = builtin_function_types(name) { if let Some((ptypes, rtype)) = builtin_function_types(name) {
if params.len() != ptypes.len() { if params.len() != ptypes.len() {
return Err(Error { Report::build(ReportKind::Error, (), expr.span.start)
position, .with_message(format!(
message: format!(
"Expected {} parameters but found {}", "Expected {} parameters but found {}",
ptypes.len(), ptypes.len(),
params.len() params.len()
), ))
}); .with_label(
Label::new(expr.span.clone())
.with_message(format!(
"Expected {} parameters but found {}",
ptypes.len(),
params.len()
))
.with_color(Color::Red),
)
.finish()
.eprint(Source::from(context.source))
.unwrap();
return Err(());
} }
for (index, (ptype, param)) in ptypes.iter().zip(params.iter_mut()).enumerate() { for (ptype, param) in ptypes.iter().zip(params.iter_mut()) {
tc_expression(context, param)?; tc_expression(context, param)?;
if param.type_.is_none() || param.type_.unwrap() != *ptype { if param.type_ != Some(*ptype) {
return Err(Error { return type_mismatch(
position, *ptype,
message: format!( &expr.span,
"Param {} is {:?} but should be {:?}",
index + 1,
param.type_, param.type_,
ptype &param.span,
), context.source,
}); );
} }
} }
rtype rtype
} else { } else {
return Err(Error { Report::build(ReportKind::Error, (), expr.span.start)
position, .with_message(format!("Unknown function {}", name))
message: format!("Unknown function '{}'", name), .with_label(
}); Label::new(expr.span.clone())
.with_message(format!("Unknown function {}", name))
.with_color(Color::Red),
)
.finish()
.eprint(Source::from(context.source))
.unwrap();
return Err(());
} }
} }
ast::Expr::Select { ast::Expr::Select {
position,
ref mut condition, ref mut condition,
ref mut if_true, ref mut if_true,
ref mut if_false, ref mut if_false,
@@ -276,40 +451,62 @@ fn tc_expression<'a>(context: &mut Context<'a>, expr: &mut ast::Expression<'a>)
tc_expression(context, if_true)?; tc_expression(context, if_true)?;
tc_expression(context, if_false)?; tc_expression(context, if_false)?;
if condition.type_ != Some(ast::Type::I32) { if condition.type_ != Some(ast::Type::I32) {
return Err(Error { return type_mismatch(
position, I32,
message: "Condition of select has to be of type i32".into(), &condition.span,
}); condition.type_,
&condition.span,
context.source,
);
} }
if let Some(true_type) = if_true.type_ {
if if_true.type_ != if_false.type_ { if if_true.type_ != if_false.type_ {
return Err(Error { return type_mismatch(
position, true_type,
message: "Types of select branches differ".into(), &if_true.span,
}); if_false.type_,
&if_false.span,
context.source,
);
} }
if if_true.type_.is_none() { } else {
return Err(Error { return expected_type(&if_true.span, context.source);
position,
message: "Types of select branches cannot be void".into(),
});
} }
if_true.type_ if_true.type_
} }
ast::Expr::Error => unreachable!(),
}; };
Ok(()) Ok(())
} }
fn tc_mem_location<'a>( fn tc_mem_location<'a>(
context: &mut Context<'a>, context: &mut Context<'a>,
mem_location: &mut ast::MemoryLocation<'a>, mem_location: &mut ast::MemoryLocation,
) -> Result<()> { ) -> Result<()> {
tc_expression(context, &mut mem_location.left)?; tc_expression(context, &mut mem_location.left)?;
tc_expression(context, &mut mem_location.right)?; tc_expression(context, &mut mem_location.right)?;
if mem_location.left.type_ != Some(I32) || mem_location.right.type_ != Some(I32) { if mem_location.left.type_ != Some(I32) {
return Err(Error { return type_mismatch(
position: mem_location.position, I32,
message: "Type mismatch".into(), &mem_location.left.span,
}); mem_location.left.type_,
&mem_location.left.span,
context.source,
);
}
if let ast::Expr::I32Const(_) = mem_location.right.expr {
} else {
Report::build(ReportKind::Error, (), mem_location.right.span.start)
.with_message("Expected I32 constant")
.with_label(
Label::new(mem_location.right.span.clone())
.with_message("Expected I32 constant")
.with_color(Color::Red),
)
.finish()
.eprint(Source::from(context.source))
.unwrap();
return Err(());
} }
Ok(()) Ok(())
} }