class TraceryNode

Attributes

children[RW]
depth[RW]
errors[RW]
finishedText[RW]
grammar[RW]

Public Class Methods

new(parent, childIndex, settings) click to toggle source
# File lib/tracery.rb, line 181
def initialize(parent, childIndex, settings)
    @errors = []
    @children = []

    if(settings[:raw].nil?) then
        @errors << "Empty input for node"
        settings[:raw] = ""
    end
    
    # If the root node of an expansion, it will have the grammar passed as the 'parent'
    # set the grammar from the 'parent', and set all other values for a root node
    if(parent.is_a? Grammar)
        @grammar = parent
        @parent = nil
        @depth = 0
        @childIndex = 0
    else
        @grammar = parent.grammar
        @parent = parent
        @depth = parent.depth + 1
        @childIndex = childIndex
    end

    @raw = settings[:raw]
    @type = settings[:type]
    @isExpanded = false
    
    @errors << "No grammar specified for this node #{self}" if (@grammar.nil?)
end

Public Instance Methods

allErrors() click to toggle source
# File lib/tracery.rb, line 331
def allErrors
    child_errors = @children.inject([]){|all, child| all.concat(child.allErrors)}
    return child_errors.concat(@errors) 
end
clearEscapeCharacters() click to toggle source
# File lib/tracery.rb, line 336
def clearEscapeCharacters
    @finishedText = @finishedText.gsub(/\\\\/, "DOUBLEBACKSLASH").gsub(/\\/, "").gsub(/DOUBLEBACKSLASH/, "\\")
end
expand(preventRecursion = false) click to toggle source

Expand this rule (possibly creating children)

# File lib/tracery.rb, line 240
def expand(preventRecursion = false)
    if(@isExpanded) then
        @errors << "Already expanded #{self}"
        return
    end

    @isExpanded = true
    #this is no longer used
    @expansionErrors = []
            
    # Types of nodes
    # -1: raw, needs parsing
    #  0: Plaintext
    #  1: Tag ("#symbol.mod.mod2.mod3#" or "#[pushTarget:pushRule]symbol.mod#")
    #  2: Action ("[pushTarget:pushRule], [pushTarget:POP]", more in the future)
    
    case(@type)
        when -1 then
            #raw rule
            expandChildren(@raw, preventRecursion)
        when 0 then
            #plaintext, do nothing but copy text into finished text
            @finishedText = @raw
        when 1 then
            #tag - Parse to find any actions, and figure out what the symbol is
            @preactions = []
            @postactions = []
            parsed = parseTag(@raw)
            @symbol = parsed[:symbol]
            @modifiers = parsed[:modifiers]

            # Create all the preactions from the raw syntax
            @preactions = parsed[:preactions].map{|preaction|
                NodeAction.new(self, preaction[:raw])
            }

            # @postactions = parsed[:preactions].map{|postaction|
            #     NodeAction.new(self, postaction.raw)
            # }
            
            # Make undo actions for all preactions (pops for each push)
            @postactions = @preactions.
                            select{|preaction| preaction.type == 0 }.
                            map{|preaction| preaction.createUndo() }
            
            @preactions.each { |preaction| preaction.activate }
            
            @finishedText = @raw

            # Expand (passing the node, this allows tracking of recursion depth)
            selectedRule = @grammar.selectRule(@symbol, self, @errors)

            expandChildren(selectedRule, preventRecursion)
            
            # Apply modifiers
            # TODO: Update parse function to not trigger on hashtags within parenthesis within tags,
            # so that modifier parameters can contain tags "#story.replace(#protagonist#, #newCharacter#)#"
            @modifiers.each{|modName|
                modParams = [];
                if (modName.include?("(")) then
                    #match something like `modifier(param, param)`, capture name and params separately
                    match = /([^\(]+)\(([^)]+)\)/.match(modName)
                    if(!match.nil?) then
                        modParams = match.captures[1].split(",")
                        modName = match.captures[0]
                    end
                end

                mod = @grammar.modifiers[modName]

                # Missing modifier?
                if(mod.nil?)
                    @errors << "Missing modifier #{modName}"
                    @finishedText += "((.#{modName}))"
                else
                    @finishedText = mod.call(@finishedText, modParams)
                end
            }
            # perform post-actions
            @postactions.each{|postaction| postaction.activate()}
        when 2 then
            # Just a bare action? Expand it!
            @action = NodeAction.new(self, @raw)
            @action.activate()
            
            # No visible text for an action
            # TODO: some visible text for if there is a failure to perform the action?
            @finishedText = ""
    end
end
expandChildren(childRule, preventRecursion) click to toggle source
# File lib/tracery.rb, line 215
def expandChildren(childRule, preventRecursion)
    @finishedText = ""
    @childRule = childRule
    
    if(!@childRule.nil?)
        parsed = parse(childRule)
        sections = parsed[:sections]

        @errors.concat(parsed[:errors])

        sections.each_with_index do |section, i|
            child = TraceryNode.new(self, i, section)
            if(!preventRecursion)
                child.expand(preventRecursion)
            end
            @finishedText += child.finishedText
            @children << child
        end
    else
        # In normal operation, this shouldn't ever happen
        @errors << "No child rule provided, can't expand children"
    end
end
to_s() click to toggle source
# File lib/tracery.rb, line 211
def to_s
    "Node('#{@raw}' #{@type} d:#{@depth})"
end