Class: Rjq::Compiler

Inherits:
Object
  • Object
show all
Defined in:
lib/rjq/compiler.rb

Constant Summary collapse

OPTION_KEYS =
%i[allow_comments library_path max_filter_depth module_resolver source_path].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Compiler

Returns a new instance of Compiler.



357
358
359
# File 'lib/rjq/compiler.rb', line 357

def initialize(opts = {})
  @opts = self.class.normalize_options(opts)
end

Class Method Details

.normalize_options(opts) ⇒ Object



313
314
315
316
317
318
# File 'lib/rjq/compiler.rb', line 313

def normalize_options(opts)
  validate_options!(opts)
  normalized = opts.dup
  normalized[:library_path] = normalized[:library_path].dup.freeze if normalized[:library_path]
  normalized.freeze
end

.options_from(opts) ⇒ Object



295
296
297
# File 'lib/rjq/compiler.rb', line 295

def options_from(opts)
  opts.slice(*OPTION_KEYS)
end

.validate_options!(opts) ⇒ Object

Raises:

  • (ArgumentError)


299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/rjq/compiler.rb', line 299

def validate_options!(opts)
  raise ArgumentError, 'options must be a Hash' unless opts.is_a?(Hash)

  unknown = opts.keys - OPTION_KEYS
  raise ArgumentError, "unknown compiler option: #{unknown.first.inspect}" unless unknown.empty?

  validate_boolean!(opts, :allow_comments)
  validate_optional_string!(opts, :source_path)
  validate_positive_integer!(opts, :max_filter_depth)
  validate_library_path!(opts[:library_path]) if opts.key?(:library_path)
  validate_module_resolver!(opts[:module_resolver]) if opts.key?(:module_resolver)
  opts
end

Instance Method Details

#compile(filter_string) ⇒ Object



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/rjq/compiler.rb', line 361

def compile(filter_string)
  max_filter_depth = @opts.fetch(:max_filter_depth, Parser::DEFAULT_MAX_FILTER_DEPTH)
  source_path = @opts[:source_path] || '<top-level>'
  parsed = Parser.new(filter_string, allow_comments: @opts.fetch(:allow_comments, true),
                                     source_name: source_path, max_filter_depth: max_filter_depth).parse
  resolver = @opts[:module_resolver] || default_module_resolver
  allow_comments = @opts.fetch(:allow_comments, true)
  loaded = ModuleLoader.new(resolver, allow_comments: allow_comments,
                                      max_filter_depth: max_filter_depth).load(parsed,
                                                                               source_path: @opts[:source_path])
  ast = loaded.program
  program = BytecodeCompiler.new(allow_comments: allow_comments, max_filter_depth: max_filter_depth).compile(
    ast,
    module_metadata: loaded.,
    module_variables: loaded.variables
  )
  SemanticAnalyzer.new(program).validate!
  program.finalize!
  CompiledProgram.new(ast, program: program)
rescue SystemStackError
  raise CompileError, "filter nesting exceeds safe parser/compiler depth in #{source_path}"
end