class Aspen::Compiler
Attributes
environment[R]
root[R]
Public Class Methods
new(root, environment = {})
click to toggle source
@param environment [Hash] @todo Make {environment} an Aspen::Environment
# File lib/aspen/compiler.rb, line 17 def initialize(root, environment = {}) @root = root @environment = environment @adapter = environment.fetch(:adapter, :cypher).to_sym # @todo FIXME: This is too much responsibility for the compiler. # This should be delegated to an object and the later calls # just messages to that object. @slug_counters = Hash.new { 1 } # @todo Move this into an Environment object—it should be set there. # and here, just run environment.validate unless Aspen.available_formats.include?(@adapter) raise Aspen::ArgumentError, <<~MSG The adapter, also known as the output format, must be one of: #{Aspen.available_formats.join(', ')}. What Aspen received was #{@adapter}. MSG end end
render(root, environment = {})
click to toggle source
@param environment [Hash] @todo Make {environment} an Aspen::Environment
# File lib/aspen/compiler.rb, line 11 def self.render(root, environment = {}) new(root, environment).render end
Public Instance Methods
discourse()
click to toggle source
# File lib/aspen/compiler.rb, line 42 def discourse @discourse ||= Discourse.from_hash(environment) end
render()
click to toggle source
# File lib/aspen/compiler.rb, line 38 def render visit(root) end
visit(node)
click to toggle source
# File lib/aspen/compiler.rb, line 46 def visit(node) short_name = node.class.to_s.split('::').last.downcase method_name = "visit_#{short_name}" send(method_name, node) end
visit_attribute(node)
click to toggle source
# File lib/aspen/compiler.rb, line 189 def visit_attribute(node) content = visit(node.content) type = visit(node.type) content.send(type.converter) end
visit_comment(node)
click to toggle source
@returns [Symbol] :comment This acts as a signal so other methods know to reject comments.
# File lib/aspen/compiler.rb, line 205 def visit_comment(node) :comment end
visit_content(node)
click to toggle source
# File lib/aspen/compiler.rb, line 199 def visit_content(node) node.content end
visit_customstatement(node)
click to toggle source
@todo Get the labels back into here. Labelreg? typereg?
This is doing too much. Can't we have typed attributes come from the Grammar?
# File lib/aspen/compiler.rb, line 76 def visit_customstatement(node) statement = visit(node.content) matcher = discourse.grammar.matcher_for(statement) results = matcher.captures(statement) template = matcher.template typereg = matcher.typereg labelreg = matcher.labelreg nodes = [] typed_results = results.inject({}) do |hash, elem| key, value = elem typed_value = case typereg[key] when :integer then value.to_i when :float then value.to_f when :numeric then value.match?(/\./) ? value.to_f : value.to_i when :string then "\"#{value}\"" when :node then # FIXME: This only handles short form. # I think we were allowing grouped and Cypher form to fill # in custom statement templates. # TODO: Add some object to nodes array. node = visit( Aspen::AST::Nodes::Node.new( attribute: value, label: labelreg[key] ) ) nodes << node node end hash[key] = typed_value hash end formatted_results = typed_results.inject({}) do |hash, elem| key, value = elem f_value = value.is_a?(Aspen::Node) ? value.nickname_node : value hash[key] = f_value # TODO: Trying to insert a p_id as well as p to be used in JSON identifiers. # if value.is_a?(Aspen::Node) # hash["#{key}_id"] = value.nickname # end # puts "TYPED VALS: #{hash.inspect}" hash end slugs = template.scan(/{{{?(?<full>uniq_(?<name>\w+))}}}?/).uniq usable_results = if slugs.any? counts = slugs.map do |full, short| [full, "#{short}_#{@slug_counters[full]}"] end.to_h context = results.merge(counts) custom_statement = CustomStatement.new( nodes: nodes, cypher: Mustache.render(template.strip, formatted_results.merge(counts)) ) slugs.each do |full, _| @slug_counters[full] = @slug_counters[full] + 1 end custom_statement else CustomStatement.new( nodes: nodes, cypher: Mustache.render(template.strip, formatted_results) ) end end
visit_edge(node)
click to toggle source
# File lib/aspen/compiler.rb, line 163 def visit_edge(node) content = visit(node.content) unless discourse.allows_edge?(content) raise Aspen::Error, """ Your narrative includes an edge called '#{content}', but only #{discourse.allowed_edges} are allowed. """ end Aspen::Edge.new( content, mutual: discourse.mutual?(visit(node.content)) ) end
visit_label(node)
click to toggle source
# File lib/aspen/compiler.rb, line 177 def visit_label(node) content = visit(node.content) label = Maybe(content).value_or(discourse.default_label) unless discourse.allows_label?(label) raise Aspen::CompileError, """ Your narrative includes a node with label '#{label}', but only #{discourse.allowed_labels} are allowed. """ end label end
visit_narrative(node)
click to toggle source
# File lib/aspen/compiler.rb, line 52 def visit_narrative(node) # Instead of letting comments be `nil` and using `#compact` # to silently remove them, possibly hiding errors, we "compile" # comments as `:comment` and filter them explicitly statements = node.statements.map do |statement| # This will visit both regular and custom statements. visit(statement) end.reject { |elem| elem == :comment } renderer_klass = Kernel.const_get("Aspen::Renderers::#{@adapter.to_s.downcase.capitalize}Renderer") renderer_klass.new(statements, environment).render end
visit_node(node)
click to toggle source
# File lib/aspen/compiler.rb, line 148 def visit_node(node) # Get the label, falling back to the default label. label = visit(node.label) # Get the attribute name, falling back to the default attribute name. attribute_name = Maybe(nil).value_or(discourse.default_attr_name(label)) typed_attribute_value = visit(node.attribute) nickname = typed_attribute_value.to_s.downcase Aspen::Node.new( label: label, attributes: { attribute_name => typed_attribute_value } ) end
visit_statement(node)
click to toggle source
# File lib/aspen/compiler.rb, line 65 def visit_statement(node) Statement.new( origin: visit(node.origin), edge: visit(node.edge), target: visit(node.target) ) end
visit_type(node)
click to toggle source
# File lib/aspen/compiler.rb, line 195 def visit_type(node) node end