Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions docs/parser.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ used instead and this parameter is ignored.
A boolean whose default value is `False`. If set to `True` parser will call
actions that will build the [parse tree](./parse_trees.md).

## call_actions_during_tree_build

By default, this parameter is set to `False`. If set to `True`, parser will call
actions during the parse tree [parse tree](./parse_trees.md) building process.
The return value of each action will be discarded, since they directly affect
the parse tree building process.

!!! note

Use this parameter with a special care when GLR is used, since actions will
be called even on trees that can't be completed (unsuccessful parses).

## prefer_shifts

By default set to `True` for LR parser and to `False` for GLR parser. In case
Expand Down
14 changes: 8 additions & 6 deletions parglare/glr.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ class GLRParser(Parser):
def __init__(self, grammar, start_production=1, actions=None,
layout_actions=None, debug=False, debug_trace=False,
debug_colors=False, debug_layout=False, ws='\n\t ',
build_tree=False, tables=LALR, layout=False, position=False,
prefer_shifts=None, prefer_shifts_over_empty=None,
error_recovery=False, dynamic_filter=None,
custom_lexical_disambiguation=None):
build_tree=False, call_actions_during_tree_build=False,
tables=LALR, layout=False, position=False, prefer_shifts=None,
prefer_shifts_over_empty=None, error_recovery=False,
dynamic_filter=None, custom_lexical_disambiguation=None):

# The default for GLR is not to use any strategy preferring shifts
# over reduce thus investigating all possibilitites.
Expand All @@ -50,8 +50,10 @@ def __init__(self, grammar, start_production=1, actions=None,
actions=actions, layout_actions=layout_actions,
debug=debug, debug_trace=debug_trace,
debug_colors=debug_colors, debug_layout=debug_layout, ws=ws,
build_tree=build_tree, tables=tables, layout=layout,
position=position, prefer_shifts=prefer_shifts,
build_tree=build_tree,
call_actions_during_tree_build=call_actions_during_tree_build,
tables=tables, layout=layout, position=position,
prefer_shifts=prefer_shifts,
prefer_shifts_over_empty=prefer_shifts_over_empty,
error_recovery=error_recovery, dynamic_filter=dynamic_filter,
custom_lexical_disambiguation=custom_lexical_disambiguation)
Expand Down
28 changes: 22 additions & 6 deletions parglare/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ class Parser(object):
def __init__(self, grammar, start_production=1, actions=None,
layout_actions=None, debug=False, debug_trace=False,
debug_colors=False, debug_layout=False, ws='\n\r\t ',
build_tree=False, tables=LALR, layout=False, position=False,
prefer_shifts=True, prefer_shifts_over_empty=True,
error_recovery=False, dynamic_filter=None,
custom_lexical_disambiguation=None):
build_tree=False, call_actions_during_tree_build=False,
tables=LALR, layout=False, position=False, prefer_shifts=True,
prefer_shifts_over_empty=True, error_recovery=False,
dynamic_filter=None, custom_lexical_disambiguation=None):
self.grammar = grammar
self.start_production = start_production
EMPTY.action = pass_none
Expand Down Expand Up @@ -62,6 +62,7 @@ def __init__(self, grammar, start_production=1, actions=None,
self.debug_layout = debug_layout

self.build_tree = build_tree
self.call_actions_during_tree_build = call_actions_during_tree_build

self.error_recovery = error_recovery
self.dynamic_filter = dynamic_filter
Expand Down Expand Up @@ -545,12 +546,21 @@ def _call_shift_action(self, symbol, matched_str, context):
Calls registered shift action for the given grammar symbol.
"""
debug = self.debug
sem_action = symbol.action

if self.build_tree:
# call action for building tree node if tree building is enabled
if debug:
h_print("Building terminal node",
"'{}'.".format(symbol.name), level=2)

# If both build_tree and call_actions_during_build are set to
# True, semantic actions will be call but their result will be
# discarded. For more info check following issue:
# https://github.com/igordejanovic/parglare/issues/44
if self.call_actions_during_tree_build and sem_action:
sem_action(context, matched_str)

return treebuild_shift_action(context, matched_str)

sem_action = symbol.action
Expand Down Expand Up @@ -578,13 +588,17 @@ def _call_reduce_action(self, production, subresults, context):
"""
debug = self.debug
result = None
bt_result = None

if self.build_tree:
# call action for building tree node if enabled.
if debug:
h_print("Building non-terminal node",
"'{}'.".format(production.symbol.name), level=2)
return treebuild_reduce_action(context, nodes=subresults)

bt_result = treebuild_reduce_action(context, nodes=subresults)
if not self.call_actions_during_tree_build:
return bt_result

sem_action = production.symbol.action
if sem_action:
Expand Down Expand Up @@ -628,7 +642,9 @@ def _call_reduce_action(self, production, subresults, context):
"type:{} value:{}"
.format(type(result), repr(result)), level=1)

return result
# If build_tree is set to True, discard the result of the semantic
# action, and return the result of treebuild_reduce_action.
return bt_result if bt_result is not None else result

def _lexical_disambiguation(self, tokens):
"""
Expand Down
42 changes: 42 additions & 0 deletions tests/func/test_build_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pytest # noqa
from parglare import Grammar, Parser


def test_call_actions_during_tree_build():
grammar = """
Program: "begin" MoveCommand* "end";
MoveCommand: "move" Direction;
Direction: "up" | "down" | "left" | "right";
"""

g = Grammar.from_string(grammar)

code = """
begin
move left
move left
move up
move down
end
"""

left_moves = []

def left_dir_collector(_, nodes):
"""Finds all 'left' moves and adds them into a list."""
term = nodes[0]
if term.value == "left":
left_moves.append(term)

parser = Parser(g, build_tree=True,
actions={"Direction": left_dir_collector})
parser.parse(code)

# call_actions_during_tree_build is False by default, so left_dir_collector
# will not be called.
assert len(left_moves) == 0

parser.call_actions_during_tree_build = True
parser.parse(code)

assert len(left_moves) == 2