require_relative 'compiler/block.rb' require_relative 'compiler/comment.rb' require_relative 'compiler/control.rb' require_relative 'compiler/define.rb' require_relative 'compiler/eval.rb' require_relative 'compiler/filter.rb' require_relative 'compiler/node.rb' require_relative 'compiler/require.rb' require_relative 'compiler/root.rb' require_relative 'compiler/text.rb' # @Opulent module Opulent # @Compiler class Compiler Buffer = :_buffer # All node Objects (Array) must follow the next convention in order # to make parsing faster # # [:node_type, :value, :attributes, :children, :indent] # # @param path [String] Current file path needed for require nodes # def initialize(settings = {}) # Setup convention accessors @type = 0 @value = 1 @options = 2 @children = 3 @indent = 4 # Set current compiled file @path = File.dirname settings.delete :path # Extract definitions for require directives @definitions = settings.delete :definitions # Create the HTML Entities encoder/decoder @entities = HTMLEntities.new # Get special node types from the settings @multi_node = Settings::MultiNode @inline_node = Settings::InlineNode # Quick accessor for default yield constant @default_yield = Settings::DefaultYield # The node stack is needed to keep track of all the visited nodes # from the current branch level @node_stack = [] # The sibling stack keeps track of the sibling count from the current # branch level being generated @sibling_stack = [] # Whenever we enter a definition compilation, add the provided blocks to # the current block stack. When exiting a definition, remove blocks. @block_stack = [] end # Compile input nodes, replace them with their definitions and # # @param root_node [Array] Root node containing all document nodes # @param context [Context] Context holding environment variables # def compile(root_node, context) # Compiler generated code @code = "" @generator = "" # Set initial parent, from which we start generating code @sibling_stack << root_node[@children].size # Start building up the code from the root node root_node[@children].each do |child| root child, 0, context end return @code end # Escape a given input value using htmlentities # # @param value [String] String to be escaped # def escape(value) @entities.encode value end # Remove the last newline from the current code buffer # def remove_trailing_newline @code.chop! if @code[-1] == "\n" end # Indent all lines of the input text using give indentation # # @param text [String] Input text to be indented # @param indent [String] Indentation string to be appended # def indent_lines text, indent text ||= "" text.lines.inject("") do |result, line| result += indent + line end end # Give an explicit error report where an unexpected sequence of tokens # appears and give indications on how to solve it # # @param context [Symbol] Context name in which the error happens # @param data [Array] Additional error information # def error(context, *data) message = case context when :enumerable "The provided each structure iteration input \"#{data[0]}\" is not Enumerable." when :binding data[0] = data[0].to_s.match(/\`(.*)\'/) data[0] = data[0][1] if data[0] "Found an undefined local variable or method \"#{data[0]}\" at \"#{data[1]}\"." when :variable_name data[0] = data[0].to_s.match(/\`(.*)\'/)[1] "Found an undefined local variable or method \"#{data[0]}\" in locals." when :extension "The extension sequence \"#{data[0]}\" is not a valid attributes extension. " + "Please use a Hash to extend attributes." when :filter_registered "The \"#{data[0]}\" filter could not be recognized by Opulent." when :filter_load "The gem required for the \"#{data[0]}\" filter is not installed. You can install it by running:\n\n#{data[1]}" when :require "The required file #{data[0]} does not exist or an incorrect path has been specified." end # Reconstruct lines to display where errors occur fail "\n\nOpulent " + Logger.red("[Runtime Error]") + "\n---\n" + "A runtime error has been encountered when building the compiled node tree.\n" + "#{message}\n\n\n" end end end