Module: Lox::AST

Defined in:
lib/loxby/helpers/ast.rb

Overview

Interface: “‘ruby

Lox::AST.define_ast(
  "ASTBaseClass",
  {
    :ast_type => [
      [:field_one_type, :field_one_name],
      [:field_two_type, :field_two_name]
    ],
    :other_ast_type => [[:field_type, :field_name]]
  }
)

“‘

This call to ‘#define_ast` generates `Lox::AST::ASTBaseClass`, as well as `::AstType` and `::OtherAstType` descending from and scoped uner it. Generated classes follow the Visitor pattern: `::AstType` generates with `#accept(visitor)` which calls `visitor.visit_ast_type(self)`.

Class Method Summary collapse

Class Method Details

.define_ast(base_name, types) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/loxby/helpers/ast.rb', line 38

def define_ast(base_name, types)
  base_class = Class.new
  base_class.include Visitable
  # Define boilerplate visitor methods
  Visitor.define_types(base_name, types.keys)
  # Dynamically create subclasses for each AST type
  types.each do |class_name, fields|
    define_type(base_class, base_name, class_name, fields)
  end

  define_class base_name.to_camel_case, base_class
end

.define_class(class_name, klass, base_class: Lox::AST) ⇒ Object



72
73
74
# File 'lib/loxby/helpers/ast.rb', line 72

def define_class(class_name, klass, base_class: Lox::AST)
  base_class.const_set class_name, klass
end

.define_type(base_class, base_class_name, subtype_name, fields) ⇒ Object

rubocop:disable Metrics/MethodLength,Metrics/AbcSize



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/loxby/helpers/ast.rb', line 51

def define_type(base_class, base_class_name, subtype_name, fields) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
  subtype = Class.new(base_class)
  parameters = fields.map { _1[1].to_s }

  subtype.class_eval <<~RUBY, __FILE__, __LINE__ + 1
    include Visitable # Visitor pattern
    #{parameters.empty? ? '' : 'attr_reader '}#{parameters.map { ":#{_1}" }.join(', ')}
    def initialize(#{parameters.map { "#{_1}:" }.join(', ')})
      #{parameters.map { "@#{_1}" }.join(', ')}#{parameters.empty? ? '' : ' = '}#{parameters.join ', '}
    end

    # This function was dynamically generated for visitor pattern.
    # Expects visitors to define `#visit_#{subtype_name}_#{base_class_name}`.
    def accept(visitor)
      visitor.visit_#{subtype_name}_#{base_class_name}(self)
    end
  RUBY

  define_class(subtype_name.to_camel_case, subtype, base_class:)
end