mirror of
https://github.com/exoticorn/curlywas.git
synced 2026-01-20 11:46:43 +01:00
implement const
This commit is contained in:
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