Class: EBNF::Base

Inherits:
Object
  • Object
show all
Includes:
BNF, LL1, Parser
Defined in:
lib/ebnf/base.rb

Instance Attribute Summary collapse

Attributes included from LL1

#branch, #first, #follow, #start, #terminals

Instance Method Summary collapse

Methods included from Parser

#alt, #diff, #eachRule, #ebnf, #postfix, #primary, #ruleParts, #seq, #terminal

Methods included from LL1

#build_tables, #first_follow, #outputTable

Methods included from BNF

#make_bnf

Constructor Details

#initialize(input, options = {}) ⇒ Base

Parse the string or file input generating an abstract syntax tree in S-Expressions (similar to SPARQL SSE)

Parameters:

  • input (#read, #to_s)
  • options (Hash{Symbol => Object}) (defaults to: {})

Options Hash (options):

  • :debug (Boolean, Array)

    Output debug information to an array or STDOUT.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/ebnf/base.rb', line 122

def initialize(input, options = {})
  @options = options
  @lineno, @depth, @errors = 1, 0, []
  terminal = false
  @ast = []

  input = input.respond_to?(:read) ? input.read : input.to_s
  scanner = StringScanner.new(input)

  eachRule(scanner) do |r|
    debug("rule string") {r.inspect}
    case r
    when /^@terminals/
      # Switch mode to parsing terminals
      terminal = true
    when /^@pass\s*(.*)$/m
      rule = depth {ruleParts("[0] " + r)}
      rule.kind = :pass
      rule.orig = r
      @ast << rule
    else
      rule = depth {ruleParts(r)}

      rule.kind = :terminal if terminal # Override after we've parsed @terminals
      rule.orig = r
      @ast << rule
    end
  end
end

Instance Attribute Details

#astArray<Rule> (readonly)

Abstract syntax tree from parse

Returns:



108
109
110
# File 'lib/ebnf/base.rb', line 108

def ast
  @ast
end

#errorsArray<String>

Grammar errors, or errors found genering parse tables

Returns:

  • (Array<String>)


113
114
115
# File 'lib/ebnf/base.rb', line 113

def errors
  @errors
end

Instance Method Details

#debug(node, message) ⇒ Object #debug(message) ⇒ Object

Progress output when debugging

Overloads:

  • #debug(node, message) ⇒ Object

    Parameters:

    • node (String)

      relative location in input

    • message (String)

      (“”)

  • #debug(message) ⇒ Object

    Parameters:

    • message (String)

      (“”)

Yield Returns:

  • (String)

    added to message



255
256
257
258
259
260
261
262
263
264
# File 'lib/ebnf/base.rb', line 255

def debug(*args)
  return unless @options[:debug]
  options = args.last.is_a?(Hash) ? args.pop : {}
  depth = options[:depth] || @depth
  args << yield if block_given?
  message = "#{args.join(': ')}"
  str = "[#{@lineno}]#{' ' * depth}#{message}"
  @options[:debug] << str if @options[:debug].is_a?(Array)
  $stderr.puts(str) if @options[:debug] == true
end

#depthObject



213
214
215
216
217
218
# File 'lib/ebnf/base.rb', line 213

def depth
  @depth += 1
  ret = yield
  @depth -= 1
  ret
end

#dupObject



173
174
175
176
177
# File 'lib/ebnf/base.rb', line 173

def dup
  new_obj = super
  new_obj.instance_variable_set(:@ast, @ast.dup)
  new_obj
end

#each(kind) {|rule| ... } ⇒ Object

Iterate over each rule or terminal

Parameters:

  • kind (:termina, :rule)

Yields:

  • rule

Yield Parameters:



156
157
158
# File 'lib/ebnf/base.rb', line 156

def each(kind, &block)
  ast.each {|r| block.call(r) if r.kind == kind}
end

#error(*args) ⇒ Object

Error output



233
234
235
236
237
238
239
240
241
242
# File 'lib/ebnf/base.rb', line 233

def error(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  depth = options[:depth] || @depth
  args << yield if block_given?
  message = "#{args.join(': ')}"
  @errors << message
  str = "[#{@lineno}]#{' ' * depth}#{message}"
  @options[:debug] << str if @options[:debug].is_a?(Array)
  $stderr.puts(str)
end

#find_rule(sym) ⇒ Rule

Find a rule given a symbol

Parameters:

  • sym (Symbol)

Returns:



183
184
185
# File 'lib/ebnf/base.rb', line 183

def find_rule(sym)
  (@find ||= {})[sym] ||= ast.detect {|r| r.sym == sym}
end

#progress(*args) ⇒ Object

Progress output, less than debugging



221
222
223
224
225
226
227
228
229
230
# File 'lib/ebnf/base.rb', line 221

def progress(*args)
  return unless @options[:progress] || @options[:debug]
  options = args.last.is_a?(Hash) ? args.pop : {}
  depth = options[:depth] || @depth
  args << yield if block_given?
  message = "#{args.join(': ')}"
  str = "[#{@lineno}]#{' ' * depth}#{message}"
  @options[:debug] << str if @options[:debug].is_a?(Array)
  $stderr.puts(str)
end

#to_sObject



171
# File 'lib/ebnf/base.rb', line 171

def to_s; to_sxp; end

#to_sxpString

Write out parsed syntax string as an S-Expression

Returns:

  • (String)


163
164
165
166
167
168
169
170
# File 'lib/ebnf/base.rb', line 163

def to_sxp
  begin
    require 'sxp'
    SXP::Generator.string(ast.sort)
  rescue LoadError
    ast.sort_by{|r| r.num.to_f}.to_sxp
  end
end

#to_ttl(prefix, ns) ⇒ String

Write out syntax tree as Turtle

Parameters:

  • prefix (String)

    for language

  • ns (String)

    URI for language

Returns:

  • (String)


192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/ebnf/base.rb', line 192

def to_ttl(prefix, ns)
  unless ast.empty?
    [
      "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.",
      "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.",
      "@prefix #{prefix}: <#{ns}>.",
      "@prefix : <#{ns}>.",
      "@prefix re: <http://www.w3.org/2000/10/swap/grammar/regex#>.",
      "@prefix g: <http://www.w3.org/2000/10/swap/grammar/ebnf#>.",
      "",
      ":language rdfs:isDefinedBy <>; g:start :#{ast.first.id}.",
      "",
    ]
  end.join("\n") +

  ast.sort.
    select {|a| [:rule, :terminal].include?(a.kind)}.
    map(&:to_ttl).
    join("\n")
end