Skip to content

Commit

Permalink
Emit a warning when the cached parser is not used.
Browse files Browse the repository at this point in the history
This change checks to see if the cached parser was discarded due to a
mismatch between the cached parser and the grammar specified in
module_ir.py, and, if so, emits a warning that the cached parser was not
used, along with informational messages on the nature of the mismatch.

Adjusted the "warning" color from magenta to yellow.  (This is the first
warning in Emboss, so no magenta messages would have ever been emitted.)

Adjusted the "note" color from "bright black" (dark grey) to "white"
(light grey), becaused at least some terminals display "bright black"
as just black.
  • Loading branch information
reventlov committed Oct 11, 2024
1 parent 78901f9 commit 37a2bb6
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 20 deletions.
30 changes: 30 additions & 0 deletions compiler/front_end/emboss_front_end.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from compiler.front_end import glue
from compiler.front_end import module_ir
from compiler.front_end import parser
from compiler.util import error
from compiler.util import ir_data_utils

Expand Down Expand Up @@ -151,6 +152,34 @@ def _find_and_read(file_name):
return _find_and_read


def _warn_if_cached_parser_is_mismatched(color_output):
cached_parser_mismatch = parser.module_parser_cache_mismatch()
extra_production_notes = [
error.note("<internal>", None, f"New production {prod}")
for prod in cached_parser_mismatch[1]
]
missing_production_notes = [
error.note("<internal>", None, f"Missing production {prod}")
for prod in cached_parser_mismatch[0]
]
if extra_production_notes or missing_production_notes:
_show_errors(
[
[
error.warn(
"<internal>",
None,
"Cached parser does not match actual grammar; using newly-generated parser.",
)
]
+ extra_production_notes
+ missing_production_notes
],
None,
color_output,
)


def parse_and_log_errors(input_file, import_dirs, color_output, stop_before_step=None):
"""Fully parses an .emb and logs any errors.
Expand All @@ -162,6 +191,7 @@ def parse_and_log_errors(input_file, import_dirs, color_output, stop_before_step
Returns:
(ir, debug_info, errors)
"""
_warn_if_cached_parser_is_mismatched(color_output)
ir, debug_info, errors = glue.parse_emboss_file(
input_file,
_find_in_dirs_and_read(import_dirs),
Expand Down
51 changes: 41 additions & 10 deletions compiler/front_end/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,70 @@

"""Routines to load a shift-reduce parser for the module_ir module."""

import collections

from compiler.front_end.generated import cached_parser
from compiler.front_end import lr1
from compiler.front_end import make_parser
from compiler.front_end import module_ir
from compiler.util import parser_types
from compiler.util import simple_memoizer

ParserAndIsCached = collections.namedtuple(
"ParserAndIsCached",
[
"parser",
"cache_mismatch",
],
)


@simple_memoizer.memoize
def _load_module_parser():
module_parser = cached_parser.module_parser()
if module_parser.productions == set(module_ir.PRODUCTIONS) | {
module_ir_productions = set(module_ir.PRODUCTIONS) | {
parser_types.Production(lr1.START_PRIME, (module_ir.START_SYMBOL,))
}:
return module_parser
return make_parser.build_module_parser()
}
if module_parser.productions == module_ir_productions:
return ParserAndIsCached(module_parser, (set(), set()))
return ParserAndIsCached(
make_parser.build_module_parser(),
(
module_parser.productions - module_ir_productions,
module_ir_productions - module_parser.productions,
),
)


@simple_memoizer.memoize
def _load_expression_parser():
expression_parser = cached_parser.expression_parser()
if expression_parser.productions == set(module_ir.PRODUCTIONS) | {
module_ir_productions = set(module_ir.PRODUCTIONS) | {
parser_types.Production(lr1.START_PRIME, (module_ir.EXPRESSION_START_SYMBOL,))
}:
return expression_parser
return make_parser.build_expression_parser()
}
if expression_parser.productions == module_ir_productions:
return ParserAndIsCached(
expression_parser,
(set(), set()),
)
return ParserAndIsCached(
make_parser.build_expression_parser(),
(
expression_parser.productions - module_ir_productions,
module_ir_productions - expression_parser.productions,
),
)


def module_parser_cache_mismatch():
return _load_module_parser().cache_mismatch


def parse_module(tokens):
"""Parses the provided Emboss token list into an Emboss module parse tree."""
return _load_module_parser().parse(tokens)
return _load_module_parser().parser.parse(tokens)


def parse_expression(tokens):
"""Parses the provided Emboss token list into an expression parse tree."""
return _load_expression_parser().parse(tokens)
return _load_expression_parser().parser.parse(tokens)
4 changes: 2 additions & 2 deletions compiler/util/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ def format(self, source_code):
# messages.
severity_colors = {
ERROR: (BRIGHT_RED, BOLD),
WARNING: (BRIGHT_MAGENTA, BOLD),
NOTE: (BRIGHT_BLACK, WHITE),
WARNING: (BRIGHT_YELLOW, BOLD),
NOTE: (WHITE, WHITE),
}

result = []
Expand Down
15 changes: 7 additions & 8 deletions compiler/util/error_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,10 @@ def test_multiline_error(self):
(error.BRIGHT_RED, "error: "), # Severity
(error.BOLD, "Bad thing\n"), # Message
(error.BOLD, "foo.emb:3:4: "), # Location, line 2
(error.BRIGHT_BLACK, "note: "), # "Note" severity, line 2
(error.WHITE, "note: "), # "Note" severity, line 2
(error.WHITE, "Some explanation\n"), # Message, line 2
(error.BOLD, "foo.emb:3:4: "), # Location, line 3
(error.BRIGHT_BLACK, "note: "), # "Note" severity, line 3
(error.WHITE, "note: "), # "Note" severity, line 3
(error.WHITE, "More explanation"), # Message, line 3
],
sourceless_format,
Expand All @@ -223,10 +223,10 @@ def test_multiline_error(self):
(error.BRIGHT_RED, "error: "), # Severity
(error.BOLD, "Bad thing\n"), # Message
(error.BOLD, "foo.emb:3:4: "), # Location, line 2
(error.BRIGHT_BLACK, "note: "), # "Note" severity, line 2
(error.WHITE, "note: "), # "Note" severity, line 2
(error.WHITE, "Some explanation\n"), # Message, line 2
(error.BOLD, "foo.emb:3:4: "), # Location, line 3
(error.BRIGHT_BLACK, "note: "), # "Note" severity, line 3
(error.WHITE, "note: "), # "Note" severity, line 3
(error.WHITE, "More explanation\n"), # Message, line 3
(error.WHITE, "abcdefghijklm\n"), # Source snippet
(error.BRIGHT_GREEN, " ^^"), # Column indicator
Expand All @@ -252,7 +252,7 @@ def test_warn(self):
self.assertEqual(
[
(error.BOLD, "foo.emb:3:4: "), # Location
(error.BRIGHT_MAGENTA, "warning: "), # Severity
(error.BRIGHT_YELLOW, "warning: "), # Severity
(error.BOLD, "Not good thing\n"), # Message
(error.WHITE, "abcdefghijklm\n"), # Source snippet
(error.BRIGHT_GREEN, " ^^"), # Column indicator
Expand All @@ -278,7 +278,7 @@ def test_note(self):
self.assertEqual(
[
(error.BOLD, "foo.emb:3:4: "), # Location
(error.BRIGHT_BLACK, "note: "), # Severity
(error.WHITE, "note: "), # Severity
(error.WHITE, "OK thing\n"), # Message
(error.WHITE, "abcdefghijklm\n"), # Source snippet
(error.BRIGHT_GREEN, " ^^"), # Column indicator
Expand Down Expand Up @@ -457,13 +457,12 @@ def test_format_errors(self):
bold = error.BOLD
reset = error.RESET
white = error.WHITE
bright_black = error.BRIGHT_BLACK
bright_green = error.BRIGHT_GREEN
self.assertEqual(
bold
+ "foo.emb:3:4: "
+ reset
+ bright_black
+ white
+ "note: "
+ reset
+ white
Expand Down

0 comments on commit 37a2bb6

Please sign in to comment.