Parse state object class. State objects keep track of where in the parse tree new nodes should be appended, and manages inclusion.
The stack of templates that have been loaded for this state; for loop detection.
The stack of containers
The parse options in effect for this parse state
The template object for this parser state
Create a new State object
# File lib/inversion/parser.rb, line 170
def initialize( template, options={} )
@template = template
@options = options.dup
@tree = []
@node_stack = [ @tree ]
@include_stack = []
end
Append operator: add nodes to the correct part of the parse tree.
# File lib/inversion/parser.rb, line 207
def <<( node )
self.log.debug "Appending %p" % [ node ]
node.before_appending( self )
self.node_stack.last << node
if node.is_container?
# Containers get pushed onto the stack so they get appended to
self.node_stack.push( node )
else
# Container nodes' #after_appending gets called in #pop
node.after_appending( self )
end
return self
rescue Inversion::ParseError => err
raise err, "%s at %s" % [ err.message, node.location ]
end
Append another Array of nodes onto this state’s node tree.
# File lib/inversion/parser.rb, line 228
def append_tree( newtree )
newtree.each do |node|
self.node_stack.last << node
end
end
Clear any parsed nodes from the state, leaving the options and #include_stack intact.
# File lib/inversion/parser.rb, line 275
def clear_nodes
@tree = []
@node_stack = [ @tree ]
end
Return the node that is currently being appended to, or nil if
there aren’t any opened container nodes.
# File lib/inversion/parser.rb, line 269
def current_node
return self.node_stack.last
end
Copy constructor – duplicate inner structures.
# File lib/inversion/parser.rb, line 180
def initialize_copy( original )
@template = original.template
@options = original.options.dup
@tree = @tree.map( &:dup )
@node_stack = [ @tree ]
@include_stack = original.include_stack.dup
end
Check to see if all open tags have been closed.
# File lib/inversion/parser.rb, line 246
def is_well_formed?
return self.node_stack.length == 1
end
Load a subtemplate from the specified path, checking for
recursive-dependency.
# File lib/inversion/parser.rb, line 282
def load_subtemplate( path )
if self.include_stack.include?( path )
stack_desc = ( self.include_stack + [path] ).join( ' --> ' )
msg = "Recursive load of %p detected: from %s" % [ path, stack_desc ]
self.log.error( msg )
raise Inversion::StackError, msg
end
self.log.debug "Include stack is: %p" % [ self.include_stack ]
substate = self.dup
substate.clear_nodes
substate.include_stack.push( path )
return Inversion::Template.load( path, substate, self.options )
end
Pop one level off of the node stack and return it.
# File lib/inversion/parser.rb, line 253
def pop
closed_node = self.node_stack.pop
# If there's nothing on the node stack, we've popped the top-level
# Array, which means there wasn't an opening container.
raise Inversion::ParseError, "unbalanced end: no open tag" if
self.node_stack.empty?
closed_node.after_appending( self )
return closed_node
end
Returns the tree if it’s well formed.
# File lib/inversion/parser.rb, line 236
def tree
unless self.is_well_formed?
raise Inversion::ParseError, "Unclosed container tag: %s, from %s" %
[ self.node_stack.last.tagname, self.node_stack.last.location ]
end
return @tree
end