Create parse for thp state machine dsl
This commit is contained in:
parent
c7b429105d
commit
838bd233ac
@ -120,7 +120,7 @@ const codeHtml = highlightCode(trimAndDedent(code));
|
|||||||
</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">
|
<div class="h-24 p-1 overflow-y-scroll">
|
||||||
<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>
|
||||||
@ -128,10 +128,10 @@ const codeHtml = highlightCode(trimAndDedent(code));
|
|||||||
</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-950 disabled:opacity-50 disabled:cursor-not-allowed" @click="alpineNext($data)" :disabled="done && 'true'">
|
<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'">
|
||||||
Step: <span x-text="ip"></span>
|
Step: <span x-text="ip"></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="font-mono px-1 rounded bg-pink-950" @click="alpineReset($data)">
|
<button class="font-mono px-1 rounded bg-pink-200 dark:bg-pink-950 text-black dark:text-white" @click="alpineReset($data)">
|
||||||
Reset
|
Reset
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -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)) {
|
||||||
|
188
src/thp_machine/machine_parser.ts
Normal file
188
src/thp_machine/machine_parser.ts
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
{
|
||||||
|
throw new Error(`Invalid word: ${value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [[token_type, value], next_p]
|
||||||
|
}
|
||||||
|
|
||||||
|
enum InstructionType {
|
||||||
|
Line,
|
||||||
|
Set,
|
||||||
|
Unset,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Instruction = {
|
||||||
|
t: InstructionType,
|
||||||
|
v0: string,
|
||||||
|
v1?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses the tokens into a instruction set
|
||||||
|
function parse(tokens: Array<Token>): Array<Array<Instruction>> {
|
||||||
|
let pos = 0;
|
||||||
|
let max = tokens.length;
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse_step(tokens: Array<Token>, _pos: number): Array<Instruction> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
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(t[pos]);
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(parse_step(lex(" step { line 20 set \"a\" \"b\" }"), 0))
|
Loading…
Reference in New Issue
Block a user