mirror of
https://github.com/exoticorn/curlywas.git
synced 2026-01-20 11:46:43 +01:00
finish porting to new parser
This commit is contained in:
166
src/ast.rs
166
src/ast.rs
@@ -1,154 +1,148 @@
|
||||
#[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)]
|
||||
pub enum BinOp {
|
||||
Add,
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
|
||||
158
src/emit.rs
158
src/emit.rs
@@ -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,34 +144,33 @@ 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(value, locals);
|
||||
}
|
||||
}
|
||||
ast::Statement::Expression(e) => collect_locals_expr(e, locals),
|
||||
ast::Statement::Poke {
|
||||
mem_location,
|
||||
value,
|
||||
..
|
||||
} => {
|
||||
collect_locals_expr(&mem_location.left, locals);
|
||||
collect_locals_expr(value, locals);
|
||||
}
|
||||
}
|
||||
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<'a>, locals: &mut Vec<(&'a str, ast::Type)>) {
|
||||
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::Expr::Poke {
|
||||
mem_location,
|
||||
value,
|
||||
..
|
||||
} => {
|
||||
collect_locals_expr(&mem_location.left, locals);
|
||||
collect_locals_expr(value, locals);
|
||||
}
|
||||
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,54 +194,15 @@ 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() {
|
||||
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);
|
||||
} else {
|
||||
emit_expression(ctx, val);
|
||||
ctx.function
|
||||
.instruction(&Instruction::LocalSet(*ctx.locals.get(v.name).unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::Statement::Poke {
|
||||
mem_location,
|
||||
value,
|
||||
..
|
||||
} => {
|
||||
emit_expression(ctx, &mem_location.left);
|
||||
emit_expression(ctx, value);
|
||||
let offset = if let ast::Expr::I32Const(v) = mem_location.right.expr {
|
||||
v as u32 as u64
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
ctx.function.instruction(&match mem_location.size {
|
||||
ast::MemSize::Byte => Instruction::I32Store8(MemArg {
|
||||
align: 0,
|
||||
memory_index: 0,
|
||||
offset,
|
||||
}),
|
||||
ast::MemSize::Word => Instruction::I32Store(MemArg {
|
||||
align: 2,
|
||||
memory_index: 0,
|
||||
offset,
|
||||
}),
|
||||
});
|
||||
}
|
||||
emit_expression(ctx, stmt);
|
||||
if stmt.type_.is_some() {
|
||||
ctx.function.instruction(&Instruction::Drop);
|
||||
}
|
||||
}
|
||||
if let Some(ref expr) = block.final_expression {
|
||||
@@ -252,6 +212,55 @@ fn emit_block<'a>(ctx: &mut FunctionContext<'a>, block: &'a ast::Block) {
|
||||
|
||||
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(name).unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::Expr::Poke {
|
||||
mem_location,
|
||||
value,
|
||||
..
|
||||
} => {
|
||||
emit_expression(ctx, &mem_location.left);
|
||||
emit_expression(ctx, value);
|
||||
let offset = if let ast::Expr::I32Const(v) = mem_location.right.expr {
|
||||
v as u32 as u64
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
ctx.function.instruction(&match mem_location.size {
|
||||
ast::MemSize::Byte => Instruction::I32Store8(MemArg {
|
||||
align: 0,
|
||||
memory_index: 0,
|
||||
offset,
|
||||
}),
|
||||
ast::MemSize::Word => Instruction::I32Store(MemArg {
|
||||
align: 2,
|
||||
memory_index: 0,
|
||||
offset,
|
||||
}),
|
||||
});
|
||||
}
|
||||
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!()
|
||||
};
|
||||
}
|
||||
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!()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
25
src/main.rs
25
src/main.rs
@@ -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);
|
||||
|
||||
|
||||
1156
src/parser.rs
1156
src/parser.rs
File diff suppressed because it is too large
Load Diff
830
src/parser2.rs
830
src/parser2.rs
@@ -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
|
||||
})
|
||||
}
|
||||
523
src/typecheck.rs
523
src/typecheck.rs
@@ -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 {
|
||||
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_) = 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 {
|
||||
return Err(Error {
|
||||
position: lv.position,
|
||||
message: "Missing type".into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
ast::Statement::Poke {
|
||||
position,
|
||||
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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
if tc_expression(context, stmt).is_err() {
|
||||
result = Err(());
|
||||
}
|
||||
}
|
||||
if let Some(ref mut expr) = block.final_expression {
|
||||
tc_expression(context, expr)?;
|
||||
}
|
||||
Ok(())
|
||||
result
|
||||
}
|
||||
|
||||
fn tc_expression<'a>(context: &mut Context<'a>, expr: &mut ast::Expression<'a>) -> 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 let Some(type_) = type_ {
|
||||
if Some(*type_) != value.type_ {
|
||||
return type_mismatch(
|
||||
*type_,
|
||||
&expr.span,
|
||||
value.type_,
|
||||
&value.span,
|
||||
context.source,
|
||||
);
|
||||
}
|
||||
} else if value.type_.is_none() {
|
||||
return expected_type(&value.span, context.source);
|
||||
} else {
|
||||
*type_ = value.type_;
|
||||
}
|
||||
}
|
||||
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 type_mismatch(I32, &expr.span, value.type_, &value.span, context.source);
|
||||
}
|
||||
None
|
||||
}
|
||||
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,
|
||||
param.type_,
|
||||
ptype
|
||||
),
|
||||
});
|
||||
if param.type_ != Some(*ptype) {
|
||||
return type_mismatch(
|
||||
*ptype,
|
||||
&expr.span,
|
||||
param.type_,
|
||||
¶m.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 if_true.type_ != if_false.type_ {
|
||||
return Err(Error {
|
||||
position,
|
||||
message: "Types of select branches differ".into(),
|
||||
});
|
||||
}
|
||||
if if_true.type_.is_none() {
|
||||
return Err(Error {
|
||||
position,
|
||||
message: "Types of select branches cannot be void".into(),
|
||||
});
|
||||
if let Some(true_type) = if_true.type_ {
|
||||
if if_true.type_ != if_false.type_ {
|
||||
return type_mismatch(
|
||||
true_type,
|
||||
&if_true.span,
|
||||
if_false.type_,
|
||||
&if_false.span,
|
||||
context.source,
|
||||
);
|
||||
}
|
||||
} 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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user