diff --git a/crates/radlr-core/grammar/build_grammar.rs b/crates/radlr-core/grammar/build_grammar.rs index 78cd909d..53edf475 100644 --- a/crates/radlr-core/grammar/build_grammar.rs +++ b/crates/radlr-core/grammar/build_grammar.rs @@ -502,8 +502,8 @@ fn process_rule_symbols( let indices = set.symbols.iter().enumerate().map(|(i, _)| i).collect::>(); - if indices.len() > 4 && set.unordered { - todo!("Create error/warning about factorial explosions with `[]` sets greater than 4: (5+)! combinatorial explosion") + if indices.len() > 5 && set.unordered { + todo!("Create error/warning about factorial explosions with `[]` sets greater than 5: (6+)! combinatorial explosion") } let candidate_symbols = set.symbols.iter().enumerate().map(|(i, s)| (i + index, s)).collect::>(); diff --git a/crates/radlr-rust-runtime/types/context.rs b/crates/radlr-rust-runtime/types/context.rs index 06f8bc3a..d280ea2e 100644 --- a/crates/radlr-rust-runtime/types/context.rs +++ b/crates/radlr-rust-runtime/types/context.rs @@ -72,8 +72,7 @@ pub struct ParserContext { /// not change during parsing unless the end of the input window has been /// reached and a larger window is requested. pub end_ptr: usize, - /// The number of characters that comprize the current - /// token. This should be 0 if the tok_id is also 0 + /// The unique id of the token that is set to be produced by the scanner pub tok_id: u32, /// The byte length of the current token pub tok_byte_len: u32, diff --git a/crates/radlr-rust-runtime/types/parse_table_data.rs b/crates/radlr-rust-runtime/types/parse_table_data.rs index b2349486..2eda3f44 100644 --- a/crates/radlr-rust-runtime/types/parse_table_data.rs +++ b/crates/radlr-rust-runtime/types/parse_table_data.rs @@ -35,7 +35,7 @@ impl<'a> From> for TableHeaderData<'a> { let mut iter = i.iter(); let input_type = iter.next_u8().unwrap() as u32; let default_delta = iter.next_u32_le().unwrap(); - let scan_address = iter.next_u32_le().unwrap() as u32; + let scan_address = iter.next_u32_le().unwrap(); let table_length = iter.next_u32_le().unwrap(); let table_meta = iter.next_u32_le().unwrap(); let table_start = i.address() + 18; diff --git a/site/assets/js/lab/notebook.ts b/site/assets/js/lab/notebook.ts index 1820fd85..87ee0ff8 100644 --- a/site/assets/js/lab/notebook.ts +++ b/site/assets/js/lab/notebook.ts @@ -534,7 +534,7 @@ export class NBContentField extends NBField { } set_content_visible(is_content_visible: boolean) { - if (is_content_visible) { + if (is_content_visible) { this.ele.classList.add("content-visible"); } else { this.ele.classList.remove("content-visible"); diff --git a/site/assets/js/lab/parser_info.ts b/site/assets/js/lab/parser_info.ts index 8b9f2c69..26b39e67 100644 --- a/site/assets/js/lab/parser_info.ts +++ b/site/assets/js/lab/parser_info.ts @@ -3,84 +3,81 @@ import * as pipeline from "./pipeline"; import { NBContentField, NBEditorField } from "./notebook"; import { Controls } from "./control"; -type StatesLU = Map; - -class StatesView { - parse_states: number[] = []; - scan_states: number[] = []; - ele: HTMLDivElement +export function init( + parser_info_field: NBContentField, + parser_input_field: NBEditorField, + grammar_pipeline_node: pipeline.GrammarDBNode, + parser_player_node: pipeline.ParserPlayerNode, + controls: Controls +) { + parser_info_field.set_content_visible(false); + let db: radlr.JSBytecodeParserDB | null = null; + let bytecode: StatefullDV | null = null; - constructor() { - this.ele = document.createElement("div"); - this.ele.classList.add("stack-view"); - } + let instruction_view = new ContextView(); + let bytecode_view = new BytecodeView(); + let last_offset = 0; - handleInstruction(dv: StatefullDV, is_scanner: boolean) { - switch (dv.u8()) { - case radlr.Opcode.Pass: { - if (is_scanner) { - this.scan_states.pop(); - } else { - this.parse_states.pop(); - } - this.write(); - } break - case radlr.Opcode.Fail: { - if (is_scanner) { - this.scan_states.pop(); - } else { - this.parse_states.pop(); - } + parser_info_field.body.appendChild(instruction_view.ele); + parser_info_field.body.appendChild(bytecode_view.ele); + parser_info_field.body.id = "parser-info" - this.write(); - } break - case radlr.Opcode.Accept: { - if (is_scanner) { - this.scan_states.pop(); - } else { - this.parse_states.pop(); - } + grammar_pipeline_node.addListener("loading", _ => { + parser_info_field.set_loading(true); + instruction_view.reset(); + bytecode_view.clear(); + last_offset = 0; + }) - this.write(); - } break - case radlr.Opcode.PushGoto: { - let parse_mode = dv.u8(); - let address = dv.u32(); + grammar_pipeline_node.addListener("bytecode_db", async new_db => { + parser_info_field.set_loading(false); + db = new_db; - if (is_scanner) { - this.scan_states.push(address); - } else { - this.parse_states.push(address); - } + bytecode = new StatefullDV(new DataView(db.bytecode.buffer), 0); + bytecode_view.create_debug_info(bytecode.to()); + }) - this.write(); + parser_player_node.addListener("execute_state", debug_info => { + instruction_view.handle_debug_info(debug_info, parser_player_node.input); + bytecode_view.handle_state(debug_info); + }) - } break; + parser_player_node.addListener("execute_instruction", debug_info => { + if (bytecode) { + bytecode_view.handle_instruction(bytecode.to(debug_info.instruction), debug_info, parser_player_node.input); } - } + instruction_view.handle_debug_info(debug_info, parser_player_node.input); + }) - reset() { - this.ele.innerHTML = "" - this.parse_states.length = 0; - this.scan_states.length = 0; - } + parser_player_node.addListener("execute_instruction", debug_info => { + let ctx = debug_info.ctx; - write() { - let data = ""; + parser_input_field.remove_character_classes(); + parser_input_field.add_character_class(ctx.input_ptr, ctx.input_ptr + 1, "dbg-input-pos"); + parser_input_field.add_character_class(ctx.anchor_ptr, ctx.anchor_ptr + 1, "dbg-anchor-pos"); + parser_input_field.add_character_class(ctx.begin_ptr, ctx.begin_ptr + 1, "dbg-begin-pos"); + parser_input_field.add_character_class(ctx.end_ptr, ctx.end_ptr + 1, "dbg-end-pos"); - for (const state of this.parse_states) { - data += "
" + state.toString(16) + "
" + if (debug_info.is_scanner) { + parser_input_field.add_character_class(ctx.sym_ptr, ctx.sym_ptr + 1, "dbg-sym-pos"); + parser_input_field.add_character_class(ctx.sym_ptr, ctx.input_ptr, "dbg-sym"); + } else if (ctx.sym_len > 0) { + parser_input_field.add_character_class(ctx.sym_ptr, ctx.sym_ptr + ctx.sym_len, "dbg-sym"); } + }); - for (const state of this.scan_states) { - data += "
" + state.toString(16) + "
" - } - this.ele.innerHTML = data; - } + controls.addListener("reset", () => { + parser_input_field.remove_character_classes(); + parser_player_node.reset(); + bytecode_view.reset(); + }); } + +type StatesLU = Map; + class ContextView { ele: HTMLDivElement fields: { @@ -155,9 +152,18 @@ class ContextView { class BytecodeView { ele: HTMLDivElement instruction_area: HTMLDivElement + parse_state_stack: HTMLDivElement + scan_state_stack: HTMLDivElement + active_parse_state: HTMLDivElement + active_scan_state: HTMLDivElement + active_parse_state_val: number = 0 + active_scan_state_val: number = 0 inst_map: Map = new Map - instructions: HTMLDivElement[] = [] + instructions: { state_head: boolean, address: number, ele: HTMLDivElement }[] = [] active_instruction: number = -1; + state_headers: Map = new Map + parse_states: number[] = []; + scan_states: number[] = []; constructor() { @@ -165,31 +171,49 @@ class BytecodeView { ele.append((document.querySelector("#bytecode-view-template")).content.cloneNode(true)); this.ele = ele.firstElementChild!; this.instruction_area = this.ele.querySelector(".bytecode-instructions") + + this.parse_state_stack = this.ele.querySelector(".parser-states .state-stack") + this.scan_state_stack = this.ele.querySelector(".scanner-states .state-stack") + this.active_parse_state = this.ele.querySelector(".parser-states .active-state") + this.active_scan_state = this.ele.querySelector(".scanner-states .active-state") } - diss(address: number, mnemonic: string): HTMLDivElement { + instr_ele(address: number, mnemonic: string): HTMLDivElement { let ele = document.createElement("div"); ele.classList.add("bytecode-view-instruction") ele.innerHTML = `
${address.toString(16)}
${mnemonic}
`; - this.instruction_area.append(ele) - this.instructions.push(ele) + this.instructions.push({ address, ele, state_head: false }) this.inst_map.set(address, this.instructions.length - 1); return ele } - reset() { + clear() { this.instruction_area.innerHTML = ""; this.instructions.length = 0; this.inst_map.clear(); + this.state_headers.set(8, false); + } + + + reset() { + this.parse_states.length = 0; + this.scan_states.length = 0; + + this.parse_state_stack.innerHTML = ""; + this.scan_state_stack.innerHTML = ""; + + this.active_parse_state.innerHTML = ""; + this.active_scan_state.innerHTML = ""; } + extra(label: string, value: any): HTMLElement { let ele = document.createElement("div"); - ele.classList.add("bytecode-view-info") + ele.classList.add("label-value") ele.innerHTML = `
${label}
${value}
@@ -204,10 +228,12 @@ class BytecodeView { return ele; } - address(address: number, label: string = ""): HTMLElement { + address(address: number, label: string = "", goto_state: boolean = false): HTMLElement { let ele = document.createElement("div"); - ele.classList.add("address-label") + ele.classList.add("label-value") + if (goto_state) + this.state_headers.set(address, false); if (label) { ele.innerHTML = ` @@ -222,149 +248,84 @@ class BytecodeView { } ele.addEventListener("click", () => { - console.log(address) this.show_address(address); }) return ele; } - mark_scanners(dv: StatefullDV) { - let len = dv.dv.byteLength; - outer: while (dv.off < len) { - let instruction_address = dv.off; - let instruction_byte = dv.u8(); - let string = ""; - switch (instruction_byte) { - case radlr.Opcode.PushGoto: { - let parse_mode = dv.u8(); - this.diss(instruction_address, "PUSH").append( - this.address(dv.u32()) - ) - } break; - case radlr.Opcode.Goto: { - let parse_mode = dv.u8() - this.diss(instruction_address, "GOTO").append(this.address(dv.u32())) - } break; - case radlr.Opcode.AssignToken: { - this.diss(instruction_address, "TK_ID").append( - this.extra("tok_id", dv.u32()), - ) - } break; - case radlr.Opcode.Reduce: { - this.diss(instruction_address, "REDUCE").append( - this.extra("nonterm", dv.u32()), - this.extra("rule", dv.u32()), - this.extra("sym count", dv.u16()), - ) - } break; - case radlr.Opcode.VectorBranch: { - this.generate_table_string(dv, this.diss(instruction_address, "V_BR")); - } break; - case radlr.Opcode.HashBranch: { - this.generate_table_string(dv, this.diss(instruction_address, "H_BR")); - } break; - case radlr.Opcode.ByteSequence: { - let off = dv.off - 1; - let len = dv.u16(); - let offset = dv.u32(); - let data = new Uint8Array(dv.dv.buffer, dv.off, len); - let data_str = String.fromCodePoint(...data); - - let success_address = dv.off + len; - - this.diss(instruction_address, "BYTES").append( - this.extra("match", data_str), - this.address(success_address, "pass"), - (offset > 0) ? this.address(off + offset, "fail") : this.extra("fail", 0), - ) - } break; - case radlr.Opcode.Fork: { - this.diss(instruction_address, "FORK") - } break; - case radlr.Opcode.ReadCodepoint: { - this.diss(instruction_address, "CP") - } break; - default: { - break; - } - } - } - } - create_debug_info(dv: StatefullDV) { let len = dv.dv.byteLength; outer: while (dv.off < len) { let instruction_address = dv.off; let instruction_byte = dv.u8(); - let string = ""; switch (instruction_byte) { case radlr.Opcode.NoOp: { - this.diss(instruction_address, "NOOP") + this.instr_ele(instruction_address, "NOOP") } break; case radlr.Opcode.Pass: { - this.diss(instruction_address, "PASS") + this.instr_ele(instruction_address, "PASS") } break; case radlr.Opcode.Fail: { - this.diss(instruction_address, "FAIL") + this.instr_ele(instruction_address, "FAIL") } break; case radlr.Opcode.ShiftChar: { - this.diss(instruction_address, "SH_CH") + this.instr_ele(instruction_address, "SCHAR") } break; case radlr.Opcode.ShiftToken: { - this.diss(instruction_address, "SH_TK") + this.instr_ele(instruction_address, "SHIFT") } break; case radlr.Opcode.PeekToken: { - this.diss(instruction_address, "PK_TK") + this.instr_ele(instruction_address, "PTOK") } break; case radlr.Opcode.PeekTokenScanless: { - this.diss(instruction_address, "PK_TK_P") + this.instr_ele(instruction_address, "PSTOKS") } break; case radlr.Opcode.SkipToken: { - this.diss(instruction_address, "SK_TK") + this.instr_ele(instruction_address, "SKIP") } break; case radlr.Opcode.SkipTokenScanless: { - this.diss(instruction_address, "SK_TK_P") + this.instr_ele(instruction_address, "SKIPS") } break; case radlr.Opcode.PeekSkipToken: { - this.diss(instruction_address, "PK_SK_TK") + this.instr_ele(instruction_address, "PSKIP") } break; case radlr.Opcode.PeekReset: { - this.diss(instruction_address, "RST") + this.instr_ele(instruction_address, "RESET") } break; case radlr.Opcode.Accept: { - this.diss(instruction_address, "ACPT") + this.instr_ele(instruction_address, "ACCEPT") } break; case radlr.Opcode.PopGoto: { - this.diss(instruction_address, "POP") + this.instr_ele(instruction_address, "POP") } break; case radlr.Opcode.PushGoto: { let parse_mode = dv.u8(); - this.diss(instruction_address, "PUSH").append( - this.address(dv.u32()) + this.instr_ele(instruction_address, "PUSH").append( + this.address(dv.u32(), "", true) ) } break; case radlr.Opcode.Goto: { let parse_mode = dv.u8() - this.diss(instruction_address, "GOTO").append(this.address(dv.u32())) + this.instr_ele(instruction_address, "GOTO").append(this.address(dv.u32(), "", true)) } break; case radlr.Opcode.AssignToken: { - this.diss(instruction_address, "TK_ID").append( + this.instr_ele(instruction_address, "TOKID").append( this.extra("tok_id", dv.u32()), ) } break; case radlr.Opcode.Reduce: { - this.diss(instruction_address, "REDUCE").append( + this.instr_ele(instruction_address, "REDUCE").append( this.extra("nonterm", dv.u32()), this.extra("rule", dv.u32()), - this.extra("sym count", dv.u16()), + this.extra("sym #", dv.u16()), ) } break; case radlr.Opcode.VectorBranch: { - this.generate_table_string(dv, this.diss(instruction_address, "V_BR")); + this.generate_table(dv, this.instr_ele(instruction_address, "BVEC")); } break; case radlr.Opcode.HashBranch: { - this.generate_table_string(dv, this.diss(instruction_address, "H_BR")); + this.generate_table(dv, this.instr_ele(instruction_address, "BHASH")); } break; case radlr.Opcode.ByteSequence: { let off = dv.off - 1; @@ -375,29 +336,73 @@ class BytecodeView { let success_address = dv.off + len; - this.diss(instruction_address, "BYTES").append( + this.instr_ele(instruction_address, "BYTES").append( this.extra("match", data_str), - this.address(success_address, "pass"), - (offset > 0) ? this.address(off + offset, "fail") : this.extra("fail", 0), + this.address(success_address, "pass", true), + (offset > 0) ? this.address(off + offset, "fail", true) : this.extra("fail", 0), ) } break; case radlr.Opcode.Fork: { - this.diss(instruction_address, "FORK") + this.instr_ele(instruction_address, "FORK") } break; case radlr.Opcode.ReadCodepoint: { - this.diss(instruction_address, "CP") + this.instr_ele(instruction_address, "CP") } break; default: { break; } } } - } - generate_table_string(dv: StatefullDV, instruction_ele: HTMLDivElement) { - let out_string = ""; + for (const [state_address, is_scanner] of this.state_headers) { + let lookup = this.inst_map.get(state_address); + if (lookup) { + let ele = this.instructions[lookup].ele; + this.instructions[lookup].state_head = true; + + } + } + + for (let i = 0; i < this.instructions.length;) { + let { ele, state_head, address } = this.instructions[i]; + + if (state_head) { + let start = i++; + while (i < this.instructions.length && !this.instructions[i].state_head) { + i++; + } + + let elements = this.instructions.slice(start, i).map(d => d.ele); + let ele = document.createElement("div"); + + ele.classList.add("bytecode-state-group") - let { table_base_address, input_type, scan_address, table_length, table_start_iter, default_address } = getHashData(dv); + + ele.innerHTML = ` +
state ${address.toString(16).toUpperCase()} +
+
${create_pseudo_code(dv.to(address), false)}
+ + + ` + ele.append(...elements); + this.instruction_area.append(ele); + + (ele.firstElementChild!).addEventListener("click", e => { + ele.classList.toggle("show-pseudo") + }); + + } else { + this.instruction_area.append(ele) + i++ + } + } + } + + generate_table(dv: StatefullDV, instruction_ele: HTMLDivElement) { + let { parse_block_address, + table_type, table_base_address, input_type, table_meta, table_length, table_start_iter, default_address, scan_address + } = getLUTableData(dv); let val = "tok"; let convert_val_to_string = (val: number): string => val.toString(); @@ -432,7 +437,7 @@ class BytecodeView { convert_val_to_string = convert_codepoint; } break; case radlr.MatchInputType.ClassScanless: { - val = "cp_class" + val = "CLASS" } break; case radlr.MatchInputType.ByteSequence: { throw "HUH?"; @@ -444,170 +449,189 @@ class BytecodeView { instruction_ele.append(this.mnemonic(val)); - out_string += `\nmatch ${val}:`; + if (scan_address < 0xFFFF_FFFF) { + instruction_ele.append(this.address(scan_address, "scanner", true)) + } + + if (table_type == "hash") { + for (let i = 0; i < table_length; i++) { + let entry = table_start_iter.u32(); + let val_id = (entry & 0x7FF); + let address_offset = ((entry >> 11) & 0x7FF); + let address = table_base_address + address_offset; + instruction_ele.append(this.address(address, convert_val_to_string(val_id))) + } + } else { + for (let i = 0; i < table_length; i++) { + let entry = table_start_iter.u32(); + let val_id = table_meta + i; + let address_offset = entry; + let address = table_base_address + address_offset; + instruction_ele.append(this.address(address, convert_val_to_string(val_id))) + } + } + + if (default_address < dv.dv.byteLength) { + instruction_ele.append(this.address(default_address, "default")) + } + + dv.off = parse_block_address; + } + update_states() { + let data = ""; - for (let i = 0; i < table_length; i++) { - let entry = table_start_iter.u32(); + for (const state of this.parse_states.slice().reverse()) { + data += "
" + state.toString(16) + "
" + } - let val_id = (entry & 0x7FF); - let address_offset = ((entry >> 11) & 0x7FF); - //let meta = ((entry >> 22) & 0x3FF) - 512; - let address = table_base_address + address_offset; + this.parse_state_stack.innerHTML = data; - instruction_ele.append(this.address(address, convert_val_to_string(val_id))) + data = "" + for (const state of this.scan_states.reverse()) { + data += "
" + state.toString(16) + "
" } - if (default_address < dv.dv.byteLength) { - instruction_ele.append(this.address(default_address, "default")) + this.scan_state_stack.innerHTML = data; + + this.active_scan_state.innerHTML = "
" + this.active_scan_state_val.toString(16) + "
"; + this.active_parse_state.innerHTML = "
" + this.active_parse_state_val.toString(16) + "
"; + } + + handle_state(debug_info: radlr.JSDebugPacket) { + let address = debug_info.instruction + let is_scanner = debug_info.is_scanner; + this.set_active_state(is_scanner, address); + } + + private set_active_state(is_scanner: boolean, address: number) { + + if (!address) return; + + if (is_scanner) { + this.active_scan_state_val = address + } else { + this.active_parse_state_val = address } + + } - handle_debug_info(dv: StatefullDV, debug_info: radlr.JSDebugPacket, input: string) { + handle_instruction(dv: StatefullDV, debug_info: radlr.JSDebugPacket, input: string) { + this.update_states(); let instruction = dv.off; + let is_scanner = debug_info.is_scanner; let index = this.inst_map.get(instruction); if (index) { if (this.active_instruction >= 0) { - this.instructions[this.active_instruction].classList.remove("active") + this.instructions[this.active_instruction].ele.classList.remove("active") } this.active_instruction = index - let ele = this.instructions[index]; + let { ele } = this.instructions[index]; ele.classList.add("active") this.show_address(instruction); + } + switch (dv.u8()) { + case radlr.Opcode.PopGoto: { + if (is_scanner) { + this.scan_states.pop(); + } else { + this.parse_states.pop(); + } + } break + case radlr.Opcode.Pass: { + if (is_scanner) { + this.set_active_state(is_scanner, this.scan_states.pop()!); + } else { + this.set_active_state(is_scanner, this.parse_states.pop()!); + } + } break + case radlr.Opcode.Goto: { + let parse_mode = dv.u8(); + let address = dv.u32(); + this.set_active_state(is_scanner, address); + } break + case radlr.Opcode.PushGoto: { + let parse_mode = dv.u8(); + let address = dv.u32(); + if (is_scanner) { + this.scan_states.push(address); + } else { + this.parse_states.push(address); + } + } break; } + + } private show_address(address: number) { let index = this.inst_map.get(address); if (index) { - let ele = this.instructions[index]; + let { ele } = this.instructions[index]; let height = this.instruction_area.getBoundingClientRect().height; - let top = this.instruction_area.scrollTop; + let top = this.instruction_area.getBoundingClientRect().top; - let ele_top = ele.offsetTop; + let ele_top = ele.getBoundingClientRect().top - if (ele_top - top > height || ele_top - top < 0) { + let diff = ele_top - top; + + if (diff > (height - 40) || diff < 0) { ele.scrollIntoView(); + this.instruction_area.scrollTop -= height / 8; } } } } -export function init( - parser_info_field: NBContentField, - parser_input_field: NBEditorField, - grammar_pipeline_node: pipeline.GrammarDBNode, - parser_player_node: pipeline.ParserPlayerNode, - controls: Controls -) { - let db: radlr.JSBytecodeParserDB | null = null; - let bytecode: StatefullDV | null = null; - - let states_view = new StatesView(); - let instruction_view = new ContextView(); - let bytecode_view = new BytecodeView(); - - - parser_info_field.body.appendChild(instruction_view.ele); - parser_info_field.body.appendChild(states_view.ele); - parser_info_field.body.appendChild(bytecode_view.ele); - parser_info_field.body.id = "parser-info" - - grammar_pipeline_node.addListener("loading", _ => { - parser_info_field.set_loading(true); - states_view.reset(); - instruction_view.reset(); - bytecode_view.reset(); - }) - - grammar_pipeline_node.addListener("bytecode_db", async new_db => { - parser_info_field.set_loading(false); - db = new_db; - - bytecode = new StatefullDV(new DataView(db.bytecode.buffer), 0); - bytecode_view.create_debug_info(bytecode.to()); - states_view.reset(); - }) - - parser_player_node.addListener("execute_state", debug_info => { - instruction_view.handle_debug_info(debug_info, parser_player_node.input); - if (bytecode) { - bytecode_view.handle_debug_info(bytecode.to(debug_info.instruction), debug_info, parser_player_node.input); - } - }) - - parser_player_node.addListener("execute_instruction", debug_info => { - if (bytecode) { - states_view.handleInstruction(bytecode.to(debug_info.instruction), debug_info.is_scanner) - bytecode_view.handle_debug_info(bytecode.to(debug_info.instruction), debug_info, parser_player_node.input); - } - instruction_view.handle_debug_info(debug_info, parser_player_node.input); - }) - - parser_player_node.addListener("execute_instruction", debug_info => { - let ctx = debug_info.ctx; - - parser_input_field.remove_character_classes(); - parser_input_field.add_character_class(ctx.input_ptr, ctx.input_ptr + 1, "dbg-input-pos"); - parser_input_field.add_character_class(ctx.anchor_ptr, ctx.anchor_ptr + 1, "dbg-anchor-pos"); - parser_input_field.add_character_class(ctx.begin_ptr, ctx.begin_ptr + 1, "dbg-begin-pos"); - parser_input_field.add_character_class(ctx.end_ptr, ctx.end_ptr + 1, "dbg-end-pos"); - - if (debug_info.is_scanner) { - parser_input_field.add_character_class(ctx.sym_ptr, ctx.sym_ptr + 1, "dbg-sym-pos"); - parser_input_field.add_character_class(ctx.sym_ptr, ctx.input_ptr, "dbg-sym"); - } else if (ctx.sym_len > 0) { - parser_input_field.add_character_class(ctx.sym_ptr, ctx.sym_ptr + ctx.sym_len, "dbg-sym"); - } - }); - - - controls.addListener("reset", () => { - parser_input_field.remove_character_classes(); - parser_player_node.reset(); - states_view.reset(); - }); +type TableInfo = { + table_type: "hash" | "vec"; + table_base_address: number; + input_type: number; + scan_address: number; + table_meta: number; + table_length: number; + table_start_iter: StatefullDV; + default_address: number; + parse_block_address: number; } -export function disassemble_bytecode(parser_db: radlr.JSBytecodeParserDB): StatesLU { - let bytecode = parser_db.bytecode; - - let dv = new DataView(bytecode.buffer); - let off = 8; - - let states: StatesLU = new Map; - - ProcessState(new StatefullDV(dv, off), states); - - return states +function getLUTableData(dv: StatefullDV): TableInfo { + let table_base_address = dv.off - 1; + let table_type = <"hash" | "vec">((dv.dv.getInt8(table_base_address) == radlr.Opcode.HashBranch) ? "hash" : "vec"); + let input_type = dv.u8(); + let default_delta = dv.u32(); + let scan_address = dv.u32(); + let table_length = dv.u32(); + let table_meta = dv.u32(); + let table_start = table_base_address + 18; + let table_start_iter = dv.to(table_start); + let default_address = table_base_address + default_delta; + let parse_block_address = table_start + table_length * 4 + return { parse_block_address, table_type, table_meta, table_base_address, input_type, scan_address, table_length, table_start_iter, default_address }; } -function ProcessState(dv: StatefullDV, states: StatesLU, is_scanner: boolean = false) { - let state_address = dv.off; - if (states.has(state_address)) { - return; - } - let data = { pseudo_code: "" }; - states.set(state_address, data); + +function create_pseudo_code(dv: StatefullDV, is_scanner: boolean = false): string { let gotos: string[] = []; let pseudo_code = ""; - pseudo_code += `state@${dv.off.toString(16)}${is_scanner ? " SCANNER " : ""}(lex, ctx) {`; - pseudo_code += merge_lines(process_instructions(dv, gotos, states, data).split("\n",)); + pseudo_code += `s_${dv.off.toString(16)}${is_scanner ? " SCANNER " : ""}(lex, ctx) {`; + pseudo_code += merge_lines(process_instructions(dv, gotos).split("\n",)); pseudo_code += merge_lines(gotos); pseudo_code += "\n}"; - data.pseudo_code = pseudo_code; + return pseudo_code; } function merge_lines(internal_data: string[], prefix: string = "\n ") { @@ -619,7 +643,7 @@ function merge_lines(internal_data: string[], prefix: string = "\n ") { return pseudo_code; } -function process_instructions(dv: StatefullDV, gotos: string[], states: StatesLU, current_state: object, root_name: string = ""): string { +function process_instructions(dv: StatefullDV, gotos: string[], root_name: string = ""): string { let pseudo_code: string = "" let i = 0; let have_root = false @@ -627,7 +651,6 @@ function process_instructions(dv: StatefullDV, gotos: string[], states: StatesLU outer: while (i++ < 10) { let instruction_address = dv.off; let instruction_byte = dv.u8(); - let string = ""; switch (instruction_byte) { case radlr.Opcode.NoOp: { } break; @@ -635,71 +658,69 @@ function process_instructions(dv: StatefullDV, gotos: string[], states: StatesLU gotos.push("return") } break outer; case radlr.Opcode.Fail: { - string += `\nthrow "could not continue"`; + pseudo_code += `\nthrow "could not continue"`; } break outer; case radlr.Opcode.ShiftChar: { - string += "\nctx.emit(SHIFT_CHAR)"; - string += "\nlex.shift_la(1)"; + pseudo_code += "\nctx.emit(SHIFT_CHAR)"; + pseudo_code += "\nlex.shift_la(1)"; } break; case radlr.Opcode.ShiftToken: { - string += "\nlex.shift(ctx.sym_len)"; - string += "\nctx.emit(SHIFT_TOKEN { id: ctx.tk_id, len: ctx.sym_len })"; - string += "\nctx.sym_len = 0"; + pseudo_code += "\nlex.shift(ctx.sym_len)"; + pseudo_code += "\nctx.emit(SHIFT { id: ctx.tk_id, len: ctx.sym_len })"; + pseudo_code += "\nctx.sym_len = 0"; } break; case radlr.Opcode.PeekToken: { - string += "\nctx.peek(tok)"; + pseudo_code += "\nctx.peek(tok)"; } break; case radlr.Opcode.PeekTokenScanless: { - string += "\nctx.tok_len = ctx.sym_len\nctx.peek(tok)"; + pseudo_code += "\nctx.tok_len = ctx.sym_len\nctx.peek(tok)"; } break; case radlr.Opcode.SkipToken: { - string += "\nctx.skip()\ngoto " + root_name; + pseudo_code += "\nctx.skip()\ngoto " + root_name; } break outer; case radlr.Opcode.SkipTokenScanless: { - string += "\nctx.skip()\ngoto " + root_name; + pseudo_code += "\nctx.skip()\ngoto " + root_name; } break outer; case radlr.Opcode.PeekSkipToken: { - string += "\nctx.peek_skip()\ngoto " + root_name; + pseudo_code += "\nctx.peek_skip()\ngoto " + root_name; } break outer; case radlr.Opcode.PeekReset: { - string += "\nctx.peek_reset()"; + pseudo_code += "\nctx.peek_reset()"; } break; case radlr.Opcode.Accept: { - string += "\nctx.emit(ACCEPT)"; + pseudo_code += "\nctx.emit(ACCEPT)"; } break outer; case radlr.Opcode.PopGoto: { - string += "\nctx.pop(1)"; + pseudo_code += "\nctx.pop(1)"; } break; case radlr.Opcode.PushGoto: { let parse_mode = dv.u8(); let address = dv.u32(); - gotos.unshift(`state@${address.toString(16)}(lex, ctx)`); - ProcessState(dv.to(address), states); + gotos.unshift(`s_${address.toString(16)}(lex, ctx)`); } break; case radlr.Opcode.Goto: { let parse_mode = dv.u8(); let addressw = dv.u32(); - string += `\nstate@${addressw.toString(16)}(lex, ctx)`; - ProcessState(dv.to(addressw), states); + pseudo_code += `\ns_${addressw.toString(16)}(lex, ctx)`; } break; case radlr.Opcode.AssignToken: { let tok_id = dv.u32(); - string += `\nctx.tok_id = ${tok_id}`; + pseudo_code += `\nctx.tok_id = ${tok_id}`; } break; case radlr.Opcode.Reduce: { let nterm = dv.u32(); let rule_id = dv.u32(); let symbol_count = dv.u16(); - string += `\nctx.nt_id = ${nterm}`; - string += `\nctx.emit(REDUCE{ nt_id: ${nterm}, rule: ${rule_id}, sym_count: ${symbol_count} })`; + pseudo_code += `\nctx.nt_id = ${nterm}`; + pseudo_code += `\nctx.emit(REDUCE { nt_id: ${nterm}, rule: ${rule_id}, sym_count: ${symbol_count} })`; } break; case radlr.Opcode.VectorBranch: { throw "Vector branch not implemented" - string += generate_table_string(dv, states, current_state); + pseudo_code += generate_table_string(dv); } break; case radlr.Opcode.HashBranch: { - string += generate_table_string(dv, states, current_state); + pseudo_code += generate_table_string(dv); } break outer; case radlr.Opcode.ByteSequence: { let off = dv.off - 1; @@ -710,21 +731,21 @@ function process_instructions(dv: StatefullDV, gotos: string[], states: StatesLU let success_address = dv.off + len; - string += `if lex.slice(${len}) == "${data_str}":` - string += `\n lex.incr(${len})` + pseudo_code += `if lex.slice(${len}) == "${data_str}"` + pseudo_code += `\n lex.incr(${len})` let gotos: string[] = []; - string += merge_lines(process_instructions(dv.to(success_address), gotos, states, current_state).split("\n")); - string += merge_lines(gotos); - string += "\nelse" + pseudo_code += merge_lines(process_instructions(dv.to(success_address), gotos).split("\n")); + pseudo_code += merge_lines(gotos); + pseudo_code += "\nelse" if (offset > 0) { let fail_address = off + offset; let gotos: string[] = []; - string += merge_lines(process_instructions(dv.to(fail_address), gotos, states, current_state).split("\n")); - string += merge_lines(gotos); + pseudo_code += merge_lines(process_instructions(dv.to(fail_address), gotos).split("\n")); + pseudo_code += merge_lines(gotos); } else { - string += `\n throw \"Lexer does not have sequence '${data_str}' at current offset\"`; + pseudo_code += `\n throw \"Lexer does not have sequence '${data_str}' at current offset\"`; } } break outer; case radlr.Opcode.Fork: { @@ -736,19 +757,15 @@ function process_instructions(dv: StatefullDV, gotos: string[], states: StatesLU break outer; } } - - states.set(instruction_address, { pseudo_code: string }); - - pseudo_code += string; } + return pseudo_code; } -function buildPseudoCode() { } -function generate_table_string(dv: StatefullDV, states: StatesLU, current_state: object): string { +function generate_table_string(dv: StatefullDV): string { let out_string = ""; - let { table_base_address, input_type, scan_address, table_length, table_start_iter, default_address } = getHashData(dv); + let { table_base_address, input_type, scan_address, table_length, table_start_iter, default_address } = getLUTableData(dv); let val = "tok"; let error = "unrecognized symbol"; @@ -764,8 +781,7 @@ function generate_table_string(dv: StatefullDV, states: StatesLU, current_state: } break; case radlr.MatchInputType.Token: { if (scan_address < 0xFFFF_FFFF) { - out_string += `\nstate@${scan_address.toString(16)}(lex, ctx)`; - ProcessState(dv.to(scan_address), states, true); + out_string += `\ns_${scan_address.toString(16)}(lex, ctx)`; } out_string += `\ntok = ctx.tok_id` } break; @@ -812,7 +828,7 @@ function generate_table_string(dv: StatefullDV, states: StatesLU, current_state: } break; } - out_string += `\nmatch ${val}:`; + out_string += `\nmatch ${val}`; let inlined_default = false; @@ -845,7 +861,7 @@ function generate_table_string(dv: StatefullDV, states: StatesLU, current_state: } let gotos: string[] = []; - out_string += merge_lines(process_instructions(dv.to(address), gotos, states, current_state, root_name).split("\n"), "\n "); + out_string += merge_lines(process_instructions(dv.to(address), gotos).split("\n"), "\n "); out_string += merge_lines(gotos, "\n "); } @@ -853,19 +869,13 @@ function generate_table_string(dv: StatefullDV, states: StatesLU, current_state: if (default_address < dv.dv.byteLength) { out_string += `\n default:`; let gotos: string[] = []; - out_string += merge_lines(process_instructions(dv.to(default_address), gotos, states, current_state, root_name).split("\n"), "\n "); + out_string += merge_lines(process_instructions(dv.to(default_address), gotos).split("\n"), "\n "); out_string += merge_lines(gotos, "\n "); } else { - out_string += `\n default:\n throw "${error}"`; } - } - states.set(table_base_address, { - pseudo_code: out_string - }); - return out_string } @@ -901,75 +911,3 @@ class StatefullDV { } } - -export class ParserView { - field: NBContentField; - states: StatesLU | null = null; - debugger_data: HTMLDivElement; - parser_info: HTMLDivElement; - - constructor(field: NBContentField) { - if (field instanceof NBEditorField) { - throw "ParserView cannot bind to a NBEditorField"; - } - - this.debugger_data = document.createElement("div"); - this.parser_info = document.createElement("div"); - - this.field = field; - this.field.body.classList.add("debugger-cst-output"); - this.field.body.append(this.parser_info); - this.field.body.append(this.debugger_data); - } - - init() { - this.field.set_content_visible(false); - this.field.set_loading(true); - } - - set_active_state(address: number) { - let val = this.states!.get(address); - if (val) { - - this.debugger_data.innerHTML = `
${val.pseudo_code}
` - } - } - - handle_new_parser(parser_db: radlr.JSBytecodeParserDB) { - this.field.set_content_visible(true); - this.field.set_loading(false); - - this.states = disassemble_bytecode(parser_db); - } - - setContextData(step: radlr.JSDebugPacket) { - this.parser_info.innerHTML = JSON.stringify({ - instruction: step.instruction, - is_scanner: step.is_scanner, - ctx: { - anchor_ptr: step.ctx.anchor_ptr, - begin_ptr: step.ctx.begin_ptr, - end_ptr: step.ctx.end_ptr, - input_ptr: step.ctx.input_ptr, - is_scanner: step.ctx.is_scanner, - sym_len: step.ctx.sym_len, - sym_ptr: step.ctx.sym_ptr, - tok_id: step.ctx.tok_id, - tok_len: step.ctx.tok_len, - } - }, undefined, "\n") - } -} - -function getHashData(dv: StatefullDV) { - let table_base_address = dv.off - 1; - let input_type = dv.u8(); - let default_delta = dv.u32(); - let scan_address = dv.u32(); - let table_length = dv.u32(); - let table_meta = dv.u32(); - let table_start = table_base_address + 18; - let table_start_iter = dv.to(table_start); - let default_address = table_base_address + default_delta; - return { table_base_address, input_type, scan_address, table_length, table_start_iter, default_address }; -} diff --git a/site/assets/sass/color.scss b/site/assets/sass/color.scss index 1cc5d86c..16d49483 100644 --- a/site/assets/sass/color.scss +++ b/site/assets/sass/color.scss @@ -60,6 +60,9 @@ $theme-industrial-solace: ( // codemirror active-color: #828282, inactive-color: #4a4a4a, + + bc-address-bg-active: #c53131, + bc-address-bg: #512626 ); $theme-desperados-unite: ( @@ -124,6 +127,9 @@ $theme-pencil: ( button-bg: red, button-active-fg: rgb(255, 255, 255), button-active-bg: rgb(251, 94, 94), + + bc-address-bg-active: rgb(255, 255, 255), + bc-address-bg: rgb(255, 255, 255) ); diff --git a/site/assets/sass/lab.sass b/site/assets/sass/lab.sass index 1850df89..f36188f9 100644 --- a/site/assets/sass/lab.sass +++ b/site/assets/sass/lab.sass @@ -395,72 +395,7 @@ border-radius: 5px box-shadow: 0px 0px 5px #00000020 -// Colors ------------------------------------------ - -@mixin lab-themable($theme-map) - - #error-reporter - color: map-get($theme-map, fg-dec-1) - background-color: map-get($theme-map, bg-inc-1) - - #controls - background-color: map-get($theme-map, bg-dec-1) - color: map-get($theme-map, fg-basic) - - .classification - color: map-get($theme-map, fg-inc-1) - - .debugger-button:hover - color: map-get($theme-map, fg-dec-1) - - .nb-field - background-color: none - - - - &.nb-blank-field div - border-color: map-get($theme-map, bg-inc-1) - - .nb-resize-handle-area - .nb-resize-handle - color: map-get($theme-map, fg-dec-1) - background-color: map-get($theme-map, bg-inc-1) - - &:hover, &:active - color: map-get($theme-map, fg-inc-2) - background-color: map-get($theme-map, bg-inc-2) - &.dragging - background-color: map-get($theme-map, bg-basic) - - &.nb-content-field - &.fullscreen - background-color: map-get($theme-map, "bg-basic") - .nb-header - color: map-get($theme-map, fg-basic) - - .nb-icon-container - color: map-get($theme-map, fg-inc-2) - - &:hover - .nb-expand-button - opacity: 1 - - &, .nb-icon-container - color: map-get($theme-map, "field-header-fg") - - background: map-get($theme-map, "field-header-bg") - - .nb-loader - background-color: map-get($theme-map, "loader-bg") - border-color: map-get($theme-map, "loader-bg") - - .loader-div - background: map-get($theme-map, "loader-fg") - - .nb-loading-screen - background-color: map-get($theme-map, "bg-basic") - color: map-get($theme-map, "fg-inc-2") //.nb-column.mini .nb-field .nb-header // background: map-get($theme-map, "field-header-bg") !important @@ -552,35 +487,12 @@ text-align: right width: 100% - .stack-view - position: absolute - top: 90px - left: 10px - width: 60px - bottom: 20px - background-color: #111 - - .parse-state-goto, .scan-state-goto - position: relative - height: 18px; - font-size: 12px - text-transform: uppercase - text-align: right - padding: 5px - - .parse-state-goto - color: red - - .scan-state-goto - color: green - - - .bytecode-view + position: absolute - top: 90px + top: 50px bottom: 20px - left: 80px + left: 0px right: 20px font-family: monospace font-size: 12px @@ -591,67 +503,299 @@ height: 20px font-size: 14px + .state-address + width: 70px + margin: 0 2px + height: 15px + border-radius: 2px + padding: 1px + font-size: 12px + text-align: right; + box-sizing: border-box + + .active-states + position: relative + width: 100% + + .state-info + display: flex + margin: 2px 0 + height: 15px + + .state + + .state-label + width: 70px + text-align: right + + .active-state + width: 80px + + .state-stack + display: flex; + overflow: scroll; + .bytecode-instructions position: absolute - top: 25px - bottom: 0px - left: 0 - right: 0 overflow: hidden overflow-y: scroll - + margin: 0 2px + top: 80px + left: 0px + right: 0px + bottom: 0 + .bytecode-view-instruction - width: 100% - margin: 5px + margin: 0.5px + min-height: 30px display: flex flex-direction: row flex-wrap: wrap + align-items: center &.active - background: #111 > .address width: 60px margin: 0 5px 0 0 padding: 4px text-align: right - background-color: #222 text-transform: uppercase .mnemonic padding: 4px - width: 80px + width: 60px text-transform: uppercase - .address-label + .label-value pointer-events: all font-size: 11px - margin: 0 2.5px - padding: 4px + margin: 0 5px + min-width: 70px + + .value + display: block + text-align: right + text-transform: uppercase + &:first-child + font-size: 14px .address - color: lightblue display: block - width: 60px text-align: right - background-color: #222 text-transform: uppercase + border-radius: 2px + padding: 0 5px + cursor: pointer + opacity: 0.8 + + &:hover + opacity: 1 &:first-child font-size: 14px + width: 100% .label display: block - width: 60px - text-align: right + text-align: left - .bytecode-view-info - width: 70px - margin: 0 2.5px - font-size: 11px - - .label + .bytecode-state-group + position: relative + margin: 5px 0 + border-radius: 5px + + .bytecode-state-group-header + height: 20px + + .pseudo-code + position: relative + display: none + background-color: black + width: 100% + z-index: 100 + padding: 20px + box-sizing: border-box + border-radius: 20px + line-height: 1.8em + + + &.show-pseudo .pseudo-code + display: block + + &.show-pseudo .bytecode-view-instruction + display: none - .value - background: black +// Colors ------------------------------------------ + +@mixin lab-themable($theme-map) + + #error-reporter + color: map-get($theme-map, fg-dec-1) + background-color: map-get($theme-map, bg-inc-1) + + #controls + background-color: map-get($theme-map, bg-dec-1) + color: map-get($theme-map, fg-basic) + + .classification + color: map-get($theme-map, fg-inc-1) + + .debugger-button:hover + color: map-get($theme-map, fg-dec-1) + + .nb-field + background-color: none + + + + &.nb-blank-field div + border-color: map-get($theme-map, bg-inc-1) + + .nb-resize-handle-area + .nb-resize-handle + color: map-get($theme-map, fg-dec-1) + background-color: map-get($theme-map, bg-inc-1) + + &:hover, &:active + color: map-get($theme-map, fg-inc-2) + background-color: map-get($theme-map, bg-inc-2) + + &.dragging + background-color: map-get($theme-map, bg-basic) + + &.nb-content-field + &.fullscreen + background-color: map-get($theme-map, "bg-basic") + .nb-header + color: map-get($theme-map, fg-basic) + + .nb-icon-container + color: map-get($theme-map, fg-inc-2) + + &:hover + .nb-expand-button + opacity: 1 + + &, .nb-icon-container + color: map-get($theme-map, "field-header-fg") + + background: map-get($theme-map, "field-header-bg") + + .nb-loader + background-color: map-get($theme-map, "loader-bg") + border-color: map-get($theme-map, "loader-bg") + + .loader-div + background: map-get($theme-map, "loader-fg") + + .nb-loading-screen + background-color: map-get($theme-map, "bg-basic") + color: map-get($theme-map, "fg-inc-2") + + #error-reporter + color: map-get($theme-map, fg-dec-1) + background-color: map-get($theme-map, bg-inc-1) + + #controls + background-color: map-get($theme-map, bg-dec-1) + color: map-get($theme-map, fg-basic) + + .classification + color: map-get($theme-map, fg-inc-1) + + .debugger-button:hover + color: map-get($theme-map, fg-dec-1) + + .nb-field + background-color: none + + + + &.nb-blank-field div + border-color: map-get($theme-map, bg-inc-1) + + .nb-resize-handle-area + .nb-resize-handle + color: map-get($theme-map, fg-dec-1) + background-color: map-get($theme-map, bg-inc-1) + + &:hover, &:active + color: map-get($theme-map, fg-inc-2) + background-color: map-get($theme-map, bg-inc-2) + + &.dragging + background-color: map-get($theme-map, bg-basic) + + &.nb-content-field + &.fullscreen + background-color: map-get($theme-map, "bg-basic") + .nb-header + color: map-get($theme-map, fg-basic) + + .nb-icon-container + color: map-get($theme-map, fg-inc-2) + + &:hover + .nb-expand-button + opacity: 1 + + &, .nb-icon-container + color: map-get($theme-map, "field-header-fg") + + background: map-get($theme-map, "field-header-bg") + + .nb-loader + background-color: map-get($theme-map, "loader-bg") + border-color: map-get($theme-map, "loader-bg") + + .loader-div + background: map-get($theme-map, "loader-fg") + + .nb-loading-screen + background-color: map-get($theme-map, "bg-basic") + color: map-get($theme-map, "fg-inc-2") + + #parser-info + + .context-view-field-val + background-color: black + + .parse-state-goto + color: red + + .scan-state-goto + color: green + + .active-state + + .state-info + color: white + + .state-address + background-color: map-get($theme-map, "bc-address-bg") + + .active-state .state-address + background-color: map-get($theme-map, "bc-address-bg-active") + + .bytecode-view-instruction + + &.active + color: map-get($theme-map, "fg-inc-1") + background: map-get($theme-map, "bg-inc-1") + + > .address + color: map-get($theme-map, "fg-basic") + background: map-get($theme-map, "bg-dec-2") + + .label-value + color: map-get($theme-map, "fg-inc-1") + .label + color: map-get($theme-map, "fg-dec-1") + + .address + background: map-get($theme-map, "bc-address-bg") + + .value + background: map-get($theme-map, "bg-dec-1") \ No newline at end of file diff --git a/site/layouts/page/lab.html b/site/layouts/page/lab.html index 40e3317a..47e8041d 100644 --- a/site/layouts/page/lab.html +++ b/site/layouts/page/lab.html @@ -288,6 +288,19 @@

Loading...