mirror of
https://github.com/exoticorn/curlywas.git
synced 2026-01-20 11:46:43 +01:00
implement const
This commit is contained in:
@@ -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,
|
||||
|
||||
13
examples/microw8/uw8.cwa
Normal file
13
examples/microw8/uw8.cwa
Normal file
@@ -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;
|
||||
30
src/ast.rs
30
src/ast.rs
@@ -8,7 +8,8 @@ pub struct Script {
|
||||
pub global_vars: Vec<GlobalVar>,
|
||||
pub functions: Vec<Function>,
|
||||
pub data: Vec<Data>,
|
||||
pub includes: Vec<Include>
|
||||
pub includes: Vec<Include>,
|
||||
pub consts: Vec<GlobalConst>,
|
||||
}
|
||||
|
||||
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<Type>,
|
||||
}
|
||||
|
||||
#[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<Expression>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Expression {
|
||||
pub type_: Option<Type>,
|
||||
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<Expression>,
|
||||
@@ -355,7 +373,7 @@ pub enum BinOp {
|
||||
pub enum MemSize {
|
||||
Byte,
|
||||
Word,
|
||||
Float
|
||||
Float,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
|
||||
146
src/constfold.rs
146
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<T> = std::result::Result<T, ()>;
|
||||
|
||||
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<String, ast::Expr>,
|
||||
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!(),
|
||||
}
|
||||
|
||||
18
src/lib.rs
18
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<P: AsRef<Path>>(path: P, options: Options) -> Result<Vec<u8>
|
||||
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<P: AsRef<Path>>(path: P, options: Options) -> Result<Vec<u8>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -925,6 +925,21 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + 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<Token, ast::Script, Error = ScriptError> + 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<Token, ast::Script, Error = ScriptError> + 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
|
||||
|
||||
@@ -298,7 +298,7 @@ impl LocalVars {
|
||||
}
|
||||
}
|
||||
|
||||
fn report_duplicate_definition(
|
||||
pub fn report_duplicate_definition(
|
||||
msg: &str,
|
||||
span: &Span,
|
||||
prev_span: &Span,
|
||||
|
||||
Reference in New Issue
Block a user