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); 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
} }

View File

@@ -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)]

View File

@@ -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),

View File

@@ -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);

View File

@@ -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
})
}

View File

@@ -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,20 +702,37 @@ 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),
&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_message("Expected I32 constant")
.with_label( .with_label(
Label::new(mem_location.right.span.clone()) Label::new(expr.span.clone())
.with_message("Expected I32 constant") .with_message("Expected I32 constant")
.with_color(Color::Red), .with_color(Color::Red),
) )
.finish() .finish()
.eprint(Source::from(context.source)) .eprint(Source::from(source))
.unwrap(); .unwrap();
return Err(()); return Err(());
} }
});
Ok(()) Ok(())
} }