Compare commits
No commits in common. "957921b793d34f3d6b1ee511bbc01223f6e395d0" and "c7b429105d06cae90a4dc595b105e28dfe8d9021" have entirely different histories.
957921b793
...
c7b429105d
@ -1,7 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { lex } from "../lexer/lexer";
|
import { lex } from "../lexer/lexer";
|
||||||
import type { Instruction } from "../thp_machine/machine_parser";
|
|
||||||
import { parse_str } from "../thp_machine/machine_parser";
|
|
||||||
const {code, steps} = Astro.props;
|
const {code, steps} = Astro.props;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,15 +97,6 @@ function highlightCode(lines: Array<string>): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const codeHtml = highlightCode(trimAndDedent(code));
|
const codeHtml = highlightCode(trimAndDedent(code));
|
||||||
let instructionSet: Array<Array<Instruction>>;
|
|
||||||
try {
|
|
||||||
instructionSet = parse_str(steps);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(Astro.url);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
const serialized_inst = JSON.stringify(instructionSet);
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="bg-black text-white rounded px-1"
|
<div class="bg-black text-white rounded px-1"
|
||||||
@ -115,7 +104,7 @@ const serialized_inst = JSON.stringify(instructionSet);
|
|||||||
line: 0,
|
line: 0,
|
||||||
stdout: "",
|
stdout: "",
|
||||||
ip: 0,
|
ip: 0,
|
||||||
inst: ${serialized_inst},
|
inst: ${steps},
|
||||||
done: false,
|
done: false,
|
||||||
state: {},
|
state: {},
|
||||||
}`}
|
}`}
|
||||||
@ -131,7 +120,7 @@ const serialized_inst = JSON.stringify(instructionSet);
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="p-1 border-b border-white">state</div>
|
<div class="p-1 border-b border-white">state</div>
|
||||||
<div class="h-24 p-1 overflow-y-scroll">
|
<div class="h-24 p-1">
|
||||||
<template x-for="(value, key) in state">
|
<template x-for="(value, key) in state">
|
||||||
<div x-text="key.replaceAll(' ', ' ') + ' = ' + value"></div>
|
<div x-text="key.replaceAll(' ', ' ') + ' = ' + value"></div>
|
||||||
</template>
|
</template>
|
||||||
@ -139,23 +128,22 @@ const serialized_inst = JSON.stringify(instructionSet);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-t border-white p-1">
|
<div class="border-t border-white p-1">
|
||||||
<button class="font-mono px-1 rounded bg-pink-200 dark:bg-pink-950 text-black dark:text-white disabled:opacity-50 disabled:cursor-not-allowed" @click="alpineNext($data)" :disabled="done && 'true'">
|
<button class="font-mono px-1 rounded bg-pink-950 disabled:opacity-50 disabled:cursor-not-allowed" @click="alpineNext($data)" :disabled="done && 'true'">
|
||||||
Step: <span x-text="ip"></span>
|
Step: <span x-text="ip"></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="font-mono px-1 rounded bg-pink-200 dark:bg-pink-950 text-black dark:text-white" @click="alpineReset($data)">
|
<button class="font-mono px-1 rounded bg-pink-950" @click="alpineReset($data)">
|
||||||
Reset
|
Reset
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { InstructionType, type Instruction } from "../thp_machine/machine_parser";
|
type Instruction = "line" | "out" | "set" | "unset";
|
||||||
|
|
||||||
type AlpineState = {
|
type AlpineState = {
|
||||||
line: number,
|
line: number,
|
||||||
stdout: string,
|
stdout: string,
|
||||||
ip: number,
|
ip: number,
|
||||||
inst: Array<Array<Instruction>>
|
inst: Array<Array<[Instruction, string, string | undefined]>>
|
||||||
done: boolean,
|
done: boolean,
|
||||||
state: {[key: string]: string},
|
state: {[key: string]: string},
|
||||||
}
|
}
|
||||||
@ -168,25 +156,22 @@ const serialized_inst = JSON.stringify(instructionSet);
|
|||||||
|
|
||||||
const instructions = data.inst[ip]!;
|
const instructions = data.inst[ip]!;
|
||||||
for (const instructionSet of instructions) {
|
for (const instructionSet of instructions) {
|
||||||
const i = instructionSet;
|
const instructionArr = instructionSet;
|
||||||
|
switch (instructionArr[0]) {
|
||||||
switch (i.t) {
|
case "line": {
|
||||||
case InstructionType.Line: {
|
data.line = Number(instructionArr[1]);
|
||||||
data.line = Number(i.v0);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case InstructionType.Out: {
|
case "out": {
|
||||||
data.stdout += i.v0
|
data.stdout += String(instructionArr[1])
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case InstructionType.Set: {
|
case "set": {
|
||||||
const i_key = i.v0.slice(1, -1);
|
data.state[String(instructionArr[1])] = String(instructionArr[2]);
|
||||||
const i_value = i.v1!.slice(1, -1);
|
|
||||||
data.state[i_key] = i_value;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case InstructionType.Unset: {
|
case "unset": {
|
||||||
delete data.state[i.v0];
|
delete data.state[String(instructionArr[1])];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,11 @@ import { is_identifier_char } from "./utils";
|
|||||||
* @param is_datatype whether the identifier is a datatype
|
* @param is_datatype whether the identifier is a datatype
|
||||||
*/
|
*/
|
||||||
export function scan_identifier(input: string, starting_position: number, is_datatype = false): [Token, number] {
|
export function scan_identifier(input: string, starting_position: number, is_datatype = false): [Token, number] {
|
||||||
let value = input[starting_position]!;
|
let value = input[starting_position];
|
||||||
let pos = starting_position + 1;
|
let pos = starting_position + 1;
|
||||||
|
|
||||||
while (pos < input.length) {
|
while (pos < input.length) {
|
||||||
const c = input[pos]!;
|
const c = input[pos];
|
||||||
|
|
||||||
if (is_identifier_char(c)) {
|
if (is_identifier_char(c)) {
|
||||||
pos += 1;
|
pos += 1;
|
||||||
|
@ -34,7 +34,7 @@ export function lex(code: string): Array<Token> {
|
|||||||
let current_default_token = "";
|
let current_default_token = "";
|
||||||
|
|
||||||
while (current_pos < code_len) {
|
while (current_pos < code_len) {
|
||||||
const c = code[current_pos]!;
|
const c = code[current_pos];
|
||||||
|
|
||||||
// try to scan a number
|
// try to scan a number
|
||||||
if (is_digit(c)) {
|
if (is_digit(c)) {
|
||||||
|
@ -83,36 +83,101 @@ Use a naked `try` when you want to rethrow an error, if there is any.
|
|||||||
val res1 = run() // First, without error
|
val res1 = run() // First, without error
|
||||||
val res2 = run() // Then, an example with error
|
val res2 = run() // Then, an example with error
|
||||||
`}
|
`}
|
||||||
steps={`
|
steps={`[
|
||||||
step { line 14 }
|
[["line", 14]],
|
||||||
step { line 7 }
|
[["line", 7]],
|
||||||
step {
|
[
|
||||||
line 8
|
["line", 8],
|
||||||
set "= = =" "run() = = = ="
|
["set", "= = =", "run() = = = ="]
|
||||||
}
|
],
|
||||||
step {
|
[
|
||||||
line 10
|
["line", 10],
|
||||||
set " dangerous()" "..."
|
["set", " dangerous()", "..."],
|
||||||
}
|
],
|
||||||
step {line 1}
|
[["line", 1]],
|
||||||
step {
|
[
|
||||||
line 2
|
["line", 2],
|
||||||
set " = = =" "dangerous() = = = ="
|
["set", " = = =", "dangerous() = = = ="]
|
||||||
}
|
],
|
||||||
step {line 3}
|
[["line", 3]],
|
||||||
step {set " Math.random()" "0.2"}
|
[
|
||||||
step {
|
["set", " Math.random()", "0.2"]
|
||||||
unset " Math.random()"
|
],
|
||||||
set " return" "Int 50"
|
[
|
||||||
}
|
["unset", " Math.random()"],
|
||||||
step {line 5}
|
["set", " return", "Int 50"]
|
||||||
step {
|
],
|
||||||
line 10
|
[["line", 5]],
|
||||||
unset " return"
|
[
|
||||||
unset " = = ="
|
["line", 10],
|
||||||
set " dangerous()" "Int 50"
|
["unset", " return"],
|
||||||
}
|
["unset", " = = ="],
|
||||||
`}
|
["set", " dangerous()", "Int 50"]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["unset", " dangerous()"],
|
||||||
|
["set", " Int result", "Int 50"]
|
||||||
|
],
|
||||||
|
[["line", 11]],
|
||||||
|
[
|
||||||
|
["line", 12],
|
||||||
|
["out", "The result is 50\\n"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["line", 14],
|
||||||
|
["unset", " Int result"],
|
||||||
|
["unset", "= = ="],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["line", 15],
|
||||||
|
["set", "!Exception res1", "<empty>"],
|
||||||
|
],
|
||||||
|
|
||||||
|
[["line", 7]],
|
||||||
|
[
|
||||||
|
["line", 8],
|
||||||
|
["set", "= = =", "run() = = = ="]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["line", 10],
|
||||||
|
["set", " dangerous()", "..."],
|
||||||
|
],
|
||||||
|
[["line", 1]],
|
||||||
|
[
|
||||||
|
["line", 2],
|
||||||
|
["set", " = = =", "dangerous() = = = ="],
|
||||||
|
],
|
||||||
|
[["line", 3]],
|
||||||
|
[
|
||||||
|
["set", " Math.random()", "0.9"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["line", "4"],
|
||||||
|
["unset", " Math.random()"],
|
||||||
|
["set", " return", "Exception(\\"Unlucky\\")"],
|
||||||
|
],
|
||||||
|
[["line", 5]],
|
||||||
|
[
|
||||||
|
["line", 10],
|
||||||
|
["unset", " return"],
|
||||||
|
["unset", " = = ="],
|
||||||
|
["set", " dangerous()", "Exception \\"Unlucky\\""],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["line", 12],
|
||||||
|
["unset", " dangerous()"],
|
||||||
|
["set", " return", "Exception(\\"Unlucky\\")"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["line", 15],
|
||||||
|
["unset", " return"],
|
||||||
|
["unset", "= = ="],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["line", 16],
|
||||||
|
["set", "!Exception res2", "Exception(\\"Unlucky\\")"],
|
||||||
|
],
|
||||||
|
]`}
|
||||||
></InteractiveCode>
|
></InteractiveCode>
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,25 +78,25 @@ The documentation contains snippets of interactive THP code, like this:
|
|||||||
|
|
||||||
f(x, y)
|
f(x, y)
|
||||||
`}
|
`}
|
||||||
steps={`
|
steps={`[
|
||||||
step {line 1}
|
[["line", 1]],
|
||||||
step {
|
[
|
||||||
line 2
|
["line", 2],
|
||||||
set "String x" "\\"android\\""
|
["set", "String x", "\\"android\\""]
|
||||||
}
|
],
|
||||||
step {
|
[
|
||||||
line 8
|
["line", 8],
|
||||||
set "Int y" "17"
|
["set", "Int y", "17"]
|
||||||
}
|
],
|
||||||
step {line 4}
|
[["line", 4]],
|
||||||
step {line 5}
|
[["line", 5]],
|
||||||
step {
|
[
|
||||||
out "hello, android 17"
|
["out", "hello, android 17"],
|
||||||
line 6
|
["line", 6]
|
||||||
}
|
],
|
||||||
step {line 8}
|
[["line", 8]],
|
||||||
step {line 0}
|
[["line", 0]]
|
||||||
`}
|
]`}
|
||||||
></InteractiveCode>
|
></InteractiveCode>
|
||||||
|
|
||||||
Use the `Step` and `Reset` buttons to emulate the execution of a
|
Use the `Step` and `Reset` buttons to emulate the execution of a
|
||||||
|
@ -1,218 +0,0 @@
|
|||||||
/*
|
|
||||||
step {
|
|
||||||
line 1
|
|
||||||
set "a" "b"
|
|
||||||
unset "a"
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { scan_number } from "../lexer/number_lexer";
|
|
||||||
import { scan_string } from "../lexer/string_lexer";
|
|
||||||
import { is_digit, is_lowercase, is_uppercase } from "../lexer/utils";
|
|
||||||
|
|
||||||
enum TokenType {
|
|
||||||
Step,
|
|
||||||
Line,
|
|
||||||
Set,
|
|
||||||
Out,
|
|
||||||
Number,
|
|
||||||
String,
|
|
||||||
Unset,
|
|
||||||
BraceOpen,
|
|
||||||
BraceClose,
|
|
||||||
};
|
|
||||||
|
|
||||||
type Token = [TokenType, string | undefined];
|
|
||||||
|
|
||||||
// Creates a stream of tokens
|
|
||||||
function lex(input: string): Array<Token> {
|
|
||||||
const characters = input.split("");
|
|
||||||
const characters_len = characters.length;
|
|
||||||
let next_p = 0;
|
|
||||||
|
|
||||||
const tokens: Array<Token> = [];
|
|
||||||
|
|
||||||
while (next_p < characters_len)
|
|
||||||
{
|
|
||||||
const c = characters[next_p]!;
|
|
||||||
|
|
||||||
// word
|
|
||||||
if (is_lowercase(c) || is_uppercase(c))
|
|
||||||
{
|
|
||||||
const [token, next] = lex_word(characters, next_p);
|
|
||||||
tokens.push(token);
|
|
||||||
next_p = next;
|
|
||||||
}
|
|
||||||
// number
|
|
||||||
else if (is_digit(c))
|
|
||||||
{
|
|
||||||
const [token, next] = scan_number(input, next_p);
|
|
||||||
tokens.push([TokenType.Number, token.v]);
|
|
||||||
next_p = next;
|
|
||||||
}
|
|
||||||
// string
|
|
||||||
else if (c === "\"")
|
|
||||||
{
|
|
||||||
const [token, next] = scan_string(input, next_p);
|
|
||||||
tokens.push([TokenType.String, token.v]);
|
|
||||||
next_p = next;
|
|
||||||
}
|
|
||||||
else if (c === "{")
|
|
||||||
{
|
|
||||||
tokens.push([TokenType.BraceOpen, undefined]);
|
|
||||||
next_p += 1;
|
|
||||||
}
|
|
||||||
else if (c === "}")
|
|
||||||
{
|
|
||||||
tokens.push([TokenType.BraceClose, undefined]);
|
|
||||||
next_p += 1;
|
|
||||||
}
|
|
||||||
else if (c === " " || c === "\n")
|
|
||||||
{
|
|
||||||
next_p += 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Error(`Invalid character: \`${c}\``);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lex_word(input: Array<string>, pos: number): [Token, number] {
|
|
||||||
let next_p = pos;
|
|
||||||
let value = "";
|
|
||||||
|
|
||||||
let c = input[next_p];
|
|
||||||
while (c !== undefined && (is_lowercase(c) || is_uppercase(c) || is_digit(c) || c === "_"))
|
|
||||||
{
|
|
||||||
value += c;
|
|
||||||
next_p += 1;
|
|
||||||
c = input[next_p];
|
|
||||||
}
|
|
||||||
|
|
||||||
let token_type;
|
|
||||||
if (value === "step") { token_type = TokenType.Step; }
|
|
||||||
else if (value === "line") { token_type = TokenType.Line; }
|
|
||||||
else if (value === "set") { token_type = TokenType.Set; }
|
|
||||||
else if (value === "unset"){ token_type = TokenType.Unset; }
|
|
||||||
else if (value === "out") { token_type = TokenType.Out; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Error(`Invalid word: ${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[token_type, value], next_p]
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum InstructionType {
|
|
||||||
Line,
|
|
||||||
Set,
|
|
||||||
Unset,
|
|
||||||
Out,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Instruction = {
|
|
||||||
t: InstructionType,
|
|
||||||
v0: string,
|
|
||||||
v1?: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parse_str(input: string): Array<Array<Instruction>> {
|
|
||||||
return parse(lex(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses the tokens into a instruction set
|
|
||||||
function parse(tokens: Array<Token>): Array<Array<Instruction>> {
|
|
||||||
let pos = 0;
|
|
||||||
let max = tokens.length;
|
|
||||||
|
|
||||||
const ret = [];
|
|
||||||
|
|
||||||
while (pos < max) {
|
|
||||||
const [steps, next_pos] = parse_step(tokens, pos);
|
|
||||||
pos = next_pos;
|
|
||||||
ret.push(steps);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parse_step(tokens: Array<Token>, _pos: number): [Array<Instruction>, number] {
|
|
||||||
let pos = _pos;
|
|
||||||
|
|
||||||
expect(tokens, pos, TokenType.Step, "expected step");
|
|
||||||
pos += 1;
|
|
||||||
expect(tokens, pos, TokenType.BraceOpen, "expected opening brace");
|
|
||||||
pos += 1;
|
|
||||||
|
|
||||||
const instructions = [];
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const [inst, next] = parse_instruction(tokens, pos);
|
|
||||||
if (inst === null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
instructions.push(inst);
|
|
||||||
pos = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(tokens, pos, TokenType.BraceClose, "expected closing brace");
|
|
||||||
pos += 1
|
|
||||||
|
|
||||||
return [instructions, pos];
|
|
||||||
}
|
|
||||||
|
|
||||||
function parse_instruction(tokens: Array<Token>, _pos: number): [Instruction|null, number] {
|
|
||||||
let pos = _pos;
|
|
||||||
|
|
||||||
let instruction_type = tokens[pos]![0];
|
|
||||||
if (instruction_type === TokenType.Line) {
|
|
||||||
pos += 1;
|
|
||||||
expect(tokens, pos, TokenType.Number, "expected a number after the `line` instruction");
|
|
||||||
return [{
|
|
||||||
t: InstructionType.Line,
|
|
||||||
v0: tokens[pos]![1]!,
|
|
||||||
}, pos + 1]
|
|
||||||
}
|
|
||||||
else if (instruction_type === TokenType.Set) {
|
|
||||||
pos += 1;
|
|
||||||
expect(tokens, pos, TokenType.String, "expected a string after the `set` instruction");
|
|
||||||
pos += 1;
|
|
||||||
expect(tokens, pos, TokenType.String, "expected a second string after the `set` instruction");
|
|
||||||
|
|
||||||
return [{
|
|
||||||
t: InstructionType.Set,
|
|
||||||
v0: tokens[pos - 1]![1]!,
|
|
||||||
v1: tokens[pos]![1]!,
|
|
||||||
}, pos + 1]
|
|
||||||
}
|
|
||||||
else if (instruction_type === TokenType.Unset) {
|
|
||||||
expect(tokens, pos + 1, TokenType.String, "expected a a string after the `unset` instruction");
|
|
||||||
|
|
||||||
return [{
|
|
||||||
t: InstructionType.Unset,
|
|
||||||
v0: tokens[pos + 1]![1]!,
|
|
||||||
}, pos + 2]
|
|
||||||
}
|
|
||||||
else if (instruction_type === TokenType.Out) {
|
|
||||||
expect(tokens, pos + 1, TokenType.String, "expected a a string after the `unset` instruction");
|
|
||||||
|
|
||||||
return [{
|
|
||||||
t: InstructionType.Out,
|
|
||||||
v0: tokens[pos + 1]![1]!,
|
|
||||||
}, pos + 2]
|
|
||||||
}
|
|
||||||
|
|
||||||
return [null, pos];
|
|
||||||
}
|
|
||||||
|
|
||||||
function expect(t: Array<Token>, pos: number, type: TokenType, err: string) {
|
|
||||||
const [t_type] = t[pos]!;
|
|
||||||
if (t_type !== type) {
|
|
||||||
console.error("`" + String(t[pos]) + "`");
|
|
||||||
throw new Error(err + " , got " + t[pos]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user