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

View File

@@ -8,22 +8,7 @@ pub fn fold_script(script: &mut ast::Script) {
fn fold_block(block: &mut ast::Block) {
for stmt in &mut block.statements {
match 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);
}
}
fold_expr(stmt);
}
if let Some(ref mut expr) = block.final_expression {
fold_expr(expr);
@@ -38,6 +23,29 @@ fn fold_mem_location(mem_location: &mut ast::MemoryLocation) {
fn fold_expr(expr: &mut ast::Expression) {
use ast::BinOp::*;
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 {
ref mut left,
op,
@@ -105,7 +113,7 @@ fn fold_expr(expr: &mut ast::Expression) {
} => fold_expr(condition),
ast::Expr::Cast { ref mut value, .. } => fold_expr(value),
ast::Expr::FuncCall {
name,
ref name,
ref mut params,
..
} => {
@@ -114,15 +122,21 @@ fn fold_expr(expr: &mut ast::Expression) {
}
use ast::Expr::*;
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()),
_ => 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(if_true);
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)..]),
)
} else {
(import.import, None)
(import.import.as_str(), None)
};
let type_: EntityType = match import.type_ {
ast::ImportType::Memory(min_size) => MemoryType {
@@ -46,7 +46,7 @@ pub fn emit(script: &ast::Script) -> Vec<u8> {
.into(),
ast::ImportType::Variable {
type_,
name,
ref name,
mutable,
} => {
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();
functions.function(type_ as u32);
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));
@@ -109,9 +109,9 @@ fn function_type_key(func: &ast::Function) -> FunctionTypeKey {
struct FunctionContext<'a> {
function: &'a mut Function,
globals: &'a HashMap<&'a str, u32>,
locals: &'a HashMap<&'a str, u32>,
locals: &'a HashMap<String, u32>,
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 {
@@ -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 locals: HashMap<&str, u32> = locals
let locals: HashMap<String, u32> = locals
.into_iter()
.enumerate()
.map(|(index, (name, _))| (name, index as u32))
@@ -144,17 +144,24 @@ fn emit_function(func: &ast::Function, globals: &HashMap<&str, u32>) -> 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 {
match stmt {
ast::Statement::LocalVariable(v) => {
locals.push((v.name, v.type_.unwrap()));
if let Some(ref value) = v.value {
collect_locals_expr(stmt, locals);
}
if let Some(ref expr) = block.final_expression {
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);
}
}
ast::Statement::Expression(e) => collect_locals_expr(e, locals),
ast::Statement::Poke {
ast::Expr::Poke {
mem_location,
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(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::UnaryOp { value, .. } => collect_locals_expr(value, locals),
ast::Expr::BinOp { left, right, .. } => {
collect_locals_expr(left, 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_false, locals);
}
ast::Expr::Error => unreachable!()
}
}
fn emit_block<'a>(ctx: &mut FunctionContext<'a>, block: &'a ast::Block) {
for stmt in &block.statements {
match stmt {
ast::Statement::Expression(e) => {
emit_expression(ctx, e);
if e.type_.is_some() {
emit_expression(ctx, stmt);
if stmt.type_.is_some() {
ctx.function.instruction(&Instruction::Drop);
}
}
ast::Statement::LocalVariable(v) => {
if let Some(ref val) = v.value {
if v.defer {
ctx.deferred_inits.insert(v.name, val);
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::Let { value, name, defer, ..} => {
if let Some(ref val) = value {
if *defer {
ctx.deferred_inits.insert(name, val);
} else {
emit_expression(ctx, val);
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,
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 {
left, op, right, ..
} => {
@@ -300,7 +309,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
.iter()
.rev()
.enumerate()
.find(|(_, l)| l == label)
.find(|(_, l)| *l == label)
.unwrap()
.0;
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, .. } => {
emit_expression(ctx, value);
let index = ctx.locals.get(*name).unwrap();
let index = ctx.locals.get(name).unwrap();
ctx.function.instruction(&Instruction::LocalTee(*index));
}
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.function.instruction(&Instruction::End);
}
ast::Expr::Variable { name, .. } => {
if let Some(index) = ctx.locals.get(*name) {
if let Some(expr) = ctx.deferred_inits.remove(*name) {
ast::Expr::Variable(name) => {
if let Some(index) = ctx.locals.get(name) {
if let Some(expr) = ctx.deferred_inits.remove(name.as_str()) {
emit_expression(ctx, expr);
ctx.function.instruction(&Instruction::LocalTee(*index));
} else {
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));
} else {
unreachable!()
@@ -371,6 +380,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
emit_expression(ctx, condition);
ctx.function.instruction(&Instruction::Select);
}
ast::Expr::Error => unreachable!()
}
}

View File

@@ -6,9 +6,10 @@ mod ast;
mod constfold;
mod emit;
mod parser;
mod parser2;
mod typecheck;
type Span = std::ops::Range<usize>;
fn main() -> Result<()> {
let mut filename = PathBuf::from(
std::env::args()
@@ -18,28 +19,14 @@ fn main() -> Result<()> {
let mut input = String::new();
File::open(&filename)?.read_to_string(&mut input)?;
if let Err(_) = parser2::parse(&input) {
bail!("Parse failed");
}
let mut script = match parser::parse(input.as_str()) {
let mut script = match parser::parse(&input) {
Ok(script) => script,
Err(err) => {
bail!(
"parse error: {}",
nom::error::convert_error(input.as_str(), err)
);
}
Err(_) => bail!("Parse failed")
};
constfold::fold_script(&mut script);
if let Err(err) = typecheck::tc_script(&mut script) {
let line = input[..(input.len() - err.position.0)]
.chars()
.filter(|c| *c == '\n')
.count()
+ 1;
bail!("{} in line {}", err.message, line);
if let Err(_) = typecheck::tc_script(&mut script, &input) {
bail!("Parse failed");
}
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 crate::ast;
use crate::Span;
use ast::Type::*;
#[derive(Debug)]
pub struct Error {
pub position: ast::Position,
pub message: String,
type Result<T> = std::result::Result<T, ()>;
struct Var {
span: Span,
type_: ast::Type,
}
type Vars = HashMap<String, Var>;
type Result<T> = std::result::Result<T, Error>;
type Vars<'a> = HashMap<&'a str, ast::Type>;
pub fn tc_script(script: &mut ast::Script) -> Result<()> {
pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
let mut context = Context {
source,
global_vars: HashMap::new(),
local_vars: HashMap::new(),
block_stack: Vec::new(),
};
let mut result = Ok(());
for import in &script.imports {
match import.type_ {
ast::ImportType::Variable { name, type_, .. } => {
if context.global_vars.contains_key(name) {
return Err(Error {
position: import.position,
message: "Duplicate global variable".into(),
});
ast::ImportType::Variable {
ref name, type_, ..
} => {
if let Some(Var { span, .. }) = context.global_vars.get(name) {
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::Memory(..) => (),
@@ -36,120 +51,261 @@ pub fn tc_script(script: &mut ast::Script) -> Result<()> {
}
for v in &script.global_vars {
if context.global_vars.contains_key(v.name) {
return Err(Error {
position: v.position,
message: "Duplicate global variable".into(),
});
if let Some(Var { span, .. }) = context.global_vars.get(&v.name) {
result = report_duplicate_definition("Global already defined", &v.span, span, source);
} else {
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 {
context.local_vars.clear();
for (name, type_) in &f.params {
if context.local_vars.contains_key(name) || context.global_vars.contains_key(name) {
return Err(Error {
position: f.position,
message: format!("Variable already defined '{}'", name),
});
if let Some(Var { span, .. }) = context
.local_vars
.get(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)?;
}
Ok(())
result
}
struct Context<'a> {
global_vars: Vars<'a>,
local_vars: Vars<'a>,
source: &'a str,
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 {
match *stmt {
ast::Statement::Expression(ref mut expr) => tc_expression(context, expr)?,
ast::Statement::LocalVariable(ref mut lv) => {
if let Some(ref mut value) = lv.value {
if tc_expression(context, stmt).is_err() {
result = Err(());
}
}
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)?;
if lv.type_.is_none() {
lv.type_ = value.type_;
} else if lv.type_ != value.type_ {
return Err(Error {
position: lv.position,
message: "Mismatched types".into(),
});
if let Some(type_) = type_ {
if Some(*type_) != value.type_ {
return type_mismatch(
*type_,
&expr.span,
value.type_,
&value.span,
context.source,
);
}
}
if let Some(type_) = lv.type_ {
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 if value.type_.is_none() {
return expected_type(&value.span, context.source);
} else {
return Err(Error {
position: lv.position,
message: "Missing type".into(),
});
*type_ = value.type_;
}
}
ast::Statement::Poke {
position,
if let Some(type_) = type_ {
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 value,
} => {
tc_mem_location(context, mem_location)?;
tc_expression(context, value)?;
if value.type_ != Some(I32) {
return Err(Error {
position,
message: "Type mismatch".into(),
});
return type_mismatch(I32, &expr.span, value.type_, &value.span, context.source);
}
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::F32Const(_) => Some(ast::Type::F32),
ast::Expr::UnaryOp {
op: _,
ref mut value,
} => {
tc_expression(context, value)?;
todo!();
}
ast::Expr::BinOp {
position,
op,
ref mut left,
ref mut right,
} => {
tc_expression(context, left)?;
tc_expression(context, right)?;
if left.type_.is_none() || left.type_ != right.type_ {
return Err(Error {
position,
message: "Type mismatch".into(),
});
if let Some(type_) = left.type_ {
if left.type_ != right.type_ {
return type_mismatch(
type_,
&left.span,
right.type_,
&right.span,
context.source,
);
}
} else {
return expected_type(&left.span, context.source);
}
use ast::BinOp::*;
match op {
Add | Sub | Mul | Div => left.type_,
Rem | And | Or | Xor => {
if left.type_ != Some(I32) {
return Err(Error {
position,
message: "Unsupported type".into(),
});
return type_mismatch(
I32,
&left.span,
left.type_,
&left.span,
context.source,
);
} else {
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),
}
}
ast::Expr::Variable { position, name } => {
if let Some(&type_) = context
ast::Expr::Variable(ref name) => {
if let Some(&Var { type_, .. }) = context
.global_vars
.get(name)
.or_else(|| context.local_vars.get(name))
{
Some(type_)
} else {
return Err(Error {
position,
message: format!("Variable '{}' not found", name),
});
return unknown_variable(&expr.span, context.source);
}
}
ast::Expr::LocalTee {
position,
name,
ref name,
ref mut 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_) {
return Err(Error {
position,
message: "Type mismatch".into(),
});
return type_mismatch(type_, span, value.type_, &value.span, context.source);
}
Some(type_)
} else {
return Err(Error {
position,
message: format!("No local variable '{}' found", name),
});
return unknown_variable(&expr.span, context.source);
}
}
ast::Expr::Loop {
position: _,
label: _,
ref label,
ref mut block,
} => {
context.block_stack.push(label.clone());
tc_block(context, block)?;
context.block_stack.pop();
block.final_expression.as_ref().and_then(|e| e.type_)
}
ast::Expr::BranchIf {
position,
ref mut condition,
label: _,
ref label,
} => {
tc_expression(context, condition)?;
if condition.type_ != Some(I32) {
return Err(Error {
position,
message: "Condition has to be i32".into(),
});
return type_mismatch(
I32,
&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
}
ast::Expr::Cast {
position,
ref mut value,
type_,
} => {
tc_expression(context, value)?;
if value.type_.is_none() {
return Err(Error {
position,
message: "Cannot cast void".into(),
});
return expected_type(&expr.span, context.source);
}
Some(type_)
}
ast::Expr::FuncCall {
position,
name,
ref name,
ref mut params,
} => {
if let Some((ptypes, rtype)) = builtin_function_types(name) {
if params.len() != ptypes.len() {
return Err(Error {
position,
message: format!(
Report::build(ReportKind::Error, (), expr.span.start)
.with_message(format!(
"Expected {} parameters but found {}",
ptypes.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)?;
if param.type_.is_none() || param.type_.unwrap() != *ptype {
return Err(Error {
position,
message: format!(
"Param {} is {:?} but should be {:?}",
index + 1,
if param.type_ != Some(*ptype) {
return type_mismatch(
*ptype,
&expr.span,
param.type_,
ptype
),
});
&param.span,
context.source,
);
}
}
rtype
} else {
return Err(Error {
position,
message: format!("Unknown function '{}'", name),
});
Report::build(ReportKind::Error, (), expr.span.start)
.with_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 {
position,
ref mut condition,
ref mut if_true,
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_false)?;
if condition.type_ != Some(ast::Type::I32) {
return Err(Error {
position,
message: "Condition of select has to be of type i32".into(),
});
return type_mismatch(
I32,
&condition.span,
condition.type_,
&condition.span,
context.source,
);
}
if let Some(true_type) = if_true.type_ {
if if_true.type_ != if_false.type_ {
return Err(Error {
position,
message: "Types of select branches differ".into(),
});
return type_mismatch(
true_type,
&if_true.span,
if_false.type_,
&if_false.span,
context.source,
);
}
if if_true.type_.is_none() {
return Err(Error {
position,
message: "Types of select branches cannot be void".into(),
});
} else {
return expected_type(&if_true.span, context.source);
}
if_true.type_
}
ast::Expr::Error => unreachable!(),
};
Ok(())
}
fn tc_mem_location<'a>(
context: &mut Context<'a>,
mem_location: &mut ast::MemoryLocation<'a>,
mem_location: &mut ast::MemoryLocation,
) -> Result<()> {
tc_expression(context, &mut mem_location.left)?;
tc_expression(context, &mut mem_location.right)?;
if mem_location.left.type_ != Some(I32) || mem_location.right.type_ != Some(I32) {
return Err(Error {
position: mem_location.position,
message: "Type mismatch".into(),
});
if mem_location.left.type_ != Some(I32) {
return type_mismatch(
I32,
&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(())
}