class Highway::Compiler::Build::Builder

This class is responsible for manifest generation based on the semantic tree. This is the third and final phase of the compiler.

Public Class Methods

new(interface:) click to toggle source

Initialize an instance.

@param interface [Highway::Interface] The interface.

# File lib/highway/compiler/build/builder.rb, line 23
def initialize(interface:)
  @interface = interface
end

Public Instance Methods

build(sema_tree:, preset:) click to toggle source

Build the manifst.

The builder resolves variables and steps in the semantic tree for given preset and builds concrete invocations.

The builder produces a manifest that is later executed by runner.

@param sema_tree [Highway::Compiler::Analyze::Tree::Root] The semantic tree.

return [Highway::Compiler::Build::Output::Manifest]

# File lib/highway/compiler/build/builder.rb, line 37
def build(sema_tree:, preset:)

  manifest = Build::Output::Manifest.new()

  manifest.preset = preset

  variables = resolve_variables(sema_tree: sema_tree, preset: preset)
  steps = resolve_steps(sema_tree: sema_tree, preset: preset)

  build_invocations(stages: sema_tree.stages, variables: variables, steps: steps, manifest: manifest)

  manifest

end

Private Instance Methods

build_invocations(stages:, variables:, steps:, manifest:) click to toggle source
# File lib/highway/compiler/build/builder.rb, line 94
def build_invocations(stages:, variables:, steps:, manifest:)
  steps.each_with_index do |step, index|
    stage = stages.find { |stage| stage.name == step.stage }
    parameters = build_value(value: step.parameters, variables: variables)
    keypath = [step.stage, step.preset, step.name]
    manifest.add_invocation(index: index + 1, step_class: step.step_class, parameters: parameters, policy: stage.policy, keypath: keypath)
  end
end
build_value(value:, variables:) click to toggle source
# File lib/highway/compiler/build/builder.rb, line 103
def build_value(value:, variables:)
  if value.is_a?(Analyze::Tree::Values::Primitive)
    Analyze::Tree::Values::Primitive.new(
      build_value_segments(segments: value.segments, variables: variables)
    )
  elsif value.is_a?(Analyze::Tree::Values::Array)
    Analyze::Tree::Values::Array.new(
      value.children.map { |value|
        build_value(value: value, variables: variables)
      }
    )
  elsif value.is_a?(Analyze::Tree::Values::Hash)
    Analyze::Tree::Values::Hash.new(
      Utilities::hash_map(value.children) { |key, value|
        [key, build_value(value: value, variables: variables)]
      }
    )
  end
end
build_value_segments(segments:, variables:) click to toggle source
# File lib/highway/compiler/build/builder.rb, line 123
def build_value_segments(segments:, variables:)

  resolved = segments.flat_map { |segment|
    if segment.is_a?(Analyze::Tree::Segments::Variable) && segment.scope == :static
      variable = variables.find { |variable| variable.name == segment.name }
      build_value_segments(segments: variable.value.segments, variables: variables)
    else
      [segment]
    end
  }

  reduced = resolved.reduce([]) { |memo, segment|
    if last = memo.pop()
      if last.is_a?(Analyze::Tree::Segments::Text) && segment.is_a?(Analyze::Tree::Segments::Text)
        memo + [Analyze::Tree::Segments::Text.new(last.value + segment.value)]
      else
        memo + [last, segment]
      end
    else
      [segment]
    end
  }

  reduced

end
resolve_steps(sema_tree:, preset:) click to toggle source
# File lib/highway/compiler/build/builder.rb, line 68
def resolve_steps(sema_tree:, preset:)

  stages = sema_tree.stages.sort_by { |stage| stage.index }

  stages.flat_map { |stage|

    exact_steps = sema_tree.steps.select { |step|
      step.preset == preset && step.stage == stage.name
    }

    default_steps = sema_tree.steps.select { |step|
      step.preset == sema_tree.default_preset && step.stage == stage.name
    }

    exact_steps = exact_steps.sort_by { |step| step.index }
    default_steps = default_steps.sort_by { |step| step.index }

    exact_steps = nil if exact_steps.empty?
    default_steps = nil if default_steps.empty?

    exact_steps || default_steps || Array.new()

  }

end
resolve_variables(sema_tree:, preset:) click to toggle source
# File lib/highway/compiler/build/builder.rb, line 54
def resolve_variables(sema_tree:, preset:)

  exact_variables = sema_tree.variables.select { |variable|
    variable.preset == preset
  }

  default_variables = sema_tree.variables.select { |variable|
    variable.preset == sema_tree.default_preset && !exact_variables.any? { |exact_variable| exact_variable.name == variable.name }
  }

  default_variables + exact_variables

end