mirror of
https://github.com/exoticorn/curlywas.git
synced 2026-01-20 19:56:42 +01:00
Compare commits
22 Commits
e58b13c8ee
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e7ea508cd | |||
| 0a0d90c801 | |||
| c22297ea82 | |||
| c59b35f9c6 | |||
| 2cf47085c1 | |||
| 01d64baaab | |||
| a52fe53a01 | |||
| 1e746be750 | |||
| 4cfc7ae8a8 | |||
| aac7bbd878 | |||
| ebc701e2f2 | |||
| 557c3a8426 | |||
| 896385654a | |||
| cda3eb868b | |||
| 5f316cf17d | |||
| e608d3bb4b | |||
| b41b7f250c | |||
| 71de622634 | |||
| f433948d4e | |||
| 132aea3996 | |||
| 1b434f6b30 | |||
| ce8435e3dc |
24
Cargo.lock
generated
24
Cargo.lock
generated
@@ -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"
|
||||||
@@ -34,9 +34,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chumsky"
|
name = "chumsky"
|
||||||
version = "0.5.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2d3efff85e8572b1c3fa0127706af58c4fff8458f8d9436d54b1e97573c7a3f"
|
checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
]
|
]
|
||||||
@@ -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"
|
||||||
@@ -139,18 +139,18 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-encoder"
|
name = "wasm-encoder"
|
||||||
version = "0.8.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db0c351632e46cc06a58a696a6c11e4cf90cad4b9f8f07a0b59128d616c29bb0"
|
checksum = "aa9d9bf45fc46f71c407837c9b30b1e874197f2dc357588430b21e5017d290ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"leb128",
|
"leb128",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmparser"
|
name = "wasmparser"
|
||||||
version = "0.81.0"
|
version = "0.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc"
|
checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yansi"
|
name = "yansi"
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ license = "MIT"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmparser = "0.81"
|
wasmparser = "0.83"
|
||||||
wasm-encoder = "0.8"
|
wasm-encoder = "0.10"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
chumsky = "0.5"
|
chumsky = "0.8"
|
||||||
ariadne = "0.1"
|
ariadne = "0.1"
|
||||||
pico-args = "0.4"
|
pico-args = "0.4"
|
||||||
|
|||||||
129
README.md
129
README.md
@@ -56,9 +56,17 @@ 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:
|
||||||
|
|
||||||
* `i32`: 32bit integer
|
* `i32`: 32bit integer
|
||||||
* `i64`: 64bit integer
|
* `i64`: 64bit integer
|
||||||
@@ -81,19 +89,28 @@ 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"`.
|
||||||
|
|
||||||
Linear memory can be imported like this:
|
Linear memory can be imported like this:
|
||||||
|
|
||||||
@@ -124,6 +141,32 @@ import "env.random" rand() -> i32; // no params
|
|||||||
import "env.atan2" atan2(f32, f32) -> f32;
|
import "env.atan2" atan2(f32, f32) -> f32;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Global variables
|
||||||
|
|
||||||
|
Global variables are declare like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
global name[: type] = value; // immutable global value
|
||||||
|
global mut name[: type] = initial_value; // mutable variable
|
||||||
|
```
|
||||||
|
|
||||||
|
An immutable global is probably of very limited use, as usually you'd most often use it by exporting it so that some other module
|
||||||
|
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.
|
||||||
|
|
||||||
|
### 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:
|
||||||
@@ -261,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
|
||||||
@@ -270,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.
|
||||||
|
|
||||||
@@ -282,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
|
||||||
|
|
||||||
@@ -298,7 +405,7 @@ Sometimes when sizeoptimizing it helps to be able to execute some side-effecty c
|
|||||||
Using a block scope, we can execute any number of statements before evaluating a final expression to an actual value. For example:
|
Using a block scope, we can execute any number of statements before evaluating a final expression to an actual value. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
let x = { randomSeed(time); random() }; // set the random seed right before optaining a random value
|
let x = { randomSeed(time); random() }; // set the random seed right before obtaining a random value
|
||||||
```
|
```
|
||||||
|
|
||||||
To execute something after evaluating the value we want to return, we can use the `<|` operator. Here is an example from the Wasm4 version of
|
To execute something after evaluating the value we want to return, we can use the `<|` operator. Here is an example from the Wasm4 version of
|
||||||
@@ -319,11 +426,11 @@ after drawing a rectangle with color `c` and setting the color for the text to `
|
|||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
The idea of Curlywas is to be able to hand-craft any valid WASM program, ie. having the same amount of control over the instruction sequence as if you would write in the web assembly text format (`.wat`) just with better ergonomics.
|
The idea of CurlyWas is to be able to hand-craft any valid WASM program, ie. having the same amount of control over the instruction sequence as if you would write in the web assembly text format (`.wat`) just with better ergonomics.
|
||||||
|
|
||||||
This goal is not yet fully reached, with the following being the main limitations:
|
This goal is not yet fully reached, with the following being the main limitations:
|
||||||
|
|
||||||
* Curlywas currently only targets MVP web assembly + non-trapping float-to-int conversions. No other post-MVP features are currently supported. Especially "Multi-value" will be problematic as this allows programs that don't map cleanly to an expression tree.
|
* CurlyWas currently only targets MVP web assembly + non-trapping float-to-int conversions. No other post-MVP features are currently supported. Especially "Multi-value" will be problematic as this allows programs that don't map cleanly to an expression tree.
|
||||||
* Memory intrinsics are still missing, so only (unsigned) 8 and 32 bit integer reads and writes are possible.
|
* Memory intrinsics are still missing, so only (unsigned) 8 and 32 bit integer reads and writes are possible.
|
||||||
* `block`s cannot return values, as the branch instructions are missing syntax to pass along a value.
|
* `block`s cannot return values, as the branch instructions are missing syntax to pass along a value.
|
||||||
* `br_table` and `call_indirect` are not yet implemented.
|
* `br_table` and `call_indirect` are not yet implemented.
|
||||||
16
examples/microw8/mem_intrinsics.cwa
Normal file
16
examples/microw8/mem_intrinsics.cwa
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import "env.memory" memory(4);
|
||||||
|
import "env.random" fn random() -> i32;
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
let i: i32;
|
||||||
|
loop pixels {
|
||||||
|
let inline left = i32.load8_u((i + (320*240 - 319)) % (320*240), 120);
|
||||||
|
let inline top = i32.load8_u((i + 320*239) % (320*240), 120, 0);
|
||||||
|
let inline here = i32.load16_u(i, 119);
|
||||||
|
let inline all = (left << 24) | (top << 16) | here;
|
||||||
|
let lazy r = random();
|
||||||
|
let inline new = (all #>> ((r & 3) * 8)) ^ ((r & 31) * !(r #>> 22));
|
||||||
|
i32.store8(new, i, 120);
|
||||||
|
branch_if (i := i + 1) < 320*240: pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
24
examples/microw8/print.cwa
Normal file
24
examples/microw8/print.cwa
Normal 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"
|
||||||
|
}
|
||||||
@@ -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
13
examples/microw8/uw8.cwa
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import "env.memory" memory(4);
|
||||||
|
|
||||||
|
import "env.pow" fn pow(f32, f32) -> f32;
|
||||||
|
import "env.sin" fn sin(f32) -> f32;
|
||||||
|
import "env.cos" fn cos(f32) -> f32;
|
||||||
|
import "env.atan2" fn atan2(f32, f32) -> f32;
|
||||||
|
import "env.tan" fn tan(f32) -> f32;
|
||||||
|
import "env.atan" fn atan(f32) -> f32;
|
||||||
|
import "env.rectangle" fn rect(f32, f32, f32, f32, i32);
|
||||||
|
|
||||||
|
const FRAMEBUFFER = 120;
|
||||||
|
const PALETTE = 0x13000;
|
||||||
|
const FONT = 0x13400;
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
49
src/ast.rs
49
src/ast.rs
@@ -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>,
|
||||||
@@ -288,7 +324,7 @@ impl Expr {
|
|||||||
Expression {
|
Expression {
|
||||||
type_: None,
|
type_: None,
|
||||||
expr: self,
|
expr: self,
|
||||||
span: span,
|
span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,6 +373,7 @@ pub enum BinOp {
|
|||||||
pub enum MemSize {
|
pub enum MemSize {
|
||||||
Byte,
|
Byte,
|
||||||
Word,
|
Word,
|
||||||
|
Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
|||||||
160
src/constfold.rs
160
src/constfold.rs
@@ -1,35 +1,103 @@
|
|||||||
use crate::ast;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast,
|
||||||
|
parser::{Sources, Span},
|
||||||
|
typecheck::{report_duplicate_definition, report_error},
|
||||||
|
};
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, ()>;
|
||||||
|
|
||||||
|
pub fn fold_script(script: &mut ast::Script, sources: &Sources) -> Result<()> {
|
||||||
|
let mut context = Context {
|
||||||
|
consts: HashMap::new(),
|
||||||
|
sources,
|
||||||
|
};
|
||||||
|
fold_consts(&mut context, &mut script.consts)?;
|
||||||
|
|
||||||
pub fn fold_script(script: &mut ast::Script) {
|
|
||||||
for var in &mut script.global_vars {
|
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!(),
|
||||||
}
|
}
|
||||||
|
|||||||
68
src/emit.rs
68
src/emit.rs
@@ -6,7 +6,11 @@ use wasm_encoder::{
|
|||||||
MemArg, MemoryType, Module, NameMap, NameSection, StartSection, TypeSection, ValType,
|
MemArg, MemoryType, Module, NameMap, NameSection, StartSection, TypeSection, ValType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{ast, intrinsics::Intrinsics, Options};
|
use crate::{
|
||||||
|
ast,
|
||||||
|
intrinsics::{Intrinsics, MemInstruction},
|
||||||
|
Options,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn emit(script: &ast::Script, module_name: &str, options: &Options) -> Vec<u8> {
|
pub fn emit(script: &ast::Script, module_name: &str, options: &Options) -> Vec<u8> {
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
@@ -65,9 +69,7 @@ pub fn emit(script: &ast::Script, module_name: &str, options: &Options) -> Vec<u
|
|||||||
} => {
|
} => {
|
||||||
function_map.insert(name.clone(), function_map.len() as u32);
|
function_map.insert(name.clone(), function_map.len() as u32);
|
||||||
EntityType::Function(
|
EntityType::Function(
|
||||||
*function_types
|
*function_types.get(&(params.clone(), *result)).unwrap() as u32
|
||||||
.get(&(params.clone(), result.clone()))
|
|
||||||
.unwrap() as u32,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -254,9 +256,7 @@ fn collect_function_types(script: &ast::Script) -> HashMap<FunctionTypeKey, usiz
|
|||||||
} = import.type_
|
} = import.type_
|
||||||
{
|
{
|
||||||
let index = types.len();
|
let index = types.len();
|
||||||
types
|
types.entry((params.clone(), *result)).or_insert(index);
|
||||||
.entry((params.clone(), result.clone()))
|
|
||||||
.or_insert(index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,6 +346,11 @@ fn mem_arg_for_location(mem_location: &ast::MemoryLocation) -> MemArg {
|
|||||||
memory_index: 0,
|
memory_index: 0,
|
||||||
offset,
|
offset,
|
||||||
},
|
},
|
||||||
|
ast::MemSize::Float => MemArg {
|
||||||
|
align: 2,
|
||||||
|
memory_index: 0,
|
||||||
|
offset,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,6 +399,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
ctx.function.instruction(&match mem_location.size {
|
ctx.function.instruction(&match mem_location.size {
|
||||||
ast::MemSize::Byte => Instruction::I32Load8_U(mem_arg),
|
ast::MemSize::Byte => Instruction::I32Load8_U(mem_arg),
|
||||||
ast::MemSize::Word => Instruction::I32Load(mem_arg),
|
ast::MemSize::Word => Instruction::I32Load(mem_arg),
|
||||||
|
ast::MemSize::Float => Instruction::F32Load(mem_arg),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ast::Expr::Poke {
|
ast::Expr::Poke {
|
||||||
@@ -403,6 +412,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
ctx.function.instruction(&match mem_location.size {
|
ctx.function.instruction(&match mem_location.size {
|
||||||
ast::MemSize::Byte => Instruction::I32Store8(mem_arg),
|
ast::MemSize::Byte => Instruction::I32Store8(mem_arg),
|
||||||
ast::MemSize::Word => Instruction::I32Store(mem_arg),
|
ast::MemSize::Word => Instruction::I32Store(mem_arg),
|
||||||
|
ast::MemSize::Float => Instruction::F32Store(mem_arg),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ast::Expr::UnaryOp { op, value } => {
|
ast::Expr::UnaryOp { op, value } => {
|
||||||
@@ -457,7 +467,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
(I32, Or) => Instruction::I32Or,
|
(I32, Or) => Instruction::I32Or,
|
||||||
(I32, Xor) => Instruction::I32Xor,
|
(I32, Xor) => Instruction::I32Xor,
|
||||||
(I32, Eq) => Instruction::I32Eq,
|
(I32, Eq) => Instruction::I32Eq,
|
||||||
(I32, Ne) => Instruction::I32Neq,
|
(I32, Ne) => Instruction::I32Ne,
|
||||||
(I32, Lt) => Instruction::I32LtS,
|
(I32, Lt) => Instruction::I32LtS,
|
||||||
(I32, LtU) => Instruction::I32LtU,
|
(I32, LtU) => Instruction::I32LtU,
|
||||||
(I32, Le) => Instruction::I32LeS,
|
(I32, Le) => Instruction::I32LeS,
|
||||||
@@ -481,7 +491,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
(I64, Or) => Instruction::I64Or,
|
(I64, Or) => Instruction::I64Or,
|
||||||
(I64, Xor) => Instruction::I64Xor,
|
(I64, Xor) => Instruction::I64Xor,
|
||||||
(I64, Eq) => Instruction::I64Eq,
|
(I64, Eq) => Instruction::I64Eq,
|
||||||
(I64, Ne) => Instruction::I64Neq,
|
(I64, Ne) => Instruction::I64Ne,
|
||||||
(I64, Lt) => Instruction::I64LtS,
|
(I64, Lt) => Instruction::I64LtS,
|
||||||
(I64, LtU) => Instruction::I64LtU,
|
(I64, LtU) => Instruction::I64LtU,
|
||||||
(I64, Le) => Instruction::I64LeS,
|
(I64, Le) => Instruction::I64LeS,
|
||||||
@@ -503,7 +513,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
DivU | Rem | RemU | And | Or | Xor | Shl | ShrU | ShrS | LtU | LeU | GtU | GeU,
|
DivU | Rem | RemU | And | Or | Xor | Shl | ShrU | ShrS | LtU | LeU | GtU | GeU,
|
||||||
) => unreachable!(),
|
) => unreachable!(),
|
||||||
(F32, Eq) => Instruction::F32Eq,
|
(F32, Eq) => Instruction::F32Eq,
|
||||||
(F32, Ne) => Instruction::F32Neq,
|
(F32, Ne) => Instruction::F32Ne,
|
||||||
(F32, Lt) => Instruction::F32Lt,
|
(F32, Lt) => Instruction::F32Lt,
|
||||||
(F32, Le) => Instruction::F32Le,
|
(F32, Le) => Instruction::F32Le,
|
||||||
(F32, Gt) => Instruction::F32Gt,
|
(F32, Gt) => Instruction::F32Gt,
|
||||||
@@ -518,7 +528,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
DivU | Rem | RemU | And | Or | Xor | Shl | ShrU | ShrS | LtU | LeU | GtU | GeU,
|
DivU | Rem | RemU | And | Or | Xor | Shl | ShrU | ShrS | LtU | LeU | GtU | GeU,
|
||||||
) => unreachable!(),
|
) => unreachable!(),
|
||||||
(F64, Eq) => Instruction::F64Eq,
|
(F64, Eq) => Instruction::F64Eq,
|
||||||
(F64, Ne) => Instruction::F64Neq,
|
(F64, Ne) => Instruction::F64Ne,
|
||||||
(F64, Lt) => Instruction::F64Lt,
|
(F64, Lt) => Instruction::F64Lt,
|
||||||
(F64, Le) => Instruction::F64Le,
|
(F64, Le) => Instruction::F64Le,
|
||||||
(F64, Gt) => Instruction::F64Gt,
|
(F64, Gt) => Instruction::F64Gt,
|
||||||
@@ -605,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!(),
|
||||||
}
|
}
|
||||||
@@ -654,6 +664,31 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::FuncCall { name, params, .. } => {
|
ast::Expr::FuncCall { name, params, .. } => {
|
||||||
|
fn mem_instruction(
|
||||||
|
inst: MemInstruction,
|
||||||
|
params: &[ast::Expression],
|
||||||
|
) -> Instruction<'static> {
|
||||||
|
let offset = params
|
||||||
|
.get(0)
|
||||||
|
.map(|e| e.const_i32() as u32 as u64)
|
||||||
|
.unwrap_or(0);
|
||||||
|
let alignment = params.get(1).map(|e| e.const_i32() as u32);
|
||||||
|
(inst.instruction)(MemArg {
|
||||||
|
offset,
|
||||||
|
align: alignment.unwrap_or(inst.natural_alignment),
|
||||||
|
memory_index: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if let Some(load) = ctx.intrinsics.find_load(name) {
|
||||||
|
emit_expression(ctx, ¶ms[0]);
|
||||||
|
ctx.function
|
||||||
|
.instruction(&mem_instruction(load, ¶ms[1..]));
|
||||||
|
} else if let Some(store) = ctx.intrinsics.find_store(name) {
|
||||||
|
emit_expression(ctx, ¶ms[1]);
|
||||||
|
emit_expression(ctx, ¶ms[0]);
|
||||||
|
ctx.function
|
||||||
|
.instruction(&mem_instruction(store, ¶ms[2..]));
|
||||||
|
} else {
|
||||||
for param in params {
|
for param in params {
|
||||||
emit_expression(ctx, param);
|
emit_expression(ctx, param);
|
||||||
}
|
}
|
||||||
@@ -669,6 +704,7 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
.instruction(&ctx.intrinsics.get_instr(name, &types).unwrap());
|
.instruction(&ctx.intrinsics.get_instr(name, &types).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ast::Expr::Select {
|
ast::Expr::Select {
|
||||||
condition,
|
condition,
|
||||||
if_true,
|
if_true,
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
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 {
|
||||||
match values {
|
if let ast::DataValues::File {
|
||||||
ast::DataValues::File {
|
|
||||||
ref path,
|
ref path,
|
||||||
ref mut data,
|
ref mut data,
|
||||||
} => {
|
} = values
|
||||||
|
{
|
||||||
let mut full_path = script_dir.to_path_buf();
|
let mut full_path = script_dir.to_path_buf();
|
||||||
full_path.push(path);
|
full_path.push(path);
|
||||||
File::open(&full_path)
|
File::open(&full_path)
|
||||||
@@ -21,8 +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);
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use crate::ast::Type;
|
use crate::ast::Type;
|
||||||
|
use enc::MemArg;
|
||||||
use std::collections::HashMap;
|
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 {
|
||||||
@@ -11,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()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -33,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();
|
||||||
@@ -129,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
|
||||||
@@ -137,4 +171,65 @@ impl Intrinsics {
|
|||||||
.or_default()
|
.or_default()
|
||||||
.insert(params.to_vec(), (ret, ins.clone()));
|
.insert(params.to_vec(), (ret, ins.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_load(&self, name: &str) -> Option<MemInstruction> {
|
||||||
|
use enc::Instruction as I;
|
||||||
|
use Type::*;
|
||||||
|
let ins = match name {
|
||||||
|
"i32.load" => MemInstruction::new(I32, I::I32Load, 2),
|
||||||
|
"i32.load8_s" => MemInstruction::new(I32, I::I32Load8_S, 0),
|
||||||
|
"i32.load8_u" => MemInstruction::new(I32, I::I32Load8_U, 0),
|
||||||
|
"i32.load16_s" => MemInstruction::new(I32, I::I32Load16_S, 1),
|
||||||
|
"i32.load16_u" => MemInstruction::new(I32, I::I32Load16_U, 1),
|
||||||
|
"i64.load" => MemInstruction::new(I64, I::I64Load, 3),
|
||||||
|
"i64.load8_s" => MemInstruction::new(I64, I::I64Load8_S, 0),
|
||||||
|
"i64.load8_u" => MemInstruction::new(I64, I::I64Load8_U, 0),
|
||||||
|
"i64.load16_s" => MemInstruction::new(I64, I::I64Load16_S, 1),
|
||||||
|
"i64.load16_u" => MemInstruction::new(I64, I::I64Load16_U, 1),
|
||||||
|
"i64.load32_s" => MemInstruction::new(I64, I::I64Load32_S, 2),
|
||||||
|
"i64.load32_u" => MemInstruction::new(I64, I::I64Load32_U, 2),
|
||||||
|
"f32.load" => MemInstruction::new(F32, I::F32Load, 2),
|
||||||
|
"f64.load" => MemInstruction::new(F64, I::F64Load, 3),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
return Some(ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_store(&self, name: &str) -> Option<MemInstruction> {
|
||||||
|
use enc::Instruction as I;
|
||||||
|
use Type::*;
|
||||||
|
let ins = match name {
|
||||||
|
"i32.store" => MemInstruction::new(I32, I::I32Store, 2),
|
||||||
|
"i32.store8" => MemInstruction::new(I32, I::I32Store8, 0),
|
||||||
|
"i32.store16" => MemInstruction::new(I32, I::I32Store16, 1),
|
||||||
|
"i64.store" => MemInstruction::new(I64, I::I64Store, 3),
|
||||||
|
"i64.store8" => MemInstruction::new(I64, I::I64Store8, 0),
|
||||||
|
"i64.store16" => MemInstruction::new(I64, I::I64Store16, 1),
|
||||||
|
"i64.store32" => MemInstruction::new(I64, I::I64Store32, 2),
|
||||||
|
"f32.store" => MemInstruction::new(F32, I::F32Store, 2),
|
||||||
|
"f64.store" => MemInstruction::new(F64, I::F64Store, 3),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
return Some(ins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemInstruction {
|
||||||
|
pub type_: Type,
|
||||||
|
pub instruction: fn(MemArg) -> enc::Instruction<'static>,
|
||||||
|
pub natural_alignment: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemInstruction {
|
||||||
|
fn new(
|
||||||
|
type_: Type,
|
||||||
|
instruction: fn(MemArg) -> enc::Instruction<'static>,
|
||||||
|
natural_alignment: u32,
|
||||||
|
) -> MemInstruction {
|
||||||
|
MemInstruction {
|
||||||
|
type_,
|
||||||
|
instruction,
|
||||||
|
natural_alignment,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
77
src/lib.rs
77
src/lib.rs
@@ -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,
|
||||||
@@ -20,31 +19,64 @@ pub struct Options {
|
|||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
pub fn with_debug(self) -> Self {
|
pub fn with_debug(self) -> Self {
|
||||||
Options {
|
Options { debug: true }
|
||||||
debug: true,
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 let Err(_) = typecheck::tc_script(&mut script, &input) {
|
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(
|
||||||
@@ -57,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())
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)?;
|
||||||
|
|
||||||
|
|||||||
589
src/parser.rs
589
src/parser.rs
@@ -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
|
||||||
@@ -138,7 +221,7 @@ fn report_errors(errors: Vec<Simple<String>>, source: &str) {
|
|||||||
} else {
|
} else {
|
||||||
error
|
error
|
||||||
.expected()
|
.expected()
|
||||||
.map(|x| x.to_string())
|
.map(|x| x.as_deref().unwrap_or("EOF"))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
}
|
}
|
||||||
@@ -161,59 +244,106 @@ 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(seq::<_, _, Simple<char>>("f64".chars()))
|
.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 = seq::<_, _, Simple<char>>("0x".chars())
|
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| {
|
||||||
u64::from_str_radix(&n, 10).map_err(|err| Simple::custom(span, err.to_string()))
|
n.parse::<u64>()
|
||||||
|
.map_err(|err| LexerError::custom(span, err.to_string()))
|
||||||
}))
|
}))
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let int64 = integer
|
let int64 = integer
|
||||||
.clone()
|
.clone()
|
||||||
.then_ignore(seq::<_, _, Simple<char>>("i64".chars()))
|
.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("+-*/%&^|<=>#".chars())
|
|
||||||
.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 ctrl = one_of("(){};,:?!".chars()).map(Token::Ctrl);
|
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)
|
||||||
|
});
|
||||||
|
|
||||||
fn ident() -> impl Parser<char, String, Error = Simple<char>> + Copy + Clone {
|
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);
|
||||||
|
|
||||||
|
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<_>, _>(
|
||||||
@@ -241,22 +371,15 @@ fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
|
|||||||
_ => Token::Ident(ident),
|
_ => Token::Ident(ident),
|
||||||
});
|
});
|
||||||
|
|
||||||
let single_line =
|
let single_line = just("//").then_ignore(take_until(text::newline()));
|
||||||
seq::<_, _, Simple<char>>("//".chars()).then_ignore(take_until(text::newline()));
|
|
||||||
|
|
||||||
let multi_line =
|
let multi_line = just("/*").then_ignore(take_until(just("*/")));
|
||||||
seq::<_, _, Simple<char>>("/*".chars()).then_ignore(take_until(seq("*/".chars())));
|
|
||||||
|
|
||||||
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
|
||||||
@@ -264,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,
|
||||||
@@ -313,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");
|
||||||
|
|
||||||
@@ -326,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())
|
||||||
@@ -342,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();
|
||||||
@@ -358,7 +588,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
|
|
||||||
let branch = just(Token::Branch)
|
let branch = just(Token::Branch)
|
||||||
.ignore_then(identifier)
|
.ignore_then(identifier)
|
||||||
.map(|label| ast::Expr::Branch(label));
|
.map(ast::Expr::Branch);
|
||||||
|
|
||||||
let branch_if = just(Token::BranchIf)
|
let branch_if = just(Token::BranchIf)
|
||||||
.ignore_then(expression.clone())
|
.ignore_then(expression.clone())
|
||||||
@@ -377,7 +607,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
.or(just(Token::Inline).to(ast::LetType::Inline)))
|
.or(just(Token::Inline).to(ast::LetType::Inline)))
|
||||||
.or_not(),
|
.or_not(),
|
||||||
)
|
)
|
||||||
.then(identifier.clone())
|
.then(identifier)
|
||||||
.then(just(Token::Ctrl(':')).ignore_then(type_parser()).or_not())
|
.then(just(Token::Ctrl(':')).ignore_then(type_parser()).or_not())
|
||||||
.then(
|
.then(
|
||||||
just(Token::Op("=".to_string()))
|
just(Token::Op("=".to_string()))
|
||||||
@@ -393,17 +623,6 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let assign = identifier
|
|
||||||
.clone()
|
|
||||||
.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
|
||||||
@@ -412,7 +631,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
.then(expression.clone())
|
.then(expression.clone())
|
||||||
.then_ignore(just(Token::Ctrl(',')))
|
.then_ignore(just(Token::Ctrl(',')))
|
||||||
.then(expression.clone())
|
.then(expression.clone())
|
||||||
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
|
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))),
|
||||||
)
|
)
|
||||||
.map(|((condition, if_true), if_false)| ast::Expr::Select {
|
.map(|((condition, if_true), if_false)| ast::Expr::Select {
|
||||||
condition: Box::new(condition),
|
condition: Box::new(condition),
|
||||||
@@ -422,12 +641,11 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let function_call = identifier
|
let function_call = identifier
|
||||||
.clone()
|
|
||||||
.then(
|
.then(
|
||||||
expression
|
expression
|
||||||
.clone()
|
.clone()
|
||||||
.separated_by(just(Token::Ctrl(',')))
|
.separated_by(just(Token::Ctrl(',')))
|
||||||
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
|
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))),
|
||||||
)
|
)
|
||||||
.map(|(name, params)| ast::Expr::FuncCall { name, params })
|
.map(|(name, params)| ast::Expr::FuncCall { name, params })
|
||||||
.boxed();
|
.boxed();
|
||||||
@@ -438,21 +656,23 @@ 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()
|
||||||
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')))
|
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))))
|
||||||
.or(block)
|
.or(block)
|
||||||
.recover_with(nested_delimiters(
|
.recover_with(nested_delimiters(
|
||||||
Token::Ctrl('('),
|
Token::Ctrl('('),
|
||||||
@@ -470,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),
|
||||||
@@ -499,7 +719,8 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
|
|
||||||
let mem_size = just(Token::Ctrl('?'))
|
let mem_size = just(Token::Ctrl('?'))
|
||||||
.to(ast::MemSize::Byte)
|
.to(ast::MemSize::Byte)
|
||||||
.or(just(Token::Ctrl('!')).to(ast::MemSize::Word));
|
.or(just(Token::Ctrl('!')).to(ast::MemSize::Word))
|
||||||
|
.or(just(Token::Ctrl('$')).to(ast::MemSize::Float));
|
||||||
|
|
||||||
let mem_op = mem_size.then(op_cast.clone());
|
let mem_op = mem_size.then(op_cast.clone());
|
||||||
|
|
||||||
@@ -509,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),
|
||||||
@@ -519,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(),
|
||||||
@@ -565,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),
|
||||||
@@ -588,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),
|
||||||
@@ -608,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),
|
||||||
@@ -645,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),
|
||||||
@@ -657,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),
|
||||||
@@ -676,7 +868,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let op_first = op_bit
|
op_bit
|
||||||
.clone()
|
.clone()
|
||||||
.then(
|
.then(
|
||||||
just(Token::Op("<|".to_string()))
|
just(Token::Op("<|".to_string()))
|
||||||
@@ -684,27 +876,72 @@ 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),
|
||||||
}
|
}
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
})
|
})
|
||||||
.boxed();
|
.boxed()
|
||||||
|
|
||||||
op_first
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expression_out = Some(expression.clone());
|
expression_out = Some(expression.clone());
|
||||||
|
|
||||||
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| {
|
||||||
@@ -718,11 +955,11 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
}
|
}
|
||||||
ast::Expr::Block {
|
ast::Expr::Block {
|
||||||
statements: statements.into_iter().map(|(expr, _)| expr).collect(),
|
statements: statements.into_iter().map(|(expr, _)| expr).collect(),
|
||||||
final_expression: final_expression.map(|e| Box::new(e)),
|
final_expression: final_expression.map(Box::new),
|
||||||
}
|
}
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
})
|
})
|
||||||
.delimited_by(Token::Ctrl('{'), Token::Ctrl('}'))
|
.delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}')))
|
||||||
.boxed()
|
.boxed()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -733,14 +970,14 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
.ignore_then(
|
.ignore_then(
|
||||||
integer
|
integer
|
||||||
.clone()
|
.clone()
|
||||||
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
|
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))),
|
||||||
)
|
)
|
||||||
.map(|min_size| ast::ImportType::Memory(min_size as u32))
|
.map(|min_size| ast::ImportType::Memory(min_size as u32))
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let import_global = just(Token::Global)
|
let import_global = just(Token::Global)
|
||||||
.ignore_then(just(Token::Mut).or_not())
|
.ignore_then(just(Token::Mut).or_not())
|
||||||
.then(identifier.clone())
|
.then(identifier)
|
||||||
.then_ignore(just(Token::Ctrl(':')))
|
.then_ignore(just(Token::Ctrl(':')))
|
||||||
.then(type_parser())
|
.then(type_parser())
|
||||||
.map(|((mut_opt, name), type_)| ast::ImportType::Variable {
|
.map(|((mut_opt, name), type_)| ast::ImportType::Variable {
|
||||||
@@ -751,11 +988,11 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let import_function = just(Token::Fn)
|
let import_function = just(Token::Fn)
|
||||||
.ignore_then(identifier.clone())
|
.ignore_then(identifier)
|
||||||
.then(
|
.then(
|
||||||
type_parser()
|
type_parser()
|
||||||
.separated_by(just(Token::Ctrl(',')))
|
.separated_by(just(Token::Ctrl(',')))
|
||||||
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
|
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))),
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
just(Token::Op("->".to_string()))
|
just(Token::Op("->".to_string()))
|
||||||
@@ -783,7 +1020,6 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
let parameter = identifier
|
let parameter = identifier
|
||||||
.clone()
|
|
||||||
.then_ignore(just(Token::Ctrl(':')))
|
.then_ignore(just(Token::Ctrl(':')))
|
||||||
.then(type_parser())
|
.then(type_parser())
|
||||||
.boxed();
|
.boxed();
|
||||||
@@ -792,11 +1028,11 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
.or_not()
|
.or_not()
|
||||||
.then(just(Token::Ident("start".to_string())).or_not())
|
.then(just(Token::Ident("start".to_string())).or_not())
|
||||||
.then_ignore(just(Token::Fn))
|
.then_ignore(just(Token::Fn))
|
||||||
.then(identifier.clone())
|
.then(identifier)
|
||||||
.then(
|
.then(
|
||||||
parameter
|
parameter
|
||||||
.separated_by(just(Token::Ctrl(',')))
|
.separated_by(just(Token::Ctrl(',')))
|
||||||
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
|
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))),
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
just(Token::Op("->".to_string()))
|
just(Token::Op("->".to_string()))
|
||||||
@@ -820,7 +1056,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
|
|
||||||
let global = just(Token::Global)
|
let global = just(Token::Global)
|
||||||
.ignore_then(just(Token::Mut).or_not())
|
.ignore_then(just(Token::Mut).or_not())
|
||||||
.then(identifier.clone())
|
.then(identifier)
|
||||||
.then(just(Token::Ctrl(':')).ignore_then(type_parser()).or_not())
|
.then(just(Token::Ctrl(':')).ignore_then(type_parser()).or_not())
|
||||||
.then(just(Token::Op("=".to_string())).ignore_then(expression.clone()))
|
.then(just(Token::Op("=".to_string())).ignore_then(expression.clone()))
|
||||||
.then_ignore(just(Token::Ctrl(';')))
|
.then_ignore(just(Token::Ctrl(';')))
|
||||||
@@ -835,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))
|
||||||
@@ -846,17 +1097,17 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
expression
|
expression
|
||||||
.clone()
|
.clone()
|
||||||
.separated_by(just(Token::Ctrl(',')))
|
.separated_by(just(Token::Ctrl(',')))
|
||||||
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
|
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))),
|
||||||
)
|
)
|
||||||
.map(|(type_, values)| ast::DataValues::Array { type_, values });
|
.map(|(type_, values)| ast::DataValues::Array { type_, values });
|
||||||
|
|
||||||
let data_string = string.clone().map(|s| ast::DataValues::String(s));
|
let data_string = string.clone().map(ast::DataValues::String);
|
||||||
|
|
||||||
let data_file = just(Token::Ident("file".to_string()))
|
let data_file = just(Token::Ident("file".to_string()))
|
||||||
.ignore_then(
|
.ignore_then(
|
||||||
string
|
string
|
||||||
.clone()
|
.clone()
|
||||||
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
|
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))),
|
||||||
)
|
)
|
||||||
.map(|s| ast::DataValues::File {
|
.map(|s| ast::DataValues::File {
|
||||||
path: s.into(),
|
path: s.into(),
|
||||||
@@ -870,7 +1121,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
|
|||||||
.or(data_string)
|
.or(data_string)
|
||||||
.or(data_file)
|
.or(data_file)
|
||||||
.repeated()
|
.repeated()
|
||||||
.delimited_by(Token::Ctrl('{'), Token::Ctrl('}')),
|
.delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}'))),
|
||||||
)
|
)
|
||||||
.map(|(offset, data)| {
|
.map(|(offset, data)| {
|
||||||
ast::TopLevelItem::Data(ast::Data {
|
ast::TopLevelItem::Data(ast::Data {
|
||||||
@@ -880,41 +1131,49 @@ 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![
|
||||||
Token::Ident("i32".into()),
|
Some(Token::Ident("i32".into())),
|
||||||
Token::Ident("i64".into()),
|
Some(Token::Ident("i64".into())),
|
||||||
Token::Ident("f32".into()),
|
Some(Token::Ident("f32".into())),
|
||||||
Token::Ident("f64".into()),
|
Some(Token::Ident("f64".into())),
|
||||||
],
|
],
|
||||||
Some(tok),
|
Some(tok),
|
||||||
)),
|
)),
|
||||||
|
|||||||
320
src/typecheck.rs
320
src/typecheck.rs
@@ -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;
|
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(),
|
||||||
@@ -160,10 +175,10 @@ pub fn tc_script(script: &mut ast::Script, source: &str) -> Result<()> {
|
|||||||
context.locals.locals[index].index = Some((locals_start + id) as u32);
|
context.locals.locals[index].index = Some((locals_start + id) as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
f.locals = std::mem::replace(&mut context.locals, ast::Locals::default());
|
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())
|
||||||
@@ -333,7 +348,7 @@ fn type_mismatch(
|
|||||||
"Expected type {:?}...",
|
"Expected type {:?}...",
|
||||||
type1
|
type1
|
||||||
.map(|t| format!("{:?}", t))
|
.map(|t| format!("{:?}", t))
|
||||||
.unwrap_or("void".to_string())
|
.unwrap_or_else(|| "void".to_string())
|
||||||
))
|
))
|
||||||
.with_color(Color::Yellow),
|
.with_color(Color::Yellow),
|
||||||
)
|
)
|
||||||
@@ -343,70 +358,48 @@ fn type_mismatch(
|
|||||||
"...but found type {}",
|
"...but found type {}",
|
||||||
type2
|
type2
|
||||||
.map(|t| format!("{:?}", t))
|
.map(|t| format!("{:?}", t))
|
||||||
.unwrap_or("void".to_string())
|
.unwrap_or_else(|| "void".to_string())
|
||||||
))
|
))
|
||||||
.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<()> {
|
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("Expected value but found expression of type void")
|
.with_message(msg)
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new(span.clone())
|
Label::new(span.clone())
|
||||||
.with_message("Expected value but found expression of type void")
|
.with_message(msg)
|
||||||
.with_color(Color::Red),
|
.with_color(Color::Red),
|
||||||
)
|
)
|
||||||
.finish()
|
.finish()
|
||||||
.eprint(Source::from(source))
|
.eprint(sources)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unknown_variable(span: &Span, source: &str) -> Result<()> {
|
fn expected_type(span: &Span, sources: &Sources) -> Result<()> {
|
||||||
Report::build(ReportKind::Error, (), span.start)
|
report_error(
|
||||||
.with_message("Unknown variable")
|
"Expected value but found expression of type void",
|
||||||
.with_label(
|
span,
|
||||||
Label::new(span.clone())
|
sources,
|
||||||
.with_message("Unknown variable")
|
|
||||||
.with_color(Color::Red),
|
|
||||||
)
|
)
|
||||||
.finish()
|
|
||||||
.eprint(Source::from(source))
|
|
||||||
.unwrap();
|
|
||||||
Err(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn immutable_assign(span: &Span, source: &str) -> Result<()> {
|
fn unknown_variable(span: &Span, sources: &Sources) -> Result<()> {
|
||||||
Report::build(ReportKind::Error, (), span.start)
|
report_error("Unknown variable", span, sources)
|
||||||
.with_message("Trying to assign to immutable variable")
|
|
||||||
.with_label(
|
|
||||||
Label::new(span.clone())
|
|
||||||
.with_message("Trying to assign to immutable variable")
|
|
||||||
.with_color(Color::Red),
|
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
.eprint(Source::from(source))
|
|
||||||
.unwrap();
|
|
||||||
Err(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn missing_label(span: &Span, source: &str) -> Result<()> {
|
fn immutable_assign(span: &Span, sources: &Sources) -> Result<()> {
|
||||||
Report::build(ReportKind::Error, (), span.start)
|
report_error("Trying to assign to immutable variable", span, sources)
|
||||||
.with_message("Label not found")
|
}
|
||||||
.with_label(
|
|
||||||
Label::new(span.clone())
|
fn missing_label(span: &Span, sources: &Sources) -> Result<()> {
|
||||||
.with_message("Label not found")
|
report_error("Label not found", span, sources)
|
||||||
.with_color(Color::Red),
|
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
.eprint(Source::from(source))
|
|
||||||
.unwrap();
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()> {
|
fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()> {
|
||||||
@@ -445,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_;
|
||||||
}
|
}
|
||||||
@@ -471,23 +464,17 @@ 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 {
|
||||||
Report::build(ReportKind::Error, (), expr.span.start)
|
return report_error("Type missing", &expr.span, context.sources);
|
||||||
.with_message("Type missing")
|
|
||||||
.with_label(
|
|
||||||
Label::new(expr.span.clone())
|
|
||||||
.with_message("Type missing")
|
|
||||||
.with_color(Color::Red),
|
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
.eprint(Source::from(context.source))
|
|
||||||
.unwrap();
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ast::Expr::Peek(ref mut mem_location) => {
|
ast::Expr::Peek(ref mut mem_location) => {
|
||||||
tc_mem_location(context, mem_location)?;
|
tc_mem_location(context, mem_location)?;
|
||||||
Some(I32)
|
let ty = match mem_location.size {
|
||||||
|
MemSize::Float => F32,
|
||||||
|
_ => I32,
|
||||||
|
};
|
||||||
|
Some(ty)
|
||||||
}
|
}
|
||||||
ast::Expr::Poke {
|
ast::Expr::Poke {
|
||||||
ref mut mem_location,
|
ref mut mem_location,
|
||||||
@@ -495,13 +482,17 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
|
|||||||
} => {
|
} => {
|
||||||
tc_mem_location(context, mem_location)?;
|
tc_mem_location(context, mem_location)?;
|
||||||
tc_expression(context, value)?;
|
tc_expression(context, value)?;
|
||||||
if value.type_ != Some(I32) {
|
let ty = match mem_location.size {
|
||||||
|
MemSize::Float => F32,
|
||||||
|
_ => I32,
|
||||||
|
};
|
||||||
|
if value.type_ != Some(ty) {
|
||||||
return type_mismatch(
|
return type_mismatch(
|
||||||
Some(I32),
|
Some(ty),
|
||||||
&expr.span,
|
&expr.span,
|
||||||
value.type_,
|
value.type_,
|
||||||
&value.span,
|
&value.span,
|
||||||
context.source,
|
context.sources,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -513,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::*;
|
||||||
@@ -526,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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -545,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 {
|
||||||
@@ -561,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_
|
||||||
@@ -575,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)
|
||||||
@@ -593,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 {
|
||||||
@@ -607,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 {
|
||||||
@@ -617,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
|
||||||
}
|
}
|
||||||
@@ -640,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_) {
|
||||||
@@ -649,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 {
|
||||||
@@ -676,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
|
||||||
}
|
}
|
||||||
@@ -697,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
|
||||||
}
|
}
|
||||||
@@ -711,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_)
|
||||||
}
|
}
|
||||||
@@ -722,10 +713,30 @@ 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(¶m.span, context.source);
|
return expected_type(¶m.span, context.sources);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(type_map) = context
|
if let Some(load) = context.intrinsics.find_load(name) {
|
||||||
|
tc_memarg(context, params.as_mut_slice(), &expr.span)?;
|
||||||
|
Some(load.type_)
|
||||||
|
} else if let Some(store) = context.intrinsics.find_store(name) {
|
||||||
|
if let Some(value) = params.first_mut() {
|
||||||
|
tc_expression(context, value)?;
|
||||||
|
if value.type_ != Some(store.type_) {
|
||||||
|
type_mismatch(
|
||||||
|
Some(store.type_),
|
||||||
|
&expr.span,
|
||||||
|
value.type_,
|
||||||
|
&value.span,
|
||||||
|
context.sources,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return report_error("Missing parameters", &expr.span, context.sources);
|
||||||
|
}
|
||||||
|
tc_memarg(context, &mut params[1..], &expr.span)?;
|
||||||
|
None
|
||||||
|
} else if let Some(type_map) = context
|
||||||
.functions
|
.functions
|
||||||
.get(name)
|
.get(name)
|
||||||
.map(|fnc| HashMap::from_iter([(fnc.params.clone(), fnc.type_)]))
|
.map(|fnc| HashMap::from_iter([(fnc.params.clone(), fnc.type_)]))
|
||||||
@@ -736,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();
|
||||||
@@ -752,24 +764,15 @@ 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 {
|
||||||
Report::build(ReportKind::Error, (), expr.span.start)
|
return report_error(
|
||||||
.with_message(format!("Unknown function {}", name))
|
&format!("Unknown function {}", name),
|
||||||
.with_label(
|
&expr.span,
|
||||||
Label::new(expr.span.clone())
|
context.sources,
|
||||||
.with_message(format!("Unknown function {}", name))
|
);
|
||||||
.with_color(Color::Red),
|
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
.eprint(Source::from(context.source))
|
|
||||||
.unwrap();
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::Select {
|
ast::Expr::Select {
|
||||||
@@ -786,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() {
|
||||||
@@ -796,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_
|
||||||
}
|
}
|
||||||
@@ -819,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_
|
||||||
@@ -837,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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -861,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) {
|
||||||
@@ -877,32 +880,53 @@ 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, sources),
|
||||||
Report::build(ReportKind::Error, (), expr.span.start)
|
|
||||||
.with_message("Expected constant value")
|
|
||||||
.with_label(
|
|
||||||
Label::new(expr.span.clone())
|
|
||||||
.with_message("Expected constant value")
|
|
||||||
.with_color(Color::Red),
|
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
.eprint(Source::from(source))
|
|
||||||
.unwrap();
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tc_memarg(context: &mut Context, params: &mut [ast::Expression], span: &Span) -> Result<()> {
|
||||||
|
if params.is_empty() || params.len() > 3 {
|
||||||
|
let msg = if params.is_empty() {
|
||||||
|
"Missing base address parameter"
|
||||||
|
} else {
|
||||||
|
"Too many MemArg parameters"
|
||||||
|
};
|
||||||
|
return report_error(msg, span, context.sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, param) in params.iter_mut().enumerate() {
|
||||||
|
tc_expression(context, param)?;
|
||||||
|
if param.type_ != Some(I32) {
|
||||||
|
return type_mismatch(Some(I32), &span, param.type_, ¶m.span, context.sources);
|
||||||
|
}
|
||||||
|
if index > 0 {
|
||||||
|
tc_const(param, context.sources)?;
|
||||||
|
}
|
||||||
|
if index == 2 {
|
||||||
|
let align = param.const_i32();
|
||||||
|
if align < 0 || align > 4 {
|
||||||
|
return report_error(
|
||||||
|
&format!("Alignment {} out of range (0-4)", align),
|
||||||
|
¶m.span,
|
||||||
|
context.sources,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
15
test/xorshift.cwa
Normal file
15
test/xorshift.cwa
Normal 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user