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)]
|
use crate::Span;
|
||||||
pub struct Position(pub usize);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Script<'a> {
|
pub struct Script {
|
||||||
pub imports: Vec<Import<'a>>,
|
pub imports: Vec<Import>,
|
||||||
pub global_vars: Vec<GlobalVar<'a>>,
|
pub global_vars: Vec<GlobalVar>,
|
||||||
pub functions: Vec<Function<'a>>,
|
pub functions: Vec<Function>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TopLevelItem<'a> {
|
pub enum TopLevelItem {
|
||||||
Import(Import<'a>),
|
Import(Import),
|
||||||
GlobalVar(GlobalVar<'a>),
|
GlobalVar(GlobalVar),
|
||||||
Function(Function<'a>),
|
Function(Function),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Import<'a> {
|
pub struct Import {
|
||||||
pub position: Position,
|
pub span: Span,
|
||||||
pub import: &'a str,
|
pub import: String,
|
||||||
pub type_: ImportType<'a>,
|
pub type_: ImportType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ImportType<'a> {
|
pub enum ImportType {
|
||||||
Memory(u32),
|
Memory(u32),
|
||||||
Variable {
|
Variable {
|
||||||
name: &'a str,
|
name: String,
|
||||||
type_: Type,
|
type_: Type,
|
||||||
mutable: bool,
|
mutable: bool,
|
||||||
},
|
},
|
||||||
// Function { name: &'a str, params: Vec<Type>, result: Option<Type> }
|
// Function { name: String, params: Vec<Type>, result: Option<Type> }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GlobalVar<'a> {
|
pub struct GlobalVar {
|
||||||
pub position: Position,
|
pub span: Span,
|
||||||
pub name: &'a str,
|
pub name: String,
|
||||||
pub type_: Type,
|
pub type_: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Function<'a> {
|
pub struct Function {
|
||||||
pub position: Position,
|
pub span: Span,
|
||||||
pub export: bool,
|
pub export: bool,
|
||||||
pub name: &'a str,
|
pub name: String,
|
||||||
pub params: Vec<(&'a str, Type)>,
|
pub params: Vec<(String, Type)>,
|
||||||
pub type_: Option<Type>,
|
pub type_: Option<Type>,
|
||||||
pub body: Block<'a>,
|
pub body: Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Block<'a> {
|
pub struct Block {
|
||||||
pub statements: Vec<Statement<'a>>,
|
pub statements: Vec<Expression>,
|
||||||
pub final_expression: Option<Expression<'a>>,
|
pub final_expression: Option<Box<Expression>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Block<'a> {
|
impl Block {
|
||||||
pub fn type_(&self) -> Option<Type> {
|
pub fn type_(&self) -> Option<Type> {
|
||||||
self.final_expression.as_ref().and_then(|e| e.type_)
|
self.final_expression.as_ref().and_then(|e| e.type_)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Statement<'a> {
|
pub struct MemoryLocation {
|
||||||
LocalVariable(LocalVariable<'a>),
|
pub span: Span,
|
||||||
Poke {
|
|
||||||
position: Position,
|
|
||||||
mem_location: MemoryLocation<'a>,
|
|
||||||
value: Expression<'a>,
|
|
||||||
},
|
|
||||||
Expression(Expression<'a>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct MemoryLocation<'a> {
|
|
||||||
pub position: Position,
|
|
||||||
pub size: MemSize,
|
pub size: MemSize,
|
||||||
pub left: Expression<'a>,
|
pub left: Box<Expression>,
|
||||||
pub right: Expression<'a>,
|
pub right: Box<Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LocalVariable<'a> {
|
pub struct Expression {
|
||||||
pub position: Position,
|
|
||||||
pub name: &'a str,
|
|
||||||
pub type_: Option<Type>,
|
pub type_: Option<Type>,
|
||||||
pub value: Option<Expression<'a>>,
|
pub expr: Expr,
|
||||||
pub defer: bool
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Expression<'a> {
|
pub enum Expr {
|
||||||
pub type_: Option<Type>,
|
|
||||||
pub expr: Expr<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Expr<'a>> for Expression<'a> {
|
|
||||||
fn from(expr: Expr<'a>) -> Expression<'a> {
|
|
||||||
Expression { type_: None, expr }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Expr<'a> {
|
|
||||||
I32Const(i32),
|
I32Const(i32),
|
||||||
F32Const(f32),
|
F32Const(f32),
|
||||||
Variable {
|
Variable(String),
|
||||||
position: Position,
|
Let {
|
||||||
name: &'a str,
|
name: String,
|
||||||
|
type_: Option<Type>,
|
||||||
|
value: Option<Box<Expression>>,
|
||||||
|
defer: bool,
|
||||||
|
},
|
||||||
|
Poke {
|
||||||
|
mem_location: MemoryLocation,
|
||||||
|
value: Box<Expression>,
|
||||||
},
|
},
|
||||||
Loop {
|
Loop {
|
||||||
position: Position,
|
label: String,
|
||||||
label: &'a str,
|
block: Box<Block>,
|
||||||
block: Box<Block<'a>>,
|
|
||||||
},
|
},
|
||||||
BranchIf {
|
BranchIf {
|
||||||
position: Position,
|
condition: Box<Expression>,
|
||||||
condition: Box<Expression<'a>>,
|
label: String,
|
||||||
label: &'a str,
|
},
|
||||||
|
UnaryOp {
|
||||||
|
op: UnaryOp,
|
||||||
|
value: Box<Expression>,
|
||||||
},
|
},
|
||||||
BinOp {
|
BinOp {
|
||||||
position: Position,
|
|
||||||
op: BinOp,
|
op: BinOp,
|
||||||
left: Box<Expression<'a>>,
|
left: Box<Expression>,
|
||||||
right: Box<Expression<'a>>,
|
right: Box<Expression>,
|
||||||
},
|
},
|
||||||
LocalTee {
|
LocalTee {
|
||||||
position: Position,
|
name: String,
|
||||||
name: &'a str,
|
value: Box<Expression>,
|
||||||
value: Box<Expression<'a>>,
|
|
||||||
},
|
},
|
||||||
Cast {
|
Cast {
|
||||||
position: Position,
|
value: Box<Expression>,
|
||||||
value: Box<Expression<'a>>,
|
|
||||||
type_: Type,
|
type_: Type,
|
||||||
},
|
},
|
||||||
FuncCall {
|
FuncCall {
|
||||||
position: Position,
|
name: String,
|
||||||
name: &'a str,
|
params: Vec<Expression>,
|
||||||
params: Vec<Expression<'a>>
|
|
||||||
},
|
},
|
||||||
Select {
|
Select {
|
||||||
position: Position,
|
condition: Box<Expression>,
|
||||||
condition: Box<Expression<'a>>,
|
if_true: Box<Expression>,
|
||||||
if_true: Box<Expression<'a>>,
|
if_false: Box<Expression>,
|
||||||
if_false: Box<Expression<'a>>
|
},
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expr {
|
||||||
|
pub fn with_span(self, span: Span) -> Expression {
|
||||||
|
Expression {
|
||||||
|
type_: None,
|
||||||
|
expr: self,
|
||||||
|
span: span,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum UnaryOp {
|
||||||
|
Negate,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum BinOp {
|
pub enum BinOp {
|
||||||
Add,
|
Add,
|
||||||
|
|||||||
@@ -8,22 +8,7 @@ pub fn fold_script(script: &mut ast::Script) {
|
|||||||
|
|
||||||
fn fold_block(block: &mut ast::Block) {
|
fn fold_block(block: &mut ast::Block) {
|
||||||
for stmt in &mut block.statements {
|
for stmt in &mut block.statements {
|
||||||
match stmt {
|
fold_expr(stmt);
|
||||||
ast::Statement::LocalVariable(lv) => {
|
|
||||||
if let Some(ref mut expr) = lv.value {
|
|
||||||
fold_expr(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::Statement::Expression(expr) => fold_expr(expr),
|
|
||||||
ast::Statement::Poke {
|
|
||||||
mem_location,
|
|
||||||
value,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
fold_mem_location(mem_location);
|
|
||||||
fold_expr(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let Some(ref mut expr) = block.final_expression {
|
if let Some(ref mut expr) = block.final_expression {
|
||||||
fold_expr(expr);
|
fold_expr(expr);
|
||||||
@@ -38,6 +23,29 @@ fn fold_mem_location(mem_location: &mut ast::MemoryLocation) {
|
|||||||
fn fold_expr(expr: &mut ast::Expression) {
|
fn fold_expr(expr: &mut ast::Expression) {
|
||||||
use ast::BinOp::*;
|
use ast::BinOp::*;
|
||||||
match expr.expr {
|
match expr.expr {
|
||||||
|
ast::Expr::Let { ref mut value, .. } => {
|
||||||
|
if let Some(ref mut expr) = value {
|
||||||
|
fold_expr(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Expr::Poke {
|
||||||
|
ref mut mem_location,
|
||||||
|
ref mut value,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
fold_mem_location(mem_location);
|
||||||
|
fold_expr(value);
|
||||||
|
}
|
||||||
|
ast::Expr::UnaryOp { op, ref mut value } => {
|
||||||
|
fold_expr(value);
|
||||||
|
let result = match (op, &value.expr) {
|
||||||
|
(ast::UnaryOp::Negate, ast::Expr::I32Const(value)) => Some(ast::Expr::I32Const(-*value)),
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
if let Some(result) = result {
|
||||||
|
expr.expr = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
ast::Expr::BinOp {
|
ast::Expr::BinOp {
|
||||||
ref mut left,
|
ref mut left,
|
||||||
op,
|
op,
|
||||||
@@ -105,7 +113,7 @@ fn fold_expr(expr: &mut ast::Expression) {
|
|||||||
} => fold_expr(condition),
|
} => fold_expr(condition),
|
||||||
ast::Expr::Cast { ref mut value, .. } => fold_expr(value),
|
ast::Expr::Cast { ref mut value, .. } => fold_expr(value),
|
||||||
ast::Expr::FuncCall {
|
ast::Expr::FuncCall {
|
||||||
name,
|
ref name,
|
||||||
ref mut params,
|
ref mut params,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
@@ -114,15 +122,21 @@ fn fold_expr(expr: &mut ast::Expression) {
|
|||||||
}
|
}
|
||||||
use ast::Expr::*;
|
use ast::Expr::*;
|
||||||
let params: Vec<_> = params.iter().map(|e| &e.expr).collect();
|
let params: Vec<_> = params.iter().map(|e| &e.expr).collect();
|
||||||
expr.expr = match (name, params.as_slice()) {
|
expr.expr = match (name.as_str(), params.as_slice()) {
|
||||||
("sqrt", [F32Const(v)]) if *v >= 0.0 => F32Const(v.sqrt()),
|
("sqrt", [F32Const(v)]) if *v >= 0.0 => F32Const(v.sqrt()),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
ast::Expr::Select { ref mut condition, ref mut if_true, ref mut if_false, .. } => {
|
ast::Expr::Select {
|
||||||
|
ref mut condition,
|
||||||
|
ref mut if_true,
|
||||||
|
ref mut if_false,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
fold_expr(condition);
|
fold_expr(condition);
|
||||||
fold_expr(if_true);
|
fold_expr(if_true);
|
||||||
fold_expr(if_false);
|
fold_expr(if_false);
|
||||||
}
|
}
|
||||||
|
ast::Expr::Error => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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)..]),
|
Some(&import.import[(dot_index + 1)..]),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(import.import, None)
|
(import.import.as_str(), None)
|
||||||
};
|
};
|
||||||
let type_: EntityType = match import.type_ {
|
let type_: EntityType = match import.type_ {
|
||||||
ast::ImportType::Memory(min_size) => MemoryType {
|
ast::ImportType::Memory(min_size) => MemoryType {
|
||||||
@@ -46,7 +46,7 @@ pub fn emit(script: &ast::Script) -> Vec<u8> {
|
|||||||
.into(),
|
.into(),
|
||||||
ast::ImportType::Variable {
|
ast::ImportType::Variable {
|
||||||
type_,
|
type_,
|
||||||
name,
|
ref name,
|
||||||
mutable,
|
mutable,
|
||||||
} => {
|
} => {
|
||||||
globals.insert(name, globals.len() as u32);
|
globals.insert(name, globals.len() as u32);
|
||||||
@@ -72,7 +72,7 @@ pub fn emit(script: &ast::Script) -> Vec<u8> {
|
|||||||
let type_ = *function_types.get(&function_type_key(func)).unwrap();
|
let type_ = *function_types.get(&function_type_key(func)).unwrap();
|
||||||
functions.function(type_ as u32);
|
functions.function(type_ as u32);
|
||||||
if func.export {
|
if func.export {
|
||||||
exports.export(func.name, Export::Function(index as u32));
|
exports.export(&func.name, Export::Function(index as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
code.function(&emit_function(func, &globals));
|
code.function(&emit_function(func, &globals));
|
||||||
@@ -109,9 +109,9 @@ fn function_type_key(func: &ast::Function) -> FunctionTypeKey {
|
|||||||
struct FunctionContext<'a> {
|
struct FunctionContext<'a> {
|
||||||
function: &'a mut Function,
|
function: &'a mut Function,
|
||||||
globals: &'a HashMap<&'a str, u32>,
|
globals: &'a HashMap<&'a str, u32>,
|
||||||
locals: &'a HashMap<&'a str, u32>,
|
locals: &'a HashMap<String, u32>,
|
||||||
labels: Vec<String>,
|
labels: Vec<String>,
|
||||||
deferred_inits: HashMap<&'a str, &'a ast::Expression<'a>>,
|
deferred_inits: HashMap<&'a str, &'a ast::Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_function(func: &ast::Function, globals: &HashMap<&str, u32>) -> Function {
|
fn emit_function(func: &ast::Function, globals: &HashMap<&str, u32>) -> Function {
|
||||||
@@ -121,7 +121,7 @@ fn emit_function(func: &ast::Function, globals: &HashMap<&str, u32>) -> Function
|
|||||||
|
|
||||||
let mut function = Function::new_with_locals_types(locals.iter().map(|(_, t)| map_type(*t)));
|
let mut function = Function::new_with_locals_types(locals.iter().map(|(_, t)| map_type(*t)));
|
||||||
|
|
||||||
let locals: HashMap<&str, u32> = locals
|
let locals: HashMap<String, u32> = locals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, (name, _))| (name, index as u32))
|
.map(|(index, (name, _))| (name, index as u32))
|
||||||
@@ -144,34 +144,33 @@ fn emit_function(func: &ast::Function, globals: &HashMap<&str, u32>) -> Function
|
|||||||
function
|
function
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_locals<'a>(block: &ast::Block<'a>, locals: &mut Vec<(&'a str, ast::Type)>) {
|
fn collect_locals<'a>(block: &ast::Block, locals: &mut Vec<(String, ast::Type)>) {
|
||||||
for stmt in &block.statements {
|
for stmt in &block.statements {
|
||||||
match stmt {
|
collect_locals_expr(stmt, locals);
|
||||||
ast::Statement::LocalVariable(v) => {
|
|
||||||
locals.push((v.name, v.type_.unwrap()));
|
|
||||||
if let Some(ref 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let Some(ref expr) = block.final_expression {
|
if let Some(ref expr) = block.final_expression {
|
||||||
collect_locals_expr(expr, locals);
|
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 {
|
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::Variable { .. } | ast::Expr::I32Const(_) | ast::Expr::F32Const(_) => (),
|
||||||
|
ast::Expr::UnaryOp { value, .. } => collect_locals_expr(value, locals),
|
||||||
ast::Expr::BinOp { left, right, .. } => {
|
ast::Expr::BinOp { left, right, .. } => {
|
||||||
collect_locals_expr(left, locals);
|
collect_locals_expr(left, locals);
|
||||||
collect_locals_expr(right, locals);
|
collect_locals_expr(right, locals);
|
||||||
@@ -195,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_true, locals);
|
||||||
collect_locals_expr(if_false, locals);
|
collect_locals_expr(if_false, locals);
|
||||||
}
|
}
|
||||||
|
ast::Expr::Error => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_block<'a>(ctx: &mut FunctionContext<'a>, block: &'a ast::Block) {
|
fn emit_block<'a>(ctx: &mut FunctionContext<'a>, block: &'a ast::Block) {
|
||||||
for stmt in &block.statements {
|
for stmt in &block.statements {
|
||||||
match stmt {
|
emit_expression(ctx, stmt);
|
||||||
ast::Statement::Expression(e) => {
|
if stmt.type_.is_some() {
|
||||||
emit_expression(ctx, e);
|
ctx.function.instruction(&Instruction::Drop);
|
||||||
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,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(ref expr) = block.final_expression {
|
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) {
|
fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression) {
|
||||||
match &expr.expr {
|
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 {
|
ast::Expr::BinOp {
|
||||||
left, op, right, ..
|
left, op, right, ..
|
||||||
} => {
|
} => {
|
||||||
@@ -300,7 +309,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, l)| l == label)
|
.find(|(_, l)| *l == label)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0;
|
.0;
|
||||||
ctx.function.instruction(&Instruction::BrIf(depth as u32));
|
ctx.function.instruction(&Instruction::BrIf(depth as u32));
|
||||||
@@ -313,7 +322,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
}
|
}
|
||||||
ast::Expr::LocalTee { name, value, .. } => {
|
ast::Expr::LocalTee { name, value, .. } => {
|
||||||
emit_expression(ctx, value);
|
emit_expression(ctx, value);
|
||||||
let index = ctx.locals.get(*name).unwrap();
|
let index = ctx.locals.get(name).unwrap();
|
||||||
ctx.function.instruction(&Instruction::LocalTee(*index));
|
ctx.function.instruction(&Instruction::LocalTee(*index));
|
||||||
}
|
}
|
||||||
ast::Expr::Loop { label, block, .. } => {
|
ast::Expr::Loop { label, block, .. } => {
|
||||||
@@ -324,15 +333,15 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
ctx.labels.pop();
|
ctx.labels.pop();
|
||||||
ctx.function.instruction(&Instruction::End);
|
ctx.function.instruction(&Instruction::End);
|
||||||
}
|
}
|
||||||
ast::Expr::Variable { name, .. } => {
|
ast::Expr::Variable(name) => {
|
||||||
if let Some(index) = ctx.locals.get(*name) {
|
if let Some(index) = ctx.locals.get(name) {
|
||||||
if let Some(expr) = ctx.deferred_inits.remove(*name) {
|
if let Some(expr) = ctx.deferred_inits.remove(name.as_str()) {
|
||||||
emit_expression(ctx, expr);
|
emit_expression(ctx, expr);
|
||||||
ctx.function.instruction(&Instruction::LocalTee(*index));
|
ctx.function.instruction(&Instruction::LocalTee(*index));
|
||||||
} else {
|
} else {
|
||||||
ctx.function.instruction(&Instruction::LocalGet(*index));
|
ctx.function.instruction(&Instruction::LocalGet(*index));
|
||||||
}
|
}
|
||||||
} else if let Some(index) = ctx.globals.get(*name) {
|
} else if let Some(index) = ctx.globals.get(name.as_str()) {
|
||||||
ctx.function.instruction(&Instruction::GlobalGet(*index));
|
ctx.function.instruction(&Instruction::GlobalGet(*index));
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
@@ -371,6 +380,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
emit_expression(ctx, condition);
|
emit_expression(ctx, condition);
|
||||||
ctx.function.instruction(&Instruction::Select);
|
ctx.function.instruction(&Instruction::Select);
|
||||||
}
|
}
|
||||||
|
ast::Expr::Error => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
25
src/main.rs
25
src/main.rs
@@ -6,9 +6,10 @@ mod ast;
|
|||||||
mod constfold;
|
mod constfold;
|
||||||
mod emit;
|
mod emit;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod parser2;
|
|
||||||
mod typecheck;
|
mod typecheck;
|
||||||
|
|
||||||
|
type Span = std::ops::Range<usize>;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let mut filename = PathBuf::from(
|
let mut filename = PathBuf::from(
|
||||||
std::env::args()
|
std::env::args()
|
||||||
@@ -18,28 +19,14 @@ fn main() -> Result<()> {
|
|||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
File::open(&filename)?.read_to_string(&mut input)?;
|
File::open(&filename)?.read_to_string(&mut input)?;
|
||||||
|
|
||||||
if let Err(_) = parser2::parse(&input) {
|
let mut script = match parser::parse(&input) {
|
||||||
bail!("Parse failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut script = match parser::parse(input.as_str()) {
|
|
||||||
Ok(script) => script,
|
Ok(script) => script,
|
||||||
Err(err) => {
|
Err(_) => bail!("Parse failed")
|
||||||
bail!(
|
|
||||||
"parse error: {}",
|
|
||||||
nom::error::convert_error(input.as_str(), err)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constfold::fold_script(&mut script);
|
constfold::fold_script(&mut script);
|
||||||
if let Err(err) = typecheck::tc_script(&mut script) {
|
if let Err(_) = typecheck::tc_script(&mut script, &input) {
|
||||||
let line = input[..(input.len() - err.position.0)]
|
bail!("Parse failed");
|
||||||
.chars()
|
|
||||||
.filter(|c| *c == '\n')
|
|
||||||
.count()
|
|
||||||
+ 1;
|
|
||||||
bail!("{} in line {}", err.message, line);
|
|
||||||
}
|
}
|
||||||
let wasm = emit::emit(&script);
|
let wasm = emit::emit(&script);
|
||||||
|
|
||||||
|
|||||||
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 std::collections::HashMap;
|
||||||
|
|
||||||
use crate::ast;
|
use crate::ast;
|
||||||
|
use crate::Span;
|
||||||
use ast::Type::*;
|
use ast::Type::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
type Result<T> = std::result::Result<T, ()>;
|
||||||
pub struct Error {
|
|
||||||
pub position: ast::Position,
|
struct Var {
|
||||||
pub message: String,
|
span: Span,
|
||||||
|
type_: ast::Type,
|
||||||
}
|
}
|
||||||
|
type Vars = HashMap<String, Var>;
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
|
||||||
|
|
||||||
type Vars<'a> = HashMap<&'a str, ast::Type>;
|
|
||||||
|
|
||||||
pub fn tc_script(script: &mut ast::Script) -> Result<()> {
|
|
||||||
let mut context = Context {
|
let mut context = Context {
|
||||||
|
source,
|
||||||
global_vars: HashMap::new(),
|
global_vars: HashMap::new(),
|
||||||
local_vars: HashMap::new(),
|
local_vars: HashMap::new(),
|
||||||
|
block_stack: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut result = Ok(());
|
||||||
|
|
||||||
for import in &script.imports {
|
for import in &script.imports {
|
||||||
match import.type_ {
|
match import.type_ {
|
||||||
ast::ImportType::Variable { name, type_, .. } => {
|
ast::ImportType::Variable {
|
||||||
if context.global_vars.contains_key(name) {
|
ref name, type_, ..
|
||||||
return Err(Error {
|
} => {
|
||||||
position: import.position,
|
if let Some(Var { span, .. }) = context.global_vars.get(name) {
|
||||||
message: "Duplicate global variable".into(),
|
result = report_duplicate_definition(
|
||||||
});
|
"Global already defined",
|
||||||
|
&import.span,
|
||||||
|
span,
|
||||||
|
source,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
context.global_vars.insert(
|
||||||
|
name.clone(),
|
||||||
|
Var {
|
||||||
|
type_,
|
||||||
|
span: import.span.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
context.global_vars.insert(name, type_);
|
|
||||||
}
|
}
|
||||||
// ast::ImportType::Function { .. } => todo!(),
|
// ast::ImportType::Function { .. } => todo!(),
|
||||||
ast::ImportType::Memory(..) => (),
|
ast::ImportType::Memory(..) => (),
|
||||||
@@ -36,120 +51,261 @@ pub fn tc_script(script: &mut ast::Script) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for v in &script.global_vars {
|
for v in &script.global_vars {
|
||||||
if context.global_vars.contains_key(v.name) {
|
if let Some(Var { span, .. }) = context.global_vars.get(&v.name) {
|
||||||
return Err(Error {
|
result = report_duplicate_definition("Global already defined", &v.span, span, source);
|
||||||
position: v.position,
|
} else {
|
||||||
message: "Duplicate global variable".into(),
|
context.global_vars.insert(
|
||||||
});
|
v.name.clone(),
|
||||||
|
Var {
|
||||||
|
type_: v.type_,
|
||||||
|
span: v.span.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
context.global_vars.insert(v.name, v.type_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for f in &mut script.functions {
|
for f in &mut script.functions {
|
||||||
context.local_vars.clear();
|
context.local_vars.clear();
|
||||||
for (name, type_) in &f.params {
|
for (name, type_) in &f.params {
|
||||||
if context.local_vars.contains_key(name) || context.global_vars.contains_key(name) {
|
if let Some(Var { span, .. }) = context
|
||||||
return Err(Error {
|
.local_vars
|
||||||
position: f.position,
|
.get(name)
|
||||||
message: format!("Variable already defined '{}'", name),
|
.or_else(|| context.global_vars.get(name))
|
||||||
});
|
{
|
||||||
|
result =
|
||||||
|
report_duplicate_definition("Variable already defined", &f.span, span, source);
|
||||||
|
} else {
|
||||||
|
context.local_vars.insert(
|
||||||
|
name.clone(),
|
||||||
|
Var {
|
||||||
|
type_: *type_,
|
||||||
|
span: f.span.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
context.local_vars.insert(name, *type_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tc_block(&mut context, &mut f.body)?;
|
tc_block(&mut context, &mut f.body)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Context<'a> {
|
struct Context<'a> {
|
||||||
global_vars: Vars<'a>,
|
source: &'a str,
|
||||||
local_vars: Vars<'a>,
|
global_vars: Vars,
|
||||||
|
local_vars: Vars,
|
||||||
|
block_stack: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tc_block<'a>(context: &mut Context<'a>, block: &mut ast::Block<'a>) -> Result<()> {
|
fn tc_block(context: &mut Context, block: &mut ast::Block) -> Result<()> {
|
||||||
|
let mut result = Ok(());
|
||||||
for stmt in &mut block.statements {
|
for stmt in &mut block.statements {
|
||||||
match *stmt {
|
if tc_expression(context, stmt).is_err() {
|
||||||
ast::Statement::Expression(ref mut expr) => tc_expression(context, expr)?,
|
result = Err(());
|
||||||
ast::Statement::LocalVariable(ref mut lv) => {
|
|
||||||
if let Some(ref mut value) = lv.value {
|
|
||||||
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 let Some(ref mut expr) = block.final_expression {
|
if let Some(ref mut expr) = block.final_expression {
|
||||||
tc_expression(context, expr)?;
|
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 {
|
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::I32Const(_) => Some(ast::Type::I32),
|
||||||
ast::Expr::F32Const(_) => Some(ast::Type::F32),
|
ast::Expr::F32Const(_) => Some(ast::Type::F32),
|
||||||
|
ast::Expr::UnaryOp {
|
||||||
|
op: _,
|
||||||
|
ref mut value,
|
||||||
|
} => {
|
||||||
|
tc_expression(context, value)?;
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
ast::Expr::BinOp {
|
ast::Expr::BinOp {
|
||||||
position,
|
|
||||||
op,
|
op,
|
||||||
ref mut left,
|
ref mut left,
|
||||||
ref mut right,
|
ref mut right,
|
||||||
} => {
|
} => {
|
||||||
tc_expression(context, left)?;
|
tc_expression(context, left)?;
|
||||||
tc_expression(context, right)?;
|
tc_expression(context, right)?;
|
||||||
if left.type_.is_none() || left.type_ != right.type_ {
|
if let Some(type_) = left.type_ {
|
||||||
return Err(Error {
|
if left.type_ != right.type_ {
|
||||||
position,
|
return type_mismatch(
|
||||||
message: "Type mismatch".into(),
|
type_,
|
||||||
});
|
&left.span,
|
||||||
|
right.type_,
|
||||||
|
&right.span,
|
||||||
|
context.source,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return expected_type(&left.span, context.source);
|
||||||
}
|
}
|
||||||
use ast::BinOp::*;
|
use ast::BinOp::*;
|
||||||
match op {
|
match op {
|
||||||
Add | Sub | Mul | Div => left.type_,
|
Add | Sub | Mul | Div => left.type_,
|
||||||
Rem | And | Or | Xor => {
|
Rem | And | Or | Xor => {
|
||||||
if left.type_ != Some(I32) {
|
if left.type_ != Some(I32) {
|
||||||
return Err(Error {
|
return type_mismatch(
|
||||||
position,
|
I32,
|
||||||
message: "Unsupported type".into(),
|
&left.span,
|
||||||
});
|
left.type_,
|
||||||
|
&left.span,
|
||||||
|
context.source,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
left.type_
|
left.type_
|
||||||
}
|
}
|
||||||
@@ -157,117 +313,136 @@ fn tc_expression<'a>(context: &mut Context<'a>, expr: &mut ast::Expression<'a>)
|
|||||||
Eq | Ne | Lt | Le | Gt | Ge => Some(I32),
|
Eq | Ne | Lt | Le | Gt | Ge => Some(I32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::Variable { position, name } => {
|
ast::Expr::Variable(ref name) => {
|
||||||
if let Some(&type_) = context
|
if let Some(&Var { type_, .. }) = context
|
||||||
.global_vars
|
.global_vars
|
||||||
.get(name)
|
.get(name)
|
||||||
.or_else(|| context.local_vars.get(name))
|
.or_else(|| context.local_vars.get(name))
|
||||||
{
|
{
|
||||||
Some(type_)
|
Some(type_)
|
||||||
} else {
|
} else {
|
||||||
return Err(Error {
|
return unknown_variable(&expr.span, context.source);
|
||||||
position,
|
|
||||||
message: format!("Variable '{}' not found", name),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::LocalTee {
|
ast::Expr::LocalTee {
|
||||||
position,
|
ref name,
|
||||||
name,
|
|
||||||
ref mut value,
|
ref mut value,
|
||||||
} => {
|
} => {
|
||||||
tc_expression(context, value)?;
|
tc_expression(context, value)?;
|
||||||
if let Some(&type_) = context.local_vars.get(name) {
|
if let Some(&Var {
|
||||||
|
type_, ref span, ..
|
||||||
|
}) = context.local_vars.get(name)
|
||||||
|
{
|
||||||
if value.type_ != Some(type_) {
|
if value.type_ != Some(type_) {
|
||||||
return Err(Error {
|
return type_mismatch(type_, span, value.type_, &value.span, context.source);
|
||||||
position,
|
|
||||||
message: "Type mismatch".into(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Some(type_)
|
Some(type_)
|
||||||
} else {
|
} else {
|
||||||
return Err(Error {
|
return unknown_variable(&expr.span, context.source);
|
||||||
position,
|
|
||||||
message: format!("No local variable '{}' found", name),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::Loop {
|
ast::Expr::Loop {
|
||||||
position: _,
|
ref label,
|
||||||
label: _,
|
|
||||||
ref mut block,
|
ref mut block,
|
||||||
} => {
|
} => {
|
||||||
|
context.block_stack.push(label.clone());
|
||||||
tc_block(context, block)?;
|
tc_block(context, block)?;
|
||||||
|
context.block_stack.pop();
|
||||||
block.final_expression.as_ref().and_then(|e| e.type_)
|
block.final_expression.as_ref().and_then(|e| e.type_)
|
||||||
}
|
}
|
||||||
ast::Expr::BranchIf {
|
ast::Expr::BranchIf {
|
||||||
position,
|
|
||||||
ref mut condition,
|
ref mut condition,
|
||||||
label: _,
|
ref label,
|
||||||
} => {
|
} => {
|
||||||
tc_expression(context, condition)?;
|
tc_expression(context, condition)?;
|
||||||
if condition.type_ != Some(I32) {
|
if condition.type_ != Some(I32) {
|
||||||
return Err(Error {
|
return type_mismatch(
|
||||||
position,
|
I32,
|
||||||
message: "Condition has to be i32".into(),
|
&expr.span,
|
||||||
});
|
condition.type_,
|
||||||
|
&condition.span,
|
||||||
|
context.source,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !context.block_stack.contains(label) {
|
||||||
|
Report::build(ReportKind::Error, (), expr.span.start)
|
||||||
|
.with_message("Label not found")
|
||||||
|
.with_label(
|
||||||
|
Label::new(expr.span.clone())
|
||||||
|
.with_message("Label not found")
|
||||||
|
.with_color(Color::Red),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
.eprint(Source::from(context.source))
|
||||||
|
.unwrap();
|
||||||
|
return Err(());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ast::Expr::Cast {
|
ast::Expr::Cast {
|
||||||
position,
|
|
||||||
ref mut value,
|
ref mut value,
|
||||||
type_,
|
type_,
|
||||||
} => {
|
} => {
|
||||||
tc_expression(context, value)?;
|
tc_expression(context, value)?;
|
||||||
if value.type_.is_none() {
|
if value.type_.is_none() {
|
||||||
return Err(Error {
|
return expected_type(&expr.span, context.source);
|
||||||
position,
|
|
||||||
message: "Cannot cast void".into(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Some(type_)
|
Some(type_)
|
||||||
}
|
}
|
||||||
ast::Expr::FuncCall {
|
ast::Expr::FuncCall {
|
||||||
position,
|
ref name,
|
||||||
name,
|
|
||||||
ref mut params,
|
ref mut params,
|
||||||
} => {
|
} => {
|
||||||
if let Some((ptypes, rtype)) = builtin_function_types(name) {
|
if let Some((ptypes, rtype)) = builtin_function_types(name) {
|
||||||
if params.len() != ptypes.len() {
|
if params.len() != ptypes.len() {
|
||||||
return Err(Error {
|
Report::build(ReportKind::Error, (), expr.span.start)
|
||||||
position,
|
.with_message(format!(
|
||||||
message: format!(
|
|
||||||
"Expected {} parameters but found {}",
|
"Expected {} parameters but found {}",
|
||||||
ptypes.len(),
|
ptypes.len(),
|
||||||
params.len()
|
params.len()
|
||||||
),
|
))
|
||||||
});
|
.with_label(
|
||||||
|
Label::new(expr.span.clone())
|
||||||
|
.with_message(format!(
|
||||||
|
"Expected {} parameters but found {}",
|
||||||
|
ptypes.len(),
|
||||||
|
params.len()
|
||||||
|
))
|
||||||
|
.with_color(Color::Red),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
.eprint(Source::from(context.source))
|
||||||
|
.unwrap();
|
||||||
|
return Err(());
|
||||||
}
|
}
|
||||||
for (index, (ptype, param)) in ptypes.iter().zip(params.iter_mut()).enumerate() {
|
for (ptype, param) in ptypes.iter().zip(params.iter_mut()) {
|
||||||
tc_expression(context, param)?;
|
tc_expression(context, param)?;
|
||||||
if param.type_.is_none() || param.type_.unwrap() != *ptype {
|
if param.type_ != Some(*ptype) {
|
||||||
return Err(Error {
|
return type_mismatch(
|
||||||
position,
|
*ptype,
|
||||||
message: format!(
|
&expr.span,
|
||||||
"Param {} is {:?} but should be {:?}",
|
param.type_,
|
||||||
index + 1,
|
¶m.span,
|
||||||
param.type_,
|
context.source,
|
||||||
ptype
|
);
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rtype
|
rtype
|
||||||
} else {
|
} else {
|
||||||
return Err(Error {
|
Report::build(ReportKind::Error, (), expr.span.start)
|
||||||
position,
|
.with_message(format!("Unknown function {}", name))
|
||||||
message: format!("Unknown function '{}'", name),
|
.with_label(
|
||||||
});
|
Label::new(expr.span.clone())
|
||||||
|
.with_message(format!("Unknown function {}", name))
|
||||||
|
.with_color(Color::Red),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
.eprint(Source::from(context.source))
|
||||||
|
.unwrap();
|
||||||
|
return Err(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::Select {
|
ast::Expr::Select {
|
||||||
position,
|
|
||||||
ref mut condition,
|
ref mut condition,
|
||||||
ref mut if_true,
|
ref mut if_true,
|
||||||
ref mut if_false,
|
ref mut if_false,
|
||||||
@@ -276,40 +451,62 @@ fn tc_expression<'a>(context: &mut Context<'a>, expr: &mut ast::Expression<'a>)
|
|||||||
tc_expression(context, if_true)?;
|
tc_expression(context, if_true)?;
|
||||||
tc_expression(context, if_false)?;
|
tc_expression(context, if_false)?;
|
||||||
if condition.type_ != Some(ast::Type::I32) {
|
if condition.type_ != Some(ast::Type::I32) {
|
||||||
return Err(Error {
|
return type_mismatch(
|
||||||
position,
|
I32,
|
||||||
message: "Condition of select has to be of type i32".into(),
|
&condition.span,
|
||||||
});
|
condition.type_,
|
||||||
|
&condition.span,
|
||||||
|
context.source,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if if_true.type_ != if_false.type_ {
|
if let Some(true_type) = if_true.type_ {
|
||||||
return Err(Error {
|
if if_true.type_ != if_false.type_ {
|
||||||
position,
|
return type_mismatch(
|
||||||
message: "Types of select branches differ".into(),
|
true_type,
|
||||||
});
|
&if_true.span,
|
||||||
}
|
if_false.type_,
|
||||||
if if_true.type_.is_none() {
|
&if_false.span,
|
||||||
return Err(Error {
|
context.source,
|
||||||
position,
|
);
|
||||||
message: "Types of select branches cannot be void".into(),
|
}
|
||||||
});
|
} else {
|
||||||
|
return expected_type(&if_true.span, context.source);
|
||||||
}
|
}
|
||||||
if_true.type_
|
if_true.type_
|
||||||
}
|
}
|
||||||
|
ast::Expr::Error => unreachable!(),
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tc_mem_location<'a>(
|
fn tc_mem_location<'a>(
|
||||||
context: &mut Context<'a>,
|
context: &mut Context<'a>,
|
||||||
mem_location: &mut ast::MemoryLocation<'a>,
|
mem_location: &mut ast::MemoryLocation,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
tc_expression(context, &mut mem_location.left)?;
|
tc_expression(context, &mut mem_location.left)?;
|
||||||
tc_expression(context, &mut mem_location.right)?;
|
tc_expression(context, &mut mem_location.right)?;
|
||||||
if mem_location.left.type_ != Some(I32) || mem_location.right.type_ != Some(I32) {
|
if mem_location.left.type_ != Some(I32) {
|
||||||
return Err(Error {
|
return type_mismatch(
|
||||||
position: mem_location.position,
|
I32,
|
||||||
message: "Type mismatch".into(),
|
&mem_location.left.span,
|
||||||
});
|
mem_location.left.type_,
|
||||||
|
&mem_location.left.span,
|
||||||
|
context.source,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let ast::Expr::I32Const(_) = mem_location.right.expr {
|
||||||
|
} else {
|
||||||
|
Report::build(ReportKind::Error, (), mem_location.right.span.start)
|
||||||
|
.with_message("Expected I32 constant")
|
||||||
|
.with_label(
|
||||||
|
Label::new(mem_location.right.span.clone())
|
||||||
|
.with_message("Expected I32 constant")
|
||||||
|
.with_color(Color::Red),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
.eprint(Source::from(context.source))
|
||||||
|
.unwrap();
|
||||||
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user