Class: ParserCombinator

Inherits:
Object
  • Object
show all
Defined in:
lib/parser_combinator.rb,
lib/parser_combinator/version.rb,
lib/parser_combinator/string_parser.rb

Direct Known Subclasses

StringParser

Defined Under Namespace

Classes: Fail, Item, Items, Ok, ParsedSeq, StandardFailStatus, StringParser

Constant Summary collapse

ParserCombinatorError =
Class.new(RuntimeError)
VERSION =
"0.0.4"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(status_handler = proc{nil}, name = nil, &proc) ⇒ ParserCombinator

Returns a new instance of ParserCombinator.



137
138
139
140
141
# File 'lib/parser_combinator.rb', line 137

def initialize(status_handler=proc{nil}, name=nil, &proc)
  @status_handler = status_handler
  @parser_name = name
  @parser_proc = proc
end

Instance Attribute Details

#parser_nameObject (readonly)

Returns the value of attribute parser_name.



136
137
138
# File 'lib/parser_combinator.rb', line 136

def parser_name
  @parser_name
end

#parser_procObject (readonly)

Returns the value of attribute parser_proc.



136
137
138
# File 'lib/parser_combinator.rb', line 136

def parser_proc
  @parser_proc
end

#status_handlerObject (readonly)

Returns the value of attribute status_handler.



136
137
138
# File 'lib/parser_combinator.rb', line 136

def status_handler
  @status_handler
end

Class Method Details

.binopl(parser, op_proc_parser) ⇒ Object



328
329
330
331
332
333
334
335
336
337
338
# File 'lib/parser_combinator.rb', line 328

def self.binopl(parser, op_proc_parser)
  rest = proc{|a|
    op_proc_parser >> proc{|f|
      parser >> proc{|b|
        rest.call(f.call(a, b))
      }} | ok(a)
  }
  parser >> proc{|a|
    rest.call(a)
  }
end

.binopl_fail(parser, op_proc_parser) ⇒ Object



340
341
342
343
344
345
346
347
348
349
350
# File 'lib/parser_combinator.rb', line 340

def self.binopl_fail(parser, op_proc_parser)
  rest = proc{|a|
    op_proc_parser >> proc{|f|
      parser >> proc{|b|
        rest.call(f.call(a, b))
      }} ^ ok(a)
  }
  parser >> proc{|a|
    rest.call(a)
  }
end

.discardl(parser1, parser2) ⇒ Object



317
318
319
# File 'lib/parser_combinator.rb', line 317

def self.discardl(parser1, parser2)
  parser1 >> proc{parser2}
end

.discardr(parser1, parser2) ⇒ Object



321
322
323
324
325
326
# File 'lib/parser_combinator.rb', line 321

def self.discardr(parser1, parser2)
  parser1 >> proc{|x|
    parser2 >> proc{
      ok(x)
    }}
end

.either(parser1, parser2) ⇒ Object



229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/parser_combinator.rb', line 229

def self.either(parser1, parser2)
  new{|i|
    case result1 = parser1.parse(i)
    when Fail
      parser2.parse(i)
    when Ok
      result1
    else
      raise "error"
    end
  }
end

.either_fail(parser1, parser2) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/parser_combinator.rb', line 242

def self.either_fail(parser1, parser2)
  new{|i|
    case result1 = parser1.parse(i)
    when Fail
      if result1.status == nil
        parser2.parse(i)
      else
        result1
      end
    when Ok
      result1
    else
      raise "error"
    end
  }
end

.end_of_inputObject



263
264
265
# File 'lib/parser_combinator.rb', line 263

def self.end_of_input
  new{|i| i.size == 0 ? Ok.new(nil, i) : Fail.new}
end

.fail(status = nil) ⇒ Object



212
213
214
# File 'lib/parser_combinator.rb', line 212

def self.fail(status=nil)
  new{|i| Fail.new(status)}
end

.itemObject



259
260
261
# File 'lib/parser_combinator.rb', line 259

def self.item
  new{|i| i.size == 0 ? Fail.new : Ok.new(i.head, i.rest)}
end

.many(parser, separator_parser = ok(nil)) ⇒ Object



291
292
293
# File 'lib/parser_combinator.rb', line 291

def self.many(parser, separator_parser=ok(nil))
  many1(parser, separator_parser) | ok([])
end

.many1(parser, separator_parser = ok(nil)) ⇒ Object



295
296
297
298
299
300
# File 'lib/parser_combinator.rb', line 295

def self.many1(parser, separator_parser=ok(nil))
  parser >> proc{|x|
     many(separator_parser > parser) >> proc{|xs|
      ok([x] + xs)
    }}
end

.many1_fail(parser, separator_parser = ok(nil)) ⇒ Object



310
311
312
313
314
315
# File 'lib/parser_combinator.rb', line 310

def self.many1_fail(parser, separator_parser=ok(nil))
  parser >> proc{|x|
    many_fail(separator_parser > parser) >> proc{|xs|
      ok([x] + xs)
    }}
end

.many_fail(parser, separator_parser = ok(nil)) ⇒ Object



306
307
308
# File 'lib/parser_combinator.rb', line 306

def self.many_fail(parser, separator_parser=ok(nil))
  many1_fail(parser, separator_parser) ^ ok([])
end

.ok(object) ⇒ Object

CoreCombinator




208
209
210
# File 'lib/parser_combinator.rb', line 208

def self.ok(object)
  new{|i| Ok.new(object, i)}
end

.opt(parser) ⇒ Object



287
288
289
# File 'lib/parser_combinator.rb', line 287

def self.opt(parser)
  parser.map{|x| [x]} | ok([])
end

.opt_fail(parser) ⇒ Object



302
303
304
# File 'lib/parser_combinator.rb', line 302

def self.opt_fail(parser)
  parser.map{|x| [x]} ^ ok([])
end

.parser(name, &proc) ⇒ Object

Memorization DSL suport (for recursive grammer)




355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/parser_combinator.rb', line 355

def self.parser(name, &proc)
  @cache ||= {}
  spcls = class << self; self end
  spcls.send(:define_method, name) do |*args|
    key = [name, args]
    if @cache[key]
      return @cache[key]
    else
      status_handler = proc{ raise "this is never-called proc (status_handler)" }
      parser_proc = proc{ raise "this is never-called proc (parser_proc)" }
      @cache[key] = self.new(proc{|*args| status_handler.call(*args)}){|*args| parser_proc.call(*args)}
      generated_parser = proc.call(*args)
      parser_proc = generated_parser.parser_proc
      status_handler = generated_parser.status_handler
      return @cache[key]
    end
  end
end

.sat(&item_cond_proc) ⇒ Object



267
268
269
270
271
# File 'lib/parser_combinator.rb', line 267

def self.sat(&item_cond_proc)
  item >> proc{|i|
    item_cond_proc.call(i.item) ? ok(i) : fail
  }
end

.seq(*parsers) ⇒ Object

UtilCombinator




276
277
278
279
280
281
282
283
284
285
# File 'lib/parser_combinator.rb', line 276

def self.seq(*parsers)
  if parsers.size == 0
    ok(ParsedSeq.empty)
  else
    parsers.first >> proc{|x|
      seq(*parsers.drop(1)) >> proc{|xs|
        ok(xs.cons(x, parsers.first.parser_name))
      }}
  end
end

.so_then(parser, &continuation_proc) ⇒ Object



216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/parser_combinator.rb', line 216

def self.so_then(parser, &continuation_proc)
  new{|i|
    case result = parser.parse(i)
    when Fail
      result
    when Ok
      continuation_proc.call(result.parsed).parse(result.rest)
    else
      raise "error"
    end
  }
end

Instance Method Details

#<(other) ⇒ Object



201
202
203
# File 'lib/parser_combinator.rb', line 201

def <(other)
  self.class.discardr(self, other)
end

#>(other) ⇒ Object



197
198
199
# File 'lib/parser_combinator.rb', line 197

def >(other)
  self.class.discardl(self, other)
end

#>>(proc) ⇒ Object



185
186
187
# File 'lib/parser_combinator.rb', line 185

def >>(proc)
  self.class.so_then(self, &proc)
end

#^(other) ⇒ Object



193
194
195
# File 'lib/parser_combinator.rb', line 193

def ^(other)
  self.class.either_fail(self, other)
end

#map(&mapping) ⇒ Object



181
182
183
# File 'lib/parser_combinator.rb', line 181

def map(&mapping)
  self >> proc{|x| self.class.ok(mapping.call(x))}
end

#name(new_name) ⇒ Object



177
178
179
# File 'lib/parser_combinator.rb', line 177

def name(new_name)
  self.class.new(@status_handler, new_name, &parser_proc)
end

#onfail(message = nil, ifnotyet = false, &status_handler) ⇒ Object



159
160
161
162
163
164
165
166
167
168
# File 'lib/parser_combinator.rb', line 159

def onfail(message=nil, ifnotyet=false, &status_handler)
  raise "Only eihter message or fail_handler can be specified" if message && status_handler
  if message
    onfail{|items, status| status == nil ? StandardFailStatus.new(message, items) : status}
  elsif status_handler
    self.class.new(status_handler, @parser_name, &parser_proc)
  else
    self
  end
end

#onparse(&proc) ⇒ Object



170
171
172
173
174
175
# File 'lib/parser_combinator.rb', line 170

def onparse(&proc)
  self.class.new(@status_handler, @parser_name) do |*args|
    proc.call(*args)
    @parser_proc.call(*args)
  end
end

#parse(items) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/parser_combinator.rb', line 143

def parse(items)
  result = @parser_proc.call(items)
  case result
  when Fail
    if  @status_handler.call(items, result.status) != nil
      result.class.new(@status_handler.call(items, result.status))
    else
      result
    end
  when Ok
    result
  else
    raise "parsed object is #{result.inspect}/#{result.class}"
  end
end

#|(other) ⇒ Object



189
190
191
# File 'lib/parser_combinator.rb', line 189

def |(other)
  self.class.either(self, other)
end