require_relative 'calc_ast_nodes' # The purpose of a CalcASTBuilder is to build piece by piece an AST # (Abstract Syntax Tree) from a sequence of input tokens and # visit events produced by walking over a GFGParsing object. # Uses the Builder GoF pattern. # The Builder pattern creates a complex object # (say, a parse tree) from simpler objects (terminal and non-terminal # nodes) and using a step by step approach. class CalcASTBuilder < Rley::Parser::ParseTreeBuilder Terminal2NodeClass = { # Plus sign character is ambiguous. It can represent an operator # or a positive value '+' => { 'add_operator[0]' => CalcAddNode, 'sign[0]' => PTree::TerminalNode }, # Minus sign character is ambiguous. It can represent an operator # or a negative value '-' => { 'add_operator[1]' => CalcSubtractNode, 'sign[1]' => CalcNegateNode }, '*' => CalcMultiplyNode, '/' => CalcDivideNode, 'number' => CalcNumberNode } protected def return_first_child(_range, _tokens, theChildren) return theChildren[0] end def return_second_child(_range, _tokens, theChildren) return theChildren[1] end def return_last_child(_range, _tokens, theChildren) return theChildren[-1] end # Overriding method. # Create a parse tree object with given # node as root node. def create_tree(aRootNode) return Rley::PTree::ParseTree.new(aRootNode) end # Overriding method. # Factory method for creating a node object for the given # input token. # @param aTerminal [Terminal] Terminal symbol associated with the token # @param aTokenPosition [Integer] Position of token in the input stream # @param aToken [Token] The input token def new_leaf_node(aProduction, aTerminal, aTokenPosition, aToken) klass = Terminal2NodeClass.fetch(aTerminal.name, CalcTerminalNode) klass = klass[aProduction.name] if klass.is_a?(Hash) # Lexical ambiguity return klass.new(aToken, aTokenPosition) end # Method to override. # Factory method for creating a parent node object. # @param aProduction [Production] Production rule # @param aRange [Range] Range of tokens matched by the rule # @param theTokens [Array] The input tokens # @param theChildren [Array] Children nodes (one per rhs symbol) def new_parent_node(aProduction, aRange, theTokens, theChildren) node = case aProduction.name when 'expression[0]' # rule 'expression' => %w[sign simple_expression] reduce_expression_0(aProduction, aRange, theTokens, theChildren) # when /value\[\d\]/ # return_first_child(aRange, theTokens, theChildren) # when 'object[0]' # reduce_object_0(aProduction, aRange, theTokens, theChildren) # when 'object[1]' # reduce_object_1(aRange, theTokens, theChildren) # when 'member-list[0]' # reduce_member_list_0(aRange, theTokens, theChildren) # when 'member-list[1]' # reduce_member_list_1(aProduction, aRange, theTokens, theChildren) # when 'member[0]' # reduce_member_0(aProduction, aRange, theTokens, theChildren) # when 'array[0]' # reduce_array_0(aProduction, aRange, theTokens, theChildren) # when 'array[1]' # reduce_array_1(aRange, theTokens, theChildren) # when 'array-items[0]' # reduce_array_items_0(aRange, theTokens, theChildren) when 'sign[0]' # rule 'sign' => 'PLUS' return_first_child(aRange, theTokens, theChildren) when 'sign[1]' # rule 'sign' => 'MINUS' return_first_child(aRange, theTokens, theChildren) when 'sign[2]' #rule 'sign' => [] reduce_sign_2(aProduction, aRange, theTokens, theChildren) else raise StandardError, "Don't know production #{aProduction.name}" end return node end # rule 'expression' => %w[sign simple_expression] def reduce_expression_0(aProduction, aRange, theTokens, theChildren) sign = theChildren[0] # Check type of sign node = if sign && sign.kind_of?(CalcNegateNode) sign.members << theChildren.last else theChildren.last end return node end # rule 'sign' => [] def reduce_sign_2(aProduction, aRange, theTokens, theChildren) return nil # TODO; check whether this make sense end =begin second_child = theChildren[1] second_child.symbol = aProduction.lhs return second_child end # rule 'object' => %w[begin-object end-object] def reduce_object_1(aRange, theTokens, theChildren) return CalcObjectNode.new(aProduction.lhs) end # rule 'member-list' => %w[member-list value-separator member] def reduce_member_list_0(aRange, theTokens, theChildren) node = theChildren[0] node.members << theChildren.last return node end # rule 'member-list' => 'member' def reduce_member_list_1(aProduction, aRange, theTokens, theChildren) node = CalcObjectNode.new(aProduction.lhs) node.members << theChildren[0] return node end # rule 'member' => %w[string name-separator value] def reduce_member_0(aProduction, aRange, theTokens, theChildren) return CalcPair.new(theChildren[0], theChildren[2], aProduction.lhs) end # rule 'object' => %w[begin-object member-list end-object] def reduce_array_0(aProduction, aRange, theTokens, theChildren) second_child = theChildren[1] second_child.symbol = aProduction.lhs return second_child end # rule 'array' => %w[begin-array end-array] def reduce_array_1(aRange, theTokens, theChildren) return CalcArrayNode.new end # rule 'array-items' => %w[array-items value-separator value] def reduce_array_items_0(aRange, theTokens, theChildren) node = theChildren[0] node.children << theChildren[2] return node end # rule 'array-items' => %w[value] def reduce_array_items_1(aProduction, aRange, theTokens, theChildren) node = CalcArrayNode.new(aProduction.lhs) node.children << theChildren[0] return node end =end end # class