diff --git a/random.cwa b/random.cwa index 5166bca..73c61c5 100644 --- a/random.cwa +++ b/random.cwa @@ -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 } diff --git a/src/ast.rs b/src/ast.rs index 0344f86..d8cf062 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -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, + pub mutable: bool } #[derive(Debug)] @@ -71,7 +73,9 @@ pub enum Expr { final_expression: Option>, }, 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)] diff --git a/src/constfold.rs b/src/constfold.rs index 33734cc..8ac1264 100644 --- a/src/constfold.rs +++ b/src/constfold.rs @@ -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), diff --git a/src/emit.rs b/src/emit.rs index a0ffc47..e7926bb 100644 --- a/src/emit.rs +++ b/src/emit.rs @@ -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 { 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 { 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); diff --git a/src/parser.rs b/src/parser.rs index 7437b0f..a25bc5d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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>, source: &str) { } fn lexer() -> impl Parser, Error = Simple> { + let float64 = text::int(10) + .chain::(just('.').chain(text::digits(10))) + .then_ignore(seq::<_, _, Simple>("f64".chars())) + .collect::() + .map(Token::Float64); + let float = text::int(10) .chain::(just('.').chain(text::digits(10))) .collect::() .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>("i64".chars())) + .map(|s: String| Token::Int64(s.parse::().unwrap() as i64)); + + let int = text::int(10).map(|s: String| Token::Int(s.parse::().unwrap() as i32)); let str_ = just('"') .ignore_then(filter(|c| *c != '"').repeated()) @@ -215,6 +230,8 @@ fn lexer() -> impl Parser, Error = Simple> { 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( }) } -fn block_parser() -> impl Parser> + Clone { - recursive(|block| { +fn script_parser() -> impl Parser> + 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> }) .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> .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> 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> 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> }) .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> }) .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> }) .boxed(); - let function_call = ident + let function_call = identifier .clone() .then( expression @@ -421,6 +451,7 @@ fn block_parser() -> impl Parser> 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> op_bit }); + expression_out = Some(expression.clone()); + let block_expression = block_expression.unwrap(); expression @@ -653,6 +686,130 @@ fn block_parser() -> impl Parser> } .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> + Clone )), }) } - -fn top_level_item_parser() -> impl Parser> + 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> + 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 - }) -} diff --git a/src/typecheck.rs b/src/typecheck.rs index 07e2e42..130fd56 100644 --- a/src/typecheck.rs +++ b/src/typecheck.rs @@ -10,6 +10,7 @@ type Result = std::result::Result; struct Var { span: Span, type_: ast::Type, + mutable: bool, } type Vars = HashMap; @@ -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,23 +702,40 @@ 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) - .with_message("Expected I32 constant") - .with_label( - Label::new(mem_location.right.span.clone()) - .with_message("Expected I32 constant") - .with_color(Color::Red), - ) - .finish() - .eprint(Source::from(context.source)) - .unwrap(); - return Err(()); + 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(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)> { use ast::Type::*; let types: (&'static [ast::Type], Option) = match name {