Compare commits

...

18 Commits

Author SHA1 Message Date
0e7ea508cd add memory.fill/memory.copy intrinsics 2022-06-11 23:31:02 +02:00
0a0d90c801 add some docs, allow leading zeros in literals 2022-05-08 19:21:19 +02:00
c22297ea82 fix <| not parsing 2022-05-08 00:06:38 +02:00
c59b35f9c6 fix #<= and #>= not parsing 2022-05-07 23:59:40 +02:00
2cf47085c1 add support for +:= etc. 2022-05-07 23:51:25 +02:00
01d64baaab implement binop-assignment (+= etc.) 2022-05-07 20:30:16 +02:00
a52fe53a01 fix let lazy/inline chains re-using the same variable 2022-05-07 18:33:26 +02:00
1e746be750 add support for 'if else', change hex escapes to '\ab' 2022-05-06 09:03:13 +02:00
4cfc7ae8a8 add support for escaped strings + (multi-)char literatls 2022-05-06 01:23:31 +02:00
aac7bbd878 implement _f literal suffix as shortcut for NUM as f32 2022-04-10 23:58:18 +02:00
ebc701e2f2 fix != operator failing to parse 2022-04-09 08:51:23 +02:00
557c3a8426 always return dependencies, even when hitting an error 2022-03-05 21:02:58 +01:00
896385654a return dependencies for compiling cwa source 2022-02-27 21:26:55 +01:00
cda3eb868b fix "i?SCREEN = 0" being parsed as "i?(SCREEN = 0)" 2022-02-26 21:00:01 +01:00
5f316cf17d add new features to readme 2022-02-26 12:14:31 +01:00
e608d3bb4b implement const 2022-02-26 11:46:38 +01:00
b41b7f250c add include support 2022-02-25 23:38:50 +01:00
71de622634 add source filename to errors to prepare for includes 2022-02-25 23:13:11 +01:00
18 changed files with 1074 additions and 456 deletions

12
Cargo.lock generated
View File

@@ -13,9 +13,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.44" version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd"
[[package]] [[package]]
name = "ariadne" name = "ariadne"
@@ -83,9 +83,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.3" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
@@ -106,9 +106,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.105" version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]] [[package]]
name = "pico-args" name = "pico-args"

105
README.md
View File

@@ -56,6 +56,14 @@ Then run it on [MicroW8](https://exoticorn.github.io/microw8/v0.1pre2)
*/ */
``` ```
### Include
Other sourcefiles can be included with the `include` top level statement:
```
include "platform_imports.cwa"
```
### Types ### Types
There are four types in WebAssembly and therefore CurlyWas: There are four types in WebAssembly and therefore CurlyWas:
@@ -81,16 +89,25 @@ For floating point numbers, only the most basic decimal format is currently impl
0.464, 3.141, -10.0 0.464, 3.141, -10.0
``` ```
String literals exist in a very basic form. No escape character implemented yet. String literals are used for include paths, import names and as literal strings in the data section. The following escapes are supported:
| Escape | Result | Comment |
| `\"` | `"` | |
| `\'` | `'` | |
| `\t` | 8 | |
| `\n` | 10 | |
| `\r` | 13 | |
| `\N` | 0x0N | (Can't be followed by a hex digit) |
| `\NN` | 0xNN | |
``` ```
"env.memory", "Hello World!" "env.memory", "Hello World!"
this does not work, yet:
"one line\nsecond line", "They said: \"Enough!\"" "one line\nsecond line", "They said: \"Enough!\""
``` ```
Character literals are enclosed in single quotes `'` and support the same escapes as strings. They can contain up to 4 characters and evaluate to the
little-endian representation of these characters. For examples: `'A'` evaluates to `0x41`, `'hi'` evaluates to 0x6968, and `'Crly'` to 0x7a6c7243.
### Imports ### Imports
WebAssembly imports are specified with a module and a name. In CurlyWas you give them inside a single string literal, seperated by a dot. So a module `env` and name `printString` would be written `"env.printString"`. WebAssembly imports are specified with a module and a name. In CurlyWas you give them inside a single string literal, seperated by a dot. So a module `env` and name `printString` would be written `"env.printString"`.
@@ -138,6 +155,18 @@ can use it. However, exporting global variable is not yet supported in CurlyWas.
The type is optional, if missing it is inferred from the init value. The type is optional, if missing it is inferred from the init value.
### Constants
Constants can be declared in the global scope:
```
const name[: type] = value;
```
`value` has to be an expression evaluating to a constant value. It may reference other constants.
The type is optional, but if given has to match the type of `value`.
### Functions ### Functions
Functions look like this: Functions look like this:
@@ -275,6 +304,9 @@ So for example this block evaluates to 12:
Blocks are used as function bodies and in flow control (`if`, `block`, `loop`), but can also used at any point inside an expression. Blocks are used as function bodies and in flow control (`if`, `block`, `loop`), but can also used at any point inside an expression.
Variable re-assignments of the form `name = name <op> expression` can be shortened to `name <op>= expression`, for example `x += 1` to increment `x` by one. This works for all arithmetic, bit and shift operators.
The same is allowed for `name := name <op> expression`, ie. `x +:= 1` increments `x` and returns the new value.
#### Flow control #### Flow control
`if condition_expression { if_true_block } [else {if_false_block}]` executes the `if_true_block` if the condition evaluates to a non-zero integer and `if condition_expression { if_true_block } [else {if_false_block}]` executes the `if_true_block` if the condition evaluates to a non-zero integer and
@@ -284,6 +316,17 @@ the `if_false_block` otherwise (if it exists). It can also be used as an express
let a = if 0 { 2 } else { 3 }; // assigns 3 to a let a = if 0 { 2 } else { 3 }; // assigns 3 to a
``` ```
If the `if_false_block` contains exactly one `if` expression or statement you may omit the curly braces, writing `else if` chains like:
```
if x == 0 {
doOneThing()
} else if x == 1 {
doThatOtherThing()
} else {
keepWaiting()
}
```
`block name { ... }` opens a named block scope. A branch statement can be used to jump to the end of the block. Currently, `block` can only be used `block name { ... }` opens a named block scope. A branch statement can be used to jump to the end of the block. Currently, `block` can only be used
as a statement, returning a value from the block is not yet supported. as a statement, returning a value from the block is not yet supported.
@@ -296,15 +339,65 @@ non-zero integer.
#### Memory load/store #### Memory load/store
To read from memory you specify a memory location as `base?offset` or `base!offset`. `?` reads a byte and `!` reads a 32bit word. To read from memory you specify a memory location as `base?offset`, `base!offset` or `base$offset`. `?` reads a byte, `!` reads a 32bit word
and `$` reads a 32bit float.
`base` can be any expression that evaluates to an `i32` while `offset` has to be a constant `i32` value. The effective memory address is the sum of both. `base` can be any expression that evaluates to an `i32` while `offset` has to be a constant `i32` value. The effective memory address is the sum of both.
Writing to memory looks just like an assignment to a memory location: `base?offset = expressoin` and `base!offset = expression`. Writing to memory looks just like an assignment to a memory location: `base?offset = expression`, `base!offset = expression` and `base$offset = expression`.
When reading/writing 32bit words you need to make sure the address is 4-byte aligned. When reading/writing 32bit words you need to make sure the address is 4-byte aligned.
These compile to `i32.load8_u`, `i32.load`, `i32.store8` and `i32.store`. Other WASM load/store instructions will be implemented as intrinsics, but aren't yet. These compile to `i32.load8_u`, `i32.load`, `f32.load`, `i32.store8`, `i32.store` and `f32.store`.
In addition, all wasm memory instructions are available as intrinsics:
```
<load-ins>(<base-address>[, <offset>, [<align>]])
offset defaults to 0, align to the natural alignment: 0 for 8bit loads, 1 for 16bit, 2 for 32 bit and 3 for 64bit.
```
with `<load-ins>` being one of `i32.load`, `i32.load8_u`, `i32.load8_s`, `i32.load16_u`, `i32.load16_s`,
`i64.load`, `i64.load8_u`, `i64.load8_s`, `i64.load16_u`, `i64.load16_s`, `i32.load32_u`, `i32.load32_s`,
`f32.load` and `f64.load`.
```
<store-ins>(<value>, <base-address>[, <offset>, [<align>]])
offset and align defaults are the same as the load intrinsics.
```
with `<store-ins>` being one of `i32.store`, `i32.store8`, `i32.store16`, `i64.store`, `i64.store8`,
`i64.store16`, `i64.store32`, `f32.store` and `f64.store`.
#### Data
Data sections are written in `data` blocks:
```
data <address> {
...
}
```
The content of such a block is loaded at the given address at module start.
Inside the data block you can include 8, 16, 32, 64, f32 or f64 values:
```
i8(1, 255) i16(655350) i32(0x12345678) i64(0x1234567890abcdefi64) f32(1.0, 3.141) f64(0.5f64)
```
Strings:
```
"First line" i8(13, 10) "Second line"
```
And binary files:
```
file("font.bin")
```
#### Advanced sequencing #### Advanced sequencing

View File

@@ -1,29 +1,8 @@
import "env.memory" memory(4); include "uw8.cwa"
import "env.pow" fn pow(f32, f32) -> f32; const SWEETY = PALETTE + 192 * 4;
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);
//export fn tic(time: i32) { export fn upd() {
// 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) {
let i: i32; let i: i32;
loop colors { loop colors {
rect((i % 16 * 15) as f32, (i / 16 * 15) as f32, 15 as f32, 15 as f32, i); 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 a = max(llimit, min(ulimit, c)) * (scale + 0.05);
let lazy b = scale * scale * 0.8; 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; 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; avg = (avg + c) * 0.5;
branch_if i := i - 1: gradients; branch_if i := i - 1: gradients;
@@ -56,15 +35,15 @@ start fn gen_palette() {
let lazy first_step = index >= 32; let lazy first_step = index >= 32;
let inline src1 = select(first_step, index % 32 / 2, index * 2); 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 src2 = select(first_step, (index + 1) % 32 / 2, index * 2 + 1);
let inline c1 = (src1 * 4 + channel)?(120+320*240+192*4); let inline c1 = (src1 * 4 + channel)?SWEETY;
let inline c2 = (src2 * 4 + channel)?(120+320*240+192*4); let inline c2 = (src2 * 4 + channel)?SWEETY;
i?(120+320*240+192*4) = (c1 + c2) * (3 + first_step) / 8; i?SWEETY = (c1 + c2) * (3 + first_step) / 8;
branch_if (i := i - 1) >= 0: expand_sweetie; branch_if (i := i - 1) >= 0: expand_sweetie;
} }
} }
data 120+320*240+192*4 { data SWEETY {
i32( i32(
0x2c1c1a, 0x2c1c1a,
0x5d275d, 0x5d275d,

View File

@@ -0,0 +1,24 @@
import "env.memory" memory(4);
import "env.printString" fn printString(i32);
import "env.printChar" fn printChar(i32);
export fn upd() {
printChar(12);
printChar('Test');
printChar('\1f\10\10');
printChar('abc\n');
printString(0);
let t = 32!32 / 1000 #% 3;
if t == 0 {
printChar('one');
} else if t == 1 {
printChar('two');
} else {
printChar('many');
}
}
data 0 {
"\0e\64\"Colors!!!\"\0e\1\r\n\0"
}

View File

@@ -1,24 +1,29 @@
import "env.memory" memory(4); import "env.memory" memory(4);
import "env.sin" fn sin(f32) -> f32; import "env.sin" fn sin(f32) -> f32;
import "env.time" fn time() -> f32;
import "env.setPixel" fn setPixel(i32, i32, i32);
export fn tic(time: i32) { export fn upd() {
let i: i32; let x: i32;
let y: i32;
loop screen { loop screen {
let lazy t = time as f32 / 2000 as f32; let inline t = time() / 2 as f32;
let lazy o = sin(t) * 0.8; let lazy o = sin(t) * 0.75;
let lazy q = (i % 320) as f32 - 160.1; let inline q = x as f32 - 160.5;
let lazy w = (i / 320 - 120) as f32; let inline w = (y - 120) as f32;
let lazy r = sqrt(q*q + w*w); let lazy r = sqrt(q*q + w*w);
let lazy z = q / r; let lazy z = q / r;
let lazy s = z * o + sqrt(z * z * o * o + 1 as f32 - o * o); let lazy s = z * o + sqrt(z * z * o * o + 1 as f32 - o * o);
let lazy q2 = (z * s - o) * 10 as f32 + t; let inline q2 = (z * s - o) * 10 as f32 + t;
let lazy w2 = w / r * s * 10 as f32 + t; let inline w2 = w / r * s * 10 as f32 + t;
let lazy s2 = s * 50 as f32 / r; let inline s2 = s * 100 as f32 / r;
i?120 = max( let inline color = max(
0 as f32, 0 as f32,
((q2 as i32 ^ w2 as i32 & ((s2 + t) * 20 as f32) as i32) & 5) as f32 * ((q2 as i32 ^ w2 as i32 & ((s2 + time()) * 10 as f32) as i32) & 5) as f32 *
(2 as f32 - s2) * 22 as f32 (4 as f32 - s2) as f32
) as i32; ) as i32 - 32;
branch_if (i := i + 1) < 320*240: screen setPixel(x, y, color);
branch_if x := (x + 1) % 320: screen;
branch_if y := (y + 1) % 320: screen;
} }
} }

13
examples/microw8/uw8.cwa Normal file
View 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;

View File

@@ -1,15 +1,16 @@
import "env.memory" memory(2); import "env.memory" memory(2);
import "env.time" fn time() -> f32;
export fn tic(time: i32) { export fn upd() {
let i: i32; let i: i32;
loop pixels { loop pixels {
let lazy x = (i % 320) as f32 - 160.1; let lazy x = (i % 320) as f32 - 160.1;
let lazy y = (i / 320 - 120) as f32; let lazy y = (i / 320 - 120) as f32;
let lazy dist = 10000.0 / (x*x + y*y); let lazy dist = 1024_f / (x*x + y*y);
let lazy t = time as f32 / 20 as f32; let inline t = time() * 4_f;
i?120 = (x * dist + t) as i32 ^ (y * dist + t) as i32; i?120 = (x * dist + t) as i32 ^ (y * dist + t) as i32 | -32;
branch_if (i := i + 1) < 320*240: pixels branch_if (i +:= 1) < 320*240: pixels
} }
} }

View File

@@ -18,14 +18,14 @@ fn rng(state: i32) -> i32 {
} }
fn set_color(color: i32) -> i32 { fn set_color(color: i32) -> i32 {
?20 = color; 0?20 = color;
6 6
} }
export fn update() { export fn update() {
let y: i32; let y: i32;
let score = pz; let score = pz;
let lazy pad = ?22; let lazy pad = 0?22;
let lazy zero = 0.0; let lazy zero = 0.0;
let lazy control_speed = 0.03; let lazy control_speed = 0.03;
@@ -33,7 +33,7 @@ export fn update() {
f = f * 0.7; f = f * 0.7;
loop lines { loop lines {
?(8003-y) = (score := score / 10) % 10 + 48; (8003-y)?0 = (score := score / 10) % 10 + 48;
let lazy z = (4000 / (y := y + 1) + pz) / 20; let lazy z = (4000 / (y := y + 1) + pz) / 20;
let lazy x = (rng(rng(rng(rng(z)))) >> 30) as f32 - px; let lazy x = (rng(rng(rng(rng(z)))) >> 30) as f32 - px;
let lazy w = 9 as f32 / sqrt(z as f32); let lazy w = 9 as f32 / sqrt(z as f32);

View File

@@ -1,13 +1,26 @@
use std::{fmt, path::PathBuf}; use std::{fmt, path::PathBuf};
use crate::Span; use crate::parser::Span;
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Script { pub struct Script {
pub imports: Vec<Import>, pub imports: Vec<Import>,
pub global_vars: Vec<GlobalVar>, pub global_vars: Vec<GlobalVar>,
pub functions: Vec<Function>, pub functions: Vec<Function>,
pub data: Vec<Data>, pub data: Vec<Data>,
pub includes: Vec<Include>,
pub consts: Vec<GlobalConst>,
}
impl Script {
pub fn merge(&mut self, mut other: Script) {
self.imports.append(&mut other.imports);
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());
}
} }
#[derive(Debug)] #[derive(Debug)]
@@ -16,6 +29,14 @@ pub enum TopLevelItem {
GlobalVar(GlobalVar), GlobalVar(GlobalVar),
Function(Function), Function(Function),
Data(Data), Data(Data),
Include(Include),
Const(GlobalConst),
}
#[derive(Debug)]
pub struct Include {
pub span: Span,
pub path: String,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -49,6 +70,14 @@ pub struct GlobalVar {
pub mutable: bool, pub mutable: bool,
} }
#[derive(Debug)]
pub struct GlobalConst {
pub span: Span,
pub name: String,
pub value: Expression,
pub type_: Option<Type>,
}
#[derive(Debug)] #[derive(Debug)]
pub struct Function { pub struct Function {
pub span: Span, pub span: Span,
@@ -152,7 +181,7 @@ pub enum DataType {
F64, F64,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct MemoryLocation { pub struct MemoryLocation {
pub span: Span, pub span: Span,
pub size: MemSize, pub size: MemSize,
@@ -160,7 +189,7 @@ pub struct MemoryLocation {
pub right: Box<Expression>, pub right: Box<Expression>,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Expression { pub struct Expression {
pub type_: Option<Type>, pub type_: Option<Type>,
pub expr: Expr, pub expr: Expr,
@@ -195,9 +224,16 @@ impl Expression {
_ => panic!("Expected F64Const"), _ => 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 { pub enum Expr {
Block { Block {
statements: Vec<Expression>, statements: Vec<Expression>,
@@ -337,7 +373,7 @@ pub enum BinOp {
pub enum MemSize { pub enum MemSize {
Byte, Byte,
Word, Word,
Float Float,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]

View File

@@ -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 { for var in &mut script.global_vars {
fold_expr(&mut var.value); fold_expr(&context, &mut var.value);
} }
for func in &mut script.functions { for func in &mut script.functions {
fold_expr(&mut func.body); fold_expr(&context, &mut func.body);
} }
for data in &mut script.data { for data in &mut script.data {
fold_expr(&mut data.offset); fold_expr(&context, &mut data.offset);
for values in &mut data.data { for values in &mut data.data {
match values { match values {
ast::DataValues::Array { values, .. } => { ast::DataValues::Array { values, .. } => {
for value in values { for value in values {
fold_expr(value); fold_expr(&context, value);
} }
} }
ast::DataValues::String(_) | ast::DataValues::File { .. } => (), ast::DataValues::String(_) | ast::DataValues::File { .. } => (),
} }
} }
} }
Ok(())
} }
fn fold_mem_location(mem_location: &mut ast::MemoryLocation) { struct Context<'a> {
fold_expr(&mut mem_location.left); consts: HashMap<String, ast::Expr>,
fold_expr(&mut mem_location.right); 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::*; use ast::BinOp::*;
match expr.expr { match expr.expr {
ast::Expr::Block { ast::Expr::Block {
@@ -37,15 +105,15 @@ fn fold_expr(expr: &mut ast::Expression) {
ref mut final_expression, ref mut final_expression,
} => { } => {
for stmt in statements { for stmt in statements {
fold_expr(stmt); fold_expr(context, stmt);
} }
if let Some(ref mut expr) = final_expression { if let Some(ref mut expr) = final_expression {
fold_expr(expr); fold_expr(context, expr);
} }
} }
ast::Expr::Let { ref mut value, .. } => { ast::Expr::Let { ref mut value, .. } => {
if let Some(ref mut expr) = value { if let Some(ref mut expr) = value {
fold_expr(expr); fold_expr(context, expr);
} }
} }
ast::Expr::Poke { ast::Expr::Poke {
@@ -53,12 +121,12 @@ fn fold_expr(expr: &mut ast::Expression) {
ref mut value, ref mut value,
.. ..
} => { } => {
fold_mem_location(mem_location); fold_mem_location(context, mem_location);
fold_expr(value); 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 } => { ast::Expr::UnaryOp { op, ref mut value } => {
fold_expr(value); fold_expr(context, value);
let result = match (op, &value.expr) { let result = match (op, &value.expr) {
(ast::UnaryOp::Negate, ast::Expr::I32Const(value)) => { (ast::UnaryOp::Negate, ast::Expr::I32Const(value)) => {
Some(ast::Expr::I32Const(-*value)) Some(ast::Expr::I32Const(-*value))
@@ -72,6 +140,20 @@ fn fold_expr(expr: &mut ast::Expression) {
(ast::UnaryOp::Negate, ast::Expr::F64Const(value)) => { (ast::UnaryOp::Negate, ast::Expr::F64Const(value)) => {
Some(ast::Expr::F64Const(-*value)) Some(ast::Expr::F64Const(-*value))
} }
(ast::UnaryOp::Negate, ast::Expr::Cast { value, type_ }) => {
if let ast::Expr::I32Const(v) = value.expr {
Some(ast::Expr::Cast {
value: Box::new(ast::Expression {
expr: ast::Expr::I32Const(-v),
span: value.span.clone(),
type_: value.type_,
}),
type_: *type_,
})
} else {
None
}
}
(ast::UnaryOp::Not, ast::Expr::I32Const(value)) => { (ast::UnaryOp::Not, ast::Expr::I32Const(value)) => {
Some(ast::Expr::I32Const((*value == 0) as i32)) Some(ast::Expr::I32Const((*value == 0) as i32))
} }
@@ -90,8 +172,8 @@ fn fold_expr(expr: &mut ast::Expression) {
ref mut right, ref mut right,
.. ..
} => { } => {
fold_expr(left); fold_expr(context, left);
fold_expr(right); fold_expr(context, right);
match (&left.expr, &right.expr) { match (&left.expr, &right.expr) {
(&ast::Expr::I32Const(left), &ast::Expr::I32Const(right)) => { (&ast::Expr::I32Const(left), &ast::Expr::I32Const(right)) => {
let result = match op { let result = match op {
@@ -237,24 +319,28 @@ fn fold_expr(expr: &mut ast::Expression) {
ast::Expr::I32Const(_) ast::Expr::I32Const(_)
| ast::Expr::I64Const(_) | ast::Expr::I64Const(_)
| ast::Expr::F32Const(_) | ast::Expr::F32Const(_)
| ast::Expr::F64Const(_) | ast::Expr::F64Const(_) => (),
| ast::Expr::Variable { .. } => (), ast::Expr::Variable { ref name, .. } => {
ast::Expr::Assign { ref mut value, .. } => fold_expr(value), if let Some(value) = context.consts.get(name) {
ast::Expr::LocalTee { ref mut value, .. } => fold_expr(value), expr.expr = value.clone();
ast::Expr::Loop { ref mut block, .. } => fold_expr(block), }
ast::Expr::LabelBlock { ref mut block, .. } => fold_expr(block), }
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::Branch(_) => (),
ast::Expr::BranchIf { ast::Expr::BranchIf {
ref mut condition, .. ref mut condition, ..
} => fold_expr(condition), } => fold_expr(context, condition),
ast::Expr::Cast { ref mut value, .. } => fold_expr(value), ast::Expr::Cast { ref mut value, .. } => fold_expr(context, value),
ast::Expr::FuncCall { ast::Expr::FuncCall {
ref name, ref name,
ref mut params, ref mut params,
.. ..
} => { } => {
for param in params.iter_mut() { for param in params.iter_mut() {
fold_expr(param); fold_expr(context, param);
} }
use ast::Expr::*; use ast::Expr::*;
let params: Vec<_> = params.iter().map(|e| &e.expr).collect(); let params: Vec<_> = params.iter().map(|e| &e.expr).collect();
@@ -269,31 +355,31 @@ fn fold_expr(expr: &mut ast::Expression) {
ref mut if_false, ref mut if_false,
.. ..
} => { } => {
fold_expr(condition); fold_expr(context, condition);
fold_expr(if_true); fold_expr(context, if_true);
fold_expr(if_false); fold_expr(context, if_false);
} }
ast::Expr::If { ast::Expr::If {
ref mut condition, ref mut condition,
ref mut if_true, ref mut if_true,
ref mut if_false, ref mut if_false,
} => { } => {
fold_expr(condition); fold_expr(context, condition);
fold_expr(if_true); fold_expr(context, if_true);
if let Some(ref mut if_false) = if_false { if let Some(ref mut if_false) = if_false {
fold_expr(if_false); fold_expr(context, if_false);
} }
} }
ast::Expr::Return { ast::Expr::Return {
value: Some(ref mut value), value: Some(ref mut value),
} => fold_expr(value), } => fold_expr(context, value),
ast::Expr::Return { value: None } => (), ast::Expr::Return { value: None } => (),
ast::Expr::First { ast::Expr::First {
ref mut value, ref mut value,
ref mut drop, ref mut drop,
} => { } => {
fold_expr(value); fold_expr(context, value);
fold_expr(drop); fold_expr(context, drop);
} }
ast::Expr::Error => unreachable!(), ast::Expr::Error => unreachable!(),
} }

View File

@@ -289,7 +289,7 @@ struct FunctionContext<'a> {
functions: &'a HashMap<String, u32>, functions: &'a HashMap<String, u32>,
locals: &'a ast::Locals, locals: &'a ast::Locals,
labels: Vec<String>, labels: Vec<String>,
let_values: HashMap<u32, (&'a ast::Expression, ast::LetType)>, let_values: HashMap<u32, Vec<(&'a ast::Expression, ast::LetType)>>,
intrinsics: &'a Intrinsics, intrinsics: &'a Intrinsics,
} }
@@ -385,7 +385,10 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
.instruction(&Instruction::LocalSet(local.index.unwrap())); .instruction(&Instruction::LocalSet(local.index.unwrap()));
} }
ast::LetType::Lazy | ast::LetType::Inline => { ast::LetType::Lazy | ast::LetType::Inline => {
ctx.let_values.insert(local_id.unwrap(), (value, *let_type)); ctx.let_values
.entry(local_id.unwrap())
.or_default()
.push((value, *let_type));
} }
} }
} }
@@ -612,17 +615,17 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
} }
ast::Expr::Variable { name, local_id } => { ast::Expr::Variable { name, local_id } => {
if let &Some(id) = local_id { if let &Some(id) = local_id {
if let Some((expr, let_type)) = ctx.let_values.get(&id) { if let Some((expr, let_type)) = ctx.let_values.get_mut(&id).and_then(|s| s.pop()) {
match let_type { match let_type {
ast::LetType::Lazy => { ast::LetType::Lazy => {
let expr = ctx.let_values.remove(&id).unwrap().0;
emit_expression(ctx, expr); emit_expression(ctx, expr);
ctx.let_values.get_mut(&id).unwrap().clear();
ctx.function ctx.function
.instruction(&Instruction::LocalTee(ctx.locals[id].index.unwrap())); .instruction(&Instruction::LocalTee(ctx.locals[id].index.unwrap()));
} }
ast::LetType::Inline => { ast::LetType::Inline => {
let expr = *expr;
emit_expression(ctx, expr); emit_expression(ctx, expr);
ctx.let_values.get_mut(&id).unwrap().push((expr, let_type));
} }
_ => unreachable!(), _ => unreachable!(),
} }

View File

@@ -1,11 +1,15 @@
use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::Path; use std::path::{Path, PathBuf};
use std::{collections::HashSet, fs::File};
use crate::ast; use crate::ast;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
pub fn resolve_includes(script: &mut ast::Script, path: &Path) -> Result<()> { pub fn resolve_includes(
script: &mut ast::Script,
dependencies: &mut HashSet<PathBuf>,
path: &Path,
) -> Result<()> {
let script_dir = path.parent().expect("Script path has no parent"); let script_dir = path.parent().expect("Script path has no parent");
for data in &mut script.data { for data in &mut script.data {
for values in &mut data.data { for values in &mut data.data {
@@ -21,6 +25,7 @@ pub fn resolve_includes(script: &mut ast::Script, path: &Path) -> Result<()> {
anyhow!("Failed to load data from {}: {}", full_path.display(), e) anyhow!("Failed to load data from {}: {}", full_path.display(), e)
})? })?
.read_to_end(data)?; .read_to_end(data)?;
dependencies.insert(full_path);
} }
} }
} }

View File

@@ -1,9 +1,11 @@
use crate::ast::Type; use crate::ast::Type;
use std::collections::HashMap;
use enc::MemArg; use enc::MemArg;
use std::collections::HashMap;
use wasm_encoder as enc; use wasm_encoder as enc;
pub struct Intrinsics(HashMap<String, HashMap<Vec<Type>, (Type, enc::Instruction<'static>)>>); pub struct Intrinsics(
HashMap<String, HashMap<Vec<Type>, (Option<Type>, enc::Instruction<'static>)>>,
);
impl Intrinsics { impl Intrinsics {
pub fn new() -> Intrinsics { pub fn new() -> Intrinsics {
@@ -12,14 +14,11 @@ impl Intrinsics {
i i
} }
pub fn find_types( pub fn find_types(&self, name: &str) -> Option<HashMap<Vec<Type>, Option<Type>>> {
&self,
name: &str,
) -> Option<HashMap<Vec<Type>, Option<Type>>> {
self.0.get(name).map(|types| { self.0.get(name).map(|types| {
types types
.iter() .iter()
.map(|(params, (ret, _))| (params.clone(), Some(*ret))) .map(|(params, (ret, _))| (params.clone(), *ret))
.collect() .collect()
}) })
} }
@@ -34,87 +33,121 @@ impl Intrinsics {
fn add_instructions(&mut self) { fn add_instructions(&mut self) {
use enc::Instruction as I; use enc::Instruction as I;
use Type::*; use Type::*;
self.inst("i32.rotl", &[I32, I32], I32, I::I32Rotl); self.inst("i32.rotl", &[I32, I32], Some(I32), I::I32Rotl);
self.inst("i32.rotr", &[I32, I32], I32, I::I32Rotr); self.inst("i32.rotr", &[I32, I32], Some(I32), I::I32Rotr);
self.inst("i32.clz", &[I32], I32, I::I32Clz); self.inst("i32.clz", &[I32], Some(I32), I::I32Clz);
self.inst("i32.ctz", &[I32], I32, I::I32Ctz); self.inst("i32.ctz", &[I32], Some(I32), I::I32Ctz);
self.inst("i32.popcnt", &[I32], I32, I::I32Popcnt); self.inst("i32.popcnt", &[I32], Some(I32), I::I32Popcnt);
self.inst("i64.rotl", &[I64, I64], I64, I::I64Rotl); self.inst("i64.rotl", &[I64, I64], Some(I64), I::I64Rotl);
self.inst("i64.rotr", &[I64, I64], I64, I::I64Rotr); self.inst("i64.rotr", &[I64, I64], Some(I64), I::I64Rotr);
self.inst("i64.clz", &[I64], I64, I::I64Clz); self.inst("i64.clz", &[I64], Some(I64), I::I64Clz);
self.inst("i64.ctz", &[I64], I64, I::I64Ctz); self.inst("i64.ctz", &[I64], Some(I64), I::I64Ctz);
self.inst("i64.popcnt", &[I64], I64, I::I64Popcnt); self.inst("i64.popcnt", &[I64], Some(I64), I::I64Popcnt);
self.inst("f32/sqrt", &[F32], F32, I::F32Sqrt); self.inst("f32/sqrt", &[F32], Some(F32), I::F32Sqrt);
self.inst("f32/min", &[F32, F32], F32, I::F32Min); self.inst("f32/min", &[F32, F32], Some(F32), I::F32Min);
self.inst("f32/max", &[F32, F32], F32, I::F32Max); self.inst("f32/max", &[F32, F32], Some(F32), I::F32Max);
self.inst("f32/ceil", &[F32], F32, I::F32Ceil); self.inst("f32/ceil", &[F32], Some(F32), I::F32Ceil);
self.inst("f32/floor", &[F32], F32, I::F32Floor); self.inst("f32/floor", &[F32], Some(F32), I::F32Floor);
self.inst("f32/trunc", &[F32], F32, I::F32Trunc); self.inst("f32/trunc", &[F32], Some(F32), I::F32Trunc);
self.inst("f32/nearest", &[F32], F32, I::F32Nearest); self.inst("f32/nearest", &[F32], Some(F32), I::F32Nearest);
self.inst("f32/abs", &[F32], F32, I::F32Abs); self.inst("f32/abs", &[F32], Some(F32), I::F32Abs);
self.inst("f32.copysign", &[F32, F32], F32, I::F32Copysign); self.inst("f32.copysign", &[F32, F32], Some(F32), I::F32Copysign);
self.inst("f64/sqrt", &[F64], F64, I::F64Sqrt); self.inst("f64/sqrt", &[F64], Some(F64), I::F64Sqrt);
self.inst("f64/min", &[F64, F64], F64, I::F64Min); self.inst("f64/min", &[F64, F64], Some(F64), I::F64Min);
self.inst("f64/max", &[F64, F64], F64, I::F64Max); self.inst("f64/max", &[F64, F64], Some(F64), I::F64Max);
self.inst("f64/ceil", &[F64], F64, I::F64Ceil); self.inst("f64/ceil", &[F64], Some(F64), I::F64Ceil);
self.inst("f64/floor", &[F64], F64, I::F64Floor); self.inst("f64/floor", &[F64], Some(F64), I::F64Floor);
self.inst("f64/trunc", &[F64], F64, I::F64Trunc); self.inst("f64/trunc", &[F64], Some(F64), I::F64Trunc);
self.inst("f64/nearest", &[F64], F64, I::F64Nearest); self.inst("f64/nearest", &[F64], Some(F64), I::F64Nearest);
self.inst("f64/abs", &[F64], F64, I::F64Abs); self.inst("f64/abs", &[F64], Some(F64), I::F64Abs);
self.inst("f64.copysign", &[F64, F64], F64, I::F64Copysign); self.inst("f64.copysign", &[F64, F64], Some(F64), I::F64Copysign);
self.inst("i32.wrap_i64", &[I64], I32, I::I32WrapI64); self.inst("i32.wrap_i64", &[I64], Some(I32), I::I32WrapI64);
self.inst("i64.extend_i32_s", &[I32], I64, I::I64ExtendI32S); self.inst("i64.extend_i32_s", &[I32], Some(I64), I::I64ExtendI32S);
self.inst("i64.extend_i32_u", &[I32], I64, I::I64ExtendI32U); self.inst("i64.extend_i32_u", &[I32], Some(I64), I::I64ExtendI32U);
self.inst("i32.trunc_f32_s", &[F32], I32, I::I32TruncF32S); self.inst("i32.trunc_f32_s", &[F32], Some(I32), I::I32TruncF32S);
self.inst("i32.trunc_f64_s", &[F64], I32, I::I32TruncF64S); self.inst("i32.trunc_f64_s", &[F64], Some(I32), I::I32TruncF64S);
self.inst("i64.trunc_f32_s", &[F32], I64, I::I64TruncF32S); self.inst("i64.trunc_f32_s", &[F32], Some(I64), I::I64TruncF32S);
self.inst("i64.trunc_f64_s", &[F64], I64, I::I64TruncF64S); self.inst("i64.trunc_f64_s", &[F64], Some(I64), I::I64TruncF64S);
self.inst("i32.trunc_f32_u", &[F32], I32, I::I32TruncF32U); self.inst("i32.trunc_f32_u", &[F32], Some(I32), I::I32TruncF32U);
self.inst("i32.trunc_f64_u", &[F64], I32, I::I32TruncF64U); self.inst("i32.trunc_f64_u", &[F64], Some(I32), I::I32TruncF64U);
self.inst("i64.trunc_f32_u", &[F32], I64, I::I64TruncF32U); self.inst("i64.trunc_f32_u", &[F32], Some(I64), I::I64TruncF32U);
self.inst("i64.trunc_f64_u", &[F64], I64, I::I64TruncF64U); self.inst("i64.trunc_f64_u", &[F64], Some(I64), I::I64TruncF64U);
self.inst("f32.demote_f64", &[F64], F32, I::F32DemoteF64); self.inst("f32.demote_f64", &[F64], Some(F32), I::F32DemoteF64);
self.inst("f64.promote_f32", &[F32], F64, I::F64PromoteF32); self.inst("f64.promote_f32", &[F32], Some(F64), I::F64PromoteF32);
self.inst("f32.convert_i32_s", &[I32], F32, I::F32ConvertI32S); self.inst("f32.convert_i32_s", &[I32], Some(F32), I::F32ConvertI32S);
self.inst("f32.convert_i64_s", &[I64], F32, I::F32ConvertI32S); self.inst("f32.convert_i64_s", &[I64], Some(F32), I::F32ConvertI32S);
self.inst("f64.convert_i32_s", &[I32], F64, I::F32ConvertI32S); self.inst("f64.convert_i32_s", &[I32], Some(F64), I::F32ConvertI32S);
self.inst("f64.convert_i64_s", &[I64], F64, I::F32ConvertI32S); self.inst("f64.convert_i64_s", &[I64], Some(F64), I::F32ConvertI32S);
self.inst("f32.convert_i32_u", &[I32], F32, I::F32ConvertI32U); self.inst("f32.convert_i32_u", &[I32], Some(F32), I::F32ConvertI32U);
self.inst("f32.convert_i64_u", &[I64], F32, I::F32ConvertI32U); self.inst("f32.convert_i64_u", &[I64], Some(F32), I::F32ConvertI32U);
self.inst("f64.convert_i32_u", &[I32], F64, I::F32ConvertI32U); self.inst("f64.convert_i32_u", &[I32], Some(F64), I::F32ConvertI32U);
self.inst("f64.convert_i64_u", &[I64], F64, I::F32ConvertI32U); self.inst("f64.convert_i64_u", &[I64], Some(F64), I::F32ConvertI32U);
self.inst("i32.reinterpret_f32", &[F32], I32, I::I32ReinterpretF32); self.inst(
self.inst("i64.reinterpret_f64", &[F64], I64, I::I64ReinterpretF64); "i32.reinterpret_f32",
self.inst("f32.reinterpret_i32", &[I32], F32, I::F32ReinterpretI32); &[F32],
self.inst("f64.reinterpret_i64", &[I64], F64, I::F64ReinterpretI64); Some(I32),
I::I32ReinterpretF32,
);
self.inst(
"i64.reinterpret_f64",
&[F64],
Some(I64),
I::I64ReinterpretF64,
);
self.inst(
"f32.reinterpret_i32",
&[I32],
Some(F32),
I::F32ReinterpretI32,
);
self.inst(
"f64.reinterpret_i64",
&[I64],
Some(F64),
I::F64ReinterpretI64,
);
self.inst("i32.extend8_s", &[I32], I32, I::I32Extend8S); self.inst("i32.extend8_s", &[I32], Some(I32), I::I32Extend8S);
self.inst("i32.extend16_s", &[I32], I32, I::I32Extend16S); self.inst("i32.extend16_s", &[I32], Some(I32), I::I32Extend16S);
self.inst("i64.extend8_s", &[I64], I64, I::I64Extend8S); self.inst("i64.extend8_s", &[I64], Some(I64), I::I64Extend8S);
self.inst("i64.extend16_s", &[I64], I64, I::I64Extend16S); self.inst("i64.extend16_s", &[I64], Some(I64), I::I64Extend16S);
self.inst("i64.extend32_s", &[I64], I64, I::I64Extend32S); self.inst("i64.extend32_s", &[I64], Some(I64), I::I64Extend32S);
self.inst("i32.trunc_sat_f32_s", &[F32], I32, I::I32TruncSatF32S); self.inst("i32.trunc_sat_f32_s", &[F32], Some(I32), I::I32TruncSatF32S);
self.inst("i32.trunc_sat_f32_u", &[F32], I32, I::I32TruncSatF32U); self.inst("i32.trunc_sat_f32_u", &[F32], Some(I32), I::I32TruncSatF32U);
self.inst("i32.trunc_sat_f64_s", &[F64], I32, I::I32TruncSatF64S); self.inst("i32.trunc_sat_f64_s", &[F64], Some(I32), I::I32TruncSatF64S);
self.inst("i32.trunc_sat_f64_u", &[F64], I32, I::I32TruncSatF64U); self.inst("i32.trunc_sat_f64_u", &[F64], Some(I32), I::I32TruncSatF64U);
self.inst("i64.trunc_sat_f32_s", &[F32], I64, I::I64TruncSatF32S); self.inst("i64.trunc_sat_f32_s", &[F32], Some(I64), I::I64TruncSatF32S);
self.inst("i64.trunc_sat_f32_u", &[F32], I64, I::I64TruncSatF32U); self.inst("i64.trunc_sat_f32_u", &[F32], Some(I64), I::I64TruncSatF32U);
self.inst("i64.trunc_sat_f64_s", &[F64], I64, I::I64TruncSatF64S); self.inst("i64.trunc_sat_f64_s", &[F64], Some(I64), I::I64TruncSatF64S);
self.inst("i64.trunc_sat_f64_u", &[F64], I64, I::I64TruncSatF64U); self.inst("i64.trunc_sat_f64_u", &[F64], Some(I64), I::I64TruncSatF64U);
self.inst(
"memory.copy",
&[I32, I32, I32],
None,
I::MemoryCopy { src: 0, dst: 0 },
);
self.inst("memory.fill", &[I32, I32, I32], None, I::MemoryFill(0));
} }
fn inst(&mut self, name: &str, params: &[Type], ret: Type, ins: enc::Instruction<'static>) { fn inst(
&mut self,
name: &str,
params: &[Type],
ret: Option<Type>,
ins: enc::Instruction<'static>,
) {
if let Some(slash_idx) = name.find('/') { if let Some(slash_idx) = name.find('/') {
self.insert(name[(slash_idx + 1)..].to_string(), params, ret, &ins); self.insert(name[(slash_idx + 1)..].to_string(), params, ret, &ins);
let mut full_name = name[..slash_idx].to_string(); let mut full_name = name[..slash_idx].to_string();
@@ -130,7 +163,7 @@ impl Intrinsics {
&mut self, &mut self,
name: String, name: String,
params: &[Type], params: &[Type],
ret: Type, ret: Option<Type>,
ins: &enc::Instruction<'static>, ins: &enc::Instruction<'static>,
) { ) {
self.0 self.0
@@ -157,7 +190,7 @@ impl Intrinsics {
"i64.load32_u" => MemInstruction::new(I64, I::I64Load32_U, 2), "i64.load32_u" => MemInstruction::new(I64, I::I64Load32_U, 2),
"f32.load" => MemInstruction::new(F32, I::F32Load, 2), "f32.load" => MemInstruction::new(F32, I::F32Load, 2),
"f64.load" => MemInstruction::new(F64, I::F64Load, 3), "f64.load" => MemInstruction::new(F64, I::F64Load, 3),
_ => return None _ => return None,
}; };
return Some(ins); return Some(ins);
} }
@@ -175,7 +208,7 @@ impl Intrinsics {
"i64.store32" => MemInstruction::new(I64, I::I64Store32, 2), "i64.store32" => MemInstruction::new(I64, I::I64Store32, 2),
"f32.store" => MemInstruction::new(F32, I::F32Store, 2), "f32.store" => MemInstruction::new(F32, I::F32Store, 2),
"f64.store" => MemInstruction::new(F64, I::F64Store, 3), "f64.store" => MemInstruction::new(F64, I::F64Store, 3),
_ => return None _ => return None,
}; };
return Some(ins); return Some(ins);
} }
@@ -184,13 +217,19 @@ impl Intrinsics {
pub struct MemInstruction { pub struct MemInstruction {
pub type_: Type, pub type_: Type,
pub instruction: fn(MemArg) -> enc::Instruction<'static>, pub instruction: fn(MemArg) -> enc::Instruction<'static>,
pub natural_alignment: u32 pub natural_alignment: u32,
} }
impl MemInstruction { impl MemInstruction {
fn new(type_: Type, instruction: fn(MemArg) -> enc::Instruction<'static>, natural_alignment: u32) -> MemInstruction { fn new(
type_: Type,
instruction: fn(MemArg) -> enc::Instruction<'static>,
natural_alignment: u32,
) -> MemInstruction {
MemInstruction { MemInstruction {
type_, instruction, natural_alignment type_,
instruction,
natural_alignment,
} }
} }
} }

View File

@@ -1,7 +1,8 @@
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use parser::Sources;
use std::collections::HashSet;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::io::prelude::*; use std::path::{Path, PathBuf};
use std::{fs::File, path::Path};
mod ast; mod ast;
mod constfold; mod constfold;
@@ -11,8 +12,6 @@ mod intrinsics;
mod parser; mod parser;
mod typecheck; mod typecheck;
type Span = std::ops::Range<usize>;
#[derive(Default)] #[derive(Default)]
pub struct Options { pub struct Options {
pub(crate) debug: bool, pub(crate) debug: bool,
@@ -24,24 +23,60 @@ impl Options {
} }
} }
pub fn compile_file<P: AsRef<Path>>(path: P, options: Options) -> Result<Vec<u8>> { pub struct CompiledModule {
let path = path.as_ref(); pub wasm: Vec<u8>,
let mut input = String::new(); pub dependencies: Vec<PathBuf>,
File::open(path)?.read_to_string(&mut input)?;
compile_str(&input, path, options)
} }
pub fn compile_str(input: &str, path: &Path, options: Options) -> Result<Vec<u8>> { pub fn compile_file<P: AsRef<Path>>(path: P, options: Options) -> (Result<Vec<u8>>, Vec<PathBuf>) {
let mut script = match parser::parse(input) { fn compile_file_inner(
path: &Path,
options: Options,
dependencies: &mut HashSet<PathBuf>,
) -> Result<Vec<u8>> {
let mut script = ast::Script::default();
let mut sources = Sources::new();
let mut pending_files = vec![(path.to_path_buf(), None)];
while let Some((path, span)) = pending_files.pop() {
match sources.add(&path) {
Ok((id, true)) => {
dependencies.insert(path.clone());
let mut new_script = match parser::parse(&sources, id) {
Ok(script) => script, Ok(script) => script,
Err(_) => bail!("Parse failed"), Err(_) => bail!("Parse failed"),
}; };
includes::resolve_includes(&mut script, path)?; includes::resolve_includes(&mut new_script, dependencies, &path)?;
constfold::fold_script(&mut script); for include in std::mem::take(&mut new_script.includes) {
if typecheck::tc_script(&mut script, input).is_err() { 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
Err(err) => {
if let Some(span) = span {
let _ = typecheck::report_error(&err.to_string(), &span, &sources);
} else {
eprintln!("Failed to load script {}: {}", path.display(), err);
}
bail!("Parse failed");
}
}
}
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"); bail!("Type check failed");
} }
let wasm = emit::emit( let wasm = emit::emit(
@@ -54,3 +89,10 @@ pub fn compile_str(input: &str, path: &Path, options: Options) -> Result<Vec<u8>
); );
Ok(wasm) Ok(wasm)
} }
let mut dependencies = HashSet::new();
let result = compile_file_inner(path.as_ref(), options, &mut dependencies);
(result, dependencies.into_iter().collect())
}

View File

@@ -15,7 +15,7 @@ fn main() -> Result<()> {
let mut filename = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?; let mut filename = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
let wasm = compile_file(&filename, options)?; let wasm = compile_file(&filename, options).0?;
wasmparser::validate(&wasm)?; wasmparser::validate(&wasm)?;

View File

@@ -1,8 +1,62 @@
use crate::ast; use crate::ast;
use crate::Span; use anyhow::Result;
use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; use ariadne::{Color, Fmt, Label, Report, ReportKind};
use chumsky::{prelude::*, stream::Stream}; use chumsky::prelude::*;
use std::fmt; use std::{
fmt,
fs::File,
io::Read,
ops::Range,
path::{Path, PathBuf},
};
pub type Span = (usize, Range<usize>);
pub struct SourceFile {
source: ariadne::Source,
path: PathBuf,
}
pub struct Sources(Vec<SourceFile>);
impl Sources {
pub fn new() -> Sources {
Sources(Vec::new())
}
pub fn add(&mut self, path: &Path) -> Result<(usize, bool)> {
let canonical = path.canonicalize()?;
for (index, source) in self.0.iter().enumerate() {
if source.path.canonicalize()? == canonical {
return Ok((index, false));
}
}
let mut source = String::new();
File::open(path)?.read_to_string(&mut source)?;
self.0.push(SourceFile {
source: ariadne::Source::from(source),
path: path.to_path_buf(),
});
Ok((self.0.len() - 1, true))
}
}
impl std::ops::Index<usize> for Sources {
type Output = SourceFile;
fn index(&self, idx: usize) -> &SourceFile {
&self.0[idx]
}
}
impl ariadne::Cache<usize> for &Sources {
fn fetch(&mut self, id: &usize) -> Result<&ariadne::Source, Box<dyn std::fmt::Debug + '_>> {
Ok(&self.0[*id].source)
}
fn display<'a>(&self, id: &'a usize) -> Option<Box<dyn std::fmt::Display + 'a>> {
Some(Box::new(self.0[*id].path.clone().display().to_string()))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum Token { enum Token {
@@ -25,6 +79,7 @@ enum Token {
Str(String), Str(String),
Int(i32), Int(i32),
Int64(i64), Int64(i64),
IntFloat(i32),
Float(String), Float(String),
Float64(String), Float64(String),
Op(String), Op(String),
@@ -53,6 +108,7 @@ impl fmt::Display for Token {
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::Int64(v) => write!(f, "{}", v),
Token::IntFloat(v) => write!(f, "{}_f", v),
Token::Float(v) => write!(f, "{}", v), Token::Float(v) => write!(f, "{}", v),
Token::Float64(v) => write!(f, "{}", v), Token::Float64(v) => write!(f, "{}", v),
Token::Op(s) => write!(f, "{}", s), Token::Op(s) => write!(f, "{}", s),
@@ -61,8 +117,36 @@ impl fmt::Display for Token {
} }
} }
pub fn parse(source: &str) -> Result<ast::Script, ()> { type SourceStream<It> = chumsky::Stream<'static, char, Span, It>;
let tokens = match lexer().parse(source) { type TokenStream<It> = chumsky::Stream<'static, Token, Span, It>;
pub fn parse(sources: &Sources, source_id: usize) -> Result<ast::Script, ()> {
let source = &sources[source_id].source;
let source_stream = SourceStream::from_iter(
(source_id, source.len()..source.len() + 1),
Box::new(
// this is a bit of an ugly hack
// once the next version of ariadne is released
// use the source string chars as input
// and only create the ariadne::Source lazily when printing errors
source
.lines()
.flat_map(|l| {
let mut chars: Vec<char> = l.chars().collect();
while chars.len() + 1 < l.len() {
chars.push(' ');
}
if chars.len() < l.len() {
chars.push('\n');
}
chars.into_iter()
})
.enumerate()
.map(|(index, char)| (char, (source_id, index..(index + 1)))),
),
);
let tokens = match lexer().parse(source_stream) {
Ok(tokens) => tokens, Ok(tokens) => tokens,
Err(errors) => { Err(errors) => {
report_errors( report_errors(
@@ -70,15 +154,14 @@ pub fn parse(source: &str) -> Result<ast::Script, ()> {
.into_iter() .into_iter()
.map(|e| e.map(|c| c.to_string())) .map(|e| e.map(|c| c.to_string()))
.collect(), .collect(),
source, sources,
); );
return Err(()); return Err(());
} }
}; };
let source_len = source.chars().count(); let script = match script_parser().parse(TokenStream::from_iter(
let script = match script_parser().parse(Stream::from_iter( (source_id, source.len()..source.len() + 1),
source_len..source_len + 1,
tokens.into_iter(), tokens.into_iter(),
)) { )) {
Ok(script) => script, Ok(script) => script,
@@ -88,7 +171,7 @@ pub fn parse(source: &str) -> Result<ast::Script, ()> {
.into_iter() .into_iter()
.map(|e| e.map(|t| t.to_string())) .map(|e| e.map(|t| t.to_string()))
.collect(), .collect(),
source, sources,
); );
return Err(()); return Err(());
} }
@@ -96,9 +179,9 @@ pub fn parse(source: &str) -> Result<ast::Script, ()> {
Ok(script) Ok(script)
} }
fn report_errors(errors: Vec<Simple<String>>, source: &str) { fn report_errors(errors: Vec<Simple<String, Span>>, sources: &Sources) {
for error in errors { for error in errors {
let report = Report::build(ReportKind::Error, (), error.span().start()); let report = Report::build(ReportKind::Error, error.span().0, error.span().1.start());
let report = match error.reason() { let report = match error.reason() {
chumsky::error::SimpleReason::Unclosed { span, delimiter } => report chumsky::error::SimpleReason::Unclosed { span, delimiter } => report
@@ -161,30 +244,31 @@ fn report_errors(errors: Vec<Simple<String>>, source: &str) {
), ),
}; };
report.finish().eprint(Source::from(source)).unwrap(); report.finish().eprint(sources).unwrap();
} }
} }
fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> { type LexerError = Simple<char, Span>;
let float64 = text::int(10) fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = LexerError> {
let float64 = text::digits(10)
.chain::<char, _, _>(just('.').chain(text::digits(10))) .chain::<char, _, _>(just('.').chain(text::digits(10)))
.then_ignore(just("f64")) .then_ignore(just("f64"))
.collect::<String>() .collect::<String>()
.map(Token::Float64); .map(Token::Float64);
let float = text::int(10) let float = text::digits(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 integer = just::<_, _, Simple<char>>("0x") let integer = just::<_, _, LexerError>("0x")
.ignore_then(text::int(16)) .ignore_then(text::digits(16))
.try_map(|n, span| { .try_map(|n, span| {
u64::from_str_radix(&n, 16).map_err(|err| Simple::custom(span, err.to_string())) u64::from_str_radix(&n, 16).map_err(|err| LexerError::custom(span, err.to_string()))
}) })
.or(text::int(10).try_map(|n: String, span: Span| { .or(text::digits(10).try_map(|n: String, span: Span| {
n.parse::<u64>() n.parse::<u64>()
.map_err(|err| Simple::custom(span, err.to_string())) .map_err(|err| LexerError::custom(span, err.to_string()))
})) }))
.boxed(); .boxed();
@@ -193,28 +277,73 @@ fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
.then_ignore(just("i64")) .then_ignore(just("i64"))
.map(|n| Token::Int64(n as i64)); .map(|n| Token::Int64(n as i64));
let int_float = integer
.clone()
.then_ignore(just("_f"))
.map(|n| Token::IntFloat(n as i32));
let int = integer.try_map(|n, span| { let int = integer.try_map(|n, span| {
u32::try_from(n) u32::try_from(n)
.map(|n| Token::Int(n as i32)) .map(|n| Token::Int(n as i32))
.map_err(|err| Simple::custom(span, err.to_string())) .map_err(|err| LexerError::custom(span, err.to_string()))
}); });
let str_ = just('"') let str_ = just('\\')
.ignore_then(filter(|c| *c != '"').repeated()) .then(any())
.then_ignore(just('"')) .map(|t| vec![t.0, t.1])
.collect::<String>() .or(none_of("\"").map(|c| vec![c]))
.map(Token::Str);
let op = one_of("+-*/%&^|<=>#")
.repeated() .repeated()
.at_least(1) .flatten()
.or(just(':').chain(just('='))) .delimited_by(just('"'), just('"'))
.collect::<String>() .collect::<String>()
.map(Token::Op); .map(|s| Token::Str(parse_string_escapes(s)));
let char_ = just('\\')
.then(any())
.map(|t| vec![t.0, t.1])
.or(none_of("\'").map(|c| vec![c]))
.repeated()
.flatten()
.delimited_by(just('\''), just('\''))
.collect::<String>()
.map(|s| {
let s = parse_string_escapes(s);
let mut value = 0;
for (i, c) in s.chars().enumerate() {
// TODO: generate error on overflow
if i < 4 {
value |= (c as u32) << (i * 8);
}
}
Token::Int(value as i32)
});
let op = choice((
just("#/"),
just("#%"),
just("<<"),
just(">>"),
just("#>>"),
just(">="),
just("<="),
just("=="),
just("!="),
just("#>="),
just("#<="),
just("#<"),
just("#>"),
just("->"),
just(":="),
just("<|"),
))
.map(|s| s.to_string())
.or(one_of("+-*/%&^|<=>").map(|s: char| s.to_string()))
.map(Token::Op)
.boxed();
let ctrl = one_of("(){};,:?!$").map(Token::Ctrl); let ctrl = one_of("(){};,:?!$").map(Token::Ctrl);
fn ident() -> impl Parser<char, String, Error = Simple<char>> + Copy + Clone { fn ident() -> impl Parser<char, String, Error = LexerError> + Copy + Clone {
filter(|c: &char| c.is_ascii_alphabetic() || *c == '_') filter(|c: &char| c.is_ascii_alphabetic() || *c == '_')
.map(Some) .map(Some)
.chain::<char, Vec<_>, _>( .chain::<char, Vec<_>, _>(
@@ -248,14 +377,9 @@ 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 = choice((
.or(float64) float, float64, int64, int_float, int, str_, char_, op, ctrl, ident,
.or(int64) ))
.or(int)
.or(str_)
.or(op)
.or(ctrl)
.or(ident)
.recover_with(skip_then_retry_until([])); .recover_with(skip_then_retry_until([]));
token token
@@ -263,44 +387,107 @@ fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
.padded() .padded()
.padded_by(comment.padded().repeated()) .padded_by(comment.padded().repeated())
.repeated() .repeated()
.boxed()
} }
fn map_token<O>( fn map_token<O>(
f: impl Fn(&Token) -> Option<O> + 'static + Clone, f: impl Fn(&Token, &Span) -> Option<O> + 'static + Clone,
) -> impl Parser<Token, O, Error = Simple<Token>> + Clone { ) -> impl Parser<Token, O, Error = ScriptError> + Clone {
filter_map(move |span, tok: Token| { filter_map(move |span, tok: Token| {
if let Some(output) = f(&tok) { if let Some(output) = f(&tok, &span) {
Ok(output) Ok(output)
} else { } else {
Err(Simple::expected_input_found(span, Vec::new(), Some(tok))) Err(ScriptError::expected_input_found(
span,
Vec::new(),
Some(tok),
))
} }
}) })
} }
fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + Clone { fn parse_string_escapes(s: String) -> String {
let mut result = String::new();
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
if c != '\\' {
result.push(c);
} else if let Some(c) = chars.next() {
match c {
'0'..='9' | 'a'..='f' | 'A'..='F' => {
let mut number = c.to_string();
if let Some('0'..='9' | 'a'..='f' | 'A'..='F') = chars.peek() {
number.push(chars.next().unwrap());
}
result.push(u8::from_str_radix(&number, 16).unwrap() as char);
}
'n' => result.push('\n'),
'r' => result.push('\r'),
't' => result.push('\t'),
other => result.push(other),
}
} else {
result.push('\\');
}
}
result
}
type ScriptError = Simple<Token, Span>;
fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + Clone {
let identifier = filter_map(|span, tok| match tok { let identifier = filter_map(|span, tok| match tok {
Token::Ident(id) => Ok(id), Token::Ident(id) => Ok(id),
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))), _ => Err(ScriptError::expected_input_found(
span,
Vec::new(),
Some(tok),
)),
}) })
.labelled("identifier"); .labelled("identifier");
let integer = map_token(|tok| match tok { let integer = map_token(|tok, _| match tok {
Token::Int(v) => Some(*v), Token::Int(v) => Some(*v),
_ => None, _ => None,
}); });
let string = map_token(|tok| match tok { let string = map_token(|tok, _| match tok {
Token::Str(s) => Some(s.clone()), Token::Str(s) => Some(s.clone()),
_ => None, _ => None,
}); });
let product_op = just(Token::Op("*".to_string()))
.to(ast::BinOp::Mul)
.or(just(Token::Op("/".to_string())).to(ast::BinOp::Div))
.or(just(Token::Op("#/".to_string())).to(ast::BinOp::DivU))
.or(just(Token::Op("%".to_string())).to(ast::BinOp::Rem))
.or(just(Token::Op("#%".to_string())).to(ast::BinOp::RemU))
.boxed();
let sum_op = just(Token::Op("+".to_string()))
.to(ast::BinOp::Add)
.or(just(Token::Op("-".to_string())).to(ast::BinOp::Sub))
.boxed();
let shift_op = just(Token::Op("<<".to_string()))
.to(ast::BinOp::Shl)
.or(just(Token::Op("#>>".to_string())).to(ast::BinOp::ShrU))
.or(just(Token::Op(">>".to_string())).to(ast::BinOp::ShrS))
.boxed();
let bit_op = just(Token::Op("&".to_string()))
.to(ast::BinOp::And)
.or(just(Token::Op("|".to_string())).to(ast::BinOp::Or))
.or(just(Token::Op("^".to_string())).to(ast::BinOp::Xor))
.boxed();
let mut expression_out = None; let mut expression_out = None;
let block = recursive(|block| { 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, span| 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::Int64(v) => Some(ast::Expr::I64Const(*v)),
Token::IntFloat(v) => Some(ast::Expr::Cast {
value: Box::new(ast::Expr::I32Const(*v).with_span(span.clone())),
type_: ast::Type::F32,
}),
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())), Token::Float64(v) => Some(ast::Expr::F64Const(v.parse().unwrap())),
_ => None, _ => None,
@@ -312,7 +499,11 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
name: id, name: id,
local_id: None, local_id: None,
}), }),
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(tok))), _ => Err(ScriptError::expected_input_found(
span,
Vec::new(),
Some(tok),
)),
}) })
.labelled("variable"); .labelled("variable");
@@ -325,6 +516,36 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
}) })
.boxed(); .boxed();
let local_tee_op = identifier
.then(
product_op
.clone()
.or(sum_op.clone())
.or(shift_op.clone())
.or(bit_op.clone()),
)
.then_ignore(just(Token::Op(":=".to_string())))
.then(expression.clone())
.map_with_span(|((name, op), expr), span| ast::Expr::LocalTee {
name: name.clone(),
value: Box::new(
ast::Expr::BinOp {
left: Box::new(
ast::Expr::Variable {
name,
local_id: None,
}
.with_span(span.clone()),
),
right: Box::new(expr),
op,
}
.with_span(span),
),
local_id: None,
})
.boxed();
let loop_expr = just(Token::Loop) let loop_expr = just(Token::Loop)
.ignore_then(identifier) .ignore_then(identifier)
.then(block.clone()) .then(block.clone())
@@ -341,14 +562,24 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
block: Box::new(block), block: Box::new(block),
}); });
let if_expr = just(Token::If) let if_expr = recursive::<_, ast::Expr, _, _, _>(|if_expr| {
just(Token::If)
.ignore_then(expression.clone()) .ignore_then(expression.clone())
.then(block.clone()) .then(block.clone())
.then(just(Token::Else).ignore_then(block.clone()).or_not()) .then(
just(Token::Else)
.ignore_then(
block
.clone()
.or(if_expr.map_with_span(|expr, span| expr.with_span(span))),
)
.or_not(),
)
.map(|((condition, if_true), if_false)| ast::Expr::If { .map(|((condition, if_true), if_false)| ast::Expr::If {
condition: Box::new(condition), condition: Box::new(condition),
if_true: Box::new(if_true), if_true: Box::new(if_true),
if_false: if_false.map(Box::new), if_false: if_false.map(Box::new),
})
}); });
let block_expr = loop_expr.or(label_block_expr).or(if_expr).boxed(); let block_expr = loop_expr.or(label_block_expr).or(if_expr).boxed();
@@ -392,16 +623,6 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
}) })
.boxed(); .boxed();
let assign = identifier
.then_ignore(just(Token::Op("=".to_string())))
.then(expression.clone())
.map(|(name, value)| ast::Expr::Assign {
name,
value: Box::new(value),
local_id: None,
})
.boxed();
let select = just(Token::Select) let select = just(Token::Select)
.ignore_then( .ignore_then(
expression expression
@@ -435,17 +656,19 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
value: value.map(Box::new), value: value.map(Box::new),
}); });
let atom = val let atom = choice((
.or(function_call) val,
.or(assign) function_call,
.or(local_tee) local_tee,
.or(variable) local_tee_op,
.or(block_expr) variable,
.or(branch) block_expr,
.or(branch_if) branch,
.or(let_) branch_if,
.or(select) let_,
.or(return_) select,
return_,
))
.map_with_span(|expr, span| expr.with_span(span)) .map_with_span(|expr, span| expr.with_span(span))
.or(expression .or(expression
.clone() .clone()
@@ -467,7 +690,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.then(atom) .then(atom)
.map(|(ops, value)| { .map(|(ops, value)| {
ops.into_iter().rev().fold(value, |acc, (op, span)| { ops.into_iter().rev().fold(value, |acc, (op, span)| {
let span = span.start..acc.span.end; let span = (span.0, span.1.start..acc.span.1.end);
ast::Expr::UnaryOp { ast::Expr::UnaryOp {
op, op,
value: Box::new(acc), value: Box::new(acc),
@@ -507,7 +730,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
poke_op: Option<((ast::MemSize, ast::Expression), ast::Expression)>, poke_op: Option<((ast::MemSize, ast::Expression), ast::Expression)>,
) -> ast::Expression { ) -> ast::Expression {
let left = peek_ops.into_iter().fold(left, |left, (size, right)| { let left = peek_ops.into_iter().fold(left, |left, (size, right)| {
let span = left.span.start..right.span.end; let span = (left.span.0, left.span.1.start..right.span.1.end);
ast::Expr::Peek(ast::MemoryLocation { ast::Expr::Peek(ast::MemoryLocation {
span: span.clone(), span: span.clone(),
left: Box::new(left), left: Box::new(left),
@@ -517,7 +740,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.with_span(span) .with_span(span)
}); });
if let Some(((size, right), value)) = poke_op { if let Some(((size, right), value)) = poke_op {
let span = left.span.start..value.span.end; let span = (left.span.0, left.span.1.start..value.span.1.end);
ast::Expr::Poke { ast::Expr::Poke {
mem_location: ast::MemoryLocation { mem_location: ast::MemoryLocation {
span: span.clone(), span: span.clone(),
@@ -563,18 +786,9 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
let op_product = memory_op let op_product = memory_op
.clone() .clone()
.then( .then(product_op.clone().then(memory_op.clone()).repeated())
just(Token::Op("*".to_string()))
.to(ast::BinOp::Mul)
.or(just(Token::Op("/".to_string())).to(ast::BinOp::Div))
.or(just(Token::Op("#/".to_string())).to(ast::BinOp::DivU))
.or(just(Token::Op("%".to_string())).to(ast::BinOp::Rem))
.or(just(Token::Op("#%".to_string())).to(ast::BinOp::RemU))
.then(memory_op.clone())
.repeated(),
)
.foldl(|left, (op, right)| { .foldl(|left, (op, right)| {
let span = left.span.start..right.span.end; let span = (left.span.0, left.span.1.start..right.span.1.end);
ast::Expr::BinOp { ast::Expr::BinOp {
op, op,
left: Box::new(left), left: Box::new(left),
@@ -586,15 +800,9 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
let op_sum = op_product let op_sum = op_product
.clone() .clone()
.then( .then(sum_op.clone().then(op_product.clone()).repeated())
just(Token::Op("+".to_string()))
.to(ast::BinOp::Add)
.or(just(Token::Op("-".to_string())).to(ast::BinOp::Sub))
.then(op_product.clone())
.repeated(),
)
.foldl(|left, (op, right)| { .foldl(|left, (op, right)| {
let span = left.span.start..right.span.end; let span = (left.span.0, left.span.1.start..right.span.1.end);
ast::Expr::BinOp { ast::Expr::BinOp {
op, op,
left: Box::new(left), left: Box::new(left),
@@ -606,16 +814,9 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
let op_shift = op_sum let op_shift = op_sum
.clone() .clone()
.then( .then(shift_op.clone().then(op_sum.clone()).repeated())
just(Token::Op("<<".to_string()))
.to(ast::BinOp::Shl)
.or(just(Token::Op("#>>".to_string())).to(ast::BinOp::ShrU))
.or(just(Token::Op(">>".to_string())).to(ast::BinOp::ShrS))
.then(op_sum.clone())
.repeated(),
)
.foldl(|left, (op, right)| { .foldl(|left, (op, right)| {
let span = left.span.start..right.span.end; let span = (left.span.0, left.span.1.start..right.span.1.end);
ast::Expr::BinOp { ast::Expr::BinOp {
op, op,
left: Box::new(left), left: Box::new(left),
@@ -643,7 +844,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.repeated(), .repeated(),
) )
.foldl(|left, (op, right)| { .foldl(|left, (op, right)| {
let span = left.span.start..right.span.end; let span = (left.span.0, left.span.1.start..right.span.1.end);
ast::Expr::BinOp { ast::Expr::BinOp {
op, op,
left: Box::new(left), left: Box::new(left),
@@ -655,16 +856,9 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
let op_bit = op_cmp let op_bit = op_cmp
.clone() .clone()
.then( .then(bit_op.clone().then(op_cmp.clone()).repeated())
just(Token::Op("&".to_string()))
.to(ast::BinOp::And)
.or(just(Token::Op("|".to_string())).to(ast::BinOp::Or))
.or(just(Token::Op("^".to_string())).to(ast::BinOp::Xor))
.then(op_cmp.clone())
.repeated(),
)
.foldl(|left, (op, right)| { .foldl(|left, (op, right)| {
let span = left.span.start..right.span.end; let span = (left.span.0, left.span.1.start..right.span.1.end);
ast::Expr::BinOp { ast::Expr::BinOp {
op, op,
left: Box::new(left), left: Box::new(left),
@@ -682,7 +876,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.repeated(), .repeated(),
) )
.foldl(|left, right| { .foldl(|left, right| {
let span = left.span.start..right.span.end; let span = (left.span.0, left.span.1.start..right.span.1.end);
ast::Expr::First { ast::Expr::First {
value: Box::new(left), value: Box::new(left),
drop: Box::new(right), drop: Box::new(right),
@@ -696,11 +890,58 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
let block_expression = block_expression.unwrap(); let block_expression = block_expression.unwrap();
let assign = identifier
.then_ignore(just(Token::Op("=".to_string())))
.then(expression.clone())
.map(|(name, value)| ast::Expr::Assign {
name,
value: Box::new(value),
local_id: None,
})
.map_with_span(|expr, span| expr.with_span(span))
.boxed();
let assign_op = identifier
.then(
product_op
.clone()
.or(sum_op.clone())
.or(shift_op.clone())
.or(bit_op.clone()),
)
.then_ignore(just(Token::Op("=".to_string())))
.then(expression.clone())
.map_with_span(|((name, op), value), span| {
ast::Expr::Assign {
name: name.clone(),
value: Box::new(
ast::Expr::BinOp {
left: Box::new(
ast::Expr::Variable {
name,
local_id: None,
}
.with_span(span.clone()),
),
right: Box::new(value),
op,
}
.with_span(span.clone()),
),
local_id: None,
}
.with_span(span)
})
.boxed();
block_expression block_expression
.clone() .clone()
.then(just(Token::Ctrl(';')).or_not()) .then(just(Token::Ctrl(';')).or_not())
.map_with_span(|(expr, semi), span| (expr.with_span(span), semi.is_none())) .map_with_span(|(expr, semi), span| (expr.with_span(span), semi.is_none()))
.or(expression.clone().then(just(Token::Ctrl(';')).to(false))) .or(assign
.or(assign_op)
.or(expression.clone())
.then(just(Token::Ctrl(';')).to(false)))
.repeated() .repeated()
.then(expression.clone().or_not()) .then(expression.clone().or_not())
.map_with_span(|(mut statements, mut final_expression), span| { .map_with_span(|(mut statements, mut final_expression), span| {
@@ -830,6 +1071,21 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
}) })
.boxed(); .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())) let data_i8 = just(Token::Ident("i8".to_string()))
.to(ast::DataType::I8) .to(ast::DataType::I8)
.or(just(Token::Ident("i16".to_string())).to(ast::DataType::I16)) .or(just(Token::Ident("i16".to_string())).to(ast::DataType::I16))
@@ -875,35 +1131,43 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
}) })
.boxed(); .boxed();
import.or(function).or(global).or(data).boxed() let include =
just(Token::Ident("include".to_string())).ignore_then(string.clone().map_with_span(
|path, span| ast::TopLevelItem::Include(ast::Include { span, path }),
));
import
.or(function)
.or(global)
.or(data)
.or(include)
.or(global_const)
.boxed()
}; };
top_level_item.repeated().then_ignore(end()).map(|items| { top_level_item.repeated().then_ignore(end()).map(|items| {
let mut script = ast::Script { let mut script = ast::Script::default();
imports: Vec::new(),
global_vars: Vec::new(),
functions: Vec::new(),
data: Vec::new(),
};
for item in items { for item in items {
match item { match item {
ast::TopLevelItem::Import(i) => script.imports.push(i), ast::TopLevelItem::Import(i) => script.imports.push(i),
ast::TopLevelItem::GlobalVar(v) => script.global_vars.push(v), ast::TopLevelItem::GlobalVar(v) => script.global_vars.push(v),
ast::TopLevelItem::Function(f) => script.functions.push(f), ast::TopLevelItem::Function(f) => script.functions.push(f),
ast::TopLevelItem::Data(d) => script.data.push(d), 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 script
}) })
} }
fn type_parser() -> impl Parser<Token, ast::Type, Error = Simple<Token>> + Clone { fn type_parser() -> impl Parser<Token, ast::Type, Error = ScriptError> + Clone {
filter_map(|span, tok| match tok { filter_map(|span, tok| match tok {
Token::Ident(id) if id == "i32" => Ok(ast::Type::I32), Token::Ident(id) if id == "i32" => Ok(ast::Type::I32),
Token::Ident(id) if id == "i64" => Ok(ast::Type::I64), Token::Ident(id) if id == "i64" => Ok(ast::Type::I64),
Token::Ident(id) if id == "f32" => Ok(ast::Type::F32), Token::Ident(id) if id == "f32" => Ok(ast::Type::F32),
Token::Ident(id) if id == "f64" => Ok(ast::Type::F64), Token::Ident(id) if id == "f64" => Ok(ast::Type::F64),
_ => Err(Simple::expected_input_found( _ => Err(ScriptError::expected_input_found(
span, span,
vec![ vec![
Some(Token::Ident("i32".into())), Some(Token::Ident("i32".into())),

View File

@@ -1,9 +1,9 @@
use ariadne::{Color, Label, Report, ReportKind, Source}; use ariadne::{Color, Label, Report, ReportKind};
use std::collections::HashMap; use std::collections::HashMap;
use crate::ast::{self, MemSize}; use crate::ast::{self, MemSize};
use crate::intrinsics::Intrinsics; use crate::intrinsics::Intrinsics;
use crate::Span; use crate::parser::{Sources, Span};
use ast::Type::*; use ast::Type::*;
type Result<T> = std::result::Result<T, ()>; type Result<T> = std::result::Result<T, ()>;
@@ -15,9 +15,9 @@ struct Var {
} }
type Vars = HashMap<String, Var>; type Vars = HashMap<String, Var>;
pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> { pub fn tc_script(script: &mut ast::Script, sources: &Sources) -> Result<()> {
let mut context = Context { let mut context = Context {
source, sources,
global_vars: HashMap::new(), global_vars: HashMap::new(),
functions: HashMap::new(), functions: HashMap::new(),
locals: ast::Locals::default(), locals: ast::Locals::default(),
@@ -41,7 +41,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
"Global already defined", "Global already defined",
&import.span, &import.span,
span, span,
source, sources,
); );
} else { } else {
context.global_vars.insert( context.global_vars.insert(
@@ -64,7 +64,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
"Function already defined", "Function already defined",
&import.span, &import.span,
&fnc.span, &fnc.span,
source, sources,
); );
} else { } else {
context.functions.insert( context.functions.insert(
@@ -83,12 +83,12 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
for v in &mut 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, sources);
} else { } else {
tc_const(&mut v.value, source)?; tc_const(&mut v.value, sources)?;
if v.type_ != v.value.type_ { if v.type_ != v.value.type_ {
if v.type_.is_some() { if v.type_.is_some() {
result = type_mismatch(v.type_, &v.span, v.value.type_, &v.value.span, source); result = type_mismatch(v.type_, &v.span, v.value.type_, &v.value.span, sources);
} else { } else {
v.type_ = v.value.type_; v.type_ = v.value.type_;
} }
@@ -104,11 +104,26 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
} }
} }
for c in &mut script.consts {
tc_const(&mut c.value, sources)?;
if c.value.type_ != c.type_ {
if c.type_.is_some() {
result = type_mismatch(c.type_, &c.span, c.value.type_, &c.value.span, sources);
} else {
c.type_ = c.value.type_;
}
}
}
for f in &script.functions { for f in &script.functions {
let params = f.params.iter().map(|(_, t)| *t).collect(); let params = f.params.iter().map(|(_, t)| *t).collect();
if let Some(fnc) = context.functions.get(&f.name) { if let Some(fnc) = context.functions.get(&f.name) {
result = result = report_duplicate_definition(
report_duplicate_definition("Function already defined", &f.span, &fnc.span, source); "Function already defined",
&f.span,
&fnc.span,
sources,
);
} else { } else {
context.functions.insert( context.functions.insert(
f.name.clone(), f.name.clone(),
@@ -132,7 +147,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
.or_else(|| context.global_vars.get(name).map(|v| &v.span)) .or_else(|| context.global_vars.get(name).map(|v| &v.span))
{ {
result = result =
report_duplicate_definition("Variable already defined", &f.span, span, source); report_duplicate_definition("Variable already defined", &f.span, span, sources);
} else { } else {
context.local_vars.insert( context.local_vars.insert(
name.clone(), name.clone(),
@@ -163,7 +178,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
f.locals = std::mem::take(&mut context.locals); f.locals = std::mem::take(&mut context.locals);
if f.body.type_ != f.type_ { if f.body.type_ != f.type_ {
result = type_mismatch(f.type_, &f.span, f.body.type_, &f.body.span, source); result = type_mismatch(f.type_, &f.span, f.body.type_, &f.body.span, sources);
} }
} }
@@ -171,7 +186,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
for f in &script.functions { for f in &script.functions {
if f.start { if f.start {
if !f.params.is_empty() || f.type_.is_some() { if !f.params.is_empty() || f.type_.is_some() {
Report::build(ReportKind::Error, (), f.span.start) Report::build(ReportKind::Error, f.span.0, f.span.1.start)
.with_message("Start function can't have params or a return value") .with_message("Start function can't have params or a return value")
.with_label( .with_label(
Label::new(f.span.clone()) Label::new(f.span.clone())
@@ -179,7 +194,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
.with_color(Color::Red), .with_color(Color::Red),
) )
.finish() .finish()
.eprint(Source::from(source)) .eprint(sources)
.unwrap(); .unwrap();
result = Err(()); result = Err(());
@@ -189,7 +204,7 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
"Start function already defined", "Start function already defined",
&f.span, &f.span,
&prev.span, &prev.span,
source, sources,
); );
} else { } else {
start_function = Some(f); start_function = Some(f);
@@ -198,14 +213,14 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
} }
for data in &mut script.data { for data in &mut script.data {
tc_const(&mut data.offset, source)?; tc_const(&mut data.offset, sources)?;
if data.offset.type_ != Some(I32) { if data.offset.type_ != Some(I32) {
result = type_mismatch( result = type_mismatch(
Some(I32), Some(I32),
&data.offset.span, &data.offset.span,
data.offset.type_, data.offset.type_,
&data.offset.span, &data.offset.span,
source, sources,
); );
} }
for values in &mut data.data { for values in &mut data.data {
@@ -220,14 +235,14 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
ast::DataType::F64 => ast::Type::F64, ast::DataType::F64 => ast::Type::F64,
}; };
for value in values { for value in values {
tc_const(value, source)?; tc_const(value, sources)?;
if value.type_ != Some(needed_type) { if value.type_ != Some(needed_type) {
result = type_mismatch( result = type_mismatch(
Some(needed_type), Some(needed_type),
&value.span, &value.span,
value.type_, value.type_,
&value.span, &value.span,
source, sources,
); );
} }
} }
@@ -247,7 +262,7 @@ struct FunctionType {
} }
struct Context<'a> { struct Context<'a> {
source: &'a str, sources: &'a Sources,
global_vars: Vars, global_vars: Vars,
functions: HashMap<String, FunctionType>, functions: HashMap<String, FunctionType>,
locals: ast::Locals, locals: ast::Locals,
@@ -294,13 +309,13 @@ impl LocalVars {
} }
} }
fn report_duplicate_definition( pub fn report_duplicate_definition(
msg: &str, msg: &str,
span: &Span, span: &Span,
prev_span: &Span, prev_span: &Span,
source: &str, sources: &Sources,
) -> Result<()> { ) -> Result<()> {
Report::build(ReportKind::Error, (), span.start) Report::build(ReportKind::Error, span.0, span.1.start)
.with_message(msg) .with_message(msg)
.with_label( .with_label(
Label::new(span.clone()) Label::new(span.clone())
@@ -313,7 +328,7 @@ fn report_duplicate_definition(
.with_color(Color::Yellow), .with_color(Color::Yellow),
) )
.finish() .finish()
.eprint(Source::from(source)) .eprint(sources)
.unwrap(); .unwrap();
Err(()) Err(())
} }
@@ -323,9 +338,9 @@ fn type_mismatch(
span1: &Span, span1: &Span,
type2: Option<ast::Type>, type2: Option<ast::Type>,
span2: &Span, span2: &Span,
source: &str, sources: &Sources,
) -> Result<()> { ) -> Result<()> {
Report::build(ReportKind::Error, (), span2.start) Report::build(ReportKind::Error, span2.0, span2.1.start)
.with_message("Type mismatch") .with_message("Type mismatch")
.with_label( .with_label(
Label::new(span1.clone()) Label::new(span1.clone())
@@ -348,13 +363,13 @@ fn type_mismatch(
.with_color(Color::Red), .with_color(Color::Red),
) )
.finish() .finish()
.eprint(Source::from(source)) .eprint(sources)
.unwrap(); .unwrap();
Err(()) Err(())
} }
fn report_error(msg: &str, span: &Span, source: &str) -> Result<()> { pub fn report_error(msg: &str, span: &Span, sources: &Sources) -> Result<()> {
Report::build(ReportKind::Error, (), span.start) Report::build(ReportKind::Error, span.0, span.1.start)
.with_message(msg) .with_message(msg)
.with_label( .with_label(
Label::new(span.clone()) Label::new(span.clone())
@@ -362,29 +377,29 @@ fn report_error(msg: &str, span: &Span, source: &str) -> Result<()> {
.with_color(Color::Red), .with_color(Color::Red),
) )
.finish() .finish()
.eprint(Source::from(source)) .eprint(sources)
.unwrap(); .unwrap();
Err(()) Err(())
} }
fn expected_type(span: &Span, source: &str) -> Result<()> { fn expected_type(span: &Span, sources: &Sources) -> Result<()> {
report_error( report_error(
"Expected value but found expression of type void", "Expected value but found expression of type void",
span, span,
source, sources,
) )
} }
fn unknown_variable(span: &Span, source: &str) -> Result<()> { fn unknown_variable(span: &Span, sources: &Sources) -> Result<()> {
report_error("Unknown variable", span, source) report_error("Unknown variable", span, sources)
} }
fn immutable_assign(span: &Span, source: &str) -> Result<()> { fn immutable_assign(span: &Span, sources: &Sources) -> Result<()> {
report_error("Trying to assign to immutable variable", span, source) report_error("Trying to assign to immutable variable", span, sources)
} }
fn missing_label(span: &Span, source: &str) -> Result<()> { fn missing_label(span: &Span, sources: &Sources) -> Result<()> {
report_error("Label not found", span, source) report_error("Label not found", span, sources)
} }
fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()> { fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()> {
@@ -423,11 +438,11 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&expr.span, &expr.span,
value.type_, value.type_,
&value.span, &value.span,
context.source, context.sources,
); );
} }
} else if value.type_.is_none() { } else if value.type_.is_none() {
return expected_type(&value.span, context.source); return expected_type(&value.span, context.sources);
} else { } else {
*type_ = value.type_; *type_ = value.type_;
} }
@@ -449,7 +464,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
*local_id = Some(id); *local_id = Some(id);
context.local_vars.insert(name.clone(), id); context.local_vars.insert(name.clone(), id);
} else { } else {
return report_error("Type missing", &expr.span, context.source); return report_error("Type missing", &expr.span, context.sources);
} }
None None
} }
@@ -477,7 +492,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&expr.span, &expr.span,
value.type_, value.type_,
&value.span, &value.span,
context.source, context.sources,
); );
} }
None None
@@ -489,7 +504,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
ast::Expr::UnaryOp { op, ref mut value } => { ast::Expr::UnaryOp { op, ref mut value } => {
tc_expression(context, value)?; tc_expression(context, value)?;
if value.type_.is_none() { if value.type_.is_none() {
return expected_type(&value.span, context.source); return expected_type(&value.span, context.sources);
} }
use ast::Type::*; use ast::Type::*;
use ast::UnaryOp::*; use ast::UnaryOp::*;
@@ -502,7 +517,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&expr.span, &expr.span,
value.type_, value.type_,
&value.span, &value.span,
context.source, context.sources,
) )
} }
}) })
@@ -521,11 +536,11 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&left.span, &left.span,
right.type_, right.type_,
&right.span, &right.span,
context.source, context.sources,
); );
} }
} else { } else {
return expected_type(&left.span, context.source); return expected_type(&left.span, context.sources);
} }
use ast::BinOp::*; use ast::BinOp::*;
match op { match op {
@@ -537,7 +552,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&left.span, &left.span,
left.type_, left.type_,
&left.span, &left.span,
context.source, context.sources,
); );
} else { } else {
left.type_ left.type_
@@ -551,7 +566,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&left.span, &left.span,
left.type_, left.type_,
&left.span, &left.span,
context.source, context.sources,
); );
} else { } else {
Some(I32) Some(I32)
@@ -569,7 +584,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
} else if let Some(&Var { type_, .. }) = context.global_vars.get(name) { } else if let Some(&Var { type_, .. }) = context.global_vars.get(name) {
Some(type_) Some(type_)
} else { } else {
return unknown_variable(&expr.span, context.source); return unknown_variable(&expr.span, context.sources);
} }
} }
ast::Expr::Assign { ast::Expr::Assign {
@@ -583,7 +598,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
*local_id = Some(id); *local_id = Some(id);
let local = &context.locals[id]; let local = &context.locals[id];
if local.index.is_none() { if local.index.is_none() {
return immutable_assign(&expr.span, context.source); return immutable_assign(&expr.span, context.sources);
} }
(local.type_, &local.span) (local.type_, &local.span)
} else if let Some(&Var { } else if let Some(&Var {
@@ -593,15 +608,15 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
}) = context.global_vars.get(name) }) = context.global_vars.get(name)
{ {
if !mutable { if !mutable {
return immutable_assign(&expr.span, context.source); return immutable_assign(&expr.span, context.sources);
} }
(type_, span) (type_, span)
} else { } else {
return unknown_variable(&expr.span, context.source); return unknown_variable(&expr.span, context.sources);
}; };
if value.type_ != Some(type_) { if value.type_ != Some(type_) {
return type_mismatch(Some(type_), span, value.type_, &value.span, context.source); return type_mismatch(Some(type_), span, value.type_, &value.span, context.sources);
} }
None None
} }
@@ -616,7 +631,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
let local = &context.locals[id]; let local = &context.locals[id];
if local.index.is_none() { if local.index.is_none() {
return immutable_assign(&expr.span, context.source); return immutable_assign(&expr.span, context.sources);
} }
if value.type_ != Some(local.type_) { if value.type_ != Some(local.type_) {
@@ -625,13 +640,13 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&local.span, &local.span,
value.type_, value.type_,
&value.span, &value.span,
context.source, context.sources,
); );
} }
Some(local.type_) Some(local.type_)
} else { } else {
return unknown_variable(&expr.span, context.source); return unknown_variable(&expr.span, context.sources);
} }
} }
ast::Expr::Loop { ast::Expr::Loop {
@@ -652,13 +667,13 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
context.block_stack.pop(); context.block_stack.pop();
if block.type_ != None { if block.type_ != None {
// TODO: implement, requires branches to optionally provide values // TODO: implement, requires branches to optionally provide values
return type_mismatch(None, &expr.span, block.type_, &block.span, context.source); return type_mismatch(None, &expr.span, block.type_, &block.span, context.sources);
} }
None None
} }
ast::Expr::Branch(ref label) => { ast::Expr::Branch(ref label) => {
if !context.block_stack.contains(label) { if !context.block_stack.contains(label) {
return missing_label(&expr.span, context.source); return missing_label(&expr.span, context.sources);
} }
None None
} }
@@ -673,11 +688,11 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&expr.span, &expr.span,
condition.type_, condition.type_,
&condition.span, &condition.span,
context.source, context.sources,
); );
} }
if !context.block_stack.contains(label) { if !context.block_stack.contains(label) {
return missing_label(&expr.span, context.source); return missing_label(&expr.span, context.sources);
} }
None None
} }
@@ -687,7 +702,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
} => { } => {
tc_expression(context, value)?; tc_expression(context, value)?;
if value.type_.is_none() { if value.type_.is_none() {
return expected_type(&expr.span, context.source); return expected_type(&expr.span, context.sources);
} }
Some(type_) Some(type_)
} }
@@ -698,7 +713,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
for param in params.iter_mut() { for param in params.iter_mut() {
tc_expression(context, param)?; tc_expression(context, param)?;
if param.type_.is_none() { if param.type_.is_none() {
return expected_type(&param.span, context.source); return expected_type(&param.span, context.sources);
} }
} }
if let Some(load) = context.intrinsics.find_load(name) { if let Some(load) = context.intrinsics.find_load(name) {
@@ -713,11 +728,11 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&expr.span, &expr.span,
value.type_, value.type_,
&value.span, &value.span,
context.source, context.sources,
)?; )?;
} }
} else { } else {
return report_error("Missing parameters", &expr.span, context.source); return report_error("Missing parameters", &expr.span, context.sources);
} }
tc_memarg(context, &mut params[1..], &expr.span)?; tc_memarg(context, &mut params[1..], &expr.span)?;
None None
@@ -732,7 +747,8 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
{ {
*rtype *rtype
} else { } else {
let mut report = Report::build(ReportKind::Error, (), expr.span.start) let mut report =
Report::build(ReportKind::Error, expr.span.0, expr.span.1.start)
.with_message("No matching function found"); .with_message("No matching function found");
for (params, rtype) in type_map { for (params, rtype) in type_map {
let param_str: Vec<_> = params.into_iter().map(|t| t.to_string()).collect(); let param_str: Vec<_> = params.into_iter().map(|t| t.to_string()).collect();
@@ -748,17 +764,14 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
); );
report = report.with_label(Label::new(expr.span.clone()).with_message(msg)); report = report.with_label(Label::new(expr.span.clone()).with_message(msg));
} }
report report.finish().eprint(context.sources).unwrap();
.finish()
.eprint(Source::from(context.source))
.unwrap();
return Err(()); return Err(());
} }
} else { } else {
return report_error( return report_error(
&format!("Unknown function {}", name), &format!("Unknown function {}", name),
&expr.span, &expr.span,
context.source, context.sources,
); );
} }
} }
@@ -776,7 +789,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&condition.span, &condition.span,
condition.type_, condition.type_,
&condition.span, &condition.span,
context.source, context.sources,
); );
} }
if if_true.type_.is_some() { if if_true.type_.is_some() {
@@ -786,11 +799,11 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&if_true.span, &if_true.span,
if_false.type_, if_false.type_,
&if_false.span, &if_false.span,
context.source, context.sources,
); );
} }
} else { } else {
return expected_type(&if_true.span, context.source); return expected_type(&if_true.span, context.sources);
} }
if_true.type_ if_true.type_
} }
@@ -809,7 +822,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&if_true.span, &if_true.span,
if_false.type_, if_false.type_,
&if_false.span, &if_false.span,
context.source, context.sources,
); );
} else { } else {
if_true.type_ if_true.type_
@@ -827,7 +840,7 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
&expr.span, &expr.span,
value.type_, value.type_,
&value.span, &value.span,
context.source, context.sources,
); );
} }
} }
@@ -851,14 +864,14 @@ 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_const(&mut mem_location.right, context.source)?; tc_const(&mut mem_location.right, context.sources)?;
if mem_location.left.type_ != Some(I32) { if mem_location.left.type_ != Some(I32) {
return type_mismatch( return type_mismatch(
Some(I32), Some(I32),
&mem_location.left.span, &mem_location.left.span,
mem_location.left.type_, mem_location.left.type_,
&mem_location.left.span, &mem_location.left.span,
context.source, context.sources,
); );
} }
if mem_location.right.type_ != Some(I32) { if mem_location.right.type_ != Some(I32) {
@@ -867,20 +880,20 @@ fn tc_mem_location<'a>(
&mem_location.right.span, &mem_location.right.span,
mem_location.right.type_, mem_location.right.type_,
&mem_location.right.span, &mem_location.right.span,
context.source, context.sources,
); );
} }
Ok(()) Ok(())
} }
fn tc_const(expr: &mut ast::Expression, source: &str) -> Result<()> { fn tc_const(expr: &mut ast::Expression, sources: &Sources) -> Result<()> {
use ast::Expr::*; use ast::Expr::*;
expr.type_ = Some(match expr.expr { expr.type_ = Some(match expr.expr {
I32Const(_) => I32, I32Const(_) => I32,
I64Const(_) => I64, I64Const(_) => I64,
F32Const(_) => F32, F32Const(_) => F32,
F64Const(_) => F64, F64Const(_) => F64,
_ => return report_error("Expected constant value", &expr.span, source), _ => return report_error("Expected constant value", &expr.span, sources),
}); });
Ok(()) Ok(())
} }
@@ -892,16 +905,16 @@ fn tc_memarg(context: &mut Context, params: &mut [ast::Expression], span: &Span)
} else { } else {
"Too many MemArg parameters" "Too many MemArg parameters"
}; };
return report_error(msg, span, context.source); return report_error(msg, span, context.sources);
} }
for (index, param) in params.iter_mut().enumerate() { for (index, param) in params.iter_mut().enumerate() {
tc_expression(context, param)?; tc_expression(context, param)?;
if param.type_ != Some(I32) { if param.type_ != Some(I32) {
return type_mismatch(Some(I32), &span, param.type_, &param.span, context.source); return type_mismatch(Some(I32), &span, param.type_, &param.span, context.sources);
} }
if index > 0 { if index > 0 {
tc_const(param, context.source)?; tc_const(param, context.sources)?;
} }
if index == 2 { if index == 2 {
let align = param.const_i32(); let align = param.const_i32();
@@ -909,7 +922,7 @@ fn tc_memarg(context: &mut Context, params: &mut [ast::Expression], span: &Span)
return report_error( return report_error(
&format!("Alignment {} out of range (0-4)", align), &format!("Alignment {} out of range (0-4)", align),
&param.span, &param.span,
context.source, context.sources,
); );
} }
} }

15
test/xorshift.cwa Normal file
View File

@@ -0,0 +1,15 @@
// simple test to see whether lazy/inline chains with the same variable compile correctly
fn xorshift(x: i32) -> i32 {
let lazy x = x ^ (x << 13);
let lazy x = x ^ (x #>> 17);
let inline x = x ^ (x << 5);
x
}
fn xorshift2(x: i32) -> i32 {
x ^= x << 13;
x ^= x #>> 17;
x ^= x << 5;
x
}