Skip to content

Commit

Permalink
Do not execute tests, aside from parsing.
Browse files Browse the repository at this point in the history
Like normal bl:mp programs, the tests must explicitly instruct the
parser to execute them using ! (or, more realistically, by preloading
a bootstrap prelude).

With this change, we can finally close #14!
  • Loading branch information
jbearer committed Oct 12, 2021
1 parent b4e0b5e commit 98b8c2d
Show file tree
Hide file tree
Showing 13 changed files with 202 additions and 53 deletions.
7 changes: 4 additions & 3 deletions blimp/src/blimp.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ static void PrintUsage(FILE *f, int argc, char *const *argv)
fprintf(f, " Search for imported modules in DIR. Multiple directories\n");
fprintf(f, " may be given by passing this option more than once.\n");
fprintf(f, "\n");
fprintf(f, " -l, --preload MODULE\n");
fprintf(f, " -l, --preload MOD\n");
fprintf(f, " Prepend the given module to the input file. MOD will be\n");
fprintf(f, " searched in the import path, using the same search\n");
fprintf(f, " procedure as `import MOD'. More than one preload module\n");
Expand Down Expand Up @@ -724,15 +724,16 @@ int main(int argc, char *const *argv)

Blimp_Check(BlimpModule_Init(blimp, options.import_path));

// Automatically prepend the `std' prelude if requested by the user.
// Automatically prepend the `std_bootstrap' prelude if requested by the
// user.
if (options.implicit_prelude) {
options.prepend = realloc(
options.prepend, ++options.prepend_len * sizeof(char *));
if (options.prepend == NULL) {
fprintf(stderr, "could not allocate preload");
return EXIT_FAILURE;
}
options.prepend[options.prepend_len-1] = "std";
options.prepend[options.prepend_len-1] = "std_bootstrap";
}

if (optind == argc) {
Expand Down
20 changes: 19 additions & 1 deletion core/src/grammar.c
Original file line number Diff line number Diff line change
Expand Up @@ -564,12 +564,30 @@ Status DefaultGrammar(Blimp *blimp, Grammar *grammar)
// Add non-terminals used by the default productions to the parser, in order
// of increasing precedence:
NonTerminal NT_Expr, NT_ExprNoMsg, NT_Stmt, NT_StmtNoMsg, NT_Custom,
NT_CustomNoMsg, NT_Semi, NT_Term, NT_TermNoMsg;
NT_CustomNoMsg, NT_Semi, NT_Term, NT_TermNoMsg, NT_Run;
TRY(GetNonTerminal(grammar,"1", &NT_Expr));
TRY(GetNonTerminal(grammar,"2", &NT_ExprNoMsg));
TRY(GetNonTerminal(grammar,"3", &NT_Stmt));
TRY(GetNonTerminal(grammar,"4", &NT_StmtNoMsg));
TRY(GetNonTerminal(grammar,"5", &NT_Semi));
// The bootstrap prelude needs some grammar symbol to prepend to the file it
// is bootstrapping to force that file to execute. It uses the symbol __run,
// but this cannot simply be a terminal, since leaving an unreduced terminal
// on the stack throughout the parsing of the bootstrapee would make it
// impossible for the bootstrapee to add and use new grammar rules. The
// __run terminal must be immediately reduced to a non-terminal with lower
// precedence than any rule the user might want to add. Since the user
// defines new rules at built-in precedence levels (custom1 and higher) any
// non-terminal defined in bootstrap prelude will have too high precedence.
// Therefore, we build in a non-terminal __run for this specific purpose,
// with precedence just lower than the first user-editable non-terminal.
//
// This is ugly, but it arises from the fact that the user-facing grammar
// defined by bootstrap is totally entangled with the built-in grammar.
// Eventually, the built-in grammar should be stripped down to a single,
// very low-precedence non-terminal, and bootstrap should be able to define
// __run itself.
TRY(GetNonTerminal(grammar,"__run", &NT_Run));
// It's useful to have precedences in between the statement grammar (3, 4)
// and the term grammar (6, 7). The user cannot define new left-recursive
// forms at precedence 5 or lower, because there will always be statements
Expand Down
34 changes: 28 additions & 6 deletions core/src/lalr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1929,8 +1929,18 @@ static Status State_GetTransition(
? "shift-reduce"
: "reduce-reduce";

const char *terminal_name = *(const char **)Vector_Index(
&grammar->terminal_strings, sym->terminal);
const char *terminal_name =
sym->terminal < grammar->num_terminals
// If the terminal is a real terminal (not a pseudo-terminal)
// use it's human-readable name.
? *(const char **)Vector_Index(
&grammar->terminal_strings, sym->terminal)
// Otherwise, it is a pseudo-terminal. Use the name of the
// corresponding non-terminal.
: *(const char **)Vector_Index(
&grammar->non_terminal_strings,
sym->terminal - grammar->num_terminals)
;

return ErrorMsg(Grammar_GetBlimp(grammar), BLIMP_AMBIGUOUS_PARSE,
"potential ambiguous parse at input `%s' (%s conflict). "
Expand Down Expand Up @@ -3940,10 +3950,22 @@ static Status ParseStream(
if (tree.grammar_symbol.is_terminal) {
// If the input that caused the error is a terminal,
// include it in the error message.
const char *name =
Object_Type(tree.symbol) == OBJ_SYMBOL
? ((const Symbol *)tree.symbol)->name
: "<object literal>";
const char *name;
if (tree.symbol == NULL) {
// NULL usually means EOF.
if (tree.grammar_symbol.terminal == TOK_EOF)
{
name = "EOF";
} else {
name = "???";
}
} else {
if (Object_Type(tree.symbol) == OBJ_SYMBOL) {
name = ((const Symbol *)tree.symbol)->name;
} else {
name = "<object literal>";
}
}
ErrorFrom(blimp, tree.range,
BLIMP_AMBIGUOUS_PARSE,
"ambiguous parse at input `%s' (%s conflict). "
Expand Down
14 changes: 12 additions & 2 deletions prelude/bootstrap.bli
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
# Define __run -> __run. We will use the keyword __run below to execute the
# contents of the file appended to this one. But we don't want the high-
# precedence terminal __run on the parser stack the whole time the input file is
# being parsed, because that makes it impossible for the input file to add and
# use new grammar rules during parsig. So we make sure that the terminal __run
# is always immediately reduced to the low-precedence non-terminal __run.
!(\> {^{`__run`}; `__run`} {^toks
{^{.}; 7}
});

# Define __run <expr>, which is exactly like !<expr>, except it takes (and
# returns) an expression with precedence 1 instead of 7. This is necessary
# because we are going to end this file with __run so that it can be prepended
# to another file, whose contents will in general be a 1-level expression. Since
# we don't have the ability to append code _after_ the bootstrapped file, we
# can't wrap it's contents in parentheses, so we have to work with the
# precedence we get.
!(\> {^{__run} {^{``}; 1}; 1} {^toks
!(\> {^{^{``}; `__run`} {^{``}; 1}; 1} {^toks
{
n_ref{^ ^};
n n_ref;
Expand Down Expand Up @@ -34,7 +44,7 @@
# Define __run as a no-op. This makes this file valid bl:mp on its own, even
# with the trailing `__run` below, so that it can be imported as well as
# prepended.
!(\> {^{__run}; 1} {^toks
!(\> {^{^{``}; `__run`}; 1} {^toks
{^ {.}; 1}
});

Expand Down
9 changes: 1 addition & 8 deletions prelude/std.bli
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,4 @@
!(import list);
!(import struct);
!(import path);
!(import bootstrap);

# The __run command from the bootstrap prelude forces whatever follows it to
# be executed eagerly. Thus, if this file is prepended to another, the
# contents of the second file will be automatically executed. __run is a no-op
# if followed by EOF, so if this file is imported normally, this will have no
# effect.
__run
.
9 changes: 9 additions & 0 deletions prelude/std_bootstrap.bli
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import std;
import bootstrap;

# The __run command from the bootstrap prelude forces whatever follows it to
# be executed eagerly. Thus, if this file is prepended to another, the
# contents of the second file will be automatically executed. __run is a no-op
# if followed by EOF, so if this file is imported normally, this will have no
# effect.
__run
2 changes: 1 addition & 1 deletion test/compiler/.options
Original file line number Diff line number Diff line change
@@ -1 +1 @@
--skip-racket
-lbootstrap --skip-racket
1 change: 1 addition & 0 deletions test/core/.options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-lbootstrap
2 changes: 1 addition & 1 deletion test/gc/.options
Original file line number Diff line number Diff line change
@@ -1 +1 @@
--skip-racket
-lbootstrap -lgc_prelude --skip-racket
3 changes: 3 additions & 0 deletions test/gc/gc_prelude.bli
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Run the garbage collector after preloaded files like bootstrap.bli, so that we
# know our tests are starting off with a clean heap.
:gc_collect;
2 changes: 1 addition & 1 deletion test/prelude/.options
Original file line number Diff line number Diff line change
@@ -1 +1 @@
--skip-racket -O
-lbootstrap --skip-racket -O
2 changes: 2 additions & 0 deletions test/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ typedef struct {
float perf_factor;
const char **preimport;
size_t num_preimport;
const char **preload;
size_t num_preload;
BlimpOptions blimp_options;
FILE *perf_report;
} Options;
Expand Down
Loading

0 comments on commit 98b8c2d

Please sign in to comment.