Skip to content

Commit c41b144

Browse files
committed
Add readme and exports
1 parent 2501c56 commit c41b144

8 files changed

Lines changed: 166 additions & 29 deletions

File tree

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# These are supported funding model platforms
2+
github: [kevinbatdorf]

README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Pinky Compiler
2+
3+
A fast, embeddable compiler for the Pinky scripting language.
4+
Compile Pinky code to WebAssembly and run it anywhere JavaScript runs.
5+
6+
---
7+
8+
## Features
9+
10+
- Compile Pinky source to WASM bytes
11+
- Run Pinky code in the browser or Node.js
12+
- TypeScript-first API
13+
- Tiny, dependency-free runtime
14+
15+
---
16+
17+
## Install
18+
19+
```sh
20+
npm install pinky-compiler
21+
```
22+
23+
---
24+
25+
## Usage
26+
27+
```ts
28+
import { compileFromSource, init } from "pinky-compiler";
29+
30+
// 1. Compile Pinky source to WASM bytes
31+
const source = `
32+
x := 5
33+
println "Hello, Pinky!"
34+
println x + 10
35+
`;
36+
const { bytes, error } = compileFromSource(source);
37+
38+
if (error) throw error;
39+
40+
// 2. Initialize the WASM runtime (once per app)
41+
const { run } = await init();
42+
43+
// 3. Run the compiled program
44+
const output = run(bytes);
45+
console.log(output.join("")); // Hello, Pinky!\n15\n
46+
```
47+
48+
---
49+
50+
## API
51+
52+
### `compileFromSource(source: string)`
53+
54+
Tokenizes, parses, and compiles Pinky source code to WASM.
55+
56+
- **Returns:** `{ bytes: Uint8Array, error, meta }`
57+
58+
### `init()`
59+
60+
Initializes the WASM runtime and returns an object with a `run` function.
61+
62+
- **Returns:** `Promise<{ run: (bytes: Uint8Array) => string[] }>`
63+
64+
### `run(bytes: Uint8Array)`
65+
66+
Runs the compiled WASM program and returns output as an array of strings.
67+
68+
---
69+
70+
## Advanced Usage
71+
72+
You can also use the lower-level building blocks:
73+
74+
```ts
75+
import { tokenize, parse, compile, init } from "pinky-compiler";
76+
77+
const { tokens } = tokenize('println "hi"');
78+
const { ast } = parse(tokens);
79+
const { bytes } = compile(ast);
80+
const { run } = await init();
81+
run(bytes);
82+
```
83+
84+
---
85+
86+
## Types
87+
88+
All major types are exported:
89+
90+
```ts
91+
import type { AST, Token, CompilerErrorType, ParseErrorType, TokenErrorType } from "pinky-compiler";
92+
```

src/compiler.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { beforeAll, expect, test } from "vitest";
2-
import { compile } from "./compiler";
3-
import { tokenize } from "./lexer";
4-
import { parse } from "./parser";
5-
import { loadWasm, type RunFunction } from "./compiler/exports";
2+
import { compile, init, parse, tokenize } from ".";
3+
import type { RunFunction } from "./compiler/exports";
4+
65
declare global {
76
var run: RunFunction;
87
}
98

109
beforeAll(async () => {
11-
const { run } = await loadWasm();
10+
const { run } = await init();
1211
globalThis.run = run;
1312
});
1413

src/compiler/exports.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -66,28 +66,26 @@ export const loadWasm = async (): Promise<{ run: RunFunction }> => {
6666
return base ** exp;
6767
},
6868
};
69-
70-
return {
71-
run: (bytes: Uint8Array): string[] => {
72-
output.length = 0; // clear previous output
73-
const program = new WebAssembly.Module(bytes);
74-
const instance = new WebAssembly.Instance(program, {
75-
env,
76-
});
77-
memory = instance.exports.memory as WebAssembly.Memory;
78-
try {
79-
(instance.exports.main as () => void)();
80-
} catch (e) {
81-
console.error("Runtime error:", e);
82-
let msg = e instanceof Error ? e.message : String(e);
83-
if (msg === "unreachable") {
84-
msg = "Unreachable code found. Infinite loop?";
85-
}
86-
return [`RuntimeError: ${msg}`];
69+
const run = (bytes: Uint8Array): string[] => {
70+
output.length = 0; // clear previous output
71+
const program = new WebAssembly.Module(bytes);
72+
const instance = new WebAssembly.Instance(program, {
73+
env,
74+
});
75+
memory = instance.exports.memory as WebAssembly.Memory;
76+
try {
77+
(instance.exports.main as () => void)();
78+
} catch (e) {
79+
console.error("Runtime error:", e);
80+
let msg = e instanceof Error ? e.message : String(e);
81+
if (msg === "unreachable") {
82+
msg = "Unreachable code found. Infinite loop?";
8783
}
88-
return [...output];
89-
},
84+
return [`RuntimeError: ${msg}`];
85+
}
86+
return [...output];
9087
};
88+
return { run };
9189
};
9290

9391
const boxString = (str: string, memory: WebAssembly.Memory): number => {

src/index.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { compile } from "./compiler";
2+
import { loadWasm } from "./compiler/exports";
3+
import { tokenize } from "./lexer";
4+
import { parse } from "./parser";
5+
6+
export type { CompilerErrorType } from "./compiler";
7+
export { compile } from "./compiler";
8+
export { loadWasm } from "./compiler/exports";
9+
export { tokenize } from "./lexer";
10+
export type { TokenErrorType } from "./lexer";
11+
export type { AST, ParseErrorType } from "./parser";
12+
export { parse } from "./parser";
13+
export * from "./syntax";
14+
export type { Token } from "./tokens";
15+
16+
/**
17+
* Compiles Pinky source code from a string.
18+
* This function tokenizes the source code, parses the tokens into an AST,
19+
* and then compiles the AST into WebAssembly binary.
20+
*
21+
* ```
22+
* const source = "x := 42 print(x)";
23+
* const wasmBytes = compileFromSource(source);
24+
* ```
25+
*/
26+
export const compileFromSource = (source: string) => {
27+
const { tokens } = tokenize(source);
28+
const { ast } = parse(tokens);
29+
return compile(ast);
30+
};
31+
32+
/**
33+
* Initialize WASM runtime and get the run function.
34+
* The run function outputs an array of strings.
35+
*
36+
* ```
37+
* const { run } = await init();
38+
* const output = run(bytes);
39+
* ```
40+
*/
41+
export const init = async () => {
42+
const { run } = await loadWasm();
43+
return { run };
44+
};

src/lexer.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { test, expect } from "vitest";
1+
import { expect, test } from "vitest";
2+
import { tokenize } from ".";
23
import {
34
advance,
45
decodeStringLiteralSegment,
@@ -11,7 +12,6 @@ import {
1112
lookahead,
1213
match,
1314
peek,
14-
tokenize,
1515
} from "./lexer";
1616

1717
test("isAlpha", () => {

src/lexer.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ export const decodeStringLiteralSegment = (
102102
};
103103
};
104104

105+
/**
106+
* Tokenizes Pinky source code into tokens.
107+
*/
105108
export const tokenize = (
106109
src: string,
107110
): { tokens: Token[]; error: TokenErrorType | null } => {

src/parser.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import { expect, test } from "vitest";
2+
import { parse, tokenize } from ".";
23
import {
34
advance,
45
expectNext,
56
isEOF,
67
isNextToken,
78
match,
8-
parse,
99
peek,
1010
previousToken,
1111
} from "./parser";
1212
import type { Token } from "./tokens";
13-
import { tokenize } from "./lexer";
1413

1514
// Helpers
1615
test("peek returns current token", () => {

0 commit comments

Comments
 (0)