diff --git a/src/parse.py b/src/parse.py index d4f7388..396f63b 100644 --- a/src/parse.py +++ b/src/parse.py @@ -6,6 +6,26 @@ from src.token import TokenStream +# Words that can't be used as references +# This should include keywords and literals +reserved_names = [ + "Done", + "Set", + "To", + "EndSet", + "If", + "Then", + "Else", + "EndIf", + "StartNote", + "EndNote", + "StartText", + "EndText", + "True", + "False", +] + + # Tasks that happen during parsing class ParseTask(enum.Enum): TEST_TASK = enum.auto() # pragma: no mutate @@ -50,6 +70,7 @@ FOUND_STARTNOTE = enum.auto() # pragma: no mutate NOT_BOOL = enum.auto() # pragma: no mutate FOUND_ENDNOTE = enum.auto() # pragma: no mutate + RESERVED_NAME = enum.auto() # pragma: no mutate # Exception thrown when a parse error is encountered @@ -163,6 +184,8 @@ def parse_reference(self, stream, parent_context): context = ParseContext(ParseTask.PARSE_REFERENCE, stream.peek(), parent_context) t = read_token(stream, None, context) + if t.value in reserved_names: + raise ParseErrorException(ParseError.RESERVED_NAME, t, None, context) return Reference(t.value) diff --git a/tests/parse/test_reference.py b/tests/parse/test_reference.py index 4153049..b81d96d 100644 --- a/tests/parse/test_reference.py +++ b/tests/parse/test_reference.py @@ -6,13 +6,13 @@ from src.ast_types import Reference from src.parse import ParseContext, ParseError, ParseErrorException, ParseTask, Parser from tests.parse.templates import template_parse_valid, template_parse_invalid -from tests.test_token import draw_token_random +from tests.test_token import draw_token_known, draw_token_unknown # Draws tokens to make a reference @composite def draw_token_reference_valid(draw): - token = draw(draw_token_random()) + token = draw(draw_token_unknown()) return ([token], Reference(token.value)) @@ -25,6 +25,17 @@ pass +# Tests parsing a reference with a reserved name errors +# We expect the following behaviour: +# - Error if a keyword or literal is encountered +@template_parse_invalid(Parser().parse_reference) +def test_parse_note_invalid_extrastartnote(draw, parent_context): + token = draw(draw_token_known()) + context = ParseContext(ParseTask.PARSE_REFERENCE, token, parent_context) + error = ParseErrorException(ParseError.RESERVED_NAME, token, None, context) + return ([token], error) + + # Tests parsing of empty references # We expect the following behaviour: # - Error if there isn't a token diff --git a/tests/test_token.py b/tests/test_token.py index dd3d3c0..01c4b70 100644 --- a/tests/test_token.py +++ b/tests/test_token.py @@ -66,16 +66,6 @@ return Token(value, location) -# Draws an unknown token -@composite -def draw_token_unknown(draw): - location = draw(draw_token_location()) - value = draw(text(min_size=1)) - assume(value not in literals) - assume(value not in keywords) - return Token(value, location) - - # Draws a bool token @composite def draw_token_bool(draw): @@ -95,19 +85,35 @@ return Token(value, location) -# All strategies used to generate tokens -all_strategies = [ - draw_token_unknown(), +# All strategies used to generate known tokens +known_strategies = [ draw_token_bool(), draw_token_keyword(), ] -# Draws a token and possibly add garbage +# Draws a random token +@composite +def draw_token_known(draw): + token = draw(one_of(known_strategies)) + return token + + +# Draws an unknown token +@composite +def draw_token_unknown(draw): + location = draw(draw_token_location()) + value = draw(text(min_size=1)) + assume(value not in literals) + assume(value not in keywords) + return Token(value, location) + + +# Draws a known token and possibly add garbage # This is to ensure that tokens must completely match a value @composite def draw_token_garbled(draw): - token = draw(one_of(all_strategies)) + token = draw(draw_token_unknown()) value = token.value if draw(booleans()): value = draw(text(min_size=1)) + value @@ -116,11 +122,17 @@ return Token(value, token.location) +# All strategies used to generate random tokens +random_strategies = known_strategies + [ + draw_token_unknown(), + draw_token_garbled(), +] + + # Draws a random token @composite def draw_token_random(draw): - strategies = all_strategies + [draw_token_garbled()] - token = draw(one_of(strategies)) + token = draw(one_of(random_strategies)) return token