Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a wasm browser based playground #41

Open
wants to merge 40 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1633608
Add 'std::' in several places as suggested by clang
mingodad Jun 22, 2023
4007497
Fix to detect identifiers referenced in rules but not defined
mingodad Jun 22, 2023
01e753c
Add preprocessor guards to allow build without threads/threadpool.
mingodad Jun 22, 2023
fe8da35
Make possible to accept associativity/precedence syntax like bison/byacc
mingodad Jun 22, 2023
98444cb
Add an error message for empty literal/regex declarations, also fix t…
mingodad Jun 22, 2023
82bfdf5
Check if '%whitespace' directive is present in the grammar and if not…
mingodad Jun 22, 2023
0e05c13
Add code to allow generate an EBNF for railroad diagram generation
mingodad Jun 22, 2023
061bf24
Add method to dump the input from the lexer.
mingodad Jun 22, 2023
7a668bb
Reuse result of already called function.
mingodad Jun 22, 2023
9243595
Add a method to show grammar compilation stats.
mingodad Jun 22, 2023
a64710d
Reorder class member for better memory usage/alignment.
mingodad Jun 22, 2023
642de0b
Rename write output function to prevent clash with C lib ::write
mingodad Jun 22, 2023
a5387f3
Use a typedef and macros to allow easy experimenting with different t…
mingodad Jun 22, 2023
d7cacbc
Simplify GrammaSymbolSet
mingodad Jun 23, 2023
06dfb8e
Only check for '%whitespace' directive if the grammar has no other er…
mingodad Jun 23, 2023
deb8e3e
Fix examples/test that were missing '%whitespace' directive.
mingodad Jun 23, 2023
49af659
Check if we are at the end and then stop
mingodad Jun 23, 2023
4e5acce
First working version of an wasm browser based playground
mingodad Jun 23, 2023
41860c1
Add a naive implementation of "%case_insensitive" directive, right no…
mingodad Jun 23, 2023
0aec408
Add column info to error messages in ErrorPolicy
mingodad Jun 23, 2023
d9e8e15
Add special regex character escape for the naive case insensitive imp…
mingodad Jun 25, 2023
9b72871
Make trivial methods inline.
mingodad Jun 26, 2023
3aa9a4a
Add column info to GrammarSymbol and error messages
mingodad Jun 26, 2023
f59b70c
First implementation for outptut an parse tree. The MissingHeaders te…
mingodad Jun 28, 2023
02178b9
Check if the input is accepted && full before print the parse tree
mingodad Jun 28, 2023
eb7ff4c
Now I've got closer to a good parse tree dump
mingodad Jul 16, 2023
ccdca1a
Missing fixes for a better parse tree output
mingodad Jul 16, 2023
9f907f6
Show an error message when associativity is assigned to a non-terminal.
mingodad Jul 16, 2023
1d09221
Undo a mistaken removing code for a better parse tree output.
mingodad Jul 16, 2023
8aa81d2
Fix generation of empty productions for genEBNF
mingodad Jul 16, 2023
51ab70e
Add YACC generation from LALR grammars.
mingodad Jul 17, 2023
208eda6
Fix to only match fully words, because before it was matching a subst…
mingodad Jul 18, 2023
fc40770
Add a missing case when generating a YACC file
mingodad Jul 18, 2023
08939ba
Add the reducing transition as a parameter to action handlers, this w…
mingodad Jul 18, 2023
ee28902
Update examples to use the extra action handler parameter recently in…
mingodad Jul 18, 2023
f5c1810
Add 2 new grammar options to easy debug, also fix line counting when …
mingodad Jul 19, 2023
6f73f32
Add code to detect user content changes and alert him/her
mingodad Nov 14, 2023
02946cc
Added grammar examples
mingodad Nov 14, 2023
d5231dc
Update code
mingodad Nov 14, 2023
2e1ef74
Create static.yml
mingodad Nov 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add YACC generation from LALR grammars.
mingodad committed Jul 17, 2023
commit 51ab70ed351d9056404dae1137ae04f7f2a357e9
8 changes: 7 additions & 1 deletion playground/index.html
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@
<option>Ada parser case insensitive</option>
<option>XML parser</option>
<option>C++ parser (bug)</option>
<option>LALR parser (not working)</option>
<option>Bison parser (not working)</option>
<option>DParser parser (not working)</option>
<option>Parse_gen parser (not working)</option>
@@ -52,7 +53,12 @@
<li><span>Input source</span></li>
<li class="editor-options">
<ul class="editor-header-options">
<li class="option"><label><input id="gen-ebnf" type="checkbox">Gen. EBNF</label></li>
<li class="option"><label>Generate </label>
<select id="generate-action">
<option>none</option>
<option value="ebnf">EBNF grammar</option>
<option value="yacc">YACC grammar</option>
</select></li>
<!--<li class="option"><label><input id="auto-refresh" type="checkbox">Auto Refresh</label></li>-->
<li class="option"><button id="parse" class="parse">Parse</button></li>
</ul>
18 changes: 13 additions & 5 deletions playground/index.js
Original file line number Diff line number Diff line change
@@ -92,6 +92,15 @@ function loadLalr_sample(self) {
codeEditor.getSession().setMode("ace/mode/text");
});
break;
case "LALR parser (not working)":
$.get(base_url + "lalr.g", function( data ) {
grammarEditor.setValue( data );
});
$.get(base_url + "lalr.g", function( data ) {
codeEditor.setValue( data );
codeEditor.getSession().setMode("ace/mode/yaml");
});
break;
case "Bison parser (not working)":
$.get(base_url + "bison.g", function( data ) {
grammarEditor.setValue( data );
@@ -240,7 +249,6 @@ function loadLalr_sample(self) {
}

//$('#ast-mode').val(localStorage.getItem('optimizationMode') || '2');
$('#gen-ebnf').prop('checked', localStorage.getItem('gen-ebnf') === 'true');
$('#auto-refresh').prop('checked', localStorage.getItem('autoRefresh') === 'true');
$('#parse').prop('disabled', $('#auto-refresh').prop('checked'));

@@ -294,7 +302,6 @@ function updateLocalStorage() {
localStorage.setItem('grammarText', grammarEditor.getValue());
localStorage.setItem('codeText', codeEditor.getValue());
//localStorage.setItem('optimizationMode', $('#opt-mode').val());
localStorage.setItem('gen-ebnf', $('#gen-ebnf').prop('checked'));
localStorage.setItem('autoRefresh', $('#auto-refresh').prop('checked'));
}

@@ -310,7 +317,8 @@ function parse() {
const codeText = codeEditor.getValue();

const astMode = $('#ast-mode').val();
const generate_ebnf = $('#gen-ebnf').prop('checked');
const generate_ebnf = $('#generate-action').val() == 'ebnf';
const generate_yacc = $('#generate-action').val() == 'yacc';
const lexer = $('#show-lexer').prop('checked');
let generate_ast = $('#show-ast').prop('checked');
if(generate_ast && astMode == 2)
@@ -342,7 +350,7 @@ function parse() {

window.setTimeout(() => {
parse_start_time = new Date().getTime();
lalr_parse(grammarText, codeText, lexer, generate_ebnf, generate_ast);
lalr_parse(grammarText, codeText, lexer, generate_ebnf, generate_yacc, generate_ast);

$('#overlay').css({
'z-index': '-1',
@@ -424,7 +432,7 @@ $('#code-info').on('click', 'li[data-ln]', makeOnClickInInfo(codeEditor));

// Event handing in the AST optimization
$('#opt-mode').on('change', setupTimer);
$('#gen-ebnf').on('change', setupTimer);
$('#generate-action').on('change', setupTimer);
$('#show-lexer').on('change', setupTimer);
$('#auto-refresh').on('change', () => {
updateLocalStorage();
6 changes: 3 additions & 3 deletions playground/lalr_playground.cpp
Original file line number Diff line number Diff line change
@@ -186,7 +186,7 @@ static void print_parsetree( const ParseTreeUserData& ast, int level )
}
}

extern "C" int parse(const char *grammar, const unsigned char *input, int dumpLexer, int generate_ebnf, int generate_parsetree)
extern "C" int parse(const char *grammar, const unsigned char *input, int dumpLexer, int generate_ebnf, int generate_yacc, int generate_parsetree)
{
int err = 0; // currently, zero is always returned; result codes for each part
// are sent to JS via set_result()
@@ -209,7 +209,7 @@ extern "C" int parse(const char *grammar, const unsigned char *input, int dumpLe

lalr::GrammarCompiler compiler;
lalr::ErrorPolicy error_policy;
int errors = compiler.compile( grammar, grammar + strlen(grammar), &error_policy, generate_ebnf != 0);
int errors = compiler.compile( grammar, grammar + strlen(grammar), &error_policy, generate_ebnf, generate_yacc);

if (errors != 0) {
fprintf(stderr, "Error compiling grammar. "
@@ -218,7 +218,7 @@ extern "C" int parse(const char *grammar, const unsigned char *input, int dumpLe
goto done;
}
else {
if(generate_ebnf) {
if(generate_ebnf || generate_yacc) {
err = -3;
parse_result = 0;
goto done;
164 changes: 164 additions & 0 deletions src/lalr/Grammar.cpp
Original file line number Diff line number Diff line change
@@ -326,6 +326,11 @@ static bool isTerminalRegex(const GrammarSymbol *symbol)
&& symbol->lexeme_type() == LexemeType::LEXEME_REGULAR_EXPRESSION;
}

static bool isTerminal(const GrammarSymbol *symbol)
{
return symbol->symbol_type() == SymbolType::SYMBOL_TERMINAL;
}

void Grammar::genEBNF()
{
printf(
@@ -390,6 +395,165 @@ void Grammar::genEBNF()
printf("\n\n// end EBNF\n");
}

void Grammar::genYACC()
{
const char *prefix = "";
printf(
"//\n"
"// YACC grammar for %s\n"
"//\n"
, identifier().c_str()
);
printf("\n/*Tokens*/\n");
GrammarSymbol* last_production_symbol = NULL;
bool production_continuation = false;
for ( vector<unique_ptr<GrammarProduction>>::const_iterator i = productions().begin(); i != productions().end(); ++i )
{
GrammarProduction* production = i->get();
LALR_ASSERT( production );
GrammarSymbol *curr_symbol = production->symbol();
bool same_production = last_production_symbol && last_production_symbol == curr_symbol;
vector<unique_ptr<GrammarProduction>>::const_iterator production_next = (i+1);
bool hasMoreProductions = same_production || (production_next != productions().end() && production_next->get()->symbol() == curr_symbol);
if(!same_production)
{
if( isTerminalRegex(curr_symbol)
|| (!hasMoreProductions && production->length() == 1 && isTerminal(production->symbol_by_position(0))) )
{
printf("%%token %s ;\n", curr_symbol->lexeme().c_str());
}
}
last_production_symbol = curr_symbol;
}

int max_prec = 0;
for ( vector<unique_ptr<GrammarSymbol>>::const_iterator i = symbols().begin(); i != symbols().end(); ++i )
{
GrammarSymbol* curr_symbol = i->get();
LALR_ASSERT( curr_symbol );

if(curr_symbol->precedence() > max_prec)
{
max_prec = curr_symbol->precedence();
}
}

if(max_prec > 0)
{
printf("\n/*Asociativity/Precedence*/\n");
for(int ip = 1; ip <= max_prec; ++ip)
{
production_continuation = false;
int last_prec = 0;
for ( vector<unique_ptr<GrammarSymbol>>::const_iterator i = symbols().begin(); i != symbols().end(); ++i )
{
GrammarSymbol* curr_symbol = i->get();
LALR_ASSERT( curr_symbol );

if(curr_symbol->precedence() == ip)
{
if(curr_symbol->precedence() > max_prec)
{
max_prec = curr_symbol->precedence();
}
if(last_prec != ip )
{
switch(curr_symbol->associativity())
{
case lalr::Associativity::ASSOCIATE_NONE:
printf("\n%%nonassoc ");
break;
case lalr::Associativity::ASSOCIATE_LEFT:
printf("\n%%left ");
break;
case lalr::Associativity::ASSOCIATE_RIGHT:
printf("\n%%right ");
break;
case lalr::Associativity::ASSOCIATE_PREC:
printf("\n%%precedence ");
break;
}
last_prec = ip;
printf(" /*%d*/ ", ip);
}
if(last_prec == curr_symbol->precedence())
{
ouptputTerminal(curr_symbol);
printf(" ");
}
}
}
if(last_prec > 0)
printf(";");
}
}

printf("\n\n%%%%\n");
last_production_symbol = NULL;
production_continuation = false;
for ( vector<unique_ptr<GrammarProduction>>::const_iterator i = productions().begin(); i != productions().end(); ++i )
{
GrammarProduction* production = i->get();
LALR_ASSERT( production );
GrammarSymbol *curr_symbol = production->symbol();
bool same_production = last_production_symbol && last_production_symbol == curr_symbol;
vector<unique_ptr<GrammarProduction>>::const_iterator production_next = (i+1);
bool hasMoreProductions = same_production || (production_next != productions().end() && production_next->get()->symbol() == curr_symbol);
if (isTerminalRegex(curr_symbol)
|| (!hasMoreProductions && production->length() == 1 && isTerminal(production->symbol_by_position(0))))
{
continue;
}
if(same_production) {
production_continuation = true;
//printf(" //%s ::= %d", production->symbol()->lexeme().c_str(), production->length());
}
else {
production_continuation = false;
const char *sym_prefix = "";
const char *sym_name = curr_symbol->lexeme().c_str();
if(sym_name[0] == '.')
{
continue;
}
//printf("\n\n%s%s ::= // %d\n\t", sym_prefix, sym_name, production->length());
if(last_production_symbol)
{
printf("\n%s\t;", prefix);
}
printf("\n\n%s%s%s :\n%s\t", prefix, sym_prefix, sym_name, prefix);
}
if(production->length() > 0) {
for(int elm=0; elm < production->length(); ++elm) {
const GrammarSymbol *sym = production->symbol_by_position(elm);
if(production_continuation) {
production_continuation = false;
printf("\n%s\t| ", prefix);
}
else printf(" ");
ouptputTerminal(sym);
}
}
else {
if(production_continuation) {
production_continuation = false;
printf("\n%s\t| ", prefix);
}
printf("/*empty*/");
}
if(production->precedence_symbol()) {
printf(" %%prec /*%d*/ ", production->precedence_symbol()->precedence());
ouptputTerminal(production->precedence_symbol());
}
last_production_symbol = curr_symbol;
}
if(last_production_symbol)
{
printf("\n%s\t;", prefix);
}
printf("\n\n// end YACC\n");
}

void Grammar::genNakedGrammar()
{
printf( "%s {\n", identifier_.c_str());
1 change: 1 addition & 0 deletions src/lalr/Grammar.hpp
Original file line number Diff line number Diff line change
@@ -69,6 +69,7 @@ class Grammar
Grammar& identifier( const char* identifier, int line, int column );
bool is_case_insensitive() const {return active_case_insensitive_;}
void genEBNF();
void genYACC();
void genNakedGrammar();

private:
7 changes: 6 additions & 1 deletion src/lalr/GrammarCompiler.cpp
Original file line number Diff line number Diff line change
@@ -73,7 +73,7 @@ void GrammarCompiler::labels_enabled( bool enabled )
labels_enabled_ = enabled;
}

int GrammarCompiler::compile( const char* begin, const char* end, ErrorPolicy* error_policy, bool genEBNF )
int GrammarCompiler::compile( const char* begin, const char* end, ErrorPolicy* error_policy, bool genEBNF, bool genYACC )
{
Grammar grammar;

@@ -87,6 +87,11 @@ int GrammarCompiler::compile( const char* begin, const char* end, ErrorPolicy* e
//grammar.genNakedGrammar();
return errors;
}
if(genYACC)
{
grammar.genYACC();
return errors;
}
bool isCaseInsensitive = grammar.is_case_insensitive();
GrammarGenerator generator;
errors = generator.generate( grammar, error_policy );
2 changes: 1 addition & 1 deletion src/lalr/GrammarCompiler.hpp
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ class GrammarCompiler
const RegexCompiler* whitespace_lexer() const;
const ParserStateMachine* parser_state_machine() const;
void labels_enabled( bool enabled );
int compile( const char* begin, const char* end, ErrorPolicy* error_policy = nullptr, bool genEBNF = false );
int compile( const char* begin, const char* end, ErrorPolicy* error_policy = nullptr, bool genEBNF = false, bool genYACC = false );
void showStats();

private: