Finish thp code DSL basic implementation

master
Araozu 2024-05-20 10:02:08 -05:00
parent 838bd233ac
commit 957921b793
4 changed files with 115 additions and 135 deletions

View File

@ -1,5 +1,7 @@
--- ---
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;
/** /**
@ -97,6 +99,15 @@ 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"
@ -104,7 +115,7 @@ const codeHtml = highlightCode(trimAndDedent(code));
line: 0, line: 0,
stdout: "", stdout: "",
ip: 0, ip: 0,
inst: ${steps}, inst: ${serialized_inst},
done: false, done: false,
state: {}, state: {},
}`} }`}
@ -138,12 +149,13 @@ const codeHtml = highlightCode(trimAndDedent(code));
</div> </div>
<script> <script>
type Instruction = "line" | "out" | "set" | "unset"; import { InstructionType, type Instruction } from "../thp_machine/machine_parser";
type AlpineState = { type AlpineState = {
line: number, line: number,
stdout: string, stdout: string,
ip: number, ip: number,
inst: Array<Array<[Instruction, string, string | undefined]>> inst: Array<Array<Instruction>>
done: boolean, done: boolean,
state: {[key: string]: string}, state: {[key: string]: string},
} }
@ -156,22 +168,25 @@ const codeHtml = highlightCode(trimAndDedent(code));
const instructions = data.inst[ip]!; const instructions = data.inst[ip]!;
for (const instructionSet of instructions) { for (const instructionSet of instructions) {
const instructionArr = instructionSet; const i = instructionSet;
switch (instructionArr[0]) {
case "line": { switch (i.t) {
data.line = Number(instructionArr[1]); case InstructionType.Line: {
data.line = Number(i.v0);
break; break;
} }
case "out": { case InstructionType.Out: {
data.stdout += String(instructionArr[1]) data.stdout += i.v0
break; break;
} }
case "set": { case InstructionType.Set: {
data.state[String(instructionArr[1])] = String(instructionArr[2]); const i_key = i.v0.slice(1, -1);
const i_value = i.v1!.slice(1, -1);
data.state[i_key] = i_value;
break; break;
} }
case "unset": { case InstructionType.Unset: {
delete data.state[String(instructionArr[1])]; delete data.state[i.v0];
break; break;
} }
} }

View File

@ -83,101 +83,36 @@ 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={`
[["line", 14]], step { line 14 }
[["line", 7]], step { line 7 }
[ step {
["line", 8], line 8
["set", "= = =", "run() = = = ="] set "= = =" "run() = = = ="
], }
[ step {
["line", 10], line 10
["set", " dangerous()", "..."], set " dangerous()" "..."
], }
[["line", 1]], step {line 1}
[ step {
["line", 2], line 2
["set", " = = =", "dangerous() = = = ="] set " = = =" "dangerous() = = = ="
], }
[["line", 3]], step {line 3}
[ step {set " Math.random()" "0.2"}
["set", " Math.random()", "0.2"] step {
], unset " Math.random()"
[ set " return" "Int 50"
["unset", " Math.random()"], }
["set", " return", "Int 50"] step {line 5}
], step {
[["line", 5]], line 10
[ unset " return"
["line", 10], unset " = = ="
["unset", " return"], set " dangerous()" "Int 50"
["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>

View File

@ -78,25 +78,25 @@ The documentation contains snippets of interactive THP code, like this:
f(x, y) f(x, y)
`} `}
steps={`[ steps={`
[["line", 1]], step {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"
], }
[["line", 4]], step {line 4}
[["line", 5]], step {line 5}
[ step {
["out", "hello, android 17"], out "hello, android 17"
["line", 6] line 6
], }
[["line", 8]], step {line 8}
[["line", 0]] step {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

View File

@ -14,6 +14,7 @@ enum TokenType {
Step, Step,
Line, Line,
Set, Set,
Out,
Number, Number,
String, String,
Unset, Unset,
@ -96,6 +97,7 @@ function lex_word(input: Array<string>, pos: number): [Token, number] {
else if (value === "line") { token_type = TokenType.Line; } else if (value === "line") { token_type = TokenType.Line; }
else if (value === "set") { token_type = TokenType.Set; } else if (value === "set") { token_type = TokenType.Set; }
else if (value === "unset"){ token_type = TokenType.Unset; } else if (value === "unset"){ token_type = TokenType.Unset; }
else if (value === "out") { token_type = TokenType.Out; }
else else
{ {
throw new Error(`Invalid word: ${value}`); throw new Error(`Invalid word: ${value}`);
@ -104,27 +106,40 @@ function lex_word(input: Array<string>, pos: number): [Token, number] {
return [[token_type, value], next_p] return [[token_type, value], next_p]
} }
enum InstructionType { export enum InstructionType {
Line, Line,
Set, Set,
Unset, Unset,
Out,
} }
type Instruction = { export type Instruction = {
t: InstructionType, t: InstructionType,
v0: string, v0: string,
v1?: string, v1?: string,
} }
export function parse_str(input: string): Array<Array<Instruction>> {
return parse(lex(input));
}
// Parses the tokens into a instruction set // Parses the tokens into a instruction set
function parse(tokens: Array<Token>): Array<Array<Instruction>> { function parse(tokens: Array<Token>): Array<Array<Instruction>> {
let pos = 0; let pos = 0;
let max = tokens.length; let max = tokens.length;
return []; 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> { function parse_step(tokens: Array<Token>, _pos: number): [Array<Instruction>, number] {
let pos = _pos; let pos = _pos;
expect(tokens, pos, TokenType.Step, "expected step"); expect(tokens, pos, TokenType.Step, "expected step");
@ -146,7 +161,7 @@ function parse_step(tokens: Array<Token>, _pos: number): Array<Instruction> {
expect(tokens, pos, TokenType.BraceClose, "expected closing brace"); expect(tokens, pos, TokenType.BraceClose, "expected closing brace");
pos += 1 pos += 1
return instructions; return [instructions, pos];
} }
function parse_instruction(tokens: Array<Token>, _pos: number): [Instruction|null, number] { function parse_instruction(tokens: Array<Token>, _pos: number): [Instruction|null, number] {
@ -173,6 +188,22 @@ function parse_instruction(tokens: Array<Token>, _pos: number): [Instruction|nul
v1: tokens[pos]![1]!, v1: tokens[pos]![1]!,
}, 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]; return [null, pos];
} }
@ -180,9 +211,8 @@ function parse_instruction(tokens: Array<Token>, _pos: number): [Instruction|nul
function expect(t: Array<Token>, pos: number, type: TokenType, err: string) { function expect(t: Array<Token>, pos: number, type: TokenType, err: string) {
const [t_type] = t[pos]!; const [t_type] = t[pos]!;
if (t_type !== type) { if (t_type !== type) {
console.error(t[pos]); console.error("`" + String(t[pos]) + "`");
throw new Error(err); throw new Error(err + " , got " + t[pos]);
} }
} }
console.log(parse_step(lex(" step { line 20 set \"a\" \"b\" }"), 0))