mirror of
https://github.com/exoticorn/curlywas.git
synced 2026-01-20 11:46:43 +01:00
add global vars + i64 & f64 support
This commit is contained in:
18
random.cwa
18
random.cwa
@@ -1,22 +1,24 @@
|
|||||||
import "env.memory" memory(4);
|
import "env.memory" memory(4);
|
||||||
|
|
||||||
|
global mut randomState: i32 = 37;
|
||||||
|
|
||||||
fn random() -> i32 {
|
fn random() -> i32 {
|
||||||
let seed = !0;
|
let state: i32;
|
||||||
seed = seed ^ (seed << 13);
|
randomState = (state := (
|
||||||
seed = seed ^ (seed >> 17);
|
state := randomState ^ (randomState << 13)
|
||||||
seed = seed ^ (seed << 5);
|
) ^ (state >> 17)
|
||||||
!0 = seed;
|
) ^ (state << 5);
|
||||||
seed * 625341585
|
randomState * 625341585
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seed(s: i32) {
|
fn seed(s: i32) {
|
||||||
!0 = (s + (s >>> 31)) * 2060714111;
|
randomState = (((s + !(s >> 31)) as i64 * 8445297036689579347i64) >> 31i64) as i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn tic(time: i32) {
|
export fn tic(time: i32) {
|
||||||
let i: i32;
|
let i: i32;
|
||||||
loop pixels {
|
loop pixels {
|
||||||
seed(i + time / 10);
|
seed(i + (time / 100) * 320);
|
||||||
i?120 = random();
|
i?120 = random();
|
||||||
branch_if (i := i + 1) < 320*256: pixels
|
branch_if (i := i + 1) < 320*256: pixels
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ pub enum ImportType {
|
|||||||
pub struct GlobalVar {
|
pub struct GlobalVar {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub type_: Type,
|
pub value: Expression,
|
||||||
|
pub type_: Option<Type>,
|
||||||
|
pub mutable: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -71,7 +73,9 @@ pub enum Expr {
|
|||||||
final_expression: Option<Box<Expression>>,
|
final_expression: Option<Box<Expression>>,
|
||||||
},
|
},
|
||||||
I32Const(i32),
|
I32Const(i32),
|
||||||
|
I64Const(i64),
|
||||||
F32Const(f32),
|
F32Const(f32),
|
||||||
|
F64Const(f64),
|
||||||
Variable(String),
|
Variable(String),
|
||||||
Let {
|
Let {
|
||||||
name: String,
|
name: String,
|
||||||
@@ -147,6 +151,7 @@ impl Expr {
|
|||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum UnaryOp {
|
pub enum UnaryOp {
|
||||||
Negate,
|
Negate,
|
||||||
|
Not
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
use crate::ast;
|
use crate::ast;
|
||||||
|
|
||||||
pub fn fold_script(script: &mut ast::Script) {
|
pub fn fold_script(script: &mut ast::Script) {
|
||||||
|
for var in &mut script.global_vars {
|
||||||
|
fold_expr(&mut var.value);
|
||||||
|
}
|
||||||
|
|
||||||
for func in &mut script.functions {
|
for func in &mut script.functions {
|
||||||
fold_expr(&mut func.body);
|
fold_expr(&mut func.body);
|
||||||
}
|
}
|
||||||
@@ -40,6 +44,11 @@ fn fold_expr(expr: &mut ast::Expression) {
|
|||||||
fold_expr(value);
|
fold_expr(value);
|
||||||
let result = match (op, &value.expr) {
|
let result = match (op, &value.expr) {
|
||||||
(ast::UnaryOp::Negate, ast::Expr::I32Const(value)) => Some(ast::Expr::I32Const(-*value)),
|
(ast::UnaryOp::Negate, ast::Expr::I32Const(value)) => Some(ast::Expr::I32Const(-*value)),
|
||||||
|
(ast::UnaryOp::Negate, ast::Expr::I64Const(value)) => Some(ast::Expr::I64Const(-*value)),
|
||||||
|
(ast::UnaryOp::Negate, ast::Expr::F32Const(value)) => Some(ast::Expr::F32Const(-*value)),
|
||||||
|
(ast::UnaryOp::Negate, ast::Expr::F64Const(value)) => Some(ast::Expr::F64Const(-*value)),
|
||||||
|
(ast::UnaryOp::Not, ast::Expr::I32Const(value)) => Some(ast::Expr::I32Const((*value == 0) as i32)),
|
||||||
|
(ast::UnaryOp::Not, ast::Expr::I64Const(value)) => Some(ast::Expr::I32Const((*value == 0) as i32)),
|
||||||
_ => None
|
_ => None
|
||||||
};
|
};
|
||||||
if let Some(result) = result {
|
if let Some(result) = result {
|
||||||
@@ -89,6 +98,40 @@ fn fold_expr(expr: &mut ast::Expression) {
|
|||||||
};
|
};
|
||||||
expr.expr = ast::Expr::I32Const(result);
|
expr.expr = ast::Expr::I32Const(result);
|
||||||
}
|
}
|
||||||
|
(&ast::Expr::I64Const(left), &ast::Expr::I64Const(right)) => {
|
||||||
|
use ast::Expr::*;
|
||||||
|
expr.expr = match op {
|
||||||
|
Add => I64Const(left.wrapping_add(right)),
|
||||||
|
Sub => I64Const(left.wrapping_sub(right)),
|
||||||
|
Mul => I64Const(left.wrapping_mul(right)),
|
||||||
|
Div => {
|
||||||
|
if let Some(result) = left.checked_div(right) {
|
||||||
|
I64Const(result)
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rem => {
|
||||||
|
if let Some(result) = left.checked_rem(right) {
|
||||||
|
I64Const(result)
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
And => I64Const(left & right),
|
||||||
|
Or => I64Const(left | right),
|
||||||
|
Xor => I64Const(left ^ right),
|
||||||
|
Eq => I32Const((left == right) as i32),
|
||||||
|
Ne => I32Const((left != right) as i32),
|
||||||
|
Lt => I32Const((left < right) as i32),
|
||||||
|
Le => I32Const((left <= right) as i32),
|
||||||
|
Gt => I32Const((left > right) as i32),
|
||||||
|
Ge => I32Const((left >= right) as i32),
|
||||||
|
Lsl => I64Const(left << right),
|
||||||
|
Lsr => I64Const(((left as u64) >> right) as i64),
|
||||||
|
Asr => I64Const(left >> right)
|
||||||
|
};
|
||||||
|
}
|
||||||
(&ast::Expr::F32Const(left), &ast::Expr::F32Const(right)) => {
|
(&ast::Expr::F32Const(left), &ast::Expr::F32Const(right)) => {
|
||||||
use ast::Expr::*;
|
use ast::Expr::*;
|
||||||
expr.expr = match op {
|
expr.expr = match op {
|
||||||
@@ -105,10 +148,26 @@ fn fold_expr(expr: &mut ast::Expression) {
|
|||||||
Ge => I32Const((left >= right) as i32),
|
Ge => I32Const((left >= right) as i32),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
(&ast::Expr::F64Const(left), &ast::Expr::F64Const(right)) => {
|
||||||
|
use ast::Expr::*;
|
||||||
|
expr.expr = match op {
|
||||||
|
Add => F64Const(left + right),
|
||||||
|
Sub => F64Const(left - right),
|
||||||
|
Mul => F64Const(left * right),
|
||||||
|
Div => F64Const(left / right),
|
||||||
|
Rem | And | Or | Xor | Lsl | Lsr | Asr => return,
|
||||||
|
Eq => I32Const((left == right) as i32),
|
||||||
|
Ne => I32Const((left != right) as i32),
|
||||||
|
Lt => I32Const((left < right) as i32),
|
||||||
|
Le => I32Const((left <= right) as i32),
|
||||||
|
Gt => I32Const((left > right) as i32),
|
||||||
|
Ge => I32Const((left >= right) as i32),
|
||||||
|
};
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::I32Const(_) | ast::Expr::F32Const(_) | ast::Expr::Variable { .. } => (),
|
ast::Expr::I32Const(_) | ast::Expr::I64Const(_) | ast::Expr::F32Const(_) | ast::Expr::F64Const(_) | ast::Expr::Variable { .. } => (),
|
||||||
ast::Expr::Assign { ref mut value, .. } => fold_expr(value),
|
ast::Expr::Assign { ref mut value, .. } => fold_expr(value),
|
||||||
ast::Expr::LocalTee { ref mut value, .. } => fold_expr(value),
|
ast::Expr::LocalTee { ref mut value, .. } => fold_expr(value),
|
||||||
ast::Expr::Loop { ref mut block, .. } => fold_expr(block),
|
ast::Expr::Loop { ref mut block, .. } => fold_expr(block),
|
||||||
|
|||||||
110
src/emit.rs
110
src/emit.rs
@@ -2,7 +2,8 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use wasm_encoder::{
|
use wasm_encoder::{
|
||||||
BlockType, CodeSection, EntityType, Export, ExportSection, Function, FunctionSection,
|
BlockType, CodeSection, EntityType, Export, ExportSection, Function, FunctionSection,
|
||||||
GlobalType, ImportSection, Instruction, MemArg, MemoryType, Module, TypeSection, ValType,
|
GlobalSection, GlobalType, ImportSection, Instruction, MemArg, MemoryType, Module, TypeSection,
|
||||||
|
ValType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::ast;
|
use crate::ast;
|
||||||
@@ -76,6 +77,18 @@ pub fn emit(script: &ast::Script) -> Vec<u8> {
|
|||||||
module.section(&imports);
|
module.section(&imports);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut global_section = GlobalSection::new();
|
||||||
|
for var in &script.global_vars {
|
||||||
|
global_section.global(
|
||||||
|
GlobalType {
|
||||||
|
val_type: map_type(var.type_.unwrap()),
|
||||||
|
mutable: var.mutable,
|
||||||
|
},
|
||||||
|
&const_instr(&var.value),
|
||||||
|
);
|
||||||
|
globals.insert(&var.name, globals.len() as u32);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut functions = FunctionSection::new();
|
let mut functions = FunctionSection::new();
|
||||||
let mut exports = ExportSection::new();
|
let mut exports = ExportSection::new();
|
||||||
@@ -89,13 +102,17 @@ 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(*function_map.get(&func.name).unwrap() as u32));
|
exports.export(
|
||||||
|
&func.name,
|
||||||
|
Export::Function(*function_map.get(&func.name).unwrap() as u32),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.function(&emit_function(func, &globals, &function_map));
|
code.function(&emit_function(func, &globals, &function_map));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.section(&functions);
|
module.section(&functions);
|
||||||
|
module.section(&global_section);
|
||||||
module.section(&exports);
|
module.section(&exports);
|
||||||
module.section(&code);
|
module.section(&code);
|
||||||
}
|
}
|
||||||
@@ -135,6 +152,14 @@ fn function_type_key(func: &ast::Function) -> FunctionTypeKey {
|
|||||||
(param_types, func.type_)
|
(param_types, func.type_)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn const_instr(expr: &ast::Expression) -> Instruction {
|
||||||
|
match expr.expr {
|
||||||
|
ast::Expr::I32Const(v) => Instruction::I32Const(v),
|
||||||
|
ast::Expr::F32Const(v) => Instruction::F32Const(v),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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>,
|
||||||
@@ -213,7 +238,11 @@ fn collect_locals_expr<'a>(expr: &ast::Expression, locals: &mut Vec<(String, ast
|
|||||||
collect_locals_expr(&mem_location.left, locals);
|
collect_locals_expr(&mem_location.left, locals);
|
||||||
collect_locals_expr(value, locals);
|
collect_locals_expr(value, locals);
|
||||||
}
|
}
|
||||||
ast::Expr::Variable { .. } | ast::Expr::I32Const(_) | ast::Expr::F32Const(_) => (),
|
ast::Expr::Variable { .. }
|
||||||
|
| ast::Expr::I32Const(_)
|
||||||
|
| ast::Expr::I64Const(_)
|
||||||
|
| ast::Expr::F32Const(_)
|
||||||
|
| ast::Expr::F64Const(_) => (),
|
||||||
ast::Expr::UnaryOp { value, .. } => collect_locals_expr(value, locals),
|
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);
|
||||||
@@ -331,12 +360,32 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
use ast::UnaryOp::*;
|
use ast::UnaryOp::*;
|
||||||
match (value.type_.unwrap(), op) {
|
match (value.type_.unwrap(), op) {
|
||||||
(I32, Negate) => {
|
(I32, Negate) => {
|
||||||
// TODO: try to improve this uglyness
|
|
||||||
ctx.function.instruction(&Instruction::I32Const(0));
|
ctx.function.instruction(&Instruction::I32Const(0));
|
||||||
emit_expression(ctx, value);
|
emit_expression(ctx, value);
|
||||||
ctx.function.instruction(&Instruction::I32Sub);
|
ctx.function.instruction(&Instruction::I32Sub);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
(I64, Negate) => {
|
||||||
|
ctx.function.instruction(&Instruction::I64Const(0));
|
||||||
|
emit_expression(ctx, value);
|
||||||
|
ctx.function.instruction(&Instruction::I64Sub);
|
||||||
|
}
|
||||||
|
(F32, Negate) => {
|
||||||
|
emit_expression(ctx, value);
|
||||||
|
ctx.function.instruction(&Instruction::F32Neg);
|
||||||
|
}
|
||||||
|
(F64, Negate) => {
|
||||||
|
emit_expression(ctx, value);
|
||||||
|
ctx.function.instruction(&Instruction::F64Neg);
|
||||||
|
}
|
||||||
|
(I32, Not) => {
|
||||||
|
emit_expression(ctx, value);
|
||||||
|
ctx.function.instruction(&Instruction::I32Eqz);
|
||||||
|
}
|
||||||
|
(I64, Not) => {
|
||||||
|
emit_expression(ctx, value);
|
||||||
|
ctx.function.instruction(&Instruction::I64Eqz);
|
||||||
|
}
|
||||||
|
(_, Not) => unreachable!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
ast::Expr::BinOp {
|
ast::Expr::BinOp {
|
||||||
@@ -365,6 +414,24 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
(I32, Lsr) => Instruction::I32ShrU,
|
(I32, Lsr) => Instruction::I32ShrU,
|
||||||
(I32, Asr) => Instruction::I32ShrS,
|
(I32, Asr) => Instruction::I32ShrS,
|
||||||
|
|
||||||
|
(I64, Add) => Instruction::I64Add,
|
||||||
|
(I64, Sub) => Instruction::I64Sub,
|
||||||
|
(I64, Mul) => Instruction::I64Mul,
|
||||||
|
(I64, Div) => Instruction::I64DivS,
|
||||||
|
(I64, Rem) => Instruction::I64RemS,
|
||||||
|
(I64, And) => Instruction::I64And,
|
||||||
|
(I64, Or) => Instruction::I64Or,
|
||||||
|
(I64, Xor) => Instruction::I64Xor,
|
||||||
|
(I64, Eq) => Instruction::I64Eq,
|
||||||
|
(I64, Ne) => Instruction::I64Neq,
|
||||||
|
(I64, Lt) => Instruction::I64LtS,
|
||||||
|
(I64, Le) => Instruction::I64LeS,
|
||||||
|
(I64, Gt) => Instruction::I64GtS,
|
||||||
|
(I64, Ge) => Instruction::I64GeS,
|
||||||
|
(I64, Lsl) => Instruction::I64Shl,
|
||||||
|
(I64, Lsr) => Instruction::I64ShrU,
|
||||||
|
(I64, Asr) => Instruction::I64ShrS,
|
||||||
|
|
||||||
(F32, Add) => Instruction::F32Add,
|
(F32, Add) => Instruction::F32Add,
|
||||||
(F32, Sub) => Instruction::F32Sub,
|
(F32, Sub) => Instruction::F32Sub,
|
||||||
(F32, Mul) => Instruction::F32Mul,
|
(F32, Mul) => Instruction::F32Mul,
|
||||||
@@ -377,8 +444,17 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
(F32, Gt) => Instruction::F32Gt,
|
(F32, Gt) => Instruction::F32Gt,
|
||||||
(F32, Ge) => Instruction::F32Ge,
|
(F32, Ge) => Instruction::F32Ge,
|
||||||
|
|
||||||
(I64, _) => todo!(),
|
(F64, Add) => Instruction::F64Add,
|
||||||
(F64, _) => todo!(),
|
(F64, Sub) => Instruction::F64Sub,
|
||||||
|
(F64, Mul) => Instruction::F64Mul,
|
||||||
|
(F64, Div) => Instruction::F64Div,
|
||||||
|
(F64, Rem | And | Or | Xor | Lsl | Lsr | Asr) => unreachable!(),
|
||||||
|
(F64, Eq) => Instruction::F64Eq,
|
||||||
|
(F64, Ne) => Instruction::F64Neq,
|
||||||
|
(F64, Lt) => Instruction::F64Lt,
|
||||||
|
(F64, Le) => Instruction::F64Le,
|
||||||
|
(F64, Gt) => Instruction::F64Gt,
|
||||||
|
(F64, Ge) => Instruction::F64Ge,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ast::Expr::Branch(label) => {
|
ast::Expr::Branch(label) => {
|
||||||
@@ -409,9 +485,15 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
ast::Expr::I32Const(v) => {
|
ast::Expr::I32Const(v) => {
|
||||||
ctx.function.instruction(&Instruction::I32Const(*v));
|
ctx.function.instruction(&Instruction::I32Const(*v));
|
||||||
}
|
}
|
||||||
|
ast::Expr::I64Const(v) => {
|
||||||
|
ctx.function.instruction(&Instruction::I64Const(*v));
|
||||||
|
}
|
||||||
ast::Expr::F32Const(v) => {
|
ast::Expr::F32Const(v) => {
|
||||||
ctx.function.instruction(&Instruction::F32Const(*v));
|
ctx.function.instruction(&Instruction::F32Const(*v));
|
||||||
}
|
}
|
||||||
|
ast::Expr::F64Const(v) => {
|
||||||
|
ctx.function.instruction(&Instruction::F64Const(*v));
|
||||||
|
}
|
||||||
ast::Expr::Assign { name, value, .. } => {
|
ast::Expr::Assign { name, value, .. } => {
|
||||||
emit_expression(ctx, value);
|
emit_expression(ctx, value);
|
||||||
if let Some(local_index) = ctx.locals.get(name) {
|
if let Some(local_index) = ctx.locals.get(name) {
|
||||||
@@ -456,10 +538,20 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
emit_expression(ctx, value);
|
emit_expression(ctx, value);
|
||||||
use ast::Type::*;
|
use ast::Type::*;
|
||||||
let inst = match (value.type_.unwrap(), *type_) {
|
let inst = match (value.type_.unwrap(), *type_) {
|
||||||
(t1, t2) if t1 == t2 => None,
|
(I32, I64) => Some(Instruction::I64ExtendI32S),
|
||||||
|
(I64, I32) => Some(Instruction::I32WrapI64),
|
||||||
(I32, F32) => Some(Instruction::F32ConvertI32S),
|
(I32, F32) => Some(Instruction::F32ConvertI32S),
|
||||||
(F32, I32) => Some(Instruction::I32TruncF32S),
|
(F32, I32) => Some(Instruction::I32TruncF32S),
|
||||||
_ => todo!(),
|
(I64, F32) => Some(Instruction::F32ConvertI64S),
|
||||||
|
(F32, I64) => Some(Instruction::I64TruncF32S),
|
||||||
|
(I32, F64) => Some(Instruction::F64ConvertI32S),
|
||||||
|
(F64, I32) => Some(Instruction::I32TruncF64S),
|
||||||
|
(I64, F64) => Some(Instruction::F64ConvertI64S),
|
||||||
|
(F64, I64) => Some(Instruction::I64TruncF64S),
|
||||||
|
(F32, F64) => Some(Instruction::F64PromoteF32),
|
||||||
|
(F64, F32) => Some(Instruction::F32DemoteF64),
|
||||||
|
|
||||||
|
(I32, I32) | (I64, I64) | (F32, F32) | (F64, F64) => None,
|
||||||
};
|
};
|
||||||
if let Some(inst) = inst {
|
if let Some(inst) = inst {
|
||||||
ctx.function.instruction(&inst);
|
ctx.function.instruction(&inst);
|
||||||
|
|||||||
316
src/parser.rs
316
src/parser.rs
@@ -25,7 +25,9 @@ enum Token {
|
|||||||
Ident(String),
|
Ident(String),
|
||||||
Str(String),
|
Str(String),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
|
Int64(i64),
|
||||||
Float(String),
|
Float(String),
|
||||||
|
Float64(String),
|
||||||
Op(String),
|
Op(String),
|
||||||
Ctrl(char),
|
Ctrl(char),
|
||||||
}
|
}
|
||||||
@@ -52,7 +54,9 @@ impl fmt::Display for Token {
|
|||||||
Token::Ident(s) => write!(f, "{}", s),
|
Token::Ident(s) => write!(f, "{}", s),
|
||||||
Token::Str(s) => write!(f, "{:?}", s),
|
Token::Str(s) => write!(f, "{:?}", s),
|
||||||
Token::Int(v) => write!(f, "{}", v),
|
Token::Int(v) => write!(f, "{}", v),
|
||||||
|
Token::Int64(v) => write!(f, "{}", v),
|
||||||
Token::Float(v) => write!(f, "{}", v),
|
Token::Float(v) => write!(f, "{}", v),
|
||||||
|
Token::Float64(v) => write!(f, "{}", v),
|
||||||
Token::Op(s) => write!(f, "{}", s),
|
Token::Op(s) => write!(f, "{}", s),
|
||||||
Token::Ctrl(c) => write!(f, "{}", c),
|
Token::Ctrl(c) => write!(f, "{}", c),
|
||||||
}
|
}
|
||||||
@@ -164,12 +168,23 @@ fn report_errors(errors: Vec<Simple<String>>, source: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
|
fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
|
||||||
|
let float64 = text::int(10)
|
||||||
|
.chain::<char, _, _>(just('.').chain(text::digits(10)))
|
||||||
|
.then_ignore(seq::<_, _, Simple<char>>("f64".chars()))
|
||||||
|
.collect::<String>()
|
||||||
|
.map(Token::Float64);
|
||||||
|
|
||||||
let float = text::int(10)
|
let float = text::int(10)
|
||||||
.chain::<char, _, _>(just('.').chain(text::digits(10)))
|
.chain::<char, _, _>(just('.').chain(text::digits(10)))
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
.map(Token::Float);
|
.map(Token::Float);
|
||||||
|
|
||||||
let int = text::int(10).map(|s: String| Token::Int(s.parse().unwrap()));
|
// TODO: support hex numbers
|
||||||
|
let int64 = text::int(10)
|
||||||
|
.then_ignore(seq::<_, _, Simple<char>>("i64".chars()))
|
||||||
|
.map(|s: String| Token::Int64(s.parse::<u64>().unwrap() as i64));
|
||||||
|
|
||||||
|
let int = text::int(10).map(|s: String| Token::Int(s.parse::<u32>().unwrap() as i32));
|
||||||
|
|
||||||
let str_ = just('"')
|
let str_ = just('"')
|
||||||
.ignore_then(filter(|c| *c != '"').repeated())
|
.ignore_then(filter(|c| *c != '"').repeated())
|
||||||
@@ -215,6 +230,8 @@ fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
|
|||||||
let comment = single_line.or(multi_line);
|
let comment = single_line.or(multi_line);
|
||||||
|
|
||||||
let token = float
|
let token = float
|
||||||
|
.or(float64)
|
||||||
|
.or(int64)
|
||||||
.or(int)
|
.or(int)
|
||||||
.or(str_)
|
.or(str_)
|
||||||
.or(op)
|
.or(op)
|
||||||
@@ -241,13 +258,32 @@ fn map_token<O>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>> + Clone {
|
fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + Clone {
|
||||||
recursive(|block| {
|
let identifier = filter_map(|span, tok| match tok {
|
||||||
|
Token::Ident(id) => Ok(id),
|
||||||
|
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))),
|
||||||
|
})
|
||||||
|
.labelled("identifier");
|
||||||
|
|
||||||
|
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 mut expression_out = None;
|
||||||
|
let block = recursive(|block| {
|
||||||
let mut block_expression = None;
|
let mut block_expression = None;
|
||||||
let expression = recursive(|expression| {
|
let expression = recursive(|expression| {
|
||||||
let val = map_token(|tok| match tok {
|
let val = map_token(|tok| match tok {
|
||||||
Token::Int(v) => Some(ast::Expr::I32Const(*v)),
|
Token::Int(v) => Some(ast::Expr::I32Const(*v)),
|
||||||
|
Token::Int64(v) => Some(ast::Expr::I64Const(*v)),
|
||||||
Token::Float(v) => Some(ast::Expr::F32Const(v.parse().unwrap())),
|
Token::Float(v) => Some(ast::Expr::F32Const(v.parse().unwrap())),
|
||||||
|
Token::Float64(v) => Some(ast::Expr::F64Const(v.parse().unwrap())),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.labelled("value");
|
.labelled("value");
|
||||||
@@ -258,13 +294,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
})
|
})
|
||||||
.labelled("variable");
|
.labelled("variable");
|
||||||
|
|
||||||
let ident = filter_map(|span, tok| match tok {
|
let local_tee = identifier
|
||||||
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()))
|
.then(just(Token::Op(":=".to_string())).ignore_then(expression.clone()))
|
||||||
.map(|(name, expr)| ast::Expr::LocalTee {
|
.map(|(name, expr)| ast::Expr::LocalTee {
|
||||||
name,
|
name,
|
||||||
@@ -273,7 +303,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let loop_expr = just(Token::Loop)
|
let loop_expr = just(Token::Loop)
|
||||||
.ignore_then(ident)
|
.ignore_then(identifier)
|
||||||
.then(
|
.then(
|
||||||
block
|
block
|
||||||
.clone()
|
.clone()
|
||||||
@@ -311,13 +341,13 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
block_expression = Some(block_expr.clone());
|
block_expression = Some(block_expr.clone());
|
||||||
|
|
||||||
let branch = just(Token::Branch)
|
let branch = just(Token::Branch)
|
||||||
.ignore_then(ident)
|
.ignore_then(identifier)
|
||||||
.map(|label| ast::Expr::Branch(label));
|
.map(|label| ast::Expr::Branch(label));
|
||||||
|
|
||||||
let branch_if = just(Token::BranchIf)
|
let branch_if = just(Token::BranchIf)
|
||||||
.ignore_then(expression.clone())
|
.ignore_then(expression.clone())
|
||||||
.then_ignore(just(Token::Ctrl(':')))
|
.then_ignore(just(Token::Ctrl(':')))
|
||||||
.then(ident)
|
.then(identifier)
|
||||||
.map(|(condition, label)| ast::Expr::BranchIf {
|
.map(|(condition, label)| ast::Expr::BranchIf {
|
||||||
condition: Box::new(condition),
|
condition: Box::new(condition),
|
||||||
label,
|
label,
|
||||||
@@ -326,7 +356,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
|
|
||||||
let let_ = just(Token::Let)
|
let let_ = just(Token::Let)
|
||||||
.ignore_then(just(Token::Defer).or_not())
|
.ignore_then(just(Token::Defer).or_not())
|
||||||
.then(ident.clone())
|
.then(identifier.clone())
|
||||||
.then(just(Token::Ctrl(':')).ignore_then(type_parser()).or_not())
|
.then(just(Token::Ctrl(':')).ignore_then(type_parser()).or_not())
|
||||||
.then(
|
.then(
|
||||||
just(Token::Op("=".to_string()))
|
just(Token::Op("=".to_string()))
|
||||||
@@ -341,7 +371,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let tee = ident
|
let tee = identifier
|
||||||
.clone()
|
.clone()
|
||||||
.then_ignore(just(Token::Op(":=".to_string())))
|
.then_ignore(just(Token::Op(":=".to_string())))
|
||||||
.then(expression.clone())
|
.then(expression.clone())
|
||||||
@@ -351,7 +381,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let assign = ident
|
let assign = identifier
|
||||||
.clone()
|
.clone()
|
||||||
.then_ignore(just(Token::Op("=".to_string())))
|
.then_ignore(just(Token::Op("=".to_string())))
|
||||||
.then(expression.clone())
|
.then(expression.clone())
|
||||||
@@ -378,7 +408,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let function_call = ident
|
let function_call = identifier
|
||||||
.clone()
|
.clone()
|
||||||
.then(
|
.then(
|
||||||
expression
|
expression
|
||||||
@@ -421,6 +451,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
|
|
||||||
let unary_op = just(Token::Op("-".to_string()))
|
let unary_op = just(Token::Op("-".to_string()))
|
||||||
.to(ast::UnaryOp::Negate)
|
.to(ast::UnaryOp::Negate)
|
||||||
|
.or(just(Token::Ctrl('!')).to(ast::UnaryOp::Not))
|
||||||
.map_with_span(|op, span| (op, span))
|
.map_with_span(|op, span| (op, span))
|
||||||
.repeated()
|
.repeated()
|
||||||
.then(atom)
|
.then(atom)
|
||||||
@@ -638,6 +669,8 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
op_bit
|
op_bit
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expression_out = Some(expression.clone());
|
||||||
|
|
||||||
let block_expression = block_expression.unwrap();
|
let block_expression = block_expression.unwrap();
|
||||||
|
|
||||||
expression
|
expression
|
||||||
@@ -653,6 +686,130 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
|
|||||||
}
|
}
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
})
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let expression = expression_out.unwrap();
|
||||||
|
|
||||||
|
let top_level_item = {
|
||||||
|
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))
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
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_,
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
let import_function = just(Token::Fn)
|
||||||
|
.ignore_then(identifier.clone())
|
||||||
|
.then(
|
||||||
|
type_parser()
|
||||||
|
.separated_by(just(Token::Ctrl(',')))
|
||||||
|
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
just(Token::Op("->".to_string()))
|
||||||
|
.ignore_then(type_parser())
|
||||||
|
.or_not(),
|
||||||
|
)
|
||||||
|
.map(|((name, params), result)| ast::ImportType::Function {
|
||||||
|
name,
|
||||||
|
params,
|
||||||
|
result,
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
let import = just(Token::Import)
|
||||||
|
.ignore_then(string)
|
||||||
|
.then(import_memory.or(import_global).or(import_function))
|
||||||
|
.then_ignore(just(Token::Ctrl(';')))
|
||||||
|
.map_with_span(|(import, type_), span| {
|
||||||
|
ast::TopLevelItem::Import(ast::Import {
|
||||||
|
span,
|
||||||
|
import,
|
||||||
|
type_,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
let parameter = identifier
|
||||||
|
.clone()
|
||||||
|
.then_ignore(just(Token::Ctrl(':')))
|
||||||
|
.then(type_parser())
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
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
|
||||||
|
.clone()
|
||||||
|
.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,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
let global = just(Token::Global)
|
||||||
|
.ignore_then(just(Token::Mut).or_not())
|
||||||
|
.then(identifier.clone())
|
||||||
|
.then(just(Token::Ctrl(':')).ignore_then(type_parser()).or_not())
|
||||||
|
.then(just(Token::Op("=".to_string())).ignore_then(expression.clone()))
|
||||||
|
.then_ignore(just(Token::Ctrl(';')))
|
||||||
|
.map_with_span(|(((mutable, name), type_), value), span| {
|
||||||
|
ast::TopLevelItem::GlobalVar(ast::GlobalVar {
|
||||||
|
name,
|
||||||
|
type_,
|
||||||
|
value,
|
||||||
|
mutable: mutable.is_some(),
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
import.or(function).or(global).boxed()
|
||||||
|
};
|
||||||
|
|
||||||
|
top_level_item.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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,128 +831,3 @@ fn type_parser() -> impl Parser<Token, ast::Type, Error = Simple<Token>> + Clone
|
|||||||
)),
|
)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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_function = just(Token::Fn)
|
|
||||||
.ignore_then(identifier.clone())
|
|
||||||
.then(
|
|
||||||
type_parser()
|
|
||||||
.separated_by(just(Token::Ctrl(',')))
|
|
||||||
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
just(Token::Op("->".to_string()))
|
|
||||||
.ignore_then(type_parser())
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.map(|((name, params), result)| ast::ImportType::Function {
|
|
||||||
name,
|
|
||||||
params,
|
|
||||||
result,
|
|
||||||
});
|
|
||||||
|
|
||||||
let import = just(Token::Import)
|
|
||||||
.ignore_then(string)
|
|
||||||
.then(import_memory.or(import_global).or(import_function))
|
|
||||||
.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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
111
src/typecheck.rs
111
src/typecheck.rs
@@ -10,6 +10,7 @@ type Result<T> = std::result::Result<T, ()>;
|
|||||||
struct Var {
|
struct Var {
|
||||||
span: Span,
|
span: Span,
|
||||||
type_: ast::Type,
|
type_: ast::Type,
|
||||||
|
mutable: bool,
|
||||||
}
|
}
|
||||||
type Vars = HashMap<String, Var>;
|
type Vars = HashMap<String, Var>;
|
||||||
|
|
||||||
@@ -28,7 +29,9 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
|
|||||||
for import in &script.imports {
|
for import in &script.imports {
|
||||||
match import.type_ {
|
match import.type_ {
|
||||||
ast::ImportType::Variable {
|
ast::ImportType::Variable {
|
||||||
ref name, type_, ..
|
ref name,
|
||||||
|
type_,
|
||||||
|
mutable,
|
||||||
} => {
|
} => {
|
||||||
if let Some(Var { span, .. }) = context.global_vars.get(name) {
|
if let Some(Var { span, .. }) = context.global_vars.get(name) {
|
||||||
result = report_duplicate_definition(
|
result = report_duplicate_definition(
|
||||||
@@ -43,6 +46,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
|
|||||||
Var {
|
Var {
|
||||||
type_,
|
type_,
|
||||||
span: import.span.clone(),
|
span: import.span.clone(),
|
||||||
|
mutable,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -74,15 +78,24 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for v in &script.global_vars {
|
for v in &mut script.global_vars {
|
||||||
if let Some(Var { span, .. }) = context.global_vars.get(&v.name) {
|
if let Some(Var { span, .. }) = context.global_vars.get(&v.name) {
|
||||||
result = report_duplicate_definition("Global already defined", &v.span, span, source);
|
result = report_duplicate_definition("Global already defined", &v.span, span, source);
|
||||||
} else {
|
} else {
|
||||||
|
tc_const(&mut v.value, source)?;
|
||||||
|
if v.type_ != v.value.type_ {
|
||||||
|
if v.type_.is_some() {
|
||||||
|
result = type_mismatch(v.type_, &v.span, v.value.type_, &v.value.span, source);
|
||||||
|
} else {
|
||||||
|
v.type_ = v.value.type_;
|
||||||
|
}
|
||||||
|
}
|
||||||
context.global_vars.insert(
|
context.global_vars.insert(
|
||||||
v.name.clone(),
|
v.name.clone(),
|
||||||
Var {
|
Var {
|
||||||
type_: v.type_,
|
type_: v.type_.unwrap(),
|
||||||
span: v.span.clone(),
|
span: v.span.clone(),
|
||||||
|
mutable: v.mutable,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -121,6 +134,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
|
|||||||
Var {
|
Var {
|
||||||
type_: *type_,
|
type_: *type_,
|
||||||
span: f.span.clone(),
|
span: f.span.clone(),
|
||||||
|
mutable: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -239,6 +253,20 @@ fn unknown_variable(span: &Span, source: &str) -> Result<()> {
|
|||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn immutable_assign(span: &Span, source: &str) -> Result<()> {
|
||||||
|
Report::build(ReportKind::Error, (), span.start)
|
||||||
|
.with_message("Trying to assign to immutable variable")
|
||||||
|
.with_label(
|
||||||
|
Label::new(span.clone())
|
||||||
|
.with_message("Trying to assign to immutable variable")
|
||||||
|
.with_color(Color::Red),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
.eprint(Source::from(source))
|
||||||
|
.unwrap();
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
fn missing_label(span: &Span, source: &str) -> Result<()> {
|
fn missing_label(span: &Span, source: &str) -> Result<()> {
|
||||||
Report::build(ReportKind::Error, (), span.start)
|
Report::build(ReportKind::Error, (), span.start)
|
||||||
.with_message("Label not found")
|
.with_message("Label not found")
|
||||||
@@ -311,6 +339,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
|
|||||||
Var {
|
Var {
|
||||||
type_: *type_,
|
type_: *type_,
|
||||||
span: expr.span.clone(),
|
span: expr.span.clone(),
|
||||||
|
mutable: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -350,13 +379,24 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
ast::Expr::I32Const(_) => Some(ast::Type::I32),
|
ast::Expr::I32Const(_) => Some(ast::Type::I32),
|
||||||
|
ast::Expr::I64Const(_) => Some(ast::Type::I64),
|
||||||
ast::Expr::F32Const(_) => Some(ast::Type::F32),
|
ast::Expr::F32Const(_) => Some(ast::Type::F32),
|
||||||
|
ast::Expr::F64Const(_) => Some(ast::Type::F64),
|
||||||
ast::Expr::UnaryOp {
|
ast::Expr::UnaryOp {
|
||||||
op: _,
|
op,
|
||||||
ref mut value,
|
ref mut value,
|
||||||
} => {
|
} => {
|
||||||
tc_expression(context, value)?;
|
tc_expression(context, value)?;
|
||||||
todo!();
|
if value.type_.is_none() {
|
||||||
|
return expected_type(&value.span, context.source);
|
||||||
|
}
|
||||||
|
use ast::Type::*;
|
||||||
|
use ast::UnaryOp::*;
|
||||||
|
Some(match (value.type_.unwrap(), op) {
|
||||||
|
(t, Negate) => t,
|
||||||
|
(I32 | I64, Not) => I32,
|
||||||
|
(_, Not) => return type_mismatch(Some(I32), &expr.span, value.type_, &value.span, context.source)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
ast::Expr::BinOp {
|
ast::Expr::BinOp {
|
||||||
op,
|
op,
|
||||||
@@ -382,7 +422,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
|
|||||||
match op {
|
match op {
|
||||||
Add | Sub | Mul | Div => left.type_,
|
Add | Sub | Mul | Div => left.type_,
|
||||||
Rem | And | Or | Xor | Lsl | Lsr | Asr => {
|
Rem | And | Or | Xor | Lsl | Lsr | Asr => {
|
||||||
if left.type_ != Some(I32) {
|
if left.type_ != Some(I32) && left.type_ != Some(I64) {
|
||||||
return type_mismatch(
|
return type_mismatch(
|
||||||
Some(I32),
|
Some(I32),
|
||||||
&left.span,
|
&left.span,
|
||||||
@@ -414,7 +454,9 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
|
|||||||
} => {
|
} => {
|
||||||
tc_expression(context, value)?;
|
tc_expression(context, value)?;
|
||||||
if let Some(&Var {
|
if let Some(&Var {
|
||||||
type_, ref span, ..
|
type_,
|
||||||
|
ref span,
|
||||||
|
mutable,
|
||||||
}) = context
|
}) = context
|
||||||
.local_vars
|
.local_vars
|
||||||
.get(name)
|
.get(name)
|
||||||
@@ -429,6 +471,9 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
|
|||||||
context.source,
|
context.source,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if !mutable {
|
||||||
|
return immutable_assign(&expr.span, context.source);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return unknown_variable(&expr.span, context.source);
|
return unknown_variable(&expr.span, context.source);
|
||||||
}
|
}
|
||||||
@@ -440,7 +485,9 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
|
|||||||
} => {
|
} => {
|
||||||
tc_expression(context, value)?;
|
tc_expression(context, value)?;
|
||||||
if let Some(&Var {
|
if let Some(&Var {
|
||||||
type_, ref span, ..
|
type_,
|
||||||
|
ref span,
|
||||||
|
mutable,
|
||||||
}) = context.local_vars.get(name)
|
}) = context.local_vars.get(name)
|
||||||
{
|
{
|
||||||
if value.type_ != Some(type_) {
|
if value.type_ != Some(type_) {
|
||||||
@@ -452,6 +499,9 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
|
|||||||
context.source,
|
context.source,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if !mutable {
|
||||||
|
return immutable_assign(&expr.span, context.source);
|
||||||
|
}
|
||||||
Some(type_)
|
Some(type_)
|
||||||
} else {
|
} else {
|
||||||
return unknown_variable(&expr.span, context.source);
|
return unknown_variable(&expr.span, context.source);
|
||||||
@@ -642,7 +692,7 @@ fn tc_mem_location<'a>(
|
|||||||
mem_location: &mut ast::MemoryLocation,
|
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_const(&mut mem_location.right, context.source)?;
|
||||||
if mem_location.left.type_ != Some(I32) {
|
if mem_location.left.type_ != Some(I32) {
|
||||||
return type_mismatch(
|
return type_mismatch(
|
||||||
Some(I32),
|
Some(I32),
|
||||||
@@ -652,23 +702,40 @@ fn tc_mem_location<'a>(
|
|||||||
context.source,
|
context.source,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let ast::Expr::I32Const(_) = mem_location.right.expr {
|
if mem_location.right.type_ != Some(I32) {
|
||||||
} else {
|
return type_mismatch(
|
||||||
Report::build(ReportKind::Error, (), mem_location.right.span.start)
|
Some(I32),
|
||||||
.with_message("Expected I32 constant")
|
&mem_location.right.span,
|
||||||
.with_label(
|
mem_location.right.type_,
|
||||||
Label::new(mem_location.right.span.clone())
|
&mem_location.right.span,
|
||||||
.with_message("Expected I32 constant")
|
context.source,
|
||||||
.with_color(Color::Red),
|
);
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
.eprint(Source::from(context.source))
|
|
||||||
.unwrap();
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tc_const(expr: &mut ast::Expression, source: &str) -> Result<()> {
|
||||||
|
use ast::Expr::*;
|
||||||
|
expr.type_ = Some(match expr.expr {
|
||||||
|
I32Const(_) => I32,
|
||||||
|
F32Const(_) => F32,
|
||||||
|
_ => {
|
||||||
|
Report::build(ReportKind::Error, (), expr.span.start)
|
||||||
|
.with_message("Expected I32 constant")
|
||||||
|
.with_label(
|
||||||
|
Label::new(expr.span.clone())
|
||||||
|
.with_message("Expected I32 constant")
|
||||||
|
.with_color(Color::Red),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
.eprint(Source::from(source))
|
||||||
|
.unwrap();
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn builtin_function_types(name: &str) -> Option<(&'static [ast::Type], Option<ast::Type>)> {
|
fn builtin_function_types(name: &str) -> Option<(&'static [ast::Type], Option<ast::Type>)> {
|
||||||
use ast::Type::*;
|
use ast::Type::*;
|
||||||
let types: (&'static [ast::Type], Option<ast::Type>) = match name {
|
let types: (&'static [ast::Type], Option<ast::Type>) = match name {
|
||||||
|
|||||||
Reference in New Issue
Block a user