mirror of
https://github.com/exoticorn/curlywas.git
synced 2026-01-21 04:06:42 +01:00
Compare commits
2 Commits
c22297ea82
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e7ea508cd | |||
| 0a0d90c801 |
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
|
||||
```
|
||||
|
||||
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!"
|
||||
|
||||
this does not work, yet:
|
||||
|
||||
"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
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
`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
|
||||
```
|
||||
|
||||
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
|
||||
as a statement, returning a value from the block is not yet supported.
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use crate::ast::Type;
|
||||
use std::collections::HashMap;
|
||||
use enc::MemArg;
|
||||
use std::collections::HashMap;
|
||||
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 {
|
||||
pub fn new() -> Intrinsics {
|
||||
@@ -12,14 +14,11 @@ impl Intrinsics {
|
||||
i
|
||||
}
|
||||
|
||||
pub fn find_types(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> Option<HashMap<Vec<Type>, Option<Type>>> {
|
||||
pub fn find_types(&self, name: &str) -> Option<HashMap<Vec<Type>, Option<Type>>> {
|
||||
self.0.get(name).map(|types| {
|
||||
types
|
||||
.iter()
|
||||
.map(|(params, (ret, _))| (params.clone(), Some(*ret)))
|
||||
.map(|(params, (ret, _))| (params.clone(), *ret))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
@@ -34,87 +33,121 @@ impl Intrinsics {
|
||||
fn add_instructions(&mut self) {
|
||||
use enc::Instruction as I;
|
||||
use Type::*;
|
||||
self.inst("i32.rotl", &[I32, I32], I32, I::I32Rotl);
|
||||
self.inst("i32.rotr", &[I32, I32], I32, I::I32Rotr);
|
||||
self.inst("i32.clz", &[I32], I32, I::I32Clz);
|
||||
self.inst("i32.ctz", &[I32], I32, I::I32Ctz);
|
||||
self.inst("i32.popcnt", &[I32], I32, I::I32Popcnt);
|
||||
self.inst("i32.rotl", &[I32, I32], Some(I32), I::I32Rotl);
|
||||
self.inst("i32.rotr", &[I32, I32], Some(I32), I::I32Rotr);
|
||||
self.inst("i32.clz", &[I32], Some(I32), I::I32Clz);
|
||||
self.inst("i32.ctz", &[I32], Some(I32), I::I32Ctz);
|
||||
self.inst("i32.popcnt", &[I32], Some(I32), I::I32Popcnt);
|
||||
|
||||
self.inst("i64.rotl", &[I64, I64], I64, I::I64Rotl);
|
||||
self.inst("i64.rotr", &[I64, I64], I64, I::I64Rotr);
|
||||
self.inst("i64.clz", &[I64], I64, I::I64Clz);
|
||||
self.inst("i64.ctz", &[I64], I64, I::I64Ctz);
|
||||
self.inst("i64.popcnt", &[I64], I64, I::I64Popcnt);
|
||||
self.inst("i64.rotl", &[I64, I64], Some(I64), I::I64Rotl);
|
||||
self.inst("i64.rotr", &[I64, I64], Some(I64), I::I64Rotr);
|
||||
self.inst("i64.clz", &[I64], Some(I64), I::I64Clz);
|
||||
self.inst("i64.ctz", &[I64], Some(I64), I::I64Ctz);
|
||||
self.inst("i64.popcnt", &[I64], Some(I64), I::I64Popcnt);
|
||||
|
||||
self.inst("f32/sqrt", &[F32], F32, I::F32Sqrt);
|
||||
self.inst("f32/min", &[F32, F32], F32, I::F32Min);
|
||||
self.inst("f32/max", &[F32, F32], F32, I::F32Max);
|
||||
self.inst("f32/ceil", &[F32], F32, I::F32Ceil);
|
||||
self.inst("f32/floor", &[F32], F32, I::F32Floor);
|
||||
self.inst("f32/trunc", &[F32], F32, I::F32Trunc);
|
||||
self.inst("f32/nearest", &[F32], F32, I::F32Nearest);
|
||||
self.inst("f32/abs", &[F32], F32, I::F32Abs);
|
||||
self.inst("f32.copysign", &[F32, F32], F32, I::F32Copysign);
|
||||
self.inst("f32/sqrt", &[F32], Some(F32), I::F32Sqrt);
|
||||
self.inst("f32/min", &[F32, F32], Some(F32), I::F32Min);
|
||||
self.inst("f32/max", &[F32, F32], Some(F32), I::F32Max);
|
||||
self.inst("f32/ceil", &[F32], Some(F32), I::F32Ceil);
|
||||
self.inst("f32/floor", &[F32], Some(F32), I::F32Floor);
|
||||
self.inst("f32/trunc", &[F32], Some(F32), I::F32Trunc);
|
||||
self.inst("f32/nearest", &[F32], Some(F32), I::F32Nearest);
|
||||
self.inst("f32/abs", &[F32], Some(F32), I::F32Abs);
|
||||
self.inst("f32.copysign", &[F32, F32], Some(F32), I::F32Copysign);
|
||||
|
||||
self.inst("f64/sqrt", &[F64], F64, I::F64Sqrt);
|
||||
self.inst("f64/min", &[F64, F64], F64, I::F64Min);
|
||||
self.inst("f64/max", &[F64, F64], F64, I::F64Max);
|
||||
self.inst("f64/ceil", &[F64], F64, I::F64Ceil);
|
||||
self.inst("f64/floor", &[F64], F64, I::F64Floor);
|
||||
self.inst("f64/trunc", &[F64], F64, I::F64Trunc);
|
||||
self.inst("f64/nearest", &[F64], F64, I::F64Nearest);
|
||||
self.inst("f64/abs", &[F64], F64, I::F64Abs);
|
||||
self.inst("f64.copysign", &[F64, F64], F64, I::F64Copysign);
|
||||
self.inst("f64/sqrt", &[F64], Some(F64), I::F64Sqrt);
|
||||
self.inst("f64/min", &[F64, F64], Some(F64), I::F64Min);
|
||||
self.inst("f64/max", &[F64, F64], Some(F64), I::F64Max);
|
||||
self.inst("f64/ceil", &[F64], Some(F64), I::F64Ceil);
|
||||
self.inst("f64/floor", &[F64], Some(F64), I::F64Floor);
|
||||
self.inst("f64/trunc", &[F64], Some(F64), I::F64Trunc);
|
||||
self.inst("f64/nearest", &[F64], Some(F64), I::F64Nearest);
|
||||
self.inst("f64/abs", &[F64], Some(F64), I::F64Abs);
|
||||
self.inst("f64.copysign", &[F64, F64], Some(F64), I::F64Copysign);
|
||||
|
||||
self.inst("i32.wrap_i64", &[I64], I32, I::I32WrapI64);
|
||||
self.inst("i64.extend_i32_s", &[I32], I64, I::I64ExtendI32S);
|
||||
self.inst("i64.extend_i32_u", &[I32], I64, I::I64ExtendI32U);
|
||||
self.inst("i32.wrap_i64", &[I64], Some(I32), I::I32WrapI64);
|
||||
self.inst("i64.extend_i32_s", &[I32], Some(I64), I::I64ExtendI32S);
|
||||
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_f64_s", &[F64], I32, I::I32TruncF64S);
|
||||
self.inst("i64.trunc_f32_s", &[F32], I64, I::I64TruncF32S);
|
||||
self.inst("i64.trunc_f64_s", &[F64], I64, I::I64TruncF64S);
|
||||
self.inst("i32.trunc_f32_s", &[F32], Some(I32), I::I32TruncF32S);
|
||||
self.inst("i32.trunc_f64_s", &[F64], Some(I32), I::I32TruncF64S);
|
||||
self.inst("i64.trunc_f32_s", &[F32], Some(I64), I::I64TruncF32S);
|
||||
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_f64_u", &[F64], I32, I::I32TruncF64U);
|
||||
self.inst("i64.trunc_f32_u", &[F32], I64, I::I64TruncF32U);
|
||||
self.inst("i64.trunc_f64_u", &[F64], I64, I::I64TruncF64U);
|
||||
self.inst("i32.trunc_f32_u", &[F32], Some(I32), I::I32TruncF32U);
|
||||
self.inst("i32.trunc_f64_u", &[F64], Some(I32), I::I32TruncF64U);
|
||||
self.inst("i64.trunc_f32_u", &[F32], Some(I64), I::I64TruncF32U);
|
||||
self.inst("i64.trunc_f64_u", &[F64], Some(I64), I::I64TruncF64U);
|
||||
|
||||
self.inst("f32.demote_f64", &[F64], F32, I::F32DemoteF64);
|
||||
self.inst("f64.promote_f32", &[F32], F64, I::F64PromoteF32);
|
||||
self.inst("f32.demote_f64", &[F64], Some(F32), I::F32DemoteF64);
|
||||
self.inst("f64.promote_f32", &[F32], Some(F64), I::F64PromoteF32);
|
||||
|
||||
self.inst("f32.convert_i32_s", &[I32], F32, I::F32ConvertI32S);
|
||||
self.inst("f32.convert_i64_s", &[I64], F32, I::F32ConvertI32S);
|
||||
self.inst("f64.convert_i32_s", &[I32], F64, I::F32ConvertI32S);
|
||||
self.inst("f64.convert_i64_s", &[I64], F64, I::F32ConvertI32S);
|
||||
self.inst("f32.convert_i32_s", &[I32], Some(F32), I::F32ConvertI32S);
|
||||
self.inst("f32.convert_i64_s", &[I64], Some(F32), I::F32ConvertI32S);
|
||||
self.inst("f64.convert_i32_s", &[I32], Some(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_i64_u", &[I64], F32, I::F32ConvertI32U);
|
||||
self.inst("f64.convert_i32_u", &[I32], F64, I::F32ConvertI32U);
|
||||
self.inst("f64.convert_i64_u", &[I64], F64, I::F32ConvertI32U);
|
||||
self.inst("f32.convert_i32_u", &[I32], Some(F32), I::F32ConvertI32U);
|
||||
self.inst("f32.convert_i64_u", &[I64], Some(F32), I::F32ConvertI32U);
|
||||
self.inst("f64.convert_i32_u", &[I32], Some(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("i64.reinterpret_f64", &[F64], I64, I::I64ReinterpretF64);
|
||||
self.inst("f32.reinterpret_i32", &[I32], F32, I::F32ReinterpretI32);
|
||||
self.inst("f64.reinterpret_i64", &[I64], F64, I::F64ReinterpretI64);
|
||||
self.inst(
|
||||
"i32.reinterpret_f32",
|
||||
&[F32],
|
||||
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.extend16_s", &[I32], I32, I::I32Extend16S);
|
||||
self.inst("i64.extend8_s", &[I64], I64, I::I64Extend8S);
|
||||
self.inst("i64.extend16_s", &[I64], I64, I::I64Extend16S);
|
||||
self.inst("i64.extend32_s", &[I64], I64, I::I64Extend32S);
|
||||
self.inst("i32.extend8_s", &[I32], Some(I32), I::I32Extend8S);
|
||||
self.inst("i32.extend16_s", &[I32], Some(I32), I::I32Extend16S);
|
||||
self.inst("i64.extend8_s", &[I64], Some(I64), I::I64Extend8S);
|
||||
self.inst("i64.extend16_s", &[I64], Some(I64), I::I64Extend16S);
|
||||
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_u", &[F32], I32, I::I32TruncSatF32U);
|
||||
self.inst("i32.trunc_sat_f64_s", &[F64], I32, I::I32TruncSatF64S);
|
||||
self.inst("i32.trunc_sat_f64_u", &[F64], I32, I::I32TruncSatF64U);
|
||||
self.inst("i64.trunc_sat_f32_s", &[F32], I64, I::I64TruncSatF32S);
|
||||
self.inst("i64.trunc_sat_f32_u", &[F32], I64, I::I64TruncSatF32U);
|
||||
self.inst("i64.trunc_sat_f64_s", &[F64], I64, I::I64TruncSatF64S);
|
||||
self.inst("i64.trunc_sat_f64_u", &[F64], I64, I::I64TruncSatF64U);
|
||||
self.inst("i32.trunc_sat_f32_s", &[F32], Some(I32), I::I32TruncSatF32S);
|
||||
self.inst("i32.trunc_sat_f32_u", &[F32], Some(I32), I::I32TruncSatF32U);
|
||||
self.inst("i32.trunc_sat_f64_s", &[F64], Some(I32), I::I32TruncSatF64S);
|
||||
self.inst("i32.trunc_sat_f64_u", &[F64], Some(I32), I::I32TruncSatF64U);
|
||||
self.inst("i64.trunc_sat_f32_s", &[F32], Some(I64), I::I64TruncSatF32S);
|
||||
self.inst("i64.trunc_sat_f32_u", &[F32], Some(I64), I::I64TruncSatF32U);
|
||||
self.inst("i64.trunc_sat_f64_s", &[F64], Some(I64), I::I64TruncSatF64S);
|
||||
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('/') {
|
||||
self.insert(name[(slash_idx + 1)..].to_string(), params, ret, &ins);
|
||||
let mut full_name = name[..slash_idx].to_string();
|
||||
@@ -130,7 +163,7 @@ impl Intrinsics {
|
||||
&mut self,
|
||||
name: String,
|
||||
params: &[Type],
|
||||
ret: Type,
|
||||
ret: Option<Type>,
|
||||
ins: &enc::Instruction<'static>,
|
||||
) {
|
||||
self.0
|
||||
@@ -157,7 +190,7 @@ impl Intrinsics {
|
||||
"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 None,
|
||||
};
|
||||
return Some(ins);
|
||||
}
|
||||
@@ -175,7 +208,7 @@ impl Intrinsics {
|
||||
"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 None,
|
||||
};
|
||||
return Some(ins);
|
||||
}
|
||||
@@ -184,13 +217,19 @@ impl Intrinsics {
|
||||
pub struct MemInstruction {
|
||||
pub type_: Type,
|
||||
pub instruction: fn(MemArg) -> enc::Instruction<'static>,
|
||||
pub natural_alignment: u32
|
||||
pub natural_alignment: u32,
|
||||
}
|
||||
|
||||
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 {
|
||||
type_, instruction, natural_alignment
|
||||
type_,
|
||||
instruction,
|
||||
natural_alignment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,23 +250,23 @@ fn report_errors(errors: Vec<Simple<String, Span>>, sources: &Sources) {
|
||||
|
||||
type LexerError = Simple<char, Span>;
|
||||
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)))
|
||||
.then_ignore(just("f64"))
|
||||
.collect::<String>()
|
||||
.map(Token::Float64);
|
||||
|
||||
let float = text::int(10)
|
||||
let float = text::digits(10)
|
||||
.chain::<char, _, _>(just('.').chain(text::digits(10)))
|
||||
.collect::<String>()
|
||||
.map(Token::Float);
|
||||
|
||||
let integer = just::<_, _, LexerError>("0x")
|
||||
.ignore_then(text::int(16))
|
||||
.ignore_then(text::digits(16))
|
||||
.try_map(|n, span| {
|
||||
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>()
|
||||
.map_err(|err| LexerError::custom(span, err.to_string()))
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user