mirror of
https://github.com/exoticorn/curlywas.git
synced 2026-01-21 04:06:42 +01:00
Compare commits
7 Commits
1e746be750
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e7ea508cd | |||
| 0a0d90c801 | |||
| c22297ea82 | |||
| c59b35f9c6 | |||
| 2cf47085c1 | |||
| 01d64baaab | |||
| a52fe53a01 |
29
README.md
29
README.md
@@ -89,16 +89,25 @@ For floating point numbers, only the most basic decimal format is currently impl
|
|||||||
0.464, 3.141, -10.0
|
0.464, 3.141, -10.0
|
||||||
```
|
```
|
||||||
|
|
||||||
String literals exist in a very basic form. No escape character implemented yet.
|
String literals are used for include paths, import names and as literal strings in the data section. The following escapes are supported:
|
||||||
|
| Escape | Result | Comment |
|
||||||
|
| `\"` | `"` | |
|
||||||
|
| `\'` | `'` | |
|
||||||
|
| `\t` | 8 | |
|
||||||
|
| `\n` | 10 | |
|
||||||
|
| `\r` | 13 | |
|
||||||
|
| `\N` | 0x0N | (Can't be followed by a hex digit) |
|
||||||
|
| `\NN` | 0xNN | |
|
||||||
|
|
||||||
```
|
```
|
||||||
"env.memory", "Hello World!"
|
"env.memory", "Hello World!"
|
||||||
|
|
||||||
this does not work, yet:
|
|
||||||
|
|
||||||
"one line\nsecond line", "They said: \"Enough!\""
|
"one line\nsecond line", "They said: \"Enough!\""
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Character literals are enclosed in single quotes `'` and support the same escapes as strings. They can contain up to 4 characters and evaluate to the
|
||||||
|
little-endian representation of these characters. For examples: `'A'` evaluates to `0x41`, `'hi'` evaluates to 0x6968, and `'Crly'` to 0x7a6c7243.
|
||||||
|
|
||||||
### Imports
|
### Imports
|
||||||
|
|
||||||
WebAssembly imports are specified with a module and a name. In CurlyWas you give them inside a single string literal, seperated by a dot. So a module `env` and name `printString` would be written `"env.printString"`.
|
WebAssembly imports are specified with a module and a name. In CurlyWas you give them inside a single string literal, seperated by a dot. So a module `env` and name `printString` would be written `"env.printString"`.
|
||||||
@@ -295,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
|
||||||
@@ -304,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.
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
13
src/emit.rs
13
src/emit.rs
@@ -289,7 +289,7 @@ struct FunctionContext<'a> {
|
|||||||
functions: &'a HashMap<String, u32>,
|
functions: &'a HashMap<String, u32>,
|
||||||
locals: &'a ast::Locals,
|
locals: &'a ast::Locals,
|
||||||
labels: Vec<String>,
|
labels: Vec<String>,
|
||||||
let_values: HashMap<u32, (&'a ast::Expression, ast::LetType)>,
|
let_values: HashMap<u32, Vec<(&'a ast::Expression, ast::LetType)>>,
|
||||||
intrinsics: &'a Intrinsics,
|
intrinsics: &'a Intrinsics,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,7 +385,10 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
.instruction(&Instruction::LocalSet(local.index.unwrap()));
|
.instruction(&Instruction::LocalSet(local.index.unwrap()));
|
||||||
}
|
}
|
||||||
ast::LetType::Lazy | ast::LetType::Inline => {
|
ast::LetType::Lazy | ast::LetType::Inline => {
|
||||||
ctx.let_values.insert(local_id.unwrap(), (value, *let_type));
|
ctx.let_values
|
||||||
|
.entry(local_id.unwrap())
|
||||||
|
.or_default()
|
||||||
|
.push((value, *let_type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -612,17 +615,17 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
|
|||||||
}
|
}
|
||||||
ast::Expr::Variable { name, local_id } => {
|
ast::Expr::Variable { name, local_id } => {
|
||||||
if let &Some(id) = local_id {
|
if let &Some(id) = local_id {
|
||||||
if let Some((expr, let_type)) = ctx.let_values.get(&id) {
|
if let Some((expr, let_type)) = ctx.let_values.get_mut(&id).and_then(|s| s.pop()) {
|
||||||
match let_type {
|
match let_type {
|
||||||
ast::LetType::Lazy => {
|
ast::LetType::Lazy => {
|
||||||
let expr = ctx.let_values.remove(&id).unwrap().0;
|
|
||||||
emit_expression(ctx, expr);
|
emit_expression(ctx, expr);
|
||||||
|
ctx.let_values.get_mut(&id).unwrap().clear();
|
||||||
ctx.function
|
ctx.function
|
||||||
.instruction(&Instruction::LocalTee(ctx.locals[id].index.unwrap()));
|
.instruction(&Instruction::LocalTee(ctx.locals[id].index.unwrap()));
|
||||||
}
|
}
|
||||||
ast::LetType::Inline => {
|
ast::LetType::Inline => {
|
||||||
let expr = *expr;
|
|
||||||
emit_expression(ctx, expr);
|
emit_expression(ctx, expr);
|
||||||
|
ctx.let_values.get_mut(&id).unwrap().push((expr, let_type));
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
use crate::ast::Type;
|
use crate::ast::Type;
|
||||||
use std::collections::HashMap;
|
|
||||||
use enc::MemArg;
|
use enc::MemArg;
|
||||||
|
use std::collections::HashMap;
|
||||||
use wasm_encoder as enc;
|
use wasm_encoder as enc;
|
||||||
|
|
||||||
pub struct Intrinsics(HashMap<String, HashMap<Vec<Type>, (Type, enc::Instruction<'static>)>>);
|
pub struct Intrinsics(
|
||||||
|
HashMap<String, HashMap<Vec<Type>, (Option<Type>, enc::Instruction<'static>)>>,
|
||||||
|
);
|
||||||
|
|
||||||
impl Intrinsics {
|
impl Intrinsics {
|
||||||
pub fn new() -> Intrinsics {
|
pub fn new() -> Intrinsics {
|
||||||
@@ -12,14 +14,11 @@ impl Intrinsics {
|
|||||||
i
|
i
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_types(
|
pub fn find_types(&self, name: &str) -> Option<HashMap<Vec<Type>, Option<Type>>> {
|
||||||
&self,
|
|
||||||
name: &str,
|
|
||||||
) -> Option<HashMap<Vec<Type>, Option<Type>>> {
|
|
||||||
self.0.get(name).map(|types| {
|
self.0.get(name).map(|types| {
|
||||||
types
|
types
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(params, (ret, _))| (params.clone(), Some(*ret)))
|
.map(|(params, (ret, _))| (params.clone(), *ret))
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -34,87 +33,121 @@ impl Intrinsics {
|
|||||||
fn add_instructions(&mut self) {
|
fn add_instructions(&mut self) {
|
||||||
use enc::Instruction as I;
|
use enc::Instruction as I;
|
||||||
use Type::*;
|
use Type::*;
|
||||||
self.inst("i32.rotl", &[I32, I32], I32, I::I32Rotl);
|
self.inst("i32.rotl", &[I32, I32], Some(I32), I::I32Rotl);
|
||||||
self.inst("i32.rotr", &[I32, I32], I32, I::I32Rotr);
|
self.inst("i32.rotr", &[I32, I32], Some(I32), I::I32Rotr);
|
||||||
self.inst("i32.clz", &[I32], I32, I::I32Clz);
|
self.inst("i32.clz", &[I32], Some(I32), I::I32Clz);
|
||||||
self.inst("i32.ctz", &[I32], I32, I::I32Ctz);
|
self.inst("i32.ctz", &[I32], Some(I32), I::I32Ctz);
|
||||||
self.inst("i32.popcnt", &[I32], I32, I::I32Popcnt);
|
self.inst("i32.popcnt", &[I32], Some(I32), I::I32Popcnt);
|
||||||
|
|
||||||
self.inst("i64.rotl", &[I64, I64], I64, I::I64Rotl);
|
self.inst("i64.rotl", &[I64, I64], Some(I64), I::I64Rotl);
|
||||||
self.inst("i64.rotr", &[I64, I64], I64, I::I64Rotr);
|
self.inst("i64.rotr", &[I64, I64], Some(I64), I::I64Rotr);
|
||||||
self.inst("i64.clz", &[I64], I64, I::I64Clz);
|
self.inst("i64.clz", &[I64], Some(I64), I::I64Clz);
|
||||||
self.inst("i64.ctz", &[I64], I64, I::I64Ctz);
|
self.inst("i64.ctz", &[I64], Some(I64), I::I64Ctz);
|
||||||
self.inst("i64.popcnt", &[I64], I64, I::I64Popcnt);
|
self.inst("i64.popcnt", &[I64], Some(I64), I::I64Popcnt);
|
||||||
|
|
||||||
self.inst("f32/sqrt", &[F32], F32, I::F32Sqrt);
|
self.inst("f32/sqrt", &[F32], Some(F32), I::F32Sqrt);
|
||||||
self.inst("f32/min", &[F32, F32], F32, I::F32Min);
|
self.inst("f32/min", &[F32, F32], Some(F32), I::F32Min);
|
||||||
self.inst("f32/max", &[F32, F32], F32, I::F32Max);
|
self.inst("f32/max", &[F32, F32], Some(F32), I::F32Max);
|
||||||
self.inst("f32/ceil", &[F32], F32, I::F32Ceil);
|
self.inst("f32/ceil", &[F32], Some(F32), I::F32Ceil);
|
||||||
self.inst("f32/floor", &[F32], F32, I::F32Floor);
|
self.inst("f32/floor", &[F32], Some(F32), I::F32Floor);
|
||||||
self.inst("f32/trunc", &[F32], F32, I::F32Trunc);
|
self.inst("f32/trunc", &[F32], Some(F32), I::F32Trunc);
|
||||||
self.inst("f32/nearest", &[F32], F32, I::F32Nearest);
|
self.inst("f32/nearest", &[F32], Some(F32), I::F32Nearest);
|
||||||
self.inst("f32/abs", &[F32], F32, I::F32Abs);
|
self.inst("f32/abs", &[F32], Some(F32), I::F32Abs);
|
||||||
self.inst("f32.copysign", &[F32, F32], F32, I::F32Copysign);
|
self.inst("f32.copysign", &[F32, F32], Some(F32), I::F32Copysign);
|
||||||
|
|
||||||
self.inst("f64/sqrt", &[F64], F64, I::F64Sqrt);
|
self.inst("f64/sqrt", &[F64], Some(F64), I::F64Sqrt);
|
||||||
self.inst("f64/min", &[F64, F64], F64, I::F64Min);
|
self.inst("f64/min", &[F64, F64], Some(F64), I::F64Min);
|
||||||
self.inst("f64/max", &[F64, F64], F64, I::F64Max);
|
self.inst("f64/max", &[F64, F64], Some(F64), I::F64Max);
|
||||||
self.inst("f64/ceil", &[F64], F64, I::F64Ceil);
|
self.inst("f64/ceil", &[F64], Some(F64), I::F64Ceil);
|
||||||
self.inst("f64/floor", &[F64], F64, I::F64Floor);
|
self.inst("f64/floor", &[F64], Some(F64), I::F64Floor);
|
||||||
self.inst("f64/trunc", &[F64], F64, I::F64Trunc);
|
self.inst("f64/trunc", &[F64], Some(F64), I::F64Trunc);
|
||||||
self.inst("f64/nearest", &[F64], F64, I::F64Nearest);
|
self.inst("f64/nearest", &[F64], Some(F64), I::F64Nearest);
|
||||||
self.inst("f64/abs", &[F64], F64, I::F64Abs);
|
self.inst("f64/abs", &[F64], Some(F64), I::F64Abs);
|
||||||
self.inst("f64.copysign", &[F64, F64], F64, I::F64Copysign);
|
self.inst("f64.copysign", &[F64, F64], Some(F64), I::F64Copysign);
|
||||||
|
|
||||||
self.inst("i32.wrap_i64", &[I64], I32, I::I32WrapI64);
|
self.inst("i32.wrap_i64", &[I64], Some(I32), I::I32WrapI64);
|
||||||
self.inst("i64.extend_i32_s", &[I32], I64, I::I64ExtendI32S);
|
self.inst("i64.extend_i32_s", &[I32], Some(I64), I::I64ExtendI32S);
|
||||||
self.inst("i64.extend_i32_u", &[I32], I64, I::I64ExtendI32U);
|
self.inst("i64.extend_i32_u", &[I32], Some(I64), I::I64ExtendI32U);
|
||||||
|
|
||||||
self.inst("i32.trunc_f32_s", &[F32], I32, I::I32TruncF32S);
|
self.inst("i32.trunc_f32_s", &[F32], Some(I32), I::I32TruncF32S);
|
||||||
self.inst("i32.trunc_f64_s", &[F64], I32, I::I32TruncF64S);
|
self.inst("i32.trunc_f64_s", &[F64], Some(I32), I::I32TruncF64S);
|
||||||
self.inst("i64.trunc_f32_s", &[F32], I64, I::I64TruncF32S);
|
self.inst("i64.trunc_f32_s", &[F32], Some(I64), I::I64TruncF32S);
|
||||||
self.inst("i64.trunc_f64_s", &[F64], I64, I::I64TruncF64S);
|
self.inst("i64.trunc_f64_s", &[F64], Some(I64), I::I64TruncF64S);
|
||||||
|
|
||||||
self.inst("i32.trunc_f32_u", &[F32], I32, I::I32TruncF32U);
|
self.inst("i32.trunc_f32_u", &[F32], Some(I32), I::I32TruncF32U);
|
||||||
self.inst("i32.trunc_f64_u", &[F64], I32, I::I32TruncF64U);
|
self.inst("i32.trunc_f64_u", &[F64], Some(I32), I::I32TruncF64U);
|
||||||
self.inst("i64.trunc_f32_u", &[F32], I64, I::I64TruncF32U);
|
self.inst("i64.trunc_f32_u", &[F32], Some(I64), I::I64TruncF32U);
|
||||||
self.inst("i64.trunc_f64_u", &[F64], I64, I::I64TruncF64U);
|
self.inst("i64.trunc_f64_u", &[F64], Some(I64), I::I64TruncF64U);
|
||||||
|
|
||||||
self.inst("f32.demote_f64", &[F64], F32, I::F32DemoteF64);
|
self.inst("f32.demote_f64", &[F64], Some(F32), I::F32DemoteF64);
|
||||||
self.inst("f64.promote_f32", &[F32], F64, I::F64PromoteF32);
|
self.inst("f64.promote_f32", &[F32], Some(F64), I::F64PromoteF32);
|
||||||
|
|
||||||
self.inst("f32.convert_i32_s", &[I32], F32, I::F32ConvertI32S);
|
self.inst("f32.convert_i32_s", &[I32], Some(F32), I::F32ConvertI32S);
|
||||||
self.inst("f32.convert_i64_s", &[I64], F32, I::F32ConvertI32S);
|
self.inst("f32.convert_i64_s", &[I64], Some(F32), I::F32ConvertI32S);
|
||||||
self.inst("f64.convert_i32_s", &[I32], F64, I::F32ConvertI32S);
|
self.inst("f64.convert_i32_s", &[I32], Some(F64), I::F32ConvertI32S);
|
||||||
self.inst("f64.convert_i64_s", &[I64], F64, I::F32ConvertI32S);
|
self.inst("f64.convert_i64_s", &[I64], Some(F64), I::F32ConvertI32S);
|
||||||
|
|
||||||
self.inst("f32.convert_i32_u", &[I32], F32, I::F32ConvertI32U);
|
self.inst("f32.convert_i32_u", &[I32], Some(F32), I::F32ConvertI32U);
|
||||||
self.inst("f32.convert_i64_u", &[I64], F32, I::F32ConvertI32U);
|
self.inst("f32.convert_i64_u", &[I64], Some(F32), I::F32ConvertI32U);
|
||||||
self.inst("f64.convert_i32_u", &[I32], F64, I::F32ConvertI32U);
|
self.inst("f64.convert_i32_u", &[I32], Some(F64), I::F32ConvertI32U);
|
||||||
self.inst("f64.convert_i64_u", &[I64], F64, I::F32ConvertI32U);
|
self.inst("f64.convert_i64_u", &[I64], Some(F64), I::F32ConvertI32U);
|
||||||
|
|
||||||
self.inst("i32.reinterpret_f32", &[F32], I32, I::I32ReinterpretF32);
|
self.inst(
|
||||||
self.inst("i64.reinterpret_f64", &[F64], I64, I::I64ReinterpretF64);
|
"i32.reinterpret_f32",
|
||||||
self.inst("f32.reinterpret_i32", &[I32], F32, I::F32ReinterpretI32);
|
&[F32],
|
||||||
self.inst("f64.reinterpret_i64", &[I64], F64, I::F64ReinterpretI64);
|
Some(I32),
|
||||||
|
I::I32ReinterpretF32,
|
||||||
|
);
|
||||||
|
self.inst(
|
||||||
|
"i64.reinterpret_f64",
|
||||||
|
&[F64],
|
||||||
|
Some(I64),
|
||||||
|
I::I64ReinterpretF64,
|
||||||
|
);
|
||||||
|
self.inst(
|
||||||
|
"f32.reinterpret_i32",
|
||||||
|
&[I32],
|
||||||
|
Some(F32),
|
||||||
|
I::F32ReinterpretI32,
|
||||||
|
);
|
||||||
|
self.inst(
|
||||||
|
"f64.reinterpret_i64",
|
||||||
|
&[I64],
|
||||||
|
Some(F64),
|
||||||
|
I::F64ReinterpretI64,
|
||||||
|
);
|
||||||
|
|
||||||
self.inst("i32.extend8_s", &[I32], I32, I::I32Extend8S);
|
self.inst("i32.extend8_s", &[I32], Some(I32), I::I32Extend8S);
|
||||||
self.inst("i32.extend16_s", &[I32], I32, I::I32Extend16S);
|
self.inst("i32.extend16_s", &[I32], Some(I32), I::I32Extend16S);
|
||||||
self.inst("i64.extend8_s", &[I64], I64, I::I64Extend8S);
|
self.inst("i64.extend8_s", &[I64], Some(I64), I::I64Extend8S);
|
||||||
self.inst("i64.extend16_s", &[I64], I64, I::I64Extend16S);
|
self.inst("i64.extend16_s", &[I64], Some(I64), I::I64Extend16S);
|
||||||
self.inst("i64.extend32_s", &[I64], I64, I::I64Extend32S);
|
self.inst("i64.extend32_s", &[I64], Some(I64), I::I64Extend32S);
|
||||||
|
|
||||||
self.inst("i32.trunc_sat_f32_s", &[F32], I32, I::I32TruncSatF32S);
|
self.inst("i32.trunc_sat_f32_s", &[F32], Some(I32), I::I32TruncSatF32S);
|
||||||
self.inst("i32.trunc_sat_f32_u", &[F32], I32, I::I32TruncSatF32U);
|
self.inst("i32.trunc_sat_f32_u", &[F32], Some(I32), I::I32TruncSatF32U);
|
||||||
self.inst("i32.trunc_sat_f64_s", &[F64], I32, I::I32TruncSatF64S);
|
self.inst("i32.trunc_sat_f64_s", &[F64], Some(I32), I::I32TruncSatF64S);
|
||||||
self.inst("i32.trunc_sat_f64_u", &[F64], I32, I::I32TruncSatF64U);
|
self.inst("i32.trunc_sat_f64_u", &[F64], Some(I32), I::I32TruncSatF64U);
|
||||||
self.inst("i64.trunc_sat_f32_s", &[F32], I64, I::I64TruncSatF32S);
|
self.inst("i64.trunc_sat_f32_s", &[F32], Some(I64), I::I64TruncSatF32S);
|
||||||
self.inst("i64.trunc_sat_f32_u", &[F32], I64, I::I64TruncSatF32U);
|
self.inst("i64.trunc_sat_f32_u", &[F32], Some(I64), I::I64TruncSatF32U);
|
||||||
self.inst("i64.trunc_sat_f64_s", &[F64], I64, I::I64TruncSatF64S);
|
self.inst("i64.trunc_sat_f64_s", &[F64], Some(I64), I::I64TruncSatF64S);
|
||||||
self.inst("i64.trunc_sat_f64_u", &[F64], I64, I::I64TruncSatF64U);
|
self.inst("i64.trunc_sat_f64_u", &[F64], Some(I64), I::I64TruncSatF64U);
|
||||||
|
|
||||||
|
self.inst(
|
||||||
|
"memory.copy",
|
||||||
|
&[I32, I32, I32],
|
||||||
|
None,
|
||||||
|
I::MemoryCopy { src: 0, dst: 0 },
|
||||||
|
);
|
||||||
|
self.inst("memory.fill", &[I32, I32, I32], None, I::MemoryFill(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inst(&mut self, name: &str, params: &[Type], ret: Type, ins: enc::Instruction<'static>) {
|
fn inst(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &[Type],
|
||||||
|
ret: Option<Type>,
|
||||||
|
ins: enc::Instruction<'static>,
|
||||||
|
) {
|
||||||
if let Some(slash_idx) = name.find('/') {
|
if let Some(slash_idx) = name.find('/') {
|
||||||
self.insert(name[(slash_idx + 1)..].to_string(), params, ret, &ins);
|
self.insert(name[(slash_idx + 1)..].to_string(), params, ret, &ins);
|
||||||
let mut full_name = name[..slash_idx].to_string();
|
let mut full_name = name[..slash_idx].to_string();
|
||||||
@@ -130,7 +163,7 @@ impl Intrinsics {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: String,
|
name: String,
|
||||||
params: &[Type],
|
params: &[Type],
|
||||||
ret: Type,
|
ret: Option<Type>,
|
||||||
ins: &enc::Instruction<'static>,
|
ins: &enc::Instruction<'static>,
|
||||||
) {
|
) {
|
||||||
self.0
|
self.0
|
||||||
@@ -157,7 +190,7 @@ impl Intrinsics {
|
|||||||
"i64.load32_u" => MemInstruction::new(I64, I::I64Load32_U, 2),
|
"i64.load32_u" => MemInstruction::new(I64, I::I64Load32_U, 2),
|
||||||
"f32.load" => MemInstruction::new(F32, I::F32Load, 2),
|
"f32.load" => MemInstruction::new(F32, I::F32Load, 2),
|
||||||
"f64.load" => MemInstruction::new(F64, I::F64Load, 3),
|
"f64.load" => MemInstruction::new(F64, I::F64Load, 3),
|
||||||
_ => return None
|
_ => return None,
|
||||||
};
|
};
|
||||||
return Some(ins);
|
return Some(ins);
|
||||||
}
|
}
|
||||||
@@ -175,7 +208,7 @@ impl Intrinsics {
|
|||||||
"i64.store32" => MemInstruction::new(I64, I::I64Store32, 2),
|
"i64.store32" => MemInstruction::new(I64, I::I64Store32, 2),
|
||||||
"f32.store" => MemInstruction::new(F32, I::F32Store, 2),
|
"f32.store" => MemInstruction::new(F32, I::F32Store, 2),
|
||||||
"f64.store" => MemInstruction::new(F64, I::F64Store, 3),
|
"f64.store" => MemInstruction::new(F64, I::F64Store, 3),
|
||||||
_ => return None
|
_ => return None,
|
||||||
};
|
};
|
||||||
return Some(ins);
|
return Some(ins);
|
||||||
}
|
}
|
||||||
@@ -184,13 +217,19 @@ impl Intrinsics {
|
|||||||
pub struct MemInstruction {
|
pub struct MemInstruction {
|
||||||
pub type_: Type,
|
pub type_: Type,
|
||||||
pub instruction: fn(MemArg) -> enc::Instruction<'static>,
|
pub instruction: fn(MemArg) -> enc::Instruction<'static>,
|
||||||
pub natural_alignment: u32
|
pub natural_alignment: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemInstruction {
|
impl MemInstruction {
|
||||||
fn new(type_: Type, instruction: fn(MemArg) -> enc::Instruction<'static>, natural_alignment: u32) -> MemInstruction {
|
fn new(
|
||||||
|
type_: Type,
|
||||||
|
instruction: fn(MemArg) -> enc::Instruction<'static>,
|
||||||
|
natural_alignment: u32,
|
||||||
|
) -> MemInstruction {
|
||||||
MemInstruction {
|
MemInstruction {
|
||||||
type_, instruction, natural_alignment
|
type_,
|
||||||
|
instruction,
|
||||||
|
natural_alignment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
226
src/parser.rs
226
src/parser.rs
@@ -250,23 +250,23 @@ fn report_errors(errors: Vec<Simple<String, Span>>, sources: &Sources) {
|
|||||||
|
|
||||||
type LexerError = Simple<char, Span>;
|
type LexerError = Simple<char, Span>;
|
||||||
fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = LexerError> {
|
fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = LexerError> {
|
||||||
let float64 = text::int(10)
|
let float64 = text::digits(10)
|
||||||
.chain::<char, _, _>(just('.').chain(text::digits(10)))
|
.chain::<char, _, _>(just('.').chain(text::digits(10)))
|
||||||
.then_ignore(just("f64"))
|
.then_ignore(just("f64"))
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
.map(Token::Float64);
|
.map(Token::Float64);
|
||||||
|
|
||||||
let float = text::int(10)
|
let float = text::digits(10)
|
||||||
.chain::<char, _, _>(just('.').chain(text::digits(10)))
|
.chain::<char, _, _>(just('.').chain(text::digits(10)))
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
.map(Token::Float);
|
.map(Token::Float);
|
||||||
|
|
||||||
let integer = just::<_, _, LexerError>("0x")
|
let integer = just::<_, _, LexerError>("0x")
|
||||||
.ignore_then(text::int(16))
|
.ignore_then(text::digits(16))
|
||||||
.try_map(|n, span| {
|
.try_map(|n, span| {
|
||||||
u64::from_str_radix(&n, 16).map_err(|err| LexerError::custom(span, err.to_string()))
|
u64::from_str_radix(&n, 16).map_err(|err| LexerError::custom(span, err.to_string()))
|
||||||
})
|
})
|
||||||
.or(text::int(10).try_map(|n: String, span: Span| {
|
.or(text::digits(10).try_map(|n: String, span: Span| {
|
||||||
n.parse::<u64>()
|
n.parse::<u64>()
|
||||||
.map_err(|err| LexerError::custom(span, err.to_string()))
|
.map_err(|err| LexerError::custom(span, err.to_string()))
|
||||||
}))
|
}))
|
||||||
@@ -318,13 +318,28 @@ fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = LexerError> {
|
|||||||
Token::Int(value as i32)
|
Token::Int(value as i32)
|
||||||
});
|
});
|
||||||
|
|
||||||
let op = one_of("+-*/%&^|<=>#")
|
let op = choice((
|
||||||
.repeated()
|
just("#/"),
|
||||||
.at_least(1)
|
just("#%"),
|
||||||
.or(just(':').chain(just('=')))
|
just("<<"),
|
||||||
.or(just('!').chain(just('=')))
|
just(">>"),
|
||||||
.collect::<String>()
|
just("#>>"),
|
||||||
.map(Token::Op);
|
just(">="),
|
||||||
|
just("<="),
|
||||||
|
just("=="),
|
||||||
|
just("!="),
|
||||||
|
just("#>="),
|
||||||
|
just("#<="),
|
||||||
|
just("#<"),
|
||||||
|
just("#>"),
|
||||||
|
just("->"),
|
||||||
|
just(":="),
|
||||||
|
just("<|"),
|
||||||
|
))
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.or(one_of("+-*/%&^|<=>").map(|s: char| s.to_string()))
|
||||||
|
.map(Token::Op)
|
||||||
|
.boxed();
|
||||||
|
|
||||||
let ctrl = one_of("(){};,:?!$").map(Token::Ctrl);
|
let ctrl = one_of("(){};,:?!$").map(Token::Ctrl);
|
||||||
|
|
||||||
@@ -362,17 +377,10 @@ fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = LexerError> {
|
|||||||
|
|
||||||
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_float)
|
.recover_with(skip_then_retry_until([]));
|
||||||
.or(int)
|
|
||||||
.or(str_)
|
|
||||||
.or(char_)
|
|
||||||
.or(op)
|
|
||||||
.or(ctrl)
|
|
||||||
.or(ident)
|
|
||||||
.recover_with(skip_then_retry_until([]));
|
|
||||||
|
|
||||||
token
|
token
|
||||||
.map_with_span(|tok, span| (tok, span))
|
.map_with_span(|tok, span| (tok, span))
|
||||||
@@ -408,11 +416,11 @@ fn parse_string_escapes(s: String) -> String {
|
|||||||
match c {
|
match c {
|
||||||
'0'..='9' | 'a'..='f' | 'A'..='F' => {
|
'0'..='9' | 'a'..='f' | 'A'..='F' => {
|
||||||
let mut number = c.to_string();
|
let mut number = c.to_string();
|
||||||
if let Some('0'..='9' | 'a' ..= 'f' | 'A'..='F') = chars.peek() {
|
if let Some('0'..='9' | 'a'..='f' | 'A'..='F') = chars.peek() {
|
||||||
number.push(chars.next().unwrap());
|
number.push(chars.next().unwrap());
|
||||||
}
|
}
|
||||||
result.push(u8::from_str_radix(&number, 16).unwrap() as char);
|
result.push(u8::from_str_radix(&number, 16).unwrap() as char);
|
||||||
},
|
}
|
||||||
'n' => result.push('\n'),
|
'n' => result.push('\n'),
|
||||||
'r' => result.push('\r'),
|
'r' => result.push('\r'),
|
||||||
't' => result.push('\t'),
|
't' => result.push('\t'),
|
||||||
@@ -447,6 +455,28 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + Clo
|
|||||||
_ => 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;
|
||||||
@@ -486,6 +516,36 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + Clo
|
|||||||
})
|
})
|
||||||
.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())
|
||||||
@@ -596,28 +656,31 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + Clo
|
|||||||
value: value.map(Box::new),
|
value: value.map(Box::new),
|
||||||
});
|
});
|
||||||
|
|
||||||
let atom = val
|
let atom = choice((
|
||||||
.or(function_call)
|
val,
|
||||||
.or(local_tee)
|
function_call,
|
||||||
.or(variable)
|
local_tee,
|
||||||
.or(block_expr)
|
local_tee_op,
|
||||||
.or(branch)
|
variable,
|
||||||
.or(branch_if)
|
block_expr,
|
||||||
.or(let_)
|
branch,
|
||||||
.or(select)
|
branch_if,
|
||||||
.or(return_)
|
let_,
|
||||||
.map_with_span(|expr, span| expr.with_span(span))
|
select,
|
||||||
.or(expression
|
return_,
|
||||||
.clone()
|
))
|
||||||
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))))
|
.map_with_span(|expr, span| expr.with_span(span))
|
||||||
.or(block)
|
.or(expression
|
||||||
.recover_with(nested_delimiters(
|
.clone()
|
||||||
Token::Ctrl('('),
|
.delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))))
|
||||||
Token::Ctrl(')'),
|
.or(block)
|
||||||
[(Token::Ctrl('{'), Token::Ctrl('}'))],
|
.recover_with(nested_delimiters(
|
||||||
|span| ast::Expr::Error.with_span(span),
|
Token::Ctrl('('),
|
||||||
))
|
Token::Ctrl(')'),
|
||||||
.boxed();
|
[(Token::Ctrl('{'), Token::Ctrl('}'))],
|
||||||
|
|span| ast::Expr::Error.with_span(span),
|
||||||
|
))
|
||||||
|
.boxed();
|
||||||
|
|
||||||
let unary_op = just(Token::Op("-".to_string()))
|
let unary_op = just(Token::Op("-".to_string()))
|
||||||
.to(ast::UnaryOp::Negate)
|
.to(ast::UnaryOp::Negate)
|
||||||
@@ -723,16 +786,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + Clo
|
|||||||
|
|
||||||
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.0, left.span.1.start..right.span.1.end);
|
let span = (left.span.0, left.span.1.start..right.span.1.end);
|
||||||
ast::Expr::BinOp {
|
ast::Expr::BinOp {
|
||||||
@@ -746,13 +800,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + Clo
|
|||||||
|
|
||||||
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.0, left.span.1.start..right.span.1.end);
|
let span = (left.span.0, left.span.1.start..right.span.1.end);
|
||||||
ast::Expr::BinOp {
|
ast::Expr::BinOp {
|
||||||
@@ -766,14 +814,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + Clo
|
|||||||
|
|
||||||
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.0, left.span.1.start..right.span.1.end);
|
let span = (left.span.0, left.span.1.start..right.span.1.end);
|
||||||
ast::Expr::BinOp {
|
ast::Expr::BinOp {
|
||||||
@@ -815,14 +856,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + Clo
|
|||||||
|
|
||||||
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.0, left.span.1.start..right.span.1.end);
|
let span = (left.span.0, left.span.1.start..right.span.1.end);
|
||||||
ast::Expr::BinOp {
|
ast::Expr::BinOp {
|
||||||
@@ -867,11 +901,45 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = ScriptError> + Clo
|
|||||||
.map_with_span(|expr, span| expr.with_span(span))
|
.map_with_span(|expr, span| expr.with_span(span))
|
||||||
.boxed();
|
.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(assign
|
.or(assign
|
||||||
|
.or(assign_op)
|
||||||
.or(expression.clone())
|
.or(expression.clone())
|
||||||
.then(just(Token::Ctrl(';')).to(false)))
|
.then(just(Token::Ctrl(';')).to(false)))
|
||||||
.repeated()
|
.repeated()
|
||||||
|
|||||||
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