diff --git a/examples/microw8/palette.cwa b/examples/microw8/palette.cwa index f1953ba..269670b 100644 --- a/examples/microw8/palette.cwa +++ b/examples/microw8/palette.cwa @@ -1,29 +1,8 @@ -import "env.memory" memory(4); +include "uw8.cwa" -import "env.pow" fn pow(f32, f32) -> f32; -import "env.sin" fn sin(f32) -> f32; -import "env.cos" fn cos(f32) -> f32; -import "env.atan2" fn atan2(f32, f32) -> f32; -import "env.tan" fn tan(f32) -> f32; -import "env.atan" fn atan(f32) -> f32; -import "env.rectangle" fn rect(f32, f32, f32, f32, i32); +const SWEETY = PALETTE + 192 * 4; -//export fn tic(time: i32) { -// let i: i32; -// loop pixels { -// let lazy x = (i % 320) as f32 - 160.5; -// let lazy y = (i / 320 - 120) as f32; -// -// let lazy dist = 4000 as f32 / sqrt(x*x + y*y + 10 as f32); -// let lazy angle = atan2(x, y) * (64.0 / 3.141); -// -// i?120 = ((((dist + time as f32 / 63 as f32) as i32 ^ angle as i32) #% 32 + 32) >> ((dist as i32 - i % 7 * 3) / 40)) + 192; -// -// branch_if (i := i + 1) < 320*240: pixels; -// } -//} - -export fn tic(time: i32) { +export fn upd() { let i: i32; loop colors { rect((i % 16 * 15) as f32, (i / 16 * 15) as f32, 15 as f32, 15 as f32, i); @@ -43,7 +22,7 @@ start fn gen_palette() { let lazy a = max(llimit, min(ulimit, c)) * (scale + 0.05); let lazy b = scale * scale * 0.8; let inline v = (select(i < 11*16*3, max(0 as f32, min(a + b - a * b, 1 as f32)), scale) * 255 as f32) as i32; - (i%3 + i/3*4)?(120+320*240) = v; + (i%3 + i/3*4)?(PALETTE) = v; avg = (avg + c) * 0.5; branch_if i := i - 1: gradients; @@ -56,15 +35,15 @@ start fn gen_palette() { let lazy first_step = index >= 32; let inline src1 = select(first_step, index % 32 / 2, index * 2); let inline src2 = select(first_step, (index + 1) % 32 / 2, index * 2 + 1); - let inline c1 = (src1 * 4 + channel)?(120+320*240+192*4); - let inline c2 = (src2 * 4 + channel)?(120+320*240+192*4); - i?(120+320*240+192*4) = (c1 + c2) * (3 + first_step) / 8; + let inline c1 = (src1 * 4 + channel)?SWEETY; + let inline c2 = (src2 * 4 + channel)?SWEETY; + i?(SWEETY) = (c1 + c2) * (3 + first_step) / 8; branch_if (i := i - 1) >= 0: expand_sweetie; } } -data 120+320*240+192*4 { +data SWEETY { i32( 0x2c1c1a, 0x5d275d, diff --git a/examples/microw8/uw8.cwa b/examples/microw8/uw8.cwa new file mode 100644 index 0000000..7ed6214 --- /dev/null +++ b/examples/microw8/uw8.cwa @@ -0,0 +1,13 @@ +import "env.memory" memory(4); + +import "env.pow" fn pow(f32, f32) -> f32; +import "env.sin" fn sin(f32) -> f32; +import "env.cos" fn cos(f32) -> f32; +import "env.atan2" fn atan2(f32, f32) -> f32; +import "env.tan" fn tan(f32) -> f32; +import "env.atan" fn atan(f32) -> f32; +import "env.rectangle" fn rect(f32, f32, f32, f32, i32); + +const FRAMEBUFFER = 120; +const PALETTE = 0x13000; +const FONT = 0x13400; \ No newline at end of file diff --git a/src/ast.rs b/src/ast.rs index 28a19c9..309e2c1 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -8,7 +8,8 @@ pub struct Script { pub global_vars: Vec, pub functions: Vec, pub data: Vec, - pub includes: Vec + pub includes: Vec, + pub consts: Vec, } impl Script { @@ -17,6 +18,7 @@ impl Script { self.global_vars.append(&mut other.global_vars); self.functions.append(&mut other.functions); self.data.append(&mut other.data); + self.consts.append(&mut other.consts); assert!(other.includes.is_empty()); } } @@ -27,7 +29,8 @@ pub enum TopLevelItem { GlobalVar(GlobalVar), Function(Function), Data(Data), - Include(Include) + Include(Include), + Const(GlobalConst), } #[derive(Debug)] @@ -67,6 +70,14 @@ pub struct GlobalVar { pub mutable: bool, } +#[derive(Debug)] +pub struct GlobalConst { + pub span: Span, + pub name: String, + pub value: Expression, + pub type_: Option, +} + #[derive(Debug)] pub struct Function { pub span: Span, @@ -170,7 +181,7 @@ pub enum DataType { F64, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MemoryLocation { pub span: Span, pub size: MemSize, @@ -178,7 +189,7 @@ pub struct MemoryLocation { pub right: Box, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Expression { pub type_: Option, pub expr: Expr, @@ -213,9 +224,16 @@ impl Expression { _ => panic!("Expected F64Const"), } } + + pub fn is_const(&self) -> bool { + match self.expr { + Expr::I32Const(_) | Expr::I64Const(_) | Expr::F32Const(_) | Expr::F64Const(_) => true, + _ => false, + } + } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Expr { Block { statements: Vec, @@ -355,7 +373,7 @@ pub enum BinOp { pub enum MemSize { Byte, Word, - Float + Float, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] diff --git a/src/constfold.rs b/src/constfold.rs index 495c0d2..0fa295a 100644 --- a/src/constfold.rs +++ b/src/constfold.rs @@ -1,35 +1,103 @@ -use crate::ast; +use std::collections::HashMap; + +use crate::{ + ast, + parser::{Sources, Span}, + typecheck::{report_duplicate_definition, report_error}, +}; + +type Result = std::result::Result; + +pub fn fold_script(script: &mut ast::Script, sources: &Sources) -> Result<()> { + let mut context = Context { + consts: HashMap::new(), + sources, + }; + fold_consts(&mut context, &mut script.consts)?; -pub fn fold_script(script: &mut ast::Script) { for var in &mut script.global_vars { - fold_expr(&mut var.value); + fold_expr(&context, &mut var.value); } for func in &mut script.functions { - fold_expr(&mut func.body); + fold_expr(&context, &mut func.body); } for data in &mut script.data { - fold_expr(&mut data.offset); + fold_expr(&context, &mut data.offset); for values in &mut data.data { match values { ast::DataValues::Array { values, .. } => { for value in values { - fold_expr(value); + fold_expr(&context, value); } } ast::DataValues::String(_) | ast::DataValues::File { .. } => (), } } } + + Ok(()) } -fn fold_mem_location(mem_location: &mut ast::MemoryLocation) { - fold_expr(&mut mem_location.left); - fold_expr(&mut mem_location.right); +struct Context<'a> { + consts: HashMap, + sources: &'a Sources, } -fn fold_expr(expr: &mut ast::Expression) { +fn fold_consts(context: &mut Context, consts: &mut [ast::GlobalConst]) -> Result<()> { + let mut spans: HashMap<&str, Span> = HashMap::new(); + + for cnst in consts.iter_mut() { + if let Some(prev_span) = spans.insert(&cnst.name, cnst.span.clone()) { + report_duplicate_definition( + "Const already defined", + &cnst.span, + &prev_span, + context.sources, + )?; + } + } + + while context.consts.len() < consts.len() { + let mut making_progress = false; + for cnst in consts.iter_mut() { + if !context.consts.contains_key(&cnst.name) { + fold_expr(context, &mut cnst.value); + if cnst.value.is_const() { + context + .consts + .insert(cnst.name.clone(), cnst.value.expr.clone()); + making_progress = true; + } + } + } + + if !making_progress { + break; + } + } + + let mut result = Ok(()); + for cnst in consts { + if !context.consts.contains_key(&cnst.name) { + result = report_error( + &format!("Failed to fold const '{}'", cnst.name), + &cnst.span, + context.sources, + ); + } + } + + result +} + +fn fold_mem_location(context: &Context, mem_location: &mut ast::MemoryLocation) { + fold_expr(context, &mut mem_location.left); + fold_expr(context, &mut mem_location.right); +} + +fn fold_expr(context: &Context, expr: &mut ast::Expression) { use ast::BinOp::*; match expr.expr { ast::Expr::Block { @@ -37,15 +105,15 @@ fn fold_expr(expr: &mut ast::Expression) { ref mut final_expression, } => { for stmt in statements { - fold_expr(stmt); + fold_expr(context, stmt); } if let Some(ref mut expr) = final_expression { - fold_expr(expr); + fold_expr(context, expr); } } ast::Expr::Let { ref mut value, .. } => { if let Some(ref mut expr) = value { - fold_expr(expr); + fold_expr(context, expr); } } ast::Expr::Poke { @@ -53,12 +121,12 @@ fn fold_expr(expr: &mut ast::Expression) { ref mut value, .. } => { - fold_mem_location(mem_location); - fold_expr(value); + fold_mem_location(context, mem_location); + fold_expr(context, value); } - ast::Expr::Peek(ref mut mem_location) => fold_mem_location(mem_location), + ast::Expr::Peek(ref mut mem_location) => fold_mem_location(context, mem_location), ast::Expr::UnaryOp { op, ref mut value } => { - fold_expr(value); + fold_expr(context, value); let result = match (op, &value.expr) { (ast::UnaryOp::Negate, ast::Expr::I32Const(value)) => { Some(ast::Expr::I32Const(-*value)) @@ -90,8 +158,8 @@ fn fold_expr(expr: &mut ast::Expression) { ref mut right, .. } => { - fold_expr(left); - fold_expr(right); + fold_expr(context, left); + fold_expr(context, right); match (&left.expr, &right.expr) { (&ast::Expr::I32Const(left), &ast::Expr::I32Const(right)) => { let result = match op { @@ -237,24 +305,28 @@ fn fold_expr(expr: &mut ast::Expression) { 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), - ast::Expr::LabelBlock { ref mut block, .. } => fold_expr(block), + | ast::Expr::F64Const(_) => (), + ast::Expr::Variable { ref name, .. } => { + if let Some(value) = context.consts.get(name) { + expr.expr = value.clone(); + } + } + ast::Expr::Assign { ref mut value, .. } => fold_expr(context, value), + ast::Expr::LocalTee { ref mut value, .. } => fold_expr(context, value), + ast::Expr::Loop { ref mut block, .. } => fold_expr(context, block), + ast::Expr::LabelBlock { ref mut block, .. } => fold_expr(context, block), ast::Expr::Branch(_) => (), ast::Expr::BranchIf { ref mut condition, .. - } => fold_expr(condition), - ast::Expr::Cast { ref mut value, .. } => fold_expr(value), + } => fold_expr(context, condition), + ast::Expr::Cast { ref mut value, .. } => fold_expr(context, value), ast::Expr::FuncCall { ref name, ref mut params, .. } => { for param in params.iter_mut() { - fold_expr(param); + fold_expr(context, param); } use ast::Expr::*; let params: Vec<_> = params.iter().map(|e| &e.expr).collect(); @@ -269,31 +341,31 @@ fn fold_expr(expr: &mut ast::Expression) { ref mut if_false, .. } => { - fold_expr(condition); - fold_expr(if_true); - fold_expr(if_false); + fold_expr(context, condition); + fold_expr(context, if_true); + fold_expr(context, if_false); } ast::Expr::If { ref mut condition, ref mut if_true, ref mut if_false, } => { - fold_expr(condition); - fold_expr(if_true); + fold_expr(context, condition); + fold_expr(context, if_true); if let Some(ref mut if_false) = if_false { - fold_expr(if_false); + fold_expr(context, if_false); } } ast::Expr::Return { value: Some(ref mut value), - } => fold_expr(value), + } => fold_expr(context, value), ast::Expr::Return { value: None } => (), ast::Expr::First { ref mut value, ref mut drop, } => { - fold_expr(value); - fold_expr(drop); + fold_expr(context, value); + fold_expr(context, drop); } ast::Expr::Error => unreachable!(), } diff --git a/src/lib.rs b/src/lib.rs index 4fb801e..c4440e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ use anyhow::{bail, Result}; +use parser::Sources; use std::ffi::OsStr; use std::path::Path; -use parser::Sources; mod ast; mod constfold; @@ -36,15 +36,18 @@ pub fn compile_file>(path: P, options: Options) -> Result Ok(script) => script, Err(_) => bail!("Parse failed"), }; - + includes::resolve_includes(&mut new_script, &path)?; - + for include in std::mem::take(&mut new_script.includes) { - let mut path = path.parent().expect("Script path has no parent").to_path_buf(); + let mut path = path + .parent() + .expect("Script path has no parent") + .to_path_buf(); path.push(include.path); pending_files.push((path, Some(include.span))); } - + script.merge(new_script); } Ok((_, false)) => (), // already parsed this include @@ -59,8 +62,9 @@ pub fn compile_file>(path: P, options: Options) -> Result } } - - constfold::fold_script(&mut script); + if constfold::fold_script(&mut script, &sources).is_err() { + bail!("Constant folding failed"); + } if typecheck::tc_script(&mut script, &sources).is_err() { bail!("Type check failed"); } diff --git a/src/parser.rs b/src/parser.rs index 77fa050..f03d4de 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -925,6 +925,21 @@ fn script_parser() -> impl Parser + Clo }) .boxed(); + let global_const = just(Token::Ident("const".to_string())) + .ignore_then(identifier) + .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(|((name, type_), value), span| { + ast::TopLevelItem::Const(ast::GlobalConst { + name, + type_, + value, + span, + }) + }) + .boxed(); + let data_i8 = just(Token::Ident("i8".to_string())) .to(ast::DataType::I8) .or(just(Token::Ident("i16".to_string())).to(ast::DataType::I16)) @@ -975,17 +990,17 @@ fn script_parser() -> impl Parser + Clo |path, span| ast::TopLevelItem::Include(ast::Include { span, path }), )); - import.or(function).or(global).or(data).or(include).boxed() + import + .or(function) + .or(global) + .or(data) + .or(include) + .or(global_const) + .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(), - data: Vec::new(), - includes: Vec::new(), - }; + let mut script = ast::Script::default(); for item in items { match item { ast::TopLevelItem::Import(i) => script.imports.push(i), @@ -993,6 +1008,7 @@ fn script_parser() -> impl Parser + Clo ast::TopLevelItem::Function(f) => script.functions.push(f), ast::TopLevelItem::Data(d) => script.data.push(d), ast::TopLevelItem::Include(i) => script.includes.push(i), + ast::TopLevelItem::Const(c) => script.consts.push(c), } } script diff --git a/src/typecheck.rs b/src/typecheck.rs index 5a0c564..826f27a 100644 --- a/src/typecheck.rs +++ b/src/typecheck.rs @@ -298,7 +298,7 @@ impl LocalVars { } } -fn report_duplicate_definition( +pub fn report_duplicate_definition( msg: &str, span: &Span, prev_span: &Span,