Compare commits

...

3 commits

Author SHA1 Message Date
1ecbe672bc [sql] Inline configuration creation, faster 2024-11-15 21:32:58 -08:00
2d10e91f9e [sql] SQL "works" but it's too dang slow 2024-11-15 19:46:51 -08:00
c23dbe3e8f [parser] More usability improvements
Implicit sequences in arguments, and now you can use terminals when
constructing regular expressions.
2024-11-12 07:43:32 -08:00
4 changed files with 1268 additions and 461 deletions

View file

@ -129,14 +129,17 @@ function render_state(state, input_editor) {
* otherwise just queue it for submission. * otherwise just queue it for submission.
*/ */
function post_document(worker, kind, state, document) { function post_document(worker, kind, state, document) {
console.log("Received document", kind)
if (window.localStorage) { if (window.localStorage) {
window.localStorage.setItem(kind, document); window.localStorage.setItem(kind, document);
} }
let new_state = {...state}; let new_state = {...state};
if (new_state.pending) { if (new_state.pending) {
console.log("Document parked", kind)
new_state.next = document; new_state.next = document;
} else { } else {
console.log("Document submitted", kind)
new_state.pending = document; new_state.pending = document;
new_state.next = null; new_state.next = null;
worker.postMessage({kind, data: document}); worker.postMessage({kind, data: document});
@ -151,6 +154,7 @@ function post_document(worker, kind, state, document) {
function rotate_document(worker, kind, state) { function rotate_document(worker, kind, state) {
let new_state = {...state, last: state.pending, pending: null}; let new_state = {...state, last: state.pending, pending: null};
if (new_state.next) { if (new_state.next) {
console.log("Rotating document", kind)
new_state.pending = new_state.next; new_state.pending = new_state.next;
new_state.next = null; new_state.next = null;
worker.postMessage({kind, data: new_state.pending}); worker.postMessage({kind, data: new_state.pending});

View file

@ -176,19 +176,21 @@ const pyodide_promise = setup_python();
async function load_grammar_module(code) { async function load_grammar_module(code) {
const pyodide = self.pyodide; const pyodide = self.pyodide;
// console.log("Running..."); console.log("eval_grammar: Running");
const my_fn = pyodide.globals.get("eval_grammar"); const my_fn = pyodide.globals.get("eval_grammar");
my_fn(code); my_fn(code);
my_fn.destroy(); my_fn.destroy();
console.log("eval_grammar: Done");
} }
async function parse_document(code) { async function parse_document(code) {
const pyodide = self.pyodide; const pyodide = self.pyodide;
// console.log("Running..."); console.log("eval_document: Running");
const my_fn = pyodide.globals.get("eval_document"); const my_fn = pyodide.globals.get("eval_document");
my_fn(code); my_fn(code);
my_fn.destroy(); my_fn.destroy();
console.log("eval_document: Done");
} }
self.onmessage = async function(event) { self.onmessage = async function(event) {
@ -197,8 +199,10 @@ self.onmessage = async function(event) {
try { try {
const { kind, data } = event.data; const { kind, data } = event.data;
if (kind === "grammar") { if (kind === "grammar") {
console.log("Worker received grammar")
await load_grammar_module(data); await load_grammar_module(data);
} else if (kind === "input") { } else if (kind === "input") {
console.log("Worker received input")
await parse_document(data); await parse_document(data);
} }
} catch (e) { } catch (e) {

File diff suppressed because it is too large Load diff

View file

@ -1116,7 +1116,9 @@ class ParserGenerator:
# Check to make sure they didn't use anything that will give us # Check to make sure they didn't use anything that will give us
# heartburn later. # heartburn later.
reserved = [a for a in alphabet if (a.startswith("__") and not a.startswith("__gen_")) or a == "$"] reserved = [
a for a in alphabet if (a.startswith("__") and not a.startswith("__gen_")) or a == "$"
]
if reserved: if reserved:
raise ValueError( raise ValueError(
"Can't use {symbols} in grammars, {what} reserved.".format( "Can't use {symbols} in grammars, {what} reserved.".format(
@ -1455,7 +1457,18 @@ class ParserGenerator:
lookahead.update(context) lookahead.update(context)
for rule in rules: for rule in rules:
new_core = Configuration.from_rule(config_next, rule) if len(rule) == 0:
next = None
else:
next = rule[0]
new_core = Configuration(
name=config_next,
symbols=rule,
position=0,
next=next,
)
todo.append((new_core, lookahead)) todo.append((new_core, lookahead))
return ItemSet(closure) return ItemSet(closure)
@ -1619,6 +1632,7 @@ class Terminal(Rule):
_CURRENT_DEFINITION: str = "__global" _CURRENT_DEFINITION: str = "__global"
_CURRENT_GEN_INDEX: int = 0 _CURRENT_GEN_INDEX: int = 0
class NonTerminal(Rule): class NonTerminal(Rule):
"""A non-terminal, or a production, in the grammar. """A non-terminal, or a production, in the grammar.
@ -1699,7 +1713,6 @@ class NonTerminal(Rule):
_CURRENT_DEFINITION = prev_defn _CURRENT_DEFINITION = prev_defn
_CURRENT_GEN_INDEX = prev_idx _CURRENT_GEN_INDEX = prev_idx
return self._body return self._body
def flatten( def flatten(
@ -1807,22 +1820,32 @@ def seq(*args: Rule) -> Rule:
def opt(*args: Rule) -> Rule: def opt(*args: Rule) -> Rule:
"""Mark a sequence as optional."""
return AlternativeRule(seq(*args), Nothing) return AlternativeRule(seq(*args), Nothing)
def mark(rule: Rule, **kwargs) -> Rule: def mark(rule: Rule, **kwargs) -> Rule:
"""Mark the specified rules with metadata."""
return MetadataRule(rule, kwargs) return MetadataRule(rule, kwargs)
def one_or_more(r: Rule) -> Rule: def one_or_more(*args: Rule) -> Rule:
"""Generate a rule that matches a repetition of one or more of the specified
rule.
The resulting list is transparent, i.e., in the parse tree all of the members
of the list will be in-line with the parent. If you want to name the list
create a named nonterminal to contain it.
"""
global _CURRENT_DEFINITION global _CURRENT_DEFINITION
global _CURRENT_GEN_INDEX global _CURRENT_GEN_INDEX
tail : NonTerminal | None = None tail: NonTerminal | None = None
def impl() -> Rule: def impl() -> Rule:
nonlocal tail nonlocal tail
assert(tail is not None) assert tail is not None
return opt(tail) + r return opt(tail) + seq(*args)
tail = NonTerminal( tail = NonTerminal(
fn=impl, fn=impl,
@ -1833,8 +1856,17 @@ def one_or_more(r: Rule) -> Rule:
return tail return tail
def zero_or_more(r:Rule) -> Rule:
return opt(one_or_more(r)) def zero_or_more(*args: Rule) -> Rule:
"""Generate a rule that matches a repetition of zero or more of the specified
rule.
The resulting list is transparent, i.e., in the parse tree all of the members
of the list will be in-line with the parent. If you want to name the list
create a named nonterminal to contain it.
"""
return opt(one_or_more(*args))
@typing.overload @typing.overload
def rule(f: typing.Callable, /) -> NonTerminal: ... def rule(f: typing.Callable, /) -> NonTerminal: ...
@ -2155,11 +2187,25 @@ class Re:
def question(self) -> "Re": def question(self) -> "Re":
return ReQuestion(self) return ReQuestion(self)
def __or__(self, value: "Re", /) -> "Re": def __or__(self, value: "Re | Terminal", /) -> "Re":
return ReAlt(self, value) if isinstance(value, Re):
other = value
elif isinstance(value.pattern, Re):
other = value.pattern
else:
other = Re.literal(value.pattern)
def __add__(self, value: "Re") -> "Re": return ReAlt(self, other)
return ReSeq(self, value)
def __add__(self, value: "Re | Terminal") -> "Re":
if isinstance(value, Re):
other = value
elif isinstance(value.pattern, Re):
other = value.pattern
else:
other = Re.literal(value.pattern)
return ReSeq(self, other)
UNICODE_MAX_CP = 1114112 UNICODE_MAX_CP = 1114112
@ -2818,9 +2864,12 @@ class TriviaMode(enum.Enum):
# Finally, the grammar class. # Finally, the grammar class.
############################################################################### ###############################################################################
PrecedenceList = list[typing.Tuple[Assoc, list[Terminal|NonTerminal]]] PrecedenceList = list[typing.Tuple[Assoc, list[Terminal | NonTerminal]]]
def gather_grammar(start: NonTerminal, trivia: list[Terminal]) -> tuple[dict[str,NonTerminal], dict[str,Terminal]]:
def gather_grammar(
start: NonTerminal, trivia: list[Terminal]
) -> tuple[dict[str, NonTerminal], dict[str, Terminal]]:
"""Starting from the given NonTerminal, gather all of the symbols """Starting from the given NonTerminal, gather all of the symbols
(NonTerminals and Terminals) that make up the grammar. (NonTerminals and Terminals) that make up the grammar.
""" """
@ -2864,9 +2913,11 @@ def gather_grammar(start: NonTerminal, trivia: list[Terminal]) -> tuple[dict[str
existing = named_rules.get(rule.name) existing = named_rules.get(rule.name)
if existing is not None: if existing is not None:
# TODO TEST # TODO TEST
raise ValueError(f"""Found more than one rule named {rule.name}: raise ValueError(
f"""Found more than one rule named {rule.name}:
- {existing.definition_location} - {existing.definition_location}
- {rule.definition_location}""") - {rule.definition_location}"""
)
named_rules[rule.name] = rule named_rules[rule.name] = rule
named_terminals: dict[str, Terminal] = {} named_terminals: dict[str, Terminal] = {}
@ -2874,16 +2925,20 @@ def gather_grammar(start: NonTerminal, trivia: list[Terminal]) -> tuple[dict[str
existing = named_terminals.get(terminal.name) existing = named_terminals.get(terminal.name)
if existing is not None: if existing is not None:
# TODO TEST # TODO TEST
raise ValueError(f"""Found more than one terminal named {terminal.name}: raise ValueError(
f"""Found more than one terminal named {terminal.name}:
- {existing.definition_location} - {existing.definition_location}
- {terminal.definition_location}""") - {terminal.definition_location}"""
)
existing_rule = named_rules.get(terminal.name) existing_rule = named_rules.get(terminal.name)
if existing_rule is not None: if existing_rule is not None:
# TODO TEST # TODO TEST
raise ValueError(f"""Found a terminal and a rule both named {terminal.name}: raise ValueError(
f"""Found a terminal and a rule both named {terminal.name}:
- The rule was defined at {existing_rule.definition_location} - The rule was defined at {existing_rule.definition_location}
- The terminal was defined at {terminal.definition_location}""") - The terminal was defined at {terminal.definition_location}"""
)
named_terminals[terminal.name] = terminal named_terminals[terminal.name] = terminal
@ -2980,7 +3035,7 @@ class Grammar:
generate_nonterminal_dict- less useful to people, probably, but it is generate_nonterminal_dict- less useful to people, probably, but it is
the input form needed by the Generator. the input form needed by the Generator.
""" """
grammar: list[tuple[str,list[str]]] = [ grammar: list[tuple[str, list[str]]] = [
(rule.name, [s.name for s in production]) (rule.name, [s.name for s in production])
for rule in self._nonterminals.values() for rule in self._nonterminals.values()
for production in rule.body for production in rule.body