initial commit, pretty much done
commit
48481825b3
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"deno.enable": true,
|
||||||
|
"deno.unstable": true,
|
||||||
|
"editor.tabSize": 4
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
# Documentation
|
||||||
|
|
||||||
|
Documentation for FlowJS.
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
FlowJS mostly uses TypeScript-style types, except for `int` and `float` for
|
||||||
|
feature parity with Flowgorithm. Note that these names are slightly different
|
||||||
|
from Flowgorithm's, `int` translates to `Integer`, `float` translates to `Real`,
|
||||||
|
and the rest have similar capitalization.
|
||||||
|
|
||||||
|
## Builtins
|
||||||
|
|
||||||
|
### `print(x: any)`
|
||||||
|
|
||||||
|
Translates to an `Output` statement.
|
||||||
|
|
||||||
|
### `input(var: <Identifier: any>)`
|
||||||
|
|
||||||
|
Translates to an `Input` statement. `var` must be a variable identifier.
|
||||||
|
|
||||||
|
### `attr(key: string, value: string)`
|
||||||
|
|
||||||
|
Applies a value to a specific program attribute. Available attributes:
|
||||||
|
|
||||||
|
- **name** - the program's name
|
||||||
|
- **authors** - the program's authors
|
||||||
|
- **about** - description of the program
|
||||||
|
- **saved** - a date string in the form of `yyyy-MM-dd HH:mm:ss a` e.g.
|
||||||
|
`2022-10-23 11:10:23 AM` (keep in mind the 12-hour time format)
|
||||||
|
|
||||||
|
**edited** and **created** are also available but are always overwritten upon
|
||||||
|
transpilation.
|
||||||
|
|
||||||
|
### `meta(key: string, value: string)`
|
||||||
|
|
||||||
|
Applies a value to a specific meta property of a program. Available keys:
|
||||||
|
|
||||||
|
- **username** - your logged in user
|
||||||
|
- **hostname** - your computer's hostname
|
||||||
|
- **editVersion** - the program's edit version (starts from 1)
|
||||||
|
- **editMysteryNumber** - idk what this even is but it starts at like 2300 iirc
|
||||||
|
|
||||||
|
### `comment(comment: string)`
|
||||||
|
|
||||||
|
Creates a comment with the given string.
|
||||||
|
|
||||||
|
### `insist(var: <Identifier: any>)`
|
||||||
|
|
||||||
|
**(temporary)** · A lazy temporary soution to make the typechecker believe
|
||||||
|
that an identifier has been **defined** without affecting the output code.
|
||||||
|
|
||||||
|
## Statements
|
||||||
|
|
||||||
|
### `for`
|
||||||
|
|
||||||
|
For statement syntax is slightly different from JS/TS syntax in order to have
|
||||||
|
compatibility with Flowgorithm's.
|
||||||
|
|
||||||
|
`for (init; <end>; upd) <BlockStatement>`
|
||||||
|
|
||||||
|
- `init` can either be `<var> = <start>` with an optional `let`. `<var>` should
|
||||||
|
always be an `<Identifier: float | int>` and `<start>` should always be a
|
||||||
|
`<NumericLiteral>`.
|
||||||
|
- `<end>` is the number to end the loop at. It should always be a
|
||||||
|
`<NumericLiteral>`.
|
||||||
|
- `upd` can either be (where `<var>` matches that of `init`):
|
||||||
|
- `<var>++/--` - This assumes step is 1 and direction is either "inc" (`++`)
|
||||||
|
or "dec" (`--`).
|
||||||
|
- `<var> +=/-= <step>` - Here you can define the step number, and direction
|
||||||
|
is either "inc" (`+=`) or "dec" (`-=`).
|
||||||
|
|
||||||
|
#### Examples:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
for (let i = 0; 10; i++) {
|
||||||
|
print("Going up! " + i);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// `i` was declared earlier
|
||||||
|
for (i = 20; 10; i -= 2) {
|
||||||
|
print("Going down! " + i )
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<flowgorithm fileversion="3.0">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="name" value="[[name]]"/>
|
||||||
|
<attribute name="authors" value="[[authors]]"/>
|
||||||
|
<attribute name="about" value="[[about]]"/>
|
||||||
|
<attribute name="saved" value="[[saved]]"/>
|
||||||
|
<attribute name="created" value="[[created]]"/>
|
||||||
|
<attribute name="edited" value="[[edited]]"/>
|
||||||
|
</attributes>
|
||||||
|
<function name="Main" type="None" variable="">
|
||||||
|
<parameters/>
|
||||||
|
<body>
|
||||||
|
[[body]]
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
</flowgorithm>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,72 @@
|
|||||||
|
import * as SWC from "../src/swc.ts";
|
||||||
|
import { CallExpression, ExpressionStatement } from "../src/swc.ts";
|
||||||
|
import * as Trans from "../src/transformers.ts";
|
||||||
|
|
||||||
|
const input = Deno.readTextFileSync("./benchmarks/benchtest.ts");
|
||||||
|
|
||||||
|
const fakeState = {
|
||||||
|
input: "",
|
||||||
|
indent: " ",
|
||||||
|
idents: new Map<string, Trans.Ident>(),
|
||||||
|
opts: {
|
||||||
|
indent: 4,
|
||||||
|
showSourceStatements: false,
|
||||||
|
attr: {},
|
||||||
|
meta: {},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ast = SWC.parse(input, {
|
||||||
|
syntax: "typescript",
|
||||||
|
comments: true,
|
||||||
|
target: "es2020",
|
||||||
|
});
|
||||||
|
|
||||||
|
const template = Deno.readTextFileSync("./assets/template.fprg");
|
||||||
|
|
||||||
|
Deno.bench("transform", () => {
|
||||||
|
Trans.transform(input, ast, {
|
||||||
|
indent: 3,
|
||||||
|
showSourceStatements: true,
|
||||||
|
}, template);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("transformBlock", () => {
|
||||||
|
Trans.transformBlock(
|
||||||
|
ast.body,
|
||||||
|
{ ...fakeState, idents: new Map<string, Trans.Ident>() },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("transformVariableDecl", () => {
|
||||||
|
Trans.transformVariableDecl(
|
||||||
|
ast.body[7] as SWC.VariableDeclaration,
|
||||||
|
{ ...fakeState, idents: new Map<string, Trans.Ident>() },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
fakeState.idents = new Map<string, Trans.Ident>([
|
||||||
|
["a", { type: "int", defined: true }],
|
||||||
|
["b", { type: "int", defined: true }],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Deno.bench("transformExpr :: `20 / ((5 + a) ^ (a + b))`", () => {
|
||||||
|
Trans.transformExpr(
|
||||||
|
(ast.body[10] as SWC.VariableDeclaration).declarations[0].init!,
|
||||||
|
fakeState.idents,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("transformBlockCallExpr :: `print(a + b)`", () => {
|
||||||
|
Trans.transformBlockCallExpr(
|
||||||
|
(ast.body[9] as ExpressionStatement).expression as CallExpression,
|
||||||
|
fakeState,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("transformIfStmt", () => {
|
||||||
|
Trans.transformIfStmt(
|
||||||
|
ast.body[19] as SWC.IfStatement,
|
||||||
|
fakeState
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
meta("username", "skybl");
|
||||||
|
meta("hostname", "pond");
|
||||||
|
meta("editVersion", "5");
|
||||||
|
meta("editMysteryNumber", "2991");
|
||||||
|
attr("name", "benchtest.ts");
|
||||||
|
attr("authors", "skybl, ezfprg");
|
||||||
|
attr("about", "test program for transpiler benchmarks");
|
||||||
|
|
||||||
|
let a: int = 1, b: int;
|
||||||
|
input(b);
|
||||||
|
print(a + b);
|
||||||
|
|
||||||
|
let c: float = 20 / ((5 + a) ^ (a + b));
|
||||||
|
print(c);
|
||||||
|
|
||||||
|
let e: float = 1.4, f = 2;
|
||||||
|
let g = e / f;
|
||||||
|
let h: boolean = false;
|
||||||
|
|
||||||
|
print(" " + e + " / " + f + " = " + g + ", h = " + h);
|
||||||
|
|
||||||
|
let i = 2 + " hello";
|
||||||
|
let j = i + " world";
|
||||||
|
print(j);
|
||||||
|
|
||||||
|
if (a < b) {
|
||||||
|
print("a is less than b");
|
||||||
|
} else {
|
||||||
|
print('a is more than b');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
print("single arm");
|
||||||
|
}
|
||||||
|
|
||||||
|
let k = 0;
|
||||||
|
|
||||||
|
while (k < 10) {
|
||||||
|
print(k);
|
||||||
|
k = k + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let l = 0; 10; l++) {
|
||||||
|
print(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (l = 20; 10; l -= 2) {
|
||||||
|
print(l);
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
//// <reference no-default-lib="true" />
|
||||||
|
|
||||||
|
declare type int = number;
|
||||||
|
declare type float = number;
|
||||||
|
|
||||||
|
declare function print(a: unknown): void;
|
||||||
|
declare function input(a: unknown): void;
|
||||||
|
declare function meta(a: "username" | "hostname" | "editVersion" | "editMysteryNumber", b: string): void;
|
||||||
|
declare function attr(a: "name" | "authors" | "about", b: string): void;
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": ".."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"deno.unstable": true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
const KEYWORDS = [
|
||||||
|
"let",
|
||||||
|
"for",
|
||||||
|
"print",
|
||||||
|
"input",
|
||||||
|
"//"
|
||||||
|
];
|
||||||
|
|
||||||
|
const TYPES = [
|
||||||
|
"int",
|
||||||
|
"float",
|
||||||
|
"string",
|
||||||
|
"bool"
|
||||||
|
];
|
||||||
|
|
||||||
|
const PATTERNS = [
|
||||||
|
"asgn": [ "ident", "eq", "expr" ],
|
||||||
|
"let": [ "type", "ident", "eq?", "expr" ],
|
||||||
|
];
|
||||||
|
|
||||||
|
class UnexpectedTokenError extends SyntaxError {
|
||||||
|
constructor (expectedTokens, token, line, col) {
|
||||||
|
const expected = expectedTokens.map((t) => `"${t}"`).join(", ");
|
||||||
|
super(`Expected ${expected} but got ${token}`);
|
||||||
|
this.lineNumber = line;
|
||||||
|
this.columnNumber = col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnexpectedEndOfInputError {
|
||||||
|
constructor (line, col) {
|
||||||
|
super(`Unexpected end of input at ${line}:${col}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const inPath = Deno.args[0];
|
||||||
|
const dec = new TextDecoder("utf-8);
|
||||||
|
const raw = Deno.readFile(inPath);
|
||||||
|
const input = dec.decode(raw);
|
||||||
|
|
||||||
|
const lines = input.split("\n");
|
||||||
|
|
||||||
|
let tree = [];
|
||||||
|
let stack = [];
|
||||||
|
let ctx = "";
|
||||||
|
|
||||||
|
// keyword, ident, type, num, string, op, group, eof
|
||||||
|
let expectedTokens = ["keyword", "ident"];
|
||||||
|
let tokenType = null;
|
||||||
|
// ` `, `{`, `}`
|
||||||
|
let delim = " ";
|
||||||
|
let token = "";
|
||||||
|
|
||||||
|
let idents = [];
|
||||||
|
|
||||||
|
for (let line = 0; line < lines.length; line++) {
|
||||||
|
const currentLine = lines[line];
|
||||||
|
for (let col = 0; col < currentLine.length; col++) {
|
||||||
|
if (currentLine[col] === delim) {
|
||||||
|
tokenType = null;
|
||||||
|
const tokenHandlers = {
|
||||||
|
"keyword": () => {
|
||||||
|
if (KEYWORDS.includes(token)) {
|
||||||
|
stack.push(token);
|
||||||
|
tokenType = "keyword";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ident": () => {
|
||||||
|
if (idents.includes(token)) {
|
||||||
|
stack.push(token);
|
||||||
|
idents.push(token);
|
||||||
|
tokenType = "ident";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (type in expectedTokens) {
|
||||||
|
tokenHandlers[type]();
|
||||||
|
if (tokenType !== null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expectedTokens.includes(tokenType)) {
|
||||||
|
throw new UnexpectedTokenError(expectedTokens, token, line, col);
|
||||||
|
} else {
|
||||||
|
switch (tokenType) {
|
||||||
|
case "keyword":
|
||||||
|
switch (token) {
|
||||||
|
case "let":
|
||||||
|
ctx = patterns["let"];
|
||||||
|
expectedToken = "ident";
|
||||||
|
delim = " ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ident":
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
token.push(line[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case "keyword": {
|
||||||
|
const keyword = line.split(" ")[0];
|
||||||
|
if (KEYWORDS.includes(keyword)) {
|
||||||
|
stack.push(keyword);
|
||||||
|
} else {
|
||||||
|
throw new UnknownKeywordError(keyword, i, 0);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
|||||||
|
import { parse } from "./src/swc.ts";
|
||||||
|
import { basename } from "https://deno.land/std@0.159.0/path/posix.ts?s=basename";
|
||||||
|
|
||||||
|
const input = Deno.readTextFileSync(Deno.args[0]);
|
||||||
|
const ast = parse(input, { syntax: "typescript", target: "es2020" });
|
||||||
|
const json = JSON.stringify(ast, null, 2);
|
||||||
|
Deno.writeTextFileSync(basename(Deno.args[0]) + ".ast.json", json);
|
@ -0,0 +1,59 @@
|
|||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fmt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- uses: denolib/setup-deno@master
|
||||||
|
with:
|
||||||
|
deno-version: 1.12.2
|
||||||
|
- name: Format Check
|
||||||
|
run: deno fmt --check --ignore=swc_wasm
|
||||||
|
|
||||||
|
swc-deno-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- uses: denolib/setup-deno@master
|
||||||
|
with:
|
||||||
|
deno-version: 1.23.0
|
||||||
|
- uses: hecrj/setup-rust-action@v1
|
||||||
|
with:
|
||||||
|
rust-version: nightly
|
||||||
|
|
||||||
|
- name: Install wasm32-unknown-unknown target
|
||||||
|
run: rustup target add wasm32-unknown-unknown
|
||||||
|
|
||||||
|
- name: Install wasm-bindgen
|
||||||
|
run: cargo install --version 0.2.72 wasm-bindgen-cli
|
||||||
|
|
||||||
|
- name: Cache Cargo home
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
# See https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry/index
|
||||||
|
~/.cargo/registry/cache
|
||||||
|
~/.cargo/git/db
|
||||||
|
key: f-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Cache build output
|
||||||
|
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
|
||||||
|
with:
|
||||||
|
path: target
|
||||||
|
key: |
|
||||||
|
f-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: deno task build
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: deno task test
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
target/
|
||||||
|
swc_wasm/pkg
|
||||||
|
builds/
|
||||||
|
*.exe
|
||||||
|
config.toml
|
||||||
|
.vscode/settings.json
|
@ -0,0 +1,3 @@
|
|||||||
|
max_width = 80
|
||||||
|
tab_spaces = 2
|
||||||
|
edition = "2021"
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
|||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"swc_wasm/"
|
||||||
|
]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
opt-level = "s"
|
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020-22 Divy Srivastava
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,78 @@
|
|||||||
|
<br />
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/littledivy/deno_swc">
|
||||||
|
<img src="https://raw.githubusercontent.com/littledivy/deno_swc/master/assets/deno_swc.png" alt="deno_swc logo" width="310">
|
||||||
|
</a>
|
||||||
|
<h3 align="center">deno_swc</h3>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
The SWC compiler for Deno.
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
`parse()`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { parse, print } from "https://deno.land/x/swc@0.2.1/mod.ts";
|
||||||
|
|
||||||
|
const code = `const x: string = "Hello, Deno SWC!"`;
|
||||||
|
|
||||||
|
const ast = parse(code, {
|
||||||
|
target: "es2019",
|
||||||
|
syntax: "typescript",
|
||||||
|
comments: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// {
|
||||||
|
// type: "Module",
|
||||||
|
// span: { start: 0, end: 36, ctxt: 0 },
|
||||||
|
// body: [
|
||||||
|
// {
|
||||||
|
// type: "VariableDeclaration",
|
||||||
|
// span: [Object],
|
||||||
|
// kind: "const",
|
||||||
|
// declare: false,
|
||||||
|
// declarations: [Array]
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
// interpreter: null
|
||||||
|
// }
|
||||||
|
```
|
||||||
|
|
||||||
|
`print()`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { code } = print(ast, {
|
||||||
|
minify: true,
|
||||||
|
module: {
|
||||||
|
type: "commonjs",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// const x = "Hello, Deno SWC!"
|
||||||
|
```
|
||||||
|
|
||||||
|
...and `transform()`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { code } = transform("const x: number = 2;", {
|
||||||
|
jsc: {
|
||||||
|
target: "es2016",
|
||||||
|
parser: {
|
||||||
|
syntax: "typescript",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// const x = 2;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Copyright
|
||||||
|
|
||||||
|
deno_swc is licensed under the MIT license. Please see the [LICENSE](LICENSE)
|
||||||
|
file.
|
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
import { compress } from "https://deno.land/x/lz4@v0.1.2/mod.ts";
|
||||||
|
|
||||||
|
const name = "./lib/deno_swc_bg.wasm";
|
||||||
|
Deno.writeFileSync(name, compress(Deno.readFileSync(name)));
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"build": "deno run -A --unstable https://raw.githubusercontent.com/denoland/wasmbuild/0e62b100df246567bee43eea227456222b7fc1dd/main.ts && deno task build:compress",
|
||||||
|
// Use a canary / local version of wasmbuild
|
||||||
|
"build:local": "deno run -A --unstable ../wasmbuild/main.ts && deno task build:compress",
|
||||||
|
"build:compress": "deno run --allow-read --allow-write compress.ts",
|
||||||
|
"fmt": "deno fmt --ignore=swc_wasm,lib,target --unstable && cargo fmt",
|
||||||
|
"test": "deno test -A --no-check tests/"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import { parse } from "../mod.ts";
|
||||||
|
|
||||||
|
const start = performance.now();
|
||||||
|
console.log(parse(
|
||||||
|
`
|
||||||
|
import * as a from "./a.ts";
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
syntax: "ecmascript",
|
||||||
|
},
|
||||||
|
));
|
||||||
|
const end = performance.now() - start;
|
||||||
|
console.log(`parse time: ${end}ms`);
|
@ -0,0 +1,32 @@
|
|||||||
|
import { parse, print } from "../mod.ts";
|
||||||
|
|
||||||
|
const code = `
|
||||||
|
interface H {
|
||||||
|
h: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const x: string = \`Hello, $\{"Hello"} Deno SWC!\`;
|
||||||
|
|
||||||
|
switch (x) {
|
||||||
|
case "value":
|
||||||
|
console.log(x);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ast = parse(code, {
|
||||||
|
target: "es2019",
|
||||||
|
syntax: "typescript",
|
||||||
|
comments: false,
|
||||||
|
});
|
||||||
|
const regeneratedCode = print(ast, {
|
||||||
|
minify: true,
|
||||||
|
module: {
|
||||||
|
type: "commonjs",
|
||||||
|
},
|
||||||
|
}).code;
|
||||||
|
|
||||||
|
console.log(regeneratedCode);
|
@ -0,0 +1,13 @@
|
|||||||
|
import { transform } from "../mod.ts";
|
||||||
|
|
||||||
|
const { code } = transform("const x: number = 2;", {
|
||||||
|
// @ts-ignore: TransformConfig typings for swc_wasm and node_swc are different
|
||||||
|
"jsc": {
|
||||||
|
"target": "es2016",
|
||||||
|
"parser": {
|
||||||
|
"syntax": "typescript",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(code);
|
@ -0,0 +1,378 @@
|
|||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
// @generated file from build script, do not edit
|
||||||
|
// deno-lint-ignore-file
|
||||||
|
// source-hash: cd615b66c9fc26758ad0fac7a225b8fdeeb8cc3f
|
||||||
|
let wasm;
|
||||||
|
|
||||||
|
const heap = new Array(32).fill(undefined);
|
||||||
|
|
||||||
|
heap.push(undefined, null, true, false);
|
||||||
|
|
||||||
|
function getObject(idx) {
|
||||||
|
return heap[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
let heap_next = heap.length;
|
||||||
|
|
||||||
|
function dropObject(idx) {
|
||||||
|
if (idx < 36) return;
|
||||||
|
heap[idx] = heap_next;
|
||||||
|
heap_next = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
function takeObject(idx) {
|
||||||
|
const ret = getObject(idx);
|
||||||
|
dropObject(idx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedTextDecoder = new TextDecoder("utf-8", {
|
||||||
|
ignoreBOM: true,
|
||||||
|
fatal: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
cachedTextDecoder.decode();
|
||||||
|
|
||||||
|
let cachedUint8Memory0;
|
||||||
|
function getUint8Memory0() {
|
||||||
|
if (cachedUint8Memory0.byteLength === 0) {
|
||||||
|
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||||
|
}
|
||||||
|
return cachedUint8Memory0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStringFromWasm0(ptr, len) {
|
||||||
|
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHeapObject(obj) {
|
||||||
|
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||||
|
const idx = heap_next;
|
||||||
|
heap_next = heap[idx];
|
||||||
|
|
||||||
|
heap[idx] = obj;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
let WASM_VECTOR_LEN = 0;
|
||||||
|
|
||||||
|
const cachedTextEncoder = new TextEncoder("utf-8");
|
||||||
|
|
||||||
|
const encodeString = function (arg, view) {
|
||||||
|
return cachedTextEncoder.encodeInto(arg, view);
|
||||||
|
};
|
||||||
|
|
||||||
|
function passStringToWasm0(arg, malloc, realloc) {
|
||||||
|
if (realloc === undefined) {
|
||||||
|
const buf = cachedTextEncoder.encode(arg);
|
||||||
|
const ptr = malloc(buf.length);
|
||||||
|
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||||
|
WASM_VECTOR_LEN = buf.length;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = arg.length;
|
||||||
|
let ptr = malloc(len);
|
||||||
|
|
||||||
|
const mem = getUint8Memory0();
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
for (; offset < len; offset++) {
|
||||||
|
const code = arg.charCodeAt(offset);
|
||||||
|
if (code > 0x7F) break;
|
||||||
|
mem[ptr + offset] = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset !== len) {
|
||||||
|
if (offset !== 0) {
|
||||||
|
arg = arg.slice(offset);
|
||||||
|
}
|
||||||
|
ptr = realloc(ptr, len, len = offset + arg.length * 3);
|
||||||
|
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
|
||||||
|
const ret = encodeString(arg, view);
|
||||||
|
|
||||||
|
offset += ret.written;
|
||||||
|
}
|
||||||
|
|
||||||
|
WASM_VECTOR_LEN = offset;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cachedInt32Memory0;
|
||||||
|
function getInt32Memory0() {
|
||||||
|
if (cachedInt32Memory0.byteLength === 0) {
|
||||||
|
cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||||
|
}
|
||||||
|
return cachedInt32Memory0;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @param {any} opts
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
export function minifySync(s, opts) {
|
||||||
|
try {
|
||||||
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||||
|
const ptr0 = passStringToWasm0(
|
||||||
|
s,
|
||||||
|
wasm.__wbindgen_malloc,
|
||||||
|
wasm.__wbindgen_realloc,
|
||||||
|
);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
wasm.minifySync(retptr, ptr0, len0, addHeapObject(opts));
|
||||||
|
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||||
|
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||||
|
var r2 = getInt32Memory0()[retptr / 4 + 2];
|
||||||
|
if (r2) {
|
||||||
|
throw takeObject(r1);
|
||||||
|
}
|
||||||
|
return takeObject(r0);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @param {any} opts
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
export function parseSync(s, opts) {
|
||||||
|
try {
|
||||||
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||||
|
const ptr0 = passStringToWasm0(
|
||||||
|
s,
|
||||||
|
wasm.__wbindgen_malloc,
|
||||||
|
wasm.__wbindgen_realloc,
|
||||||
|
);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
wasm.parseSync(retptr, ptr0, len0, addHeapObject(opts));
|
||||||
|
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||||
|
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||||
|
var r2 = getInt32Memory0()[retptr / 4 + 2];
|
||||||
|
if (r2) {
|
||||||
|
throw takeObject(r1);
|
||||||
|
}
|
||||||
|
return takeObject(r0);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} s
|
||||||
|
* @param {any} opts
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
export function printSync(s, opts) {
|
||||||
|
try {
|
||||||
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||||
|
wasm.printSync(retptr, addHeapObject(s), addHeapObject(opts));
|
||||||
|
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||||
|
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||||
|
var r2 = getInt32Memory0()[retptr / 4 + 2];
|
||||||
|
if (r2) {
|
||||||
|
throw takeObject(r1);
|
||||||
|
}
|
||||||
|
return takeObject(r0);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @param {any} opts
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
export function transformSync(s, opts) {
|
||||||
|
try {
|
||||||
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||||
|
const ptr0 = passStringToWasm0(
|
||||||
|
s,
|
||||||
|
wasm.__wbindgen_malloc,
|
||||||
|
wasm.__wbindgen_realloc,
|
||||||
|
);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
wasm.transformSync(retptr, ptr0, len0, addHeapObject(opts));
|
||||||
|
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||||
|
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||||
|
var r2 = getInt32Memory0()[retptr / 4 + 2];
|
||||||
|
if (r2) {
|
||||||
|
throw takeObject(r1);
|
||||||
|
}
|
||||||
|
return takeObject(r0);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const imports = {
|
||||||
|
__wbindgen_placeholder__: {
|
||||||
|
__wbg_new0_6b49a1fca8534d39: function () {
|
||||||
|
const ret = new Date();
|
||||||
|
return addHeapObject(ret);
|
||||||
|
},
|
||||||
|
__wbg_getTimezoneOffset_d7a89256f8181a06: function (arg0) {
|
||||||
|
const ret = getObject(arg0).getTimezoneOffset();
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
__wbindgen_object_drop_ref: function (arg0) {
|
||||||
|
takeObject(arg0);
|
||||||
|
},
|
||||||
|
__wbg_getTime_7c8d3b79f51e2b87: function (arg0) {
|
||||||
|
const ret = getObject(arg0).getTime();
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
__wbg_new_693216e109162396: function () {
|
||||||
|
const ret = new Error();
|
||||||
|
return addHeapObject(ret);
|
||||||
|
},
|
||||||
|
__wbg_stack_0ddaca5d1abfb52f: function (arg0, arg1) {
|
||||||
|
const ret = getObject(arg1).stack;
|
||||||
|
const ptr0 = passStringToWasm0(
|
||||||
|
ret,
|
||||||
|
wasm.__wbindgen_malloc,
|
||||||
|
wasm.__wbindgen_realloc,
|
||||||
|
);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||||
|
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||||
|
},
|
||||||
|
__wbg_error_09919627ac0992f5: function (arg0, arg1) {
|
||||||
|
try {
|
||||||
|
console.error(getStringFromWasm0(arg0, arg1));
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(arg0, arg1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
__wbindgen_string_new: function (arg0, arg1) {
|
||||||
|
const ret = getStringFromWasm0(arg0, arg1);
|
||||||
|
return addHeapObject(ret);
|
||||||
|
},
|
||||||
|
__wbindgen_json_serialize: function (arg0, arg1) {
|
||||||
|
const obj = getObject(arg1);
|
||||||
|
const ret = JSON.stringify(obj === undefined ? null : obj);
|
||||||
|
const ptr0 = passStringToWasm0(
|
||||||
|
ret,
|
||||||
|
wasm.__wbindgen_malloc,
|
||||||
|
wasm.__wbindgen_realloc,
|
||||||
|
);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||||
|
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||||
|
},
|
||||||
|
__wbindgen_json_parse: function (arg0, arg1) {
|
||||||
|
const ret = JSON.parse(getStringFromWasm0(arg0, arg1));
|
||||||
|
return addHeapObject(ret);
|
||||||
|
},
|
||||||
|
__wbindgen_throw: function (arg0, arg1) {
|
||||||
|
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wasm_url = new URL("deno_swc_bg.wasm", import.meta.url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompression callback
|
||||||
|
*
|
||||||
|
* @callback decompressCallback
|
||||||
|
* @param {Uint8Array} compressed
|
||||||
|
* @return {Uint8Array} decompressed
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Instantiates an instance of the Wasm module returning its functions.
|
||||||
|
* @remarks It is safe to call this multiple times and once successfully
|
||||||
|
* loaded it will always return a reference to the same object.
|
||||||
|
* @param {decompressCallback=} transform
|
||||||
|
*/
|
||||||
|
export async function instantiate(transform) {
|
||||||
|
return (await instantiateWithInstance(transform)).exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
let instanceWithExports;
|
||||||
|
let lastLoadPromise;
|
||||||
|
|
||||||
|
/** Instantiates an instance of the Wasm module along with its exports.
|
||||||
|
* @remarks It is safe to call this multiple times and once successfully
|
||||||
|
* loaded it will always return a reference to the same object.
|
||||||
|
* @param {decompressCallback=} transform
|
||||||
|
* @returns {Promise<{
|
||||||
|
* instance: WebAssembly.Instance;
|
||||||
|
* exports: { minifySync: typeof minifySync; parseSync: typeof parseSync; printSync: typeof printSync; transformSync: typeof transformSync }
|
||||||
|
* }>}
|
||||||
|
*/
|
||||||
|
export function instantiateWithInstance(transform) {
|
||||||
|
if (instanceWithExports != null) {
|
||||||
|
return Promise.resolve(instanceWithExports);
|
||||||
|
}
|
||||||
|
if (lastLoadPromise == null) {
|
||||||
|
lastLoadPromise = (async () => {
|
||||||
|
try {
|
||||||
|
const instance = (await instantiateModule(transform)).instance;
|
||||||
|
wasm = instance.exports;
|
||||||
|
cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||||
|
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||||
|
instanceWithExports = {
|
||||||
|
instance,
|
||||||
|
exports: { minifySync, parseSync, printSync, transformSync },
|
||||||
|
};
|
||||||
|
return instanceWithExports;
|
||||||
|
} finally {
|
||||||
|
lastLoadPromise = null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
return lastLoadPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets if the Wasm module has been instantiated. */
|
||||||
|
export function isInstantiated() {
|
||||||
|
return instanceWithExports != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function instantiateModule(transform) {
|
||||||
|
switch (wasm_url.protocol) {
|
||||||
|
case "file:": {
|
||||||
|
if (typeof Deno !== "object") {
|
||||||
|
throw new Error("file urls are not supported in this environment");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("permissions" in Deno) {
|
||||||
|
Deno.permissions.request({ name: "read", path: wasm_url });
|
||||||
|
}
|
||||||
|
const wasmCode = await Deno.readFile(wasm_url);
|
||||||
|
return WebAssembly.instantiate(
|
||||||
|
!transform ? wasmCode : transform(wasmCode),
|
||||||
|
imports,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case "https:":
|
||||||
|
case "http:": {
|
||||||
|
if (typeof Deno === "object" && "permissions" in Deno) {
|
||||||
|
Deno.permissions.request({ name: "net", host: wasm_url.host });
|
||||||
|
}
|
||||||
|
const wasmResponse = await fetch(wasm_url);
|
||||||
|
if (transform) {
|
||||||
|
const wasmCode = new Uint8Array(await wasmResponse.arrayBuffer());
|
||||||
|
return WebAssembly.instantiate(transform(wasmCode), imports);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
wasmResponse.headers.get("content-type")?.toLowerCase().startsWith(
|
||||||
|
"application/wasm",
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return WebAssembly.instantiateStreaming(wasmResponse, imports);
|
||||||
|
} else {
|
||||||
|
return WebAssembly.instantiate(
|
||||||
|
await wasmResponse.arrayBuffer(),
|
||||||
|
imports,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported protocol: ${wasm_url.protocol}`);
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
@ -0,0 +1,21 @@
|
|||||||
|
import { decompress } from "https://deno.land/x/lz4@v0.1.2/mod.ts";
|
||||||
|
import type {
|
||||||
|
Config,
|
||||||
|
ParseOptions,
|
||||||
|
Program,
|
||||||
|
} from "https://esm.sh/@swc/core@1.2.212/types.d.ts";
|
||||||
|
import { instantiate } from "./lib/deno_swc.generated.js";
|
||||||
|
|
||||||
|
const { parseSync, printSync, transformSync } = await instantiate(decompress);
|
||||||
|
|
||||||
|
export function parse(source: string, opts: ParseOptions): Program {
|
||||||
|
return parseSync(source, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function print(program: Program, opts?: Config): { code: string } {
|
||||||
|
return printSync(program, opts || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transform(source: string, opts: Config): { code: string } {
|
||||||
|
return transformSync(source, opts);
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "deno_swc"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Divy Srivastava <dj.srivastava23@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate_type = ["cdylib"]
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.42"
|
||||||
|
wee_alloc = { version = "0.4.5", optional = true }
|
||||||
|
console_error_panic_hook = "0.1.6"
|
||||||
|
once_cell = "1.3.1"
|
||||||
|
path-clean = "0.1"
|
||||||
|
serde = {version = "1", features = ["derive"]}
|
||||||
|
serde_json = "1"
|
||||||
|
swc = { git = "https://github.com/swc-project/swc", rev = "fd3501b" }
|
||||||
|
swc_ecmascript = { git = "https://github.com/swc-project/swc", rev = "fd3501b" }
|
||||||
|
swc_common = { git = "https://github.com/swc-project/swc", rev = "fd3501b" }
|
||||||
|
wasm-bindgen = {version = "0.2", features = ["serde-serialize"]}
|
||||||
|
wasm-bindgen-futures = "0.4.8"
|
||||||
|
syn = "1.0.65"
|
||||||
|
url = "2.2.2"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["wee_alloc"]
|
||||||
|
|
@ -0,0 +1,106 @@
|
|||||||
|
import { encode } from "https://deno.land/std@0.103.0/encoding/base64.ts";
|
||||||
|
import Terser from "https://esm.sh/terser@4.8.0";
|
||||||
|
import * as lz4 from "https://deno.land/x/lz4@v0.1.2/mod.ts";
|
||||||
|
|
||||||
|
const name = "deno_swc";
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
|
async function requires(...executables) {
|
||||||
|
const where = Deno.build.os === "windows" ? "where" : "which";
|
||||||
|
|
||||||
|
for (const executable of executables) {
|
||||||
|
const process = Deno.run({
|
||||||
|
cmd: [where, executable],
|
||||||
|
stderr: "null",
|
||||||
|
stdin: "null",
|
||||||
|
stdout: "null",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!(await process.status()).success) {
|
||||||
|
err(`Could not find required build tool ${executable}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run(msg, cmd) {
|
||||||
|
log(msg);
|
||||||
|
|
||||||
|
const process = Deno.run({ cmd });
|
||||||
|
|
||||||
|
if (!(await process.status()).success) {
|
||||||
|
err(`${msg} failed`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function log(text) {
|
||||||
|
console.log(`[log] ${text}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function err(text) {
|
||||||
|
console.log(`[err] ${text}`);
|
||||||
|
return Deno.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await requires("rustup", "rustc", "cargo", "wasm-bindgen");
|
||||||
|
|
||||||
|
if (!(await Deno.stat("Cargo.toml")).isFile) {
|
||||||
|
err(`the build script should be executed in the "${name}" root`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await run("building wasm", ["cargo", "build", "--release", "--target", "wasm32-unknown-unknown"]);
|
||||||
|
|
||||||
|
await run(
|
||||||
|
"building using wasm-pack",
|
||||||
|
["wasm-bindgen", "target/wasm32-unknown-unknown/release/deno_swc.wasm" , "--target", "deno", "--weak-refs", "--out-dir", "pkg/"],
|
||||||
|
);
|
||||||
|
|
||||||
|
const wasm = await Deno.readFile(`pkg/${name}_bg.wasm`);
|
||||||
|
|
||||||
|
const compressed = lz4.compress(wasm);
|
||||||
|
console.log(
|
||||||
|
`compressed wasm using lz4 (reduction: ${wasm.length -
|
||||||
|
compressed.length} bytes, size: ${compressed.length} bytes)`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const encoded = encode(compressed);
|
||||||
|
|
||||||
|
log(
|
||||||
|
`encoded wasm using base64, size increase: ${encoded.length -
|
||||||
|
wasm.length} bytes`,
|
||||||
|
);
|
||||||
|
|
||||||
|
log("inlining wasm in js");
|
||||||
|
const source = `import * as lz4 from "https://deno.land/x/lz4@v0.1.2/mod.ts";export const source=lz4.decompress(Uint8Array.from(atob("${encoded}"),c=>c.charCodeAt(0)));`;
|
||||||
|
|
||||||
|
let init = await Deno.readTextFile(`pkg/${name}.js`);
|
||||||
|
let lines = init.split('\n');
|
||||||
|
// We want to replace this code.
|
||||||
|
for (let i = 1; i < 4; i++) lines.splice(-i);
|
||||||
|
init = lines.join('\n');
|
||||||
|
init += `\nconst wasmModule = new WebAssembly.Module(source);\nconst wasmInstance = new WebAssembly.Instance(wasmModule, imports);\nconst wasm = wasmInstance.exports;\n`;
|
||||||
|
console.log(init)
|
||||||
|
|
||||||
|
log("minifying js");
|
||||||
|
const output = Terser.minify(`${source}\n${init}`, {
|
||||||
|
mangle: { module: true },
|
||||||
|
output: {
|
||||||
|
preamble: "//deno-fmt-ignore-file",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (output.error) {
|
||||||
|
err(`encountered error when minifying: ${output.error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const reduction = new Blob([(`${source}\n${init}`)]).size -
|
||||||
|
new Blob([output.code]).size;
|
||||||
|
log(`minified js, size reduction: ${reduction} bytes`);
|
||||||
|
|
||||||
|
log(`writing output to file ("wasm.js")`);
|
||||||
|
await Deno.writeFile("wasm.js", encoder.encode(output.code));
|
||||||
|
|
||||||
|
const outputFile = await Deno.stat("wasm.js");
|
||||||
|
log(
|
||||||
|
`output file ("wasm.js"), final size is: ${outputFile.size} bytes`,
|
||||||
|
);
|
@ -0,0 +1,184 @@
|
|||||||
|
// From https://github.com/swc-project/swc/blob/main/crates/binding_core_wasm/src/lib.rs
|
||||||
|
|
||||||
|
use anyhow::{Context, Error};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use swc::{
|
||||||
|
config::{
|
||||||
|
ErrorFormat, JsMinifyOptions, Options, ParseOptions, SourceMapsConfig,
|
||||||
|
},
|
||||||
|
try_with_handler, Compiler,
|
||||||
|
};
|
||||||
|
use swc_common::{comments::Comments, FileName, FilePathMapping, SourceMap};
|
||||||
|
use swc_ecmascript::ast::{EsVersion, Program};
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
fn convert_err(err: Error, error_format: ErrorFormat) -> JsValue {
|
||||||
|
error_format.format(&err).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = "minifySync")]
|
||||||
|
pub fn minify_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
let c = compiler();
|
||||||
|
|
||||||
|
try_with_handler(
|
||||||
|
c.cm.clone(),
|
||||||
|
swc::HandlerOpts {
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
|handler| {
|
||||||
|
c.run(|| {
|
||||||
|
let opts: JsMinifyOptions =
|
||||||
|
opts.into_serde().context("failed to parse options")?;
|
||||||
|
|
||||||
|
let fm = c.cm.new_source_file(FileName::Anon, s.into());
|
||||||
|
let program = c
|
||||||
|
.minify(fm, handler, &opts)
|
||||||
|
.context("failed to minify file")?;
|
||||||
|
|
||||||
|
JsValue::from_serde(&program).context("failed to serialize json")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|e| convert_err(e, ErrorFormat::Normal))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = "parseSync")]
|
||||||
|
pub fn parse_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
let c = compiler();
|
||||||
|
|
||||||
|
try_with_handler(
|
||||||
|
c.cm.clone(),
|
||||||
|
swc::HandlerOpts {
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
|handler| {
|
||||||
|
c.run(|| {
|
||||||
|
let opts: ParseOptions =
|
||||||
|
opts.into_serde().context("failed to parse options")?;
|
||||||
|
|
||||||
|
let fm = c.cm.new_source_file(FileName::Anon, s.into());
|
||||||
|
|
||||||
|
let cmts = c.comments().clone();
|
||||||
|
let comments = if opts.comments {
|
||||||
|
Some(&cmts as &dyn Comments)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let program = c
|
||||||
|
.parse_js(
|
||||||
|
fm,
|
||||||
|
handler,
|
||||||
|
opts.target,
|
||||||
|
opts.syntax,
|
||||||
|
opts.is_module,
|
||||||
|
comments,
|
||||||
|
)
|
||||||
|
.context("failed to parse code")?;
|
||||||
|
|
||||||
|
JsValue::from_serde(&program).context("failed to serialize json")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|e| convert_err(e, ErrorFormat::Normal))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = "printSync")]
|
||||||
|
pub fn print_sync(s: JsValue, opts: JsValue) -> Result<JsValue, JsValue> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
let c = compiler();
|
||||||
|
|
||||||
|
try_with_handler(
|
||||||
|
c.cm.clone(),
|
||||||
|
swc::HandlerOpts {
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
|_handler| {
|
||||||
|
c.run(|| {
|
||||||
|
let opts: Options =
|
||||||
|
opts.into_serde().context("failed to parse options")?;
|
||||||
|
|
||||||
|
let program: Program =
|
||||||
|
s.into_serde().context("failed to deserialize program")?;
|
||||||
|
|
||||||
|
let s = c
|
||||||
|
.print(
|
||||||
|
&program,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
opts.codegen_target().unwrap_or(EsVersion::Es2020),
|
||||||
|
opts
|
||||||
|
.source_maps
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(SourceMapsConfig::Bool(false)),
|
||||||
|
&Default::default(),
|
||||||
|
None,
|
||||||
|
opts.config.minify.into(),
|
||||||
|
None,
|
||||||
|
opts.config.emit_source_map_columns.into_bool(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.context("failed to print code")?;
|
||||||
|
|
||||||
|
JsValue::from_serde(&s).context("failed to serialize json")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|e| convert_err(e, ErrorFormat::Normal))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = "transformSync")]
|
||||||
|
pub fn transform_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
let c = compiler();
|
||||||
|
let opts: Options = opts
|
||||||
|
.into_serde()
|
||||||
|
.context("failed to parse options")
|
||||||
|
.map_err(|e| convert_err(e, ErrorFormat::Normal))?;
|
||||||
|
|
||||||
|
let error_format = opts.experimental.error_format.unwrap_or_default();
|
||||||
|
|
||||||
|
try_with_handler(
|
||||||
|
c.cm.clone(),
|
||||||
|
swc::HandlerOpts {
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
|handler| {
|
||||||
|
c.run(|| {
|
||||||
|
let fm = c.cm.new_source_file(
|
||||||
|
if opts.filename.is_empty() {
|
||||||
|
FileName::Anon
|
||||||
|
} else {
|
||||||
|
FileName::Real(opts.filename.clone().into())
|
||||||
|
},
|
||||||
|
s.into(),
|
||||||
|
);
|
||||||
|
let out = c
|
||||||
|
.process_js_file(fm, handler, &opts)
|
||||||
|
.context("failed to process input file")?;
|
||||||
|
|
||||||
|
JsValue::from_serde(&out).context("failed to serialize json")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|e| convert_err(e, error_format))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get global sourcemap
|
||||||
|
fn compiler() -> Arc<Compiler> {
|
||||||
|
static C: Lazy<Arc<Compiler>> = Lazy::new(|| {
|
||||||
|
let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
|
||||||
|
|
||||||
|
Arc::new(Compiler::new(cm))
|
||||||
|
});
|
||||||
|
|
||||||
|
C.clone()
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export { assert, assertEquals } from "https://deno.land/std/testing/asserts.ts";
|
@ -0,0 +1,49 @@
|
|||||||
|
import { parse } from "../mod.ts";
|
||||||
|
import { assertEquals } from "./deps.ts";
|
||||||
|
|
||||||
|
Deno.test("parse (no error)", () => {
|
||||||
|
const result = parse("const x: number = 2;", {
|
||||||
|
"syntax": "typescript",
|
||||||
|
});
|
||||||
|
assertEquals(result, {
|
||||||
|
type: "Module",
|
||||||
|
body: [
|
||||||
|
{
|
||||||
|
declarations: [
|
||||||
|
{
|
||||||
|
definite: false,
|
||||||
|
id: {
|
||||||
|
optional: false,
|
||||||
|
span: { ctxt: 0, end: 8, start: 7 },
|
||||||
|
type: "Identifier",
|
||||||
|
typeAnnotation: {
|
||||||
|
span: { ctxt: 0, end: 16, start: 8 },
|
||||||
|
type: "TsTypeAnnotation",
|
||||||
|
typeAnnotation: {
|
||||||
|
kind: "number",
|
||||||
|
span: { ctxt: 0, end: 16, start: 10 },
|
||||||
|
type: "TsKeywordType",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value: "x",
|
||||||
|
},
|
||||||
|
init: {
|
||||||
|
raw: "2",
|
||||||
|
span: { ctxt: 0, end: 20, start: 19 },
|
||||||
|
type: "NumericLiteral",
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
span: { ctxt: 0, end: 20, start: 7 },
|
||||||
|
type: "VariableDeclarator",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
declare: false,
|
||||||
|
kind: "const",
|
||||||
|
span: { ctxt: 0, end: 21, start: 1 },
|
||||||
|
type: "VariableDeclaration",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
interpreter: null,
|
||||||
|
span: { ctxt: 0, end: 21, start: 1 },
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,32 @@
|
|||||||
|
import { print } from "../mod.ts";
|
||||||
|
import { assertEquals } from "./deps.ts";
|
||||||
|
|
||||||
|
Deno.test("print (no error)", () => {
|
||||||
|
const result = print({
|
||||||
|
"type": "Module",
|
||||||
|
"span": { "start": 21, "end": 33, "ctxt": 0 },
|
||||||
|
"body": [{
|
||||||
|
"type": "ClassDeclaration",
|
||||||
|
"identifier": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": { "start": 27, "end": 28, "ctxt": 0 },
|
||||||
|
"value": "X",
|
||||||
|
"optional": false,
|
||||||
|
},
|
||||||
|
"declare": false,
|
||||||
|
"span": { "start": 21, "end": 32, "ctxt": 0 },
|
||||||
|
"decorators": [],
|
||||||
|
"body": [],
|
||||||
|
"superClass": null,
|
||||||
|
"isAbstract": false,
|
||||||
|
"typeParams": null,
|
||||||
|
"superTypeParams": null,
|
||||||
|
"implements": [],
|
||||||
|
}, {
|
||||||
|
"type": "EmptyStatement",
|
||||||
|
"span": { "start": 32, "end": 33, "ctxt": 0 },
|
||||||
|
}],
|
||||||
|
"interpreter": null,
|
||||||
|
}, {});
|
||||||
|
assertEquals(result.code.trim(), "class X {\n}\n;");
|
||||||
|
});
|
@ -0,0 +1,16 @@
|
|||||||
|
import { transform } from "../mod.ts";
|
||||||
|
import { assertEquals } from "./deps.ts";
|
||||||
|
|
||||||
|
Deno.test("transform (no error)", () => {
|
||||||
|
const result = transform("const x: number = 2; console.log(x);", {
|
||||||
|
// deno-lint-ignore ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
"jsc": {
|
||||||
|
"target": "es2016",
|
||||||
|
"parser": {
|
||||||
|
"syntax": "typescript",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assertEquals(result.code.trim(), "const x = 2;\nconsole.log(x);");
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
export const version = "0.2.1";
|
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<flowgorithm fileversion="3.0">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="name" value=""/>
|
||||||
|
<attribute name="authors" value="skybl"/>
|
||||||
|
<attribute name="about" value=""/>
|
||||||
|
<attribute name="saved" value="2022-10-11 01:08:26 AM"/>
|
||||||
|
<attribute name="created" value="c2t5Ymw7UE9ORDsyMDIyLTEwLTExOzAxOjA1OjQ0IEFNOzIxMDY="/>
|
||||||
|
<attribute name="edited" value="c2t5Ymw7UE9ORDsyMDIyLTEwLTExOzAxOjA4OjI2IEFNOzQ7MjIyMA=="/>
|
||||||
|
</attributes>
|
||||||
|
<function name="Main" type="None" variable="">
|
||||||
|
<parameters/>
|
||||||
|
<body/>
|
||||||
|
</function>
|
||||||
|
</flowgorithm>
|
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<flowgorithm fileversion="3.0">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="name" value="benchtest.ts"/>
|
||||||
|
<attribute name="authors" value="skybl, ezfprg"/>
|
||||||
|
<attribute name="about" value="test program for transpiler benchmarks"/>
|
||||||
|
<attribute name="saved" value="2022-10-19 01:17:24 AM"/>
|
||||||
|
<attribute name="created" value="c2t5Ymw7cG9uZDsyMDIyLTEwLTE5OzAxOjE3OjI0IEFN"/>
|
||||||
|
<attribute name="edited" value="c2t5Ymw7cG9uZDsyMDIyLTEwLTE5OzAxOjE3OjI0IEFNOzU7Mjk5MQ=="/>
|
||||||
|
</attributes>
|
||||||
|
<function name="Main" type="None" variable="">
|
||||||
|
<parameters/>
|
||||||
|
<body>
|
||||||
|
<declare name="a, b" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="a" expression="1"/>
|
||||||
|
<input variable="b"/>
|
||||||
|
<output expression="a + b" newline="True"/>
|
||||||
|
<declare name="c" type="Real" array="False" size=""/>
|
||||||
|
<assign variable="c" expression="20 / ((5 + a) ^ (a + b))"/>
|
||||||
|
<output expression="c" newline="True"/>
|
||||||
|
<declare name="f" type="Integer" array="False" size=""/>
|
||||||
|
<declare name="e" type="Real" array="False" size=""/>
|
||||||
|
<assign variable="e" expression="1.4"/>
|
||||||
|
<assign variable="f" expression="2"/>
|
||||||
|
<declare name="g" type="Real" array="False" size=""/>
|
||||||
|
<assign variable="g" expression="e / f"/>
|
||||||
|
<declare name="h" type="Boolean" array="False" size=""/>
|
||||||
|
<assign variable="h" expression="false"/>
|
||||||
|
<output expression="" " & e & " / " & f & " = " & g & ", h = " & h" newline="True"/>
|
||||||
|
<declare name="i" type="String" array="False" size=""/>
|
||||||
|
<assign variable="i" expression="2 & " hello""/>
|
||||||
|
<declare name="j" type="String" array="False" size=""/>
|
||||||
|
<assign variable="j" expression="i & " world""/>
|
||||||
|
<output expression="j" newline="True"/>
|
||||||
|
<if expression="a < b">
|
||||||
|
<then>
|
||||||
|
<output expression=""a is less than b"" newline="True"/>
|
||||||
|
</then>
|
||||||
|
<else>
|
||||||
|
<output expression=""a is more than b"" newline="True"/>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
<if expression="true">
|
||||||
|
<then>
|
||||||
|
<output expression=""single arm"" newline="True"/>
|
||||||
|
</then>
|
||||||
|
<else>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
<declare name="k" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="k" expression="0"/>
|
||||||
|
<while expression="k < 10">
|
||||||
|
<output expression="k" newline="True"/>
|
||||||
|
<assign variable="k" expression="k + 1"/>
|
||||||
|
</while>
|
||||||
|
<declare name="l" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="l" expression="0"/>
|
||||||
|
<for variable="l" start="0" end="10" direction="inc" step="1">
|
||||||
|
<output expression="l" newline="True"/>
|
||||||
|
</for>
|
||||||
|
<for variable="l" start="20" end="10" direction="dec" step="2">
|
||||||
|
<output expression="l" newline="True"/>
|
||||||
|
</for>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
</flowgorithm>
|
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<flowgorithm fileversion="3.0">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="name" value=""/>
|
||||||
|
<attribute name="authors" value="skybl"/>
|
||||||
|
<attribute name="about" value=""/>
|
||||||
|
<attribute name="saved" value="2022-10-07 06:41:15 PM"/>
|
||||||
|
<attribute name="created" value="c2t5Ymw7UE9ORDsyMDIyLTEwLTA3OzA2OjQwOjQ4IFBNOzIxMzQ="/>
|
||||||
|
<attribute name="edited" value="c2t5Ymw7UE9ORDsyMDIyLTEwLTA3OzA2OjQxOjE1IFBNOzE7MjIzNw=="/>
|
||||||
|
</attributes>
|
||||||
|
<function name="Main" type="None" variable="">
|
||||||
|
<parameters/>
|
||||||
|
<body>
|
||||||
|
<output expression="10 / 2" newline="True"/>
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
</flowgorithm>
|
@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<flowgorithm fileversion="3.0">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="name" value="Test Name"/>
|
||||||
|
<attribute name="authors" value="skybl"/>
|
||||||
|
<attribute name="about" value="Test Description"/>
|
||||||
|
<attribute name="saved" value="2022-10-07 01:22:57 AM"/>
|
||||||
|
<attribute name="created" value="c2t5Ymw7REVTS1RPUC03VEFWOEpBOzIwMjItMTAtMDY7MDE6NDU6MDMgQU07Mjg3Mg=="/>
|
||||||
|
<attribute name="edited" value="c2t5Ymw7REVTS1RPUC03VEFWOEpBOzIwMjItMTAtMDc7MDE6MjI6NTcgQU07NTsyOTg5"/>
|
||||||
|
</attributes>
|
||||||
|
<function name="Main" type="None" variable="">
|
||||||
|
<parameters/>
|
||||||
|
<body>
|
||||||
|
<comment text="this is a comment"/>
|
||||||
|
<declare name="a, b" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="a" expression="1"/>
|
||||||
|
<input variable="b"/>
|
||||||
|
<output expression="a + b" newline="True"/>
|
||||||
|
<if expression="a < b">
|
||||||
|
<then>
|
||||||
|
<output expression=""good"" newline="True"/>
|
||||||
|
</then>
|
||||||
|
<else>
|
||||||
|
<output expression=""bad"" newline="True"/>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
<while expression="a < 5">
|
||||||
|
<output expression=""a is now " & a" newline="True"/>
|
||||||
|
<assign variable="a" expression="a + 1"/>
|
||||||
|
</while>
|
||||||
|
<for variable="b" start="0" end="10" direction="inc" step="1">
|
||||||
|
<output expression=""b is now " & b" newline="True"/>
|
||||||
|
</for>
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
<function name="foo" type="Integer" variable="b">
|
||||||
|
<parameters>
|
||||||
|
<parameter name="a" type="Integer" array="False"/>
|
||||||
|
</parameters>
|
||||||
|
<body>
|
||||||
|
<declare name="b" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="b" expression="a"/>
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
</flowgorithm>
|
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<flowgorithm fileversion="3.0">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="name" value="Test Name"/>
|
||||||
|
<attribute name="authors" value="skybl"/>
|
||||||
|
<attribute name="about" value="Test Description"/>
|
||||||
|
<attribute name="saved" value="2022-10-07 12:29:19 AM"/>
|
||||||
|
<attribute name="created" value="c2t5Ymw7REVTS1RPUC03VEFWOEpBOzIwMjItMTAtMDY7MDE6NDU6MDMgQU07Mjg3Mg=="/>
|
||||||
|
<attribute name="edited" value="c2t5Ymw7REVTS1RPUC03VEFWOEpBOzIwMjItMTAtMDc7MTI6Mjk6MTkgQU07NDsyOTk1"/>
|
||||||
|
</attributes>
|
||||||
|
<function name="Main" type="None" variable="">
|
||||||
|
<parameters/>
|
||||||
|
<body>
|
||||||
|
<declare name="a, b" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="a" expression="1"/>
|
||||||
|
<input variable="b"/>
|
||||||
|
<output expression="a + b" newline="True"/>
|
||||||
|
<if expression="a < b">
|
||||||
|
<then>
|
||||||
|
<output expression=""good"" newline="True"/>
|
||||||
|
</then>
|
||||||
|
<else>
|
||||||
|
<output expression=""bad"" newline="True"/>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
<while expression="a < 5">
|
||||||
|
<output expression=""a is now " & a" newline="True"/>
|
||||||
|
<assign variable="a" expression="a + 1"/>
|
||||||
|
</while>
|
||||||
|
<for variable="b" start="0" end="10" direction="inc" step="1">
|
||||||
|
<output expression=""b is now " & b" newline="True"/>
|
||||||
|
</for>
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
<function name="foo" type="Integer" variable="b">
|
||||||
|
<parameters>
|
||||||
|
<parameter name="a" type="Integer" array="False"/>
|
||||||
|
</parameters>
|
||||||
|
<body>
|
||||||
|
<declare name="b" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="b" expression="a"/>
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
</flowgorithm>
|
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<flowgorithm fileversion="3.0">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="name" value="Test Name"/>
|
||||||
|
<attribute name="authors" value="skybl"/>
|
||||||
|
<attribute name="about" value="Test Description"/>
|
||||||
|
<attribute name="saved" value="2022-10-07 12:17:19 AM"/>
|
||||||
|
<attribute name="created" value="c2t5Ymw7REVTS1RPUC03VEFWOEpBOzIwMjItMTAtMDY7MDE6NDU6MDMgQU07Mjg3Mg=="/>
|
||||||
|
<attribute name="edited" value="c2t5Ymw7REVTS1RPUC03VEFWOEpBOzIwMjItMTAtMDc7MTI6MTc6MTkgQU07MjsyOTkw"/>
|
||||||
|
</attributes>
|
||||||
|
<function name="Main" type="None" variable="">
|
||||||
|
<parameters/>
|
||||||
|
<body>
|
||||||
|
<declare name="a, b" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="a" expression="1"/>
|
||||||
|
<input variable="b"/>
|
||||||
|
<output expression="a + b" newline="True"/>
|
||||||
|
<if expression="a < b">
|
||||||
|
<then>
|
||||||
|
<output expression=""good"" newline="True"/>
|
||||||
|
</then>
|
||||||
|
<else>
|
||||||
|
<output expression=""bad"" newline="True"/>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
<while expression="a < 5">
|
||||||
|
<output expression=""a is now " & a" newline="True"/>
|
||||||
|
<assign variable="a" expression="a + 1"/>
|
||||||
|
</while>
|
||||||
|
<for variable="b" start="0" end="10" direction="inc" step="1">
|
||||||
|
<output expression=""b is now " & b" newline="True"/>
|
||||||
|
</for>
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
<function name="foo" type="Integer" variable="b">
|
||||||
|
<parameters>
|
||||||
|
<parameter name="a" type="Integer" array="False"/>
|
||||||
|
</parameters>
|
||||||
|
<body>
|
||||||
|
<declare name="b" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="b" expression="a"/>
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
</flowgorithm>
|
@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<flowgorithm fileversion="3.0">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="name" value="Test Name"/>
|
||||||
|
<attribute name="authors" value="skybl"/>
|
||||||
|
<attribute name="about" value="Test Description"/>
|
||||||
|
<attribute name="saved" value="2022-10-06 01:53:41 AM"/>
|
||||||
|
<attribute name="created" value="c2t5Ymw7REVTS1RPUC03VEFWOEpBOzIwMjItMTAtMDY7MDE6NDU6MDMgQU07Mjg3Mg=="/>
|
||||||
|
<attribute name="edited" value="c2t5Ymw7REVTS1RPUC03VEFWOEpBOzIwMjItMTAtMDY7MDE6NTM6NDEgQU07MTsyOTgx"/>
|
||||||
|
</attributes>
|
||||||
|
<function name="Main" type="None" variable="">
|
||||||
|
<parameters/>
|
||||||
|
<body>
|
||||||
|
<declare name="a, b" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="a" expression="1"/>
|
||||||
|
<input variable="b"/>
|
||||||
|
<output expression="a + b" newline="True"/>
|
||||||
|
<if expression="a < b">
|
||||||
|
<then>
|
||||||
|
<output expression=""good"" newline="True"/>
|
||||||
|
</then>
|
||||||
|
<else>
|
||||||
|
<output expression=""bad"" newline="True"/>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
<while expression="a < 5">
|
||||||
|
<output expression=""a is now " & a" newline="True"/>
|
||||||
|
<assign variable="a" expression="a + 1"/>
|
||||||
|
</while>
|
||||||
|
<for variable="b" start="0" end="10" direction="inc" step="1">
|
||||||
|
<output expression=""b is now " & b" newline="True"/>
|
||||||
|
</for>
|
||||||
|
<call expression="foo(a)"/>
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
<function name="foo" type="Integer" variable="b">
|
||||||
|
<parameters>
|
||||||
|
<parameter name="a" type="Integer" array="False"/>
|
||||||
|
</parameters>
|
||||||
|
<body>
|
||||||
|
<declare name="b" type="Integer" array="False" size=""/>
|
||||||
|
<assign variable="b" expression="a"/>
|
||||||
|
</body>
|
||||||
|
</function>
|
||||||
|
</flowgorithm>
|
@ -0,0 +1,22 @@
|
|||||||
|
meta("username", "skybl");
|
||||||
|
meta("hostname", "pond");
|
||||||
|
meta("editVersion", "4");
|
||||||
|
meta("editMysteryNumber", "2982");
|
||||||
|
attr("name", "DESIGN/CREATE A PROGRAM USING THE SEQUENCE STRUCTURE AND NAMED CONSTANTS");
|
||||||
|
attr("authors", "Jacob Teatro");
|
||||||
|
attr("about", "Current Semester: 1\nCourse Section: 0072\nBlackboard Username: jtteatro");
|
||||||
|
|
||||||
|
let userBal: int, userOverdrafts: int;
|
||||||
|
let OVERDRAFTfee = 2, MINfee = 0.01;
|
||||||
|
|
||||||
|
print("Bank Calculator v0.0.1");
|
||||||
|
print("Please enter your current balance: ");
|
||||||
|
input(userBal);
|
||||||
|
print("Please enter the number of overdrafts you have had this month: ");
|
||||||
|
input(userOverdrafts);
|
||||||
|
|
||||||
|
let totalMinFee = userBal * MINfee;
|
||||||
|
let totalOverdraftFee = userOverdrafts * OVERDRAFTfee;
|
||||||
|
|
||||||
|
print("Your total fee is: " + (totalMinFee + totalOverdraftFee));
|
||||||
|
print("Thank you for using Bank Calculator v0.0.1");
|
@ -0,0 +1,7 @@
|
|||||||
|
meta("username", "skybl");
|
||||||
|
meta("hostname", "pond");
|
||||||
|
meta("editVersion", "4");
|
||||||
|
meta("editMysteryNumber", "2982");
|
||||||
|
attr("name", "DESIGN/CREATE A PROGRAM USING THE SEQUENCE STRUCTURE AND NAMED CONSTANTS");
|
||||||
|
attr("authors", "Jacob Teatro");
|
||||||
|
attr("about", "Current Semester: 1\nCourse Section: 0072\nBlackboard Username: jtteatro");
|
@ -0,0 +1,7 @@
|
|||||||
|
let y = 0;
|
||||||
|
while (y < 2) {
|
||||||
|
let i = 0;
|
||||||
|
print(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
print(i);
|
@ -0,0 +1,72 @@
|
|||||||
|
benchmark time (avg) (min … max) p75 p99 p995
|
||||||
|
----------------------------------------------------------------------------------- -----------------------------
|
||||||
|
transform 53.55 µs/iter (40.47 µs … 3.71 ms) 49.03 µs 221.54 µs 332.98 µs
|
||||||
|
transformBlock 11.67 µs/iter (9.46 µs … 869.33 µs) 10.8 µs 33.09 µs 36.68 µs
|
||||||
|
transformVariableDecl 612.18 ns/iter (531.9 ns … 960.45 ns) 615.87 ns 960.45 ns 960.45 ns
|
||||||
|
transformExpr :: `20 / ((5 + a) ^ (a + b))` 509.35 ns/iter (466.79 ns … 773.72 ns) 515.96 ns 756.48 ns 773.72 ns
|
||||||
|
transformBlockCallExpr :: `print(a + b)` 451 ns/iter (403.7 ns … 772.28 ns) 462.06 ns 700.4 ns 772.28 ns
|
||||||
|
|
||||||
|
[+] check if string contains escapable characters before
|
||||||
|
replacing
|
||||||
|
|
||||||
|
transform 36.27 µs/iter (29.66 µs … 1.42 ms) 33.1 µs 104.82 µs 247.19 µs
|
||||||
|
transformBlock 5.72 µs/iter (5.63 µs … 6.17 µs) 5.71 µs 6.17 µs 6.17 µs
|
||||||
|
transformVariableDecl 312.97 ns/iter (289.81 ns … 432.07 ns) 319.6 ns 389.74 ns 432.07 ns
|
||||||
|
transformExpr :: `20 / ((5 + a) ^ (a + b))` 453.37 ns/iter (429.78 ns … 523.84 ns) 459.99 ns 495.72 ns 523.84 ns
|
||||||
|
transformBlockCallExpr :: `print(a + b)` 194.72 ns/iter (184.87 ns … 239.98 ns) 198.91 ns 227.68 ns 239.71 ns
|
||||||
|
|
||||||
|
[-] move idents into TransformState, had to change
|
||||||
|
benchmarks slightly
|
||||||
|
|
||||||
|
transform 37.73 µs/iter (29.89 µs … 1.74 ms) 34.41 µs 147.62 µs 244.81 µs
|
||||||
|
transformBlock 6.17 µs/iter (5.36 µs … 660.08 µs) 6.28 µs 8.71 µs 11.31 µs
|
||||||
|
transformVariableDecl 329.67 ns/iter (300.06 ns … 503.69 ns) 332.52 ns 492.28 ns 503.69 ns
|
||||||
|
transformExpr :: `20 / ((5 + a) ^ (a + b))` 473.44 ns/iter (431.62 ns … 637.82 ns) 479.82 ns 582.41 ns 637.82 ns
|
||||||
|
transformBlockCallExpr :: `print(a + b)` 197.26 ns/iter (179.65 ns … 319.4 ns) 202.18 ns 241.29 ns 244.67 ns
|
||||||
|
|
||||||
|
[-] switch back to using strings as types
|
||||||
|
|
||||||
|
transform 37.13 µs/iter (31.27 µs … 2.54 ms) 33.76 µs 112.42 µs 226.41 µs
|
||||||
|
transformBlock 7.41 µs/iter (6.83 µs … 576.56 µs) 7.19 µs 10.87 µs 21.64 µs
|
||||||
|
transformVariableDecl 523.43 ns/iter (503.78 ns … 672.13 ns) 525.52 ns 628.98 ns 672.13 ns
|
||||||
|
transformExpr :: `20 / ((5 + a) ^ (a + b))` 450.4 ns/iter (430.57 ns … 542.85 ns) 455.2 ns 514.45 ns 542.85 ns
|
||||||
|
transformBlockCallExpr :: `print(a + b)` 189.01 ns/iter (180.33 ns … 286.61 ns) 192.17 ns 219.97 ns 248.98 ns
|
||||||
|
|
||||||
|
[+] iterate through typeNames instead of Object.entries()
|
||||||
|
in transformVariableDecl()
|
||||||
|
|
||||||
|
transform 36.18 µs/iter (29.69 µs … 3.26 ms) 32.74 µs 94.19 µs 242.39 µs
|
||||||
|
transformBlock 5.93 µs/iter (5.44 µs … 832.39 µs) 5.85 µs 8.3 µs 9.77 µs
|
||||||
|
transformVariableDecl 333.23 ns/iter (316.84 ns … 426.59 ns) 336.35 ns 412.75 ns 426.59 ns
|
||||||
|
transformExpr :: `20 / ((5 + a) ^ (a + b))` 445.46 ns/iter (426.16 ns … 512.61 ns) 450.72 ns 501.26 ns 512.61 ns
|
||||||
|
transformBlockCallExpr :: `print(a + b)` 191.11 ns/iter (183.08 ns … 240.99 ns) 194.36 ns 216 ns 223.62 ns
|
||||||
|
|
||||||
|
[+] made indent a string in state, add indent string
|
||||||
|
before statement instead of passing it
|
||||||
|
|
||||||
|
transform 34.06 µs/iter (28.41 µs … 1.44 ms) 30.99 µs 110.45 µs 222.66 µs
|
||||||
|
transformBlock 5.62 µs/iter (5.01 µs … 1.08 ms) 5.58 µs 6.8 µs 8.48 µs
|
||||||
|
transformVariableDecl 306.54 ns/iter (291.54 ns … 392.86 ns) 309.48 ns 344.42 ns 392.86 ns
|
||||||
|
transformExpr :: `20 / ((5 + a) ^ (a + b))` 457.71 ns/iter (431.29 ns … 581.16 ns) 461.86 ns 526.14 ns 581.16 ns
|
||||||
|
transformBlockCallExpr :: `print(a + b)` 184.39 ns/iter (172.76 ns … 208.97 ns) 188.07 ns 200.83 ns 201.95 ns
|
||||||
|
|
||||||
|
[-] dammit turns out that the regex escape thing wasn't
|
||||||
|
even matching
|
||||||
|
|
||||||
|
transform 42.14 µs/iter (35.64 µs … 1.48 ms) 38.14 µs 148.44 µs 270.71 µs
|
||||||
|
transformBlock 7.67 µs/iter (7.17 µs … 533.04 µs) 7.57 µs 9.11 µs 10.88 µs
|
||||||
|
transformVariableDecl 305.22 ns/iter (291.67 ns … 367.68 ns) 308.55 ns 348.5 ns 367.68 ns
|
||||||
|
transformExpr :: `20 / ((5 + a) ^ (a + b))` 442.14 ns/iter (425.39 ns … 481.67 ns) 446.63 ns 479.34 ns 481.67 ns
|
||||||
|
transformBlockCallExpr :: `print(a + b)` 173.47 ns/iter (166.02 ns … 210.7 ns) 177.27 ns 190.81 ns 198.78 ns
|
||||||
|
|
||||||
|
[-] added if statement support, longer benchtest.ts
|
||||||
|
|
||||||
|
transform 50.17 µs/iter (41.6 µs … 2.42 ms) 44.86 µs 200.54 µs 320.54 µs
|
||||||
|
transformBlock 10.34 µs/iter (9.74 µs … 548.07 µs) 10.14 µs 14.01 µs 20.94 µs
|
||||||
|
transformVariableDecl 302.8 ns/iter (290.01 ns … 361.69 ns) 305.75 ns 338.17 ns 361.69 ns
|
||||||
|
transformExpr :: `20 / ((5 + a) ^ (a + b))` 468.38 ns/iter (451.07 ns … 499.1 ns) 472.42 ns 497.34 ns 499.1 ns
|
||||||
|
transformBlockCallExpr :: `print(a + b)` 180.34 ns/iter (173.4 ns … 205.18 ns) 183.79 ns 196.68 ns 204.69 ns
|
||||||
|
transformIfStmt 1.56 µs/iter (1.53 µs … 1.67 µs) 1.58 µs 1.67 µs 1.67 µs
|
||||||
|
|
||||||
|
? try making each type declaration group a string and directly append to it?
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
// new
|
||||||
|
function a(arg: unknown): asserts arg is string;
|
||||||
|
|
||||||
|
// my impl
|
||||||
|
function b<T>(arg: T): T extends string ? void : never;
|
||||||
|
|
||||||
|
declare let v: string | number;
|
||||||
|
a(v);
|
||||||
|
let x: string = v;
|
@ -0,0 +1,28 @@
|
|||||||
|
const lua: string[] = [
|
||||||
|
"zero",
|
||||||
|
"one",
|
||||||
|
"two",
|
||||||
|
"three",
|
||||||
|
"four",
|
||||||
|
"five",
|
||||||
|
"six",
|
||||||
|
"seven",
|
||||||
|
"eight",
|
||||||
|
"nine"
|
||||||
|
];
|
||||||
|
|
||||||
|
Deno.bench("randomgen", () => {
|
||||||
|
lua[Math.round(Math.random() * 10)];
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("includes", () => {
|
||||||
|
const input = lua[Math.round(Math.random() * 10)];
|
||||||
|
|
||||||
|
["zero", "five", "six", "nine"].includes(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("logic", () => {
|
||||||
|
const input = lua[Math.round(Math.random() * 10)];
|
||||||
|
|
||||||
|
input === "zero" || input === "five" || input === "six" || input === "nine";
|
||||||
|
});
|
@ -0,0 +1,82 @@
|
|||||||
|
import { Type as NType } from "../src/type_utils.ts";
|
||||||
|
|
||||||
|
const sTypes = ["int", "string", "bool"];
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
const r1 = Math.floor(Math.random() * 10 / 3);
|
||||||
|
const r2 = Math.floor(Math.random() * 10 / 3);
|
||||||
|
|
||||||
|
console.log(`run ${i} comparing ${sTypes[r1]} and ${sTypes[r2]}`);
|
||||||
|
|
||||||
|
const _a: NType = r1;
|
||||||
|
const _b: NType = r2
|
||||||
|
|
||||||
|
Deno.bench("NType (boolop)", () => {
|
||||||
|
_a === _b || _a + _b !== 8;
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("NType (switch)", () => {
|
||||||
|
let c: boolean;
|
||||||
|
|
||||||
|
if (_a === _b) {
|
||||||
|
c = true;
|
||||||
|
} else {
|
||||||
|
switch (_a + _b) {
|
||||||
|
case 4:
|
||||||
|
case 10: c = true; break
|
||||||
|
case 8: c = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const a = sTypes[r1];
|
||||||
|
const b = sTypes[r2];
|
||||||
|
|
||||||
|
Deno.bench("SType (if)", () => {
|
||||||
|
let c: boolean;
|
||||||
|
|
||||||
|
if (a === b) {
|
||||||
|
c = true;
|
||||||
|
} else {
|
||||||
|
if ((a === "int" && b === "string") || (a === "string" && b === "int")) {
|
||||||
|
c = true;
|
||||||
|
} else if ((a === "int" && b === "boolean") || (a === "boolean" && b === "int")) {
|
||||||
|
c = false;
|
||||||
|
} else if ((a === "boolean" && b === "string") || (a === "string" && b === "boolean")) {
|
||||||
|
c = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("SType (boolop)", () => {
|
||||||
|
let c: boolean;
|
||||||
|
|
||||||
|
if (a === b) {
|
||||||
|
c = true;
|
||||||
|
} else {
|
||||||
|
c = (
|
||||||
|
((a === "int" && b === "string") || (a === "string" && b === "int"))
|
||||||
|
|| ((a === "boolean" && b === "string") || (a === "string" && b === "boolean"))
|
||||||
|
|| !((a === "int" && b === "boolean") || (a === "boolean" || b === "int"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("SType (if+includes)", () => {
|
||||||
|
let c: boolean;
|
||||||
|
|
||||||
|
if (a === b) {
|
||||||
|
c = true;
|
||||||
|
} else {
|
||||||
|
c = (
|
||||||
|
([a, b].includes("int") && [a, b].includes("string"))
|
||||||
|
|| ([a, b].includes("boolean") && [a, b].includes("string"))
|
||||||
|
|| !([a, b].includes("int") && [a, b].includes("boolean"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}, 5000);
|
@ -0,0 +1,54 @@
|
|||||||
|
const lua: string[] = [
|
||||||
|
"zero",
|
||||||
|
"one",
|
||||||
|
"two",
|
||||||
|
"three",
|
||||||
|
"four",
|
||||||
|
"five",
|
||||||
|
"six",
|
||||||
|
"seven",
|
||||||
|
"eight",
|
||||||
|
"nine"
|
||||||
|
];
|
||||||
|
|
||||||
|
Deno.bench("randomgen", () => {
|
||||||
|
lua[Math.round(Math.random() * 10)];
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("switch", () => {
|
||||||
|
const input = lua[Math.round(Math.random() * 10)];
|
||||||
|
|
||||||
|
let a;
|
||||||
|
|
||||||
|
switch (input) {
|
||||||
|
case "zero": a = 0; break;
|
||||||
|
case "one": a = 1; break;
|
||||||
|
case "two": a = 2; break;
|
||||||
|
case "three": a = 3; break;
|
||||||
|
case "four": a = 4; break;
|
||||||
|
case "five": a = 5; break;
|
||||||
|
case "six": a = 6; break;
|
||||||
|
case "seven": a = 7; break;
|
||||||
|
case "eight": a = 8; break;
|
||||||
|
case "nine": a = 9; break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("lookup table", () => {
|
||||||
|
const input = lua[Math.round(Math.random() * 10)];
|
||||||
|
|
||||||
|
const lut: Record<string, number> = {
|
||||||
|
zero: 0,
|
||||||
|
one: 1,
|
||||||
|
two: 2,
|
||||||
|
three: 3,
|
||||||
|
four: 4,
|
||||||
|
five: 5,
|
||||||
|
six: 6,
|
||||||
|
seven: 7,
|
||||||
|
eight: 8,
|
||||||
|
nine: 9
|
||||||
|
};
|
||||||
|
|
||||||
|
const a = lut[input];
|
||||||
|
});
|
@ -0,0 +1,92 @@
|
|||||||
|
import { Type, validKeys } from "./type_utils.ts";
|
||||||
|
|
||||||
|
export class InvalidTypeAnnotation extends SyntaxError {
|
||||||
|
constructor (value: string, position: number) {
|
||||||
|
super(`Could not determine type from \`<: ${value}>\`. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MissingType extends SyntaxError {
|
||||||
|
constructor (variable: string, position: number) {
|
||||||
|
super(`A type was not given when declaring \`${variable}\`. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidType extends TypeError {
|
||||||
|
constructor (expected: Type | string, got: Type | string, position: number) {
|
||||||
|
super(`Expected \`${expected}\`, got \`${got}\`. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidSyntaxType extends SyntaxError {
|
||||||
|
constructor (expected: string, got: string, position: number) {
|
||||||
|
super(`Expected \`<${expected}>\`, got \`<${got}>\`. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidExpression extends SyntaxError {
|
||||||
|
constructor (type: string, position: number) {
|
||||||
|
super(`Unknown or unsupported expression or literal type \`${type}\`. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidBinaryOp extends TypeError {
|
||||||
|
constructor (a: Type, b: Type, op: string, position: number) {
|
||||||
|
super(`Cannot perform operation \`${op}\` on \`${a}\` and \`${b}\`. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UndefinedReference extends ReferenceError {
|
||||||
|
constructor (name: string, position: number) {
|
||||||
|
super(`\`${name}\` is not defined, or is a builtin that cannot be used in an expression. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidAssignmentTarget extends SyntaxError {
|
||||||
|
constructor (position: number) {
|
||||||
|
super(`Left-hand side of assignment must be an \`Identifier\`. Destructuring not supported. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidArgumentCount extends SyntaxError {
|
||||||
|
constructor (name: string, count: number, expected: number, position: number) {
|
||||||
|
super(`\`${name}\` expects ${expected} arguments, got ${count}. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidAttrOrMeta extends Error {
|
||||||
|
constructor (key: string, group: "attr" | "meta", position: number) {
|
||||||
|
super(`Expected \`${validKeys[group].join(", ")}\`, got \`${key}\`. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AlreadyDefined extends ReferenceError {
|
||||||
|
constructor (decl: string, position: number) {
|
||||||
|
super(`\`${decl}\` is already defined. (at ${position})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ForMissingEnd extends SyntaxError {
|
||||||
|
constructor (position: number) {
|
||||||
|
super(`Missing direction and end in for statement (at ${position})\nSee DOCS.md for more info on for statements.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ForMissingInit extends SyntaxError {
|
||||||
|
constructor (position: number) {
|
||||||
|
super(`Missing init in for statement (at ${position})\nSee DOCS.md for more info on for statements.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ForVariableMismatch extends SyntaxError {
|
||||||
|
constructor (expected: string, got: string, position: number) {
|
||||||
|
super(`Expected \`${expected}\`, got \`${got}\` (at ${position})\nSee DOCS.md for more info on for statements.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ForInvalidOperator extends InvalidSyntaxType {
|
||||||
|
constructor (op: string, position: number) {
|
||||||
|
super("+= | -=", op, position);
|
||||||
|
this.message = `Invalid operator in for statement: ${this.message}\nSee DOCS.md for more info on for statements.`;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
import { parse } from "./swc.ts";
|
||||||
|
import { transform } from "./transformers.ts";
|
||||||
|
import { join, basename, dirname } from "https://deno.land/std/path/mod.ts";
|
||||||
|
|
||||||
|
const inPath = Deno.args[0];
|
||||||
|
const input = Deno.readTextFileSync(inPath);
|
||||||
|
|
||||||
|
const ast = parse(input, {
|
||||||
|
syntax: "typescript",
|
||||||
|
comments: true,
|
||||||
|
target: "es2020",
|
||||||
|
});
|
||||||
|
|
||||||
|
const template = Deno.readTextFileSync("./assets/template.fprg");
|
||||||
|
|
||||||
|
const output = transform(input, ast, {
|
||||||
|
indent: 3,
|
||||||
|
showSourceStatements: false
|
||||||
|
}, template);
|
||||||
|
|
||||||
|
if (Deno.args.length > 1) {
|
||||||
|
Deno.writeTextFileSync(Deno.args[1], output);
|
||||||
|
} else {
|
||||||
|
const inPathBasename = basename(inPath);
|
||||||
|
const dot = inPathBasename.lastIndexOf(".");
|
||||||
|
const outPath = join(
|
||||||
|
dirname(inPath),
|
||||||
|
dot > -1
|
||||||
|
? inPathBasename.slice(0, dot) + ".fprg"
|
||||||
|
: inPathBasename + ".fprg"
|
||||||
|
);
|
||||||
|
console.log(outPath);
|
||||||
|
Deno.writeTextFileSync(outPath, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(output);
|
@ -0,0 +1,78 @@
|
|||||||
|
import { Type } from "./type_utils.ts";
|
||||||
|
import { escapeXML, capitalize } from "./utils.ts";
|
||||||
|
|
||||||
|
const typeToFPRGType: Record<Type, string> = {
|
||||||
|
int: "Integer",
|
||||||
|
float: "Real",
|
||||||
|
string: "String",
|
||||||
|
boolean: "Boolean",
|
||||||
|
}
|
||||||
|
|
||||||
|
export function declare (name: string, type: Type): string {
|
||||||
|
return `<declare `
|
||||||
|
+ `name="${name}" `
|
||||||
|
+ `type="${typeToFPRGType[type]}" `
|
||||||
|
+ `array="False" `
|
||||||
|
+ `size=""/>\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assign (variable: string, expr: string): string {
|
||||||
|
return `<assign `
|
||||||
|
+ `variable="${variable}" `
|
||||||
|
+ `expression="${escapeXML(expr)}"/>\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function output (expr: string, newline: boolean): string {
|
||||||
|
return `<output `
|
||||||
|
+ `expression="${escapeXML(expr)}" `
|
||||||
|
+ `newline="${capitalize(newline.toString())}"/>\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function input (variable: string): string {
|
||||||
|
return `<input `
|
||||||
|
+ `variable="${variable}"/>\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function comment (text: string): string {
|
||||||
|
return `<comment text="${escapeXML(text)}"/>\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function _if (
|
||||||
|
indent: string,
|
||||||
|
expr: string,
|
||||||
|
arm1: string,
|
||||||
|
arm2?: string
|
||||||
|
): string {
|
||||||
|
const exIndent = indent + " ";
|
||||||
|
return `${indent}<if expression="${escapeXML(expr)}">\n`
|
||||||
|
+ `${exIndent}<then>\n${arm1}${exIndent}</then>\n`
|
||||||
|
+ `${exIndent}<else>\n${arm2 ?? ""}${exIndent}</else>\n`
|
||||||
|
+ `${indent}</if>\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function _while (
|
||||||
|
indent: string,
|
||||||
|
expr: string,
|
||||||
|
arm: string
|
||||||
|
): string {
|
||||||
|
return `${indent}<while expression="${escapeXML(expr)}">\n`
|
||||||
|
+ `${arm}${indent}</while>\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function _for (
|
||||||
|
indent: string,
|
||||||
|
variable: string,
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
direction: "inc" | "dec",
|
||||||
|
step: number,
|
||||||
|
body: string
|
||||||
|
): string {
|
||||||
|
return `${indent}<for `
|
||||||
|
+ `variable="${variable}" `
|
||||||
|
+ `start="${start}" `
|
||||||
|
+ `end="${end}" `
|
||||||
|
+ `direction="${direction}" `
|
||||||
|
+ `step="${step}">\n`
|
||||||
|
+ `${body}${indent}</for>\n`;
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
import {instantiate} from "../lib/deno_swc/lib/deno_swc.generated.js";
|
||||||
|
import {decompress} from "https://deno.land/x/lz4@v0.1.2/mod.ts";
|
||||||
|
import type {
|
||||||
|
Config,
|
||||||
|
JsMinifyOptions,
|
||||||
|
ParseOptions,
|
||||||
|
Program,
|
||||||
|
} from "https://esm.sh/@swc/core@1.2.212/types.d.ts";
|
||||||
|
|
||||||
|
const {
|
||||||
|
parseSync,
|
||||||
|
printSync,
|
||||||
|
transformSync,
|
||||||
|
minifySync,
|
||||||
|
} = await instantiate(decompress);
|
||||||
|
|
||||||
|
export function parse(source: string, opts: ParseOptions): Program{
|
||||||
|
return parseSync(source, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function print(program: Program, opts?: Config): string{
|
||||||
|
return printSync(program, opts ?? {}).code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformSync(source, opts).code;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function minify(source: string, opts?: JsMinifyOptions): string{
|
||||||
|
return minifySync(source, opts ?? {}).code;
|
||||||
|
}
|
||||||
|
|
||||||
|
export * from "https://esm.sh/@swc/core@1.2.212/types.d.ts";
|
@ -0,0 +1,738 @@
|
|||||||
|
import { format } from "https://deno.land/std@0.146.0/datetime/mod.ts";
|
||||||
|
|
||||||
|
import { Type } from "./type_utils.ts";
|
||||||
|
import * as TypeUtils from "./type_utils.ts";
|
||||||
|
|
||||||
|
import { capitalize, escapeXML } from "./utils.ts";
|
||||||
|
|
||||||
|
import * as SWC from "./swc.ts";
|
||||||
|
import * as Errors from "./errors.ts";
|
||||||
|
import * as Statements from "./statements.ts";
|
||||||
|
import { BlockStatement, NumericLiteral, Statement } from "./swc.ts";
|
||||||
|
|
||||||
|
export interface Ident { type: Type; defined: boolean; }
|
||||||
|
export interface Assignment { name: string; value: string }
|
||||||
|
export type Idents = Map<string, Ident>;
|
||||||
|
export type ProgramAttributes = Partial<{
|
||||||
|
[index: string]: string;
|
||||||
|
name: string;
|
||||||
|
authors: string;
|
||||||
|
about: string;
|
||||||
|
saved: string;
|
||||||
|
created: string;
|
||||||
|
edited: string;
|
||||||
|
}>
|
||||||
|
export type ProgramMetadata = Partial<{
|
||||||
|
[index: string]: string | number;
|
||||||
|
username: string;
|
||||||
|
hostname: string;
|
||||||
|
editversion: number;
|
||||||
|
editMysteryNumber: number;
|
||||||
|
}>;
|
||||||
|
export interface TransformOptions {
|
||||||
|
[index: string]: number | boolean | ProgramAttributes | ProgramMetadata;
|
||||||
|
showSourceStatements: boolean;
|
||||||
|
attr: ProgramAttributes;
|
||||||
|
meta: ProgramMetadata;
|
||||||
|
};
|
||||||
|
export interface TransformState {
|
||||||
|
input: string;
|
||||||
|
idents: Map<string, Ident>;
|
||||||
|
indent: string;
|
||||||
|
opts: TransformOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transform (
|
||||||
|
input: string,
|
||||||
|
ast: SWC.Module | SWC.Program,
|
||||||
|
options: Partial<TransformOptions>,
|
||||||
|
customTemplate?: string
|
||||||
|
): string {
|
||||||
|
const state: TransformState = {
|
||||||
|
input: input,
|
||||||
|
indent: " ".repeat(3),
|
||||||
|
idents: new Map(),
|
||||||
|
opts: {
|
||||||
|
showSourceStatements: options.showSourceStatements ?? false,
|
||||||
|
attr: { ...options.attr },
|
||||||
|
meta: { ...options.meta },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const body = transformBlock(ast.body, state);
|
||||||
|
|
||||||
|
const meta: ProgramMetadata = {
|
||||||
|
username: state.opts.meta.username ?? "ezfprg",
|
||||||
|
hostname: state.opts.meta.hostname ?? "deno",
|
||||||
|
editVersion: state.opts.meta.editVersion ?? 1,
|
||||||
|
editMysteryNumber: state.opts.meta.editMysteryNumber ?? 2980,
|
||||||
|
};
|
||||||
|
|
||||||
|
const now = format(new Date(), "yyyy-MM-dd hh:mm:ss a");
|
||||||
|
const created = `${meta.username};${meta.hostname};${now.replace(" ", ";")}`;
|
||||||
|
const edited = `${created};${meta.editVersion};${meta.editMysteryNumber}`;
|
||||||
|
|
||||||
|
const attr: ProgramAttributes = {
|
||||||
|
name: state.opts.attr.name ?? "Untitled",
|
||||||
|
authors: state.opts.attr.authors ?? "ezfprg",
|
||||||
|
about: state.opts.attr.about ?? "Converted from TypeScript by ezfprg",
|
||||||
|
saved: state.opts.attr.saved ?? now,
|
||||||
|
created: btoa(created),
|
||||||
|
edited: btoa(edited),
|
||||||
|
};
|
||||||
|
|
||||||
|
let template = customTemplate
|
||||||
|
?? Deno.readTextFileSync("./assets/template.fprg");
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(attr)) {
|
||||||
|
template = template.replace(`[[${k}]]`, v!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return template.replace("[[body]]", () => body);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformBlock (
|
||||||
|
block: Array<SWC.Statement> | Array<SWC.ModuleItem>,
|
||||||
|
state: TransformState,
|
||||||
|
) {
|
||||||
|
let output = "";
|
||||||
|
|
||||||
|
for (const stmt of block) {
|
||||||
|
if (state.opts.showSourceStatements) {
|
||||||
|
const rawStmt = state.input.slice(
|
||||||
|
stmt.span.start - 1,
|
||||||
|
stmt.span.end - 1
|
||||||
|
);
|
||||||
|
output += state.indent + Statements.comment(rawStmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (stmt.type) {
|
||||||
|
case "VariableDeclaration":
|
||||||
|
output += transformVariableDecl(stmt, state);
|
||||||
|
break;
|
||||||
|
case "ExpressionStatement": {
|
||||||
|
switch (stmt.expression.type) {
|
||||||
|
case "AssignmentExpression":
|
||||||
|
output += transformAssignmentExpr(stmt.expression, state);
|
||||||
|
break;
|
||||||
|
case "CallExpression":
|
||||||
|
output += transformBlockCallExpr(stmt.expression, state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case "IfStatement":
|
||||||
|
output += transformIfStmt(stmt, state);
|
||||||
|
break;
|
||||||
|
case "WhileStatement":
|
||||||
|
output += transformWhileStmt(stmt, state);
|
||||||
|
break;
|
||||||
|
case "ForStatement":
|
||||||
|
output += transformForStmt(stmt, state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformNewScope (
|
||||||
|
body: BlockStatement,
|
||||||
|
state: TransformState,
|
||||||
|
): string {
|
||||||
|
if (body.stmts.length === 0) return "";
|
||||||
|
|
||||||
|
const oldIndent = state.indent.toString();
|
||||||
|
const oldIdents: Idents = new Map(state.idents);
|
||||||
|
|
||||||
|
state.indent = state.indent + " ";
|
||||||
|
|
||||||
|
const out = transformBlock(body.stmts, state);
|
||||||
|
|
||||||
|
state.indent = oldIndent;
|
||||||
|
state.idents = oldIdents;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformExpr (
|
||||||
|
expr: SWC.Literal | SWC.Expression,
|
||||||
|
idents: Map<string, Ident>,
|
||||||
|
): [string, Type] {
|
||||||
|
switch (expr.type) {
|
||||||
|
case "NumericLiteral": {
|
||||||
|
// @ts-ignore - ts thinks that raw does not exist in a NumericLiteral
|
||||||
|
const val = "raw" in expr ? expr.raw : expr.value.toString();
|
||||||
|
return [val, val.includes(".") ? "float" : "int"];
|
||||||
|
}
|
||||||
|
case "StringLiteral":
|
||||||
|
return [`"${expr.value}"`, "string"];
|
||||||
|
case "BooleanLiteral":
|
||||||
|
return [expr.value.toString(), "boolean"];
|
||||||
|
case "Identifier":
|
||||||
|
case "CallExpression": {
|
||||||
|
let name: string;
|
||||||
|
if (expr.type === "Identifier") {
|
||||||
|
name = expr.value;
|
||||||
|
} else if ("value" in expr.callee) {
|
||||||
|
name = expr.callee.value.toString();
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidExpression(expr.callee.type, expr.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchedIdent = idents.get(name);
|
||||||
|
if (!matchedIdent || !matchedIdent.defined) {
|
||||||
|
throw new Errors.UndefinedReference(name, expr.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [name, matchedIdent.type];
|
||||||
|
}
|
||||||
|
case "ParenthesisExpression": {
|
||||||
|
const [_expr, exprType] = transformExpr(expr.expression, idents);
|
||||||
|
return [`(${_expr})`, exprType];
|
||||||
|
}
|
||||||
|
case "UnaryExpression": {
|
||||||
|
const [_expr, exprType] = transformExpr(expr.argument, idents);
|
||||||
|
return [`${expr.operator}${_expr}`, exprType];
|
||||||
|
}
|
||||||
|
case "BinaryExpression": {
|
||||||
|
const [left, lType] = transformExpr(expr.left, idents);
|
||||||
|
const [right, rType] = transformExpr(expr.right, idents);
|
||||||
|
|
||||||
|
if (!TypeUtils.validateBinaryOpTypes(lType, rType, expr.operator)) {
|
||||||
|
throw new Errors.InvalidBinaryOp(
|
||||||
|
lType,
|
||||||
|
rType,
|
||||||
|
expr.operator,
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const op = [lType, rType].includes("string") && expr.operator === "+"
|
||||||
|
? "&"
|
||||||
|
: expr.operator;
|
||||||
|
const type = TypeUtils.castValidBinaryOpTypes(lType, rType, op);
|
||||||
|
|
||||||
|
return [`${left} ${op} ${right}`, type];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// @ts-ignore: fuck you ts
|
||||||
|
throw new Errors.InvalidExpression(expr.type, expr.span.start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformBlockCallExpr (
|
||||||
|
expr: SWC.CallExpression,
|
||||||
|
state: TransformState,
|
||||||
|
): string {
|
||||||
|
let name: string;
|
||||||
|
|
||||||
|
if ("value" in expr.callee) {
|
||||||
|
name = expr.callee.value.toString();
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidExpression(
|
||||||
|
expr.callee.type,
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case "println": {
|
||||||
|
if (expr.arguments.length < 1) {
|
||||||
|
throw new Errors.InvalidArgumentCount(
|
||||||
|
name,
|
||||||
|
expr.arguments.length,
|
||||||
|
1,
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [newExpr, _] = transformExpr(
|
||||||
|
expr.arguments[0].expression,
|
||||||
|
state.idents,
|
||||||
|
);
|
||||||
|
|
||||||
|
return state.indent + Statements.output(newExpr, true);
|
||||||
|
}
|
||||||
|
case "print": {
|
||||||
|
if (expr.arguments.length < 1) {
|
||||||
|
throw new Errors.InvalidArgumentCount(
|
||||||
|
name,
|
||||||
|
expr.arguments.length,
|
||||||
|
1,
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [newExpr, _] = transformExpr(
|
||||||
|
expr.arguments[0].expression,
|
||||||
|
state.idents,
|
||||||
|
);
|
||||||
|
|
||||||
|
return state.indent + Statements.output(newExpr, false);
|
||||||
|
}
|
||||||
|
case "input": {
|
||||||
|
if (expr.arguments.length !== 1) {
|
||||||
|
throw new Errors.InvalidArgumentCount(
|
||||||
|
name,
|
||||||
|
expr.arguments.length,
|
||||||
|
1,
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expr.arguments[0].expression.type !== "Identifier") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
expr.arguments[0].expression.type,
|
||||||
|
"<Identifier>",
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const variable = expr.arguments[0].expression.value.toString();
|
||||||
|
const matchedIdent = state.idents.get(variable);
|
||||||
|
|
||||||
|
if (!matchedIdent) {
|
||||||
|
throw new Errors.UndefinedReference(variable, expr.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.idents.set(variable, { ...matchedIdent, defined: true });
|
||||||
|
return state.indent + Statements.input(variable);
|
||||||
|
}
|
||||||
|
case "attr":
|
||||||
|
case "meta": {
|
||||||
|
if (expr.arguments.length !== 2) {
|
||||||
|
throw new Errors.InvalidArgumentCount(
|
||||||
|
name,
|
||||||
|
expr.arguments.length,
|
||||||
|
2,
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const arg of expr.arguments) {
|
||||||
|
if (arg.expression.type !== "StringLiteral") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
expr.arguments[0].expression.type,
|
||||||
|
"<StringLiteral>",
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = expr.arguments
|
||||||
|
.map((a) => a.expression as SWC.StringLiteral);
|
||||||
|
|
||||||
|
const key: string = args[0].value;
|
||||||
|
|
||||||
|
if (TypeUtils.validKeys[name].includes(key)) {
|
||||||
|
if (state.opts[name][key] === undefined) {
|
||||||
|
state.opts[name][key] = args[1].value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidAttrOrMeta(key, name, expr.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
case "comment": {
|
||||||
|
if (expr.arguments.length !== 1) {
|
||||||
|
throw new Errors.InvalidArgumentCount(
|
||||||
|
name,
|
||||||
|
expr.arguments.length,
|
||||||
|
1,
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expr.arguments[0].expression.type !== "StringLiteral") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
expr.arguments[0].expression.type,
|
||||||
|
"<StringLiteral>",
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const comment = expr.arguments[0].expression.value.toString();
|
||||||
|
return state.indent + Statements.comment(comment);
|
||||||
|
}
|
||||||
|
case "insist": {
|
||||||
|
if (expr.arguments.length !== 1) {
|
||||||
|
throw new Errors.InvalidArgumentCount(
|
||||||
|
name,
|
||||||
|
expr.arguments.length,
|
||||||
|
1,
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expr.arguments[0].expression.type !== "Identifier") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
expr.arguments[0].expression.type,
|
||||||
|
"<Identifier>",
|
||||||
|
expr.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ident = expr.arguments[0].expression.value;
|
||||||
|
const matchedIdent = state.idents.get(ident);
|
||||||
|
|
||||||
|
if (!matchedIdent) {
|
||||||
|
throw new Errors.UndefinedReference(ident, expr.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.idents.set(ident, { ...matchedIdent, defined: true });
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Errors.UndefinedReference(name, expr.span.start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformAssignmentExpr (
|
||||||
|
expr: SWC.AssignmentExpression,
|
||||||
|
state: TransformState
|
||||||
|
): string {
|
||||||
|
if (expr.left.type !== "Identifier") {
|
||||||
|
throw new Errors.InvalidAssignmentTarget(expr.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = expr.left.value;
|
||||||
|
const matchedIdent = state.idents.get(name);
|
||||||
|
|
||||||
|
if (!matchedIdent) {
|
||||||
|
throw new Errors.UndefinedReference(name, expr.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = matchedIdent.type;
|
||||||
|
const [newExpr, exprType] = transformExpr(expr.right, state.idents);
|
||||||
|
|
||||||
|
if (!TypeUtils.validateAssignmentTypes(type, exprType)) {
|
||||||
|
throw new Errors.InvalidType(type, exprType, expr.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchedIdent.defined) {
|
||||||
|
state.idents.set(name, { ...matchedIdent, defined: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.indent + Statements.assign(name, newExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformVariableDecl (
|
||||||
|
stmt: SWC.VariableDeclaration,
|
||||||
|
state: TransformState,
|
||||||
|
) {
|
||||||
|
let output = "";
|
||||||
|
|
||||||
|
const declarations: Record<Type, string[]> = {
|
||||||
|
int: [],
|
||||||
|
float: [],
|
||||||
|
string: [],
|
||||||
|
boolean: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const assignments = new Array<Assignment>();
|
||||||
|
|
||||||
|
for (const decl of stmt.declarations) {
|
||||||
|
if (decl.id.type !== "Identifier") {
|
||||||
|
throw new Errors.InvalidAssignmentTarget(decl.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.idents.has(decl.id.value)) {
|
||||||
|
throw new Errors.AlreadyDefined(decl.id.value, decl.id.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
let type: Type | undefined;
|
||||||
|
|
||||||
|
if (decl.id.typeAnnotation) {
|
||||||
|
const annot = decl.id.typeAnnotation.typeAnnotation;
|
||||||
|
|
||||||
|
if ("kind" in annot) {
|
||||||
|
type = annot.kind as Type;
|
||||||
|
} else if ("typeName" in annot) {
|
||||||
|
// @ts-ignore: who the fuck asked + L + ratio
|
||||||
|
type = annot.typeName.value;
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidTypeAnnotation(
|
||||||
|
annot.type,
|
||||||
|
decl.id.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TypeUtils.typeNames.includes(type!)) {
|
||||||
|
throw new Errors.InvalidType(
|
||||||
|
"int | float | string | boolean",
|
||||||
|
type!,
|
||||||
|
stmt.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decl.init) {
|
||||||
|
const [expr, exprType] = transformExpr(decl.init, state.idents);
|
||||||
|
|
||||||
|
if (type && !TypeUtils.validateAssignmentTypes(type!, exprType)) {
|
||||||
|
throw new Errors.InvalidType(type, exprType, decl.span.start);
|
||||||
|
} else if (!type) {
|
||||||
|
type = exprType;
|
||||||
|
}
|
||||||
|
assignments.push({ name: decl.id.value, value: expr });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
throw new Errors.MissingType(decl.id.value, stmt.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
declarations[type].push(decl.id.value);
|
||||||
|
state.idents.set(
|
||||||
|
decl.id.value,
|
||||||
|
{ type: type, defined: decl.init !== null }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeUtils.typeNames.forEach((type) => {
|
||||||
|
const names = declarations[type];
|
||||||
|
if (names.length > 0) {
|
||||||
|
output += state.indent + Statements.declare(
|
||||||
|
names.join(", "),
|
||||||
|
type as Type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assignments.forEach((a) => {
|
||||||
|
output += state.indent + Statements.assign(a.name, a.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformIfStmt (
|
||||||
|
stmt: SWC.IfStatement,
|
||||||
|
state: TransformState
|
||||||
|
): string {
|
||||||
|
const [expr, exprType] = transformExpr(stmt.test, state.idents);
|
||||||
|
|
||||||
|
if (exprType !== "boolean") {
|
||||||
|
throw new Errors.InvalidType("boolean", exprType, stmt.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
let arm1: string;
|
||||||
|
if (stmt.consequent && stmt.consequent.type === "BlockStatement") {
|
||||||
|
arm1 = transformNewScope(stmt.consequent, state);
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"BlockStatement",
|
||||||
|
stmt.consequent.type,
|
||||||
|
stmt.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let arm2 = "";
|
||||||
|
|
||||||
|
if (stmt.alternate) {
|
||||||
|
if (stmt.alternate.type === "BlockStatement") {
|
||||||
|
arm2 = transformNewScope(stmt.alternate, state);
|
||||||
|
} else if (stmt.alternate.type === "IfStatement") {
|
||||||
|
const oldIndent = state.indent;
|
||||||
|
state.indent += " ";
|
||||||
|
arm2 = transformIfStmt(stmt.alternate, state);
|
||||||
|
state.indent = oldIndent;
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"BlockStatement",
|
||||||
|
stmt.alternate.type,
|
||||||
|
stmt.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Statements._if(state.indent, expr, arm1, arm2);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformWhileStmt (
|
||||||
|
stmt: SWC.WhileStatement,
|
||||||
|
state: TransformState
|
||||||
|
): string {
|
||||||
|
const [expr, exprType] = transformExpr(stmt.test, state.idents);
|
||||||
|
|
||||||
|
if (exprType !== "boolean") {
|
||||||
|
throw new Errors.InvalidType("boolean", exprType, stmt.span.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: string;
|
||||||
|
|
||||||
|
if (stmt.body && stmt.body.type === "BlockStatement") {
|
||||||
|
body = transformNewScope(stmt.body, state);
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"BlockStatement",
|
||||||
|
stmt.body.type,
|
||||||
|
stmt.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Statements._while(state.indent, expr, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformForStmt (
|
||||||
|
stmt: SWC.ForStatement,
|
||||||
|
state: TransformState
|
||||||
|
): string {
|
||||||
|
let output = "";
|
||||||
|
let variable: string;
|
||||||
|
let start: number;
|
||||||
|
|
||||||
|
if (!stmt.init) {
|
||||||
|
throw new Errors.ForMissingInit(stmt.span.start);
|
||||||
|
} else if (stmt.init.type === "VariableDeclaration") {
|
||||||
|
const decl = stmt.init.declarations[0];
|
||||||
|
if (decl.id.type !== "Identifier") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"Identifier",
|
||||||
|
decl.id.type,
|
||||||
|
stmt.init.span.start
|
||||||
|
);
|
||||||
|
} else if (!decl.init || decl.init.type !== "NumericLiteral") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"NumericLiteral",
|
||||||
|
decl.init?.type ?? "Nothing",
|
||||||
|
stmt.init.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
variable = (decl.id as SWC.Identifier).value;
|
||||||
|
start = (decl.init as SWC.NumericLiteral).value;
|
||||||
|
|
||||||
|
output += state.indent + transformVariableDecl(stmt.init, state);
|
||||||
|
} else if (stmt.init.type === "AssignmentExpression") {
|
||||||
|
if (stmt.init.left.type !== "Identifier") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"Identifier",
|
||||||
|
stmt.init.left.type,
|
||||||
|
stmt.init.span.start
|
||||||
|
);
|
||||||
|
} else if (stmt.init.right.type !== "NumericLiteral") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"NumericLiteral",
|
||||||
|
stmt.init?.type,
|
||||||
|
stmt.init.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
variable = stmt.init.left.value;
|
||||||
|
start = stmt.init.right.value;
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"VariableDeclaration | AssignmentExpression",
|
||||||
|
stmt.init.type,
|
||||||
|
stmt.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stmt.test) {
|
||||||
|
throw new SyntaxError(
|
||||||
|
);
|
||||||
|
} else if (stmt.test.type !== "NumericLiteral") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"NumericLiteral",
|
||||||
|
stmt.test.type,
|
||||||
|
stmt.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const end: number = stmt.test.value;
|
||||||
|
let step: number;
|
||||||
|
let direction: "inc" | "dec";
|
||||||
|
|
||||||
|
if (!stmt.update) {
|
||||||
|
throw new Errors.ForMissingEnd(stmt.span.start);
|
||||||
|
} else if (stmt.update.type === "UpdateExpression") {
|
||||||
|
if (stmt.update.argument.type !== "Identifier") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"Identifier",
|
||||||
|
stmt.update.argument.type,
|
||||||
|
stmt.update.span.start
|
||||||
|
);
|
||||||
|
} else if (stmt.update.argument.value !== variable) {
|
||||||
|
throw new Errors.ForVariableMismatch(
|
||||||
|
variable,
|
||||||
|
stmt.update.argument.value,
|
||||||
|
stmt.update.span.start
|
||||||
|
);
|
||||||
|
} else if (stmt.update.operator === "++") {
|
||||||
|
step = 1;
|
||||||
|
direction = "inc";
|
||||||
|
} else if (stmt.update.operator === "--") {
|
||||||
|
step = 1;
|
||||||
|
direction = "dec";
|
||||||
|
} else {
|
||||||
|
throw new Errors.ForInvalidOperator(
|
||||||
|
stmt.update.operator,
|
||||||
|
stmt.update.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (stmt.update.type === "AssignmentExpression") {
|
||||||
|
if (stmt.update.left.type !== "Identifier") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"Identifier",
|
||||||
|
stmt.update.left.type,
|
||||||
|
stmt.update.span.start
|
||||||
|
);
|
||||||
|
} else if (stmt.update.left.value !== variable) {
|
||||||
|
throw new Errors.ForVariableMismatch(
|
||||||
|
variable,
|
||||||
|
stmt.update.left.value,
|
||||||
|
stmt.update.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stmt.update.operator === "+=") {
|
||||||
|
direction = "inc";
|
||||||
|
} else if (stmt.update.operator === "-=") {
|
||||||
|
direction = "dec";
|
||||||
|
} else {
|
||||||
|
throw new Errors.ForInvalidOperator(
|
||||||
|
stmt.update.operator,
|
||||||
|
stmt.update.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stmt.update.right.type !== "NumericLiteral") {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"NumericLiteral",
|
||||||
|
stmt.update.right.type,
|
||||||
|
stmt.update.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
step = stmt.update.right.value;
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"UpdateExpression | AssignmentExpression",
|
||||||
|
stmt.update.type,
|
||||||
|
stmt.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: string;
|
||||||
|
if (stmt.body && stmt.body.type === "BlockStatement") {
|
||||||
|
body = transformNewScope(stmt.body, state);
|
||||||
|
} else {
|
||||||
|
throw new Errors.InvalidSyntaxType(
|
||||||
|
"BlockStatement",
|
||||||
|
stmt.body.type,
|
||||||
|
stmt.span.start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
output += Statements._for(
|
||||||
|
state.indent,
|
||||||
|
variable,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
direction,
|
||||||
|
step,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
return output;
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
import * as SWC from "./swc.ts";
|
||||||
|
|
||||||
|
export type Type = "int" | "float" | "string" | "boolean";
|
||||||
|
|
||||||
|
export const typeNames: Array<Type> = ["int", "float", "string", "boolean"];
|
||||||
|
export const validKeys: Record<string, Array<string>> = {
|
||||||
|
attr: ["name", "authors", "about"],
|
||||||
|
meta: ["username", "hostname", "editVersion", "editMysteryNumber"],
|
||||||
|
};
|
||||||
|
export const numOps = [
|
||||||
|
"+", "-", "*", "/", "%", "^",
|
||||||
|
"<=", ">=", "<", ">", "==", "!="
|
||||||
|
];
|
||||||
|
export const boolOps = ["&&", "||", "!", "==", "!="];
|
||||||
|
export const stringOps = ["+", "==", "!="];
|
||||||
|
|
||||||
|
export function validateBinaryOpTypes (
|
||||||
|
a: Type,
|
||||||
|
b: Type,
|
||||||
|
op: SWC.BinaryOperator,
|
||||||
|
): boolean {
|
||||||
|
// For purposes of validation, float and int are the same
|
||||||
|
a = a === "float" ? "int" : a;
|
||||||
|
b = b === "float" ? "int" : b;
|
||||||
|
if (a === b) {
|
||||||
|
switch (a) {
|
||||||
|
case "int": return numOps.includes(op);
|
||||||
|
case "string": return stringOps.includes(op);
|
||||||
|
case "boolean": return boolOps.includes(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [a, b].includes("string") && op === "+";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateAssignmentTypes (a: Type, b: Type) {
|
||||||
|
return a === b
|
||||||
|
|| (a === "float" && b === "int")
|
||||||
|
|| (a === "int" && b === "float");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function castValidBinaryOpTypes (
|
||||||
|
a: Type,
|
||||||
|
b: Type,
|
||||||
|
op: SWC.BinaryOperator
|
||||||
|
): Type {
|
||||||
|
return ["<", ">", "<=", ">=", "==", "!=", "&&", "||"].includes(op)
|
||||||
|
? "boolean"
|
||||||
|
: a === b
|
||||||
|
? a
|
||||||
|
: [a, b].includes("string") ? "string" : "float";
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
export function escapeXML (input: string): string {
|
||||||
|
if (input.match(/[&<>"\n]/)) return input
|
||||||
|
.replaceAll("&", "&")
|
||||||
|
.replaceAll("<", "<")
|
||||||
|
.replaceAll(">", ">")
|
||||||
|
.replaceAll(`"`, """)
|
||||||
|
.replaceAll("\n", " ");
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function capitalize (str: string): string {
|
||||||
|
return str[0].toUpperCase() + str.slice(1);
|
||||||
|
}
|
Loading…
Reference in New Issue