Class: Ibex::Frontend::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/frontend/parser.rb

Overview

Public grammar parser backed by Ibex's generated LR frontend.

Constant Summary collapse

DEFAULT_MAX_DIAGNOSTICS =

: Integer

20

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, file: "(grammar)", mode: :default) ⇒ Parser

Returns a new instance of Parser.

Raises:

  • (ArgumentError)


23
24
25
26
27
28
29
30
31
32
33
# File 'lib/ibex/frontend/parser.rb', line 23

def initialize(source, file: "(grammar)", mode: :default)
  raise ArgumentError, "mode must be :default or :extended" unless %i[default extended].include?(mode)

  @mode = mode
  @lexical_diagnostics = [] #: Array[Diagnostic]
  @parse_error_message = nil
  @source_document_error_message = nil
  tokens = source.is_a?(Array) ? source : tokenize_source(source, file)
  @tokens = tokens
  @implementation = GeneratedParser.new(tokens, mode: mode)
end

Instance Attribute Details

#implementationObject (readonly)

: GeneratedParser



11
12
13
# File 'lib/ibex/frontend/parser.rb', line 11

def implementation
  @implementation
end

Instance Method Details

#parseObject

Raises:



36
37
38
39
40
41
# File 'lib/ibex/frontend/parser.rb', line 36

def parse
  node = parse_node
  return node if node.is_a?(AST::Root)

  raise Ibex::Error, "#{node.loc}: fragment input requires Parser#parse_fragment"
end

#parse_documentObject

Parse the grammar and return its lossless source model.



54
55
56
57
58
59
# File 'lib/ibex/frontend/parser.rb', line 54

def parse_document
  parsed_document = @parsed_document
  return parsed_document if parsed_document && parsed_document.ast.is_a?(AST::Root)

  @parsed_document = source_document!.with_ast(parse)
end

#parse_fragmentObject

Parse an explicit fragment without resolving its includes.

Raises:



45
46
47
48
49
50
# File 'lib/ibex/frontend/parser.rb', line 45

def parse_fragment
  node = parse_node
  return node if node.is_a?(AST::Fragment)

  raise Ibex::Error, "#{node.loc}: root grammar input cannot be parsed as a fragment"
end

#parse_source_documentObject

Parse either a root grammar or an explicit fragment and return its lossless source model.



63
64
65
66
67
68
# File 'lib/ibex/frontend/parser.rb', line 63

def parse_source_document
  parsed_document = @parsed_document
  return parsed_document if parsed_document

  @parsed_document = source_document!.with_ast(parse_node)
end

#parse_with_diagnostics(max_diagnostics: DEFAULT_MAX_DIAGNOSTICS) ⇒ Object

Parse with conservative boundary recovery and collect multiple errors.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/ibex/frontend/parser.rb', line 72

def parse_with_diagnostics(max_diagnostics: DEFAULT_MAX_DIAGNOSTICS)
  unless max_diagnostics.is_a?(Integer) && max_diagnostics.positive?
    raise ArgumentError, "max_diagnostics must be a positive integer"
  end

  lexical_diagnostics = lexical_diagnostics_for(max_diagnostics)
  if @source_document.nil? && @lexical_diagnostics.any?
    return ParseResult.new(diagnostics: lexical_diagnostics, ast: nil, document: nil)
  end

  recovery = DiagnosticRecovery.new(
    @tokens, mode: @mode, max_diagnostics: max_diagnostics
  )
  node, syntax_diagnostics = recovery.parse
  node = enrich_rule_documentation(node) if node
  ast, syntax_diagnostics = root_diagnostic_result(node, syntax_diagnostics)
  diagnostics = merge_diagnostics(lexical_diagnostics, syntax_diagnostics, max_diagnostics)
  document = if diagnostics.empty? && ast
               @source_document&.with_ast(ast)
             else
               @source_document
             end
  ParseResult.new(diagnostics: diagnostics, ast: ast, document: document)
end