add global vars + i64 & f64 support

This commit is contained in:
2021-11-07 22:20:24 +01:00
parent f0474a3bbc
commit 63012947f3
6 changed files with 440 additions and 183 deletions

View File

@@ -1,22 +1,24 @@
import "env.memory" memory(4);
global mut randomState: i32 = 37;
fn random() -> i32 {
let seed = !0;
seed = seed ^ (seed << 13);
seed = seed ^ (seed >> 17);
seed = seed ^ (seed << 5);
!0 = seed;
seed * 625341585
let state: i32;
randomState = (state := (
state := randomState ^ (randomState << 13)
) ^ (state >> 17)
) ^ (state << 5);
randomState * 625341585
}
fn seed(s: i32) {
!0 = (s + (s >>> 31)) * 2060714111;
randomState = (((s + !(s >> 31)) as i64 * 8445297036689579347i64) >> 31i64) as i32;
}
export fn tic(time: i32) {
let i: i32;
loop pixels {
seed(i + time / 10);
seed(i + (time / 100) * 320);
i?120 = random();
branch_if (i := i + 1) < 320*256: pixels
}

View File

@@ -36,7 +36,9 @@ pub enum ImportType {
pub struct GlobalVar {
pub span: Span,
pub name: String,
pub type_: Type,
pub value: Expression,
pub type_: Option<Type>,
pub mutable: bool
}
#[derive(Debug)]
@@ -71,7 +73,9 @@ pub enum Expr {
final_expression: Option<Box<Expression>>,
},
I32Const(i32),
I64Const(i64),
F32Const(f32),
F64Const(f64),
Variable(String),
Let {
name: String,
@@ -147,6 +151,7 @@ impl Expr {
#[derive(Debug, Clone, Copy)]
pub enum UnaryOp {
Negate,
Not
}
#[derive(Debug, Clone, Copy)]

View File

@@ -1,6 +1,10 @@
use crate::ast;
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 {
fold_expr(&mut func.body);
}
@@ -40,6 +44,11 @@ fn fold_expr(expr: &mut ast::Expression) {
fold_expr(value);
let result = match (op, &value.expr) {
(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
};
if let Some(result) = result {
@@ -89,6 +98,40 @@ fn fold_expr(expr: &mut ast::Expression) {
};
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)) => {
use ast::Expr::*;
expr.expr = match op {
@@ -105,10 +148,26 @@ fn fold_expr(expr: &mut ast::Expression) {
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::LocalTee { ref mut value, .. } => fold_expr(value),
ast::Expr::Loop { ref mut block, .. } => fold_expr(block),

View File

@@ -2,7 +2,8 @@ use std::collections::HashMap;
use wasm_encoder::{
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;
@@ -76,6 +77,18 @@ pub fn emit(script: &ast::Script) -> Vec<u8> {
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 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();
functions.function(type_ as u32);
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));
}
module.section(&functions);
module.section(&global_section);
module.section(&exports);
module.section(&code);
}
@@ -135,6 +152,14 @@ fn function_type_key(func: &ast::Function) -> FunctionTypeKey {
(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> {
function: &'a mut Function,
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(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::BinOp { left, right, .. } => {
collect_locals_expr(left, locals);
@@ -331,12 +360,32 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
use ast::UnaryOp::*;
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!(),
(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 {
@@ -365,6 +414,24 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
(I32, Lsr) => Instruction::I32ShrU,
(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, Sub) => Instruction::F32Sub,
(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, Ge) => Instruction::F32Ge,
(I64, _) => todo!(),
(F64, _) => todo!(),
(F64, Add) => Instruction::F64Add,
(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) => {
@@ -409,9 +485,15 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
ast::Expr::I32Const(v) => {
ctx.function.instruction(&Instruction::I32Const(*v));
}
ast::Expr::I64Const(v) => {
ctx.function.instruction(&Instruction::I64Const(*v));
}
ast::Expr::F32Const(v) => {
ctx.function.instruction(&Instruction::F32Const(*v));
}
ast::Expr::F64Const(v) => {
ctx.function.instruction(&Instruction::F64Const(*v));
}
ast::Expr::Assign { name, value, .. } => {
emit_expression(ctx, value);
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);
use ast::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),
(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 {
ctx.function.instruction(&inst);

View File

@@ -25,7 +25,9 @@ enum Token {
Ident(String),
Str(String),
Int(i32),
Int64(i64),
Float(String),
Float64(String),
Op(String),
Ctrl(char),
}
@@ -52,7 +54,9 @@ impl fmt::Display for Token {
Token::Ident(s) => write!(f, "{}", s),
Token::Str(s) => write!(f, "{:?}", s),
Token::Int(v) => write!(f, "{}", v),
Token::Int64(v) => write!(f, "{}", v),
Token::Float(v) => write!(f, "{}", v),
Token::Float64(v) => write!(f, "{}", v),
Token::Op(s) => write!(f, "{}", s),
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>> {
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)
.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()));
// 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('"')
.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 token = float
.or(float64)
.or(int64)
.or(int)
.or(str_)
.or(op)
@@ -241,13 +258,32 @@ fn map_token<O>(
})
}
fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>> + Clone {
recursive(|block| {
fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + Clone {
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 expression = recursive(|expression| {
let val = map_token(|tok| match tok {
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::Float64(v) => Some(ast::Expr::F64Const(v.parse().unwrap())),
_ => None,
})
.labelled("value");
@@ -258,13 +294,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
})
.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
let local_tee = identifier
.then(just(Token::Op(":=".to_string())).ignore_then(expression.clone()))
.map(|(name, expr)| ast::Expr::LocalTee {
name,
@@ -273,7 +303,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
.boxed();
let loop_expr = just(Token::Loop)
.ignore_then(ident)
.ignore_then(identifier)
.then(
block
.clone()
@@ -311,13 +341,13 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
block_expression = Some(block_expr.clone());
let branch = just(Token::Branch)
.ignore_then(ident)
.ignore_then(identifier)
.map(|label| ast::Expr::Branch(label));
let branch_if = just(Token::BranchIf)
.ignore_then(expression.clone())
.then_ignore(just(Token::Ctrl(':')))
.then(ident)
.then(identifier)
.map(|(condition, label)| ast::Expr::BranchIf {
condition: Box::new(condition),
label,
@@ -326,7 +356,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
let let_ = just(Token::Let)
.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::Op("=".to_string()))
@@ -341,7 +371,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
})
.boxed();
let tee = ident
let tee = identifier
.clone()
.then_ignore(just(Token::Op(":=".to_string())))
.then(expression.clone())
@@ -351,7 +381,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
})
.boxed();
let assign = ident
let assign = identifier
.clone()
.then_ignore(just(Token::Op("=".to_string())))
.then(expression.clone())
@@ -378,7 +408,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
})
.boxed();
let function_call = ident
let function_call = identifier
.clone()
.then(
expression
@@ -421,6 +451,7 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
let unary_op = just(Token::Op("-".to_string()))
.to(ast::UnaryOp::Negate)
.or(just(Token::Ctrl('!')).to(ast::UnaryOp::Not))
.map_with_span(|op, span| (op, span))
.repeated()
.then(atom)
@@ -638,6 +669,8 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
op_bit
});
expression_out = Some(expression.clone());
let block_expression = block_expression.unwrap();
expression
@@ -653,6 +686,130 @@ fn block_parser() -> impl Parser<Token, ast::Expression, Error = Simple<Token>>
}
.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
})
}

View File

@@ -10,6 +10,7 @@ type Result<T> = std::result::Result<T, ()>;
struct Var {
span: Span,
type_: ast::Type,
mutable: bool,
}
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 {
match import.type_ {
ast::ImportType::Variable {
ref name, type_, ..
ref name,
type_,
mutable,
} => {
if let Some(Var { span, .. }) = context.global_vars.get(name) {
result = report_duplicate_definition(
@@ -43,6 +46,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
Var {
type_,
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) {
result = report_duplicate_definition("Global already defined", &v.span, span, source);
} 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(
v.name.clone(),
Var {
type_: v.type_,
type_: v.type_.unwrap(),
span: v.span.clone(),
mutable: v.mutable,
},
);
}
@@ -121,6 +134,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
Var {
type_: *type_,
span: f.span.clone(),
mutable: true,
},
);
}
@@ -239,6 +253,20 @@ fn unknown_variable(span: &Span, source: &str) -> Result<()> {
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<()> {
Report::build(ReportKind::Error, (), span.start)
.with_message("Label not found")
@@ -311,6 +339,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
Var {
type_: *type_,
span: expr.span.clone(),
mutable: true,
},
);
} else {
@@ -350,13 +379,24 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
None
}
ast::Expr::I32Const(_) => Some(ast::Type::I32),
ast::Expr::I64Const(_) => Some(ast::Type::I64),
ast::Expr::F32Const(_) => Some(ast::Type::F32),
ast::Expr::F64Const(_) => Some(ast::Type::F64),
ast::Expr::UnaryOp {
op: _,
op,
ref mut 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 {
op,
@@ -382,7 +422,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
match op {
Add | Sub | Mul | Div => left.type_,
Rem | And | Or | Xor | Lsl | Lsr | Asr => {
if left.type_ != Some(I32) {
if left.type_ != Some(I32) && left.type_ != Some(I64) {
return type_mismatch(
Some(I32),
&left.span,
@@ -414,7 +454,9 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
} => {
tc_expression(context, value)?;
if let Some(&Var {
type_, ref span, ..
type_,
ref span,
mutable,
}) = context
.local_vars
.get(name)
@@ -429,6 +471,9 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
context.source,
);
}
if !mutable {
return immutable_assign(&expr.span, context.source);
}
} else {
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)?;
if let Some(&Var {
type_, ref span, ..
type_,
ref span,
mutable,
}) = context.local_vars.get(name)
{
if value.type_ != Some(type_) {
@@ -452,6 +499,9 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
context.source,
);
}
if !mutable {
return immutable_assign(&expr.span, context.source);
}
Some(type_)
} else {
return unknown_variable(&expr.span, context.source);
@@ -642,7 +692,7 @@ fn tc_mem_location<'a>(
mem_location: &mut ast::MemoryLocation,
) -> Result<()> {
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) {
return type_mismatch(
Some(I32),
@@ -652,20 +702,37 @@ fn tc_mem_location<'a>(
context.source,
);
}
if let ast::Expr::I32Const(_) = mem_location.right.expr {
} else {
Report::build(ReportKind::Error, (), mem_location.right.span.start)
if mem_location.right.type_ != Some(I32) {
return type_mismatch(
Some(I32),
&mem_location.right.span,
mem_location.right.type_,
&mem_location.right.span,
context.source,
);
}
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(mem_location.right.span.clone())
Label::new(expr.span.clone())
.with_message("Expected I32 constant")
.with_color(Color::Red),
)
.finish()
.eprint(Source::from(context.source))
.eprint(Source::from(source))
.unwrap();
return Err(());
}
});
Ok(())
}