module BaseParsers

Public Instance Methods

anyChar(chars) click to toggle source
# File lib/base_parsers.rb, line 151
def anyChar(chars)
  Parser.new do |input|
    first_char = input[0]
    result     = ParserResult.fail(input)

    chars.each do |char|
      if first_char == char
        result = ParserResult.ok(matched: char, remaining: input[1..-1])
        break
      end
    end

    result
  end
end
anyCharBut(chars) click to toggle source
# File lib/base_parsers.rb, line 167
def anyCharBut(chars)
  Parser.new do |input|
    first_char = input[0]
    result     = ParserResult.ok(matched: first_char, remaining: input[1..-1])
    
    if !(input == "" || input.nil?)
      chars.each do |char|
        if first_char == char
          result = ParserResult.fail(input)
          break
        end
      end
    else
      result = ParserResult.fail(input) 
    end

    result
  end
end
anyLetter() click to toggle source
# File lib/base_parsers.rb, line 44
def anyLetter
  anyChar(('a'..'z').to_a + ('A'..'Z').to_a)
end
anyNumber() click to toggle source
# File lib/base_parsers.rb, line 48
def anyNumber
  anyChar ('0'..'9').to_a
end
empty() click to toggle source
# File lib/base_parsers.rb, line 14
def empty
  Parser.new do |input|
    ParserResult.ok(matched: "", remaining: input)
  end
end
eof() click to toggle source
# File lib/base_parsers.rb, line 4
def eof
  Parser.new do |input|
    if input == "" || input.nil?
      ParserResult.ok(matched: "", remaining: input)
    else
      ParserResult.fail(input)
    end
  end
end
exactly(n, &wrapper) click to toggle source
# File lib/base_parsers.rb, line 187
def exactly(n, &wrapper)
  parser = wrapper.call
  Parser.new do |input|
    matched   = ""
    remaining = input
    success   = true

    n.to_i.times do
      result = parser.run(remaining)
      if result.fail?
        success = false
        break
      end
      matched   = matched + result.matched
      remaining = result.remaining
    end

    if success
      ParserResult.ok(matched: matched, remaining: remaining)
    else
      ParserResult.fail(input)
    end
  end
end
many0(&wrapper) click to toggle source
# File lib/base_parsers.rb, line 74
def many0(&wrapper)
  Parser.new do |input|
    if input.nil? || input == ""
      ParserResult.ok(matched: "", remaining: input)
    else
      many1(&wrapper).run(input)
    end
  end
end
many1(&wrapper) click to toggle source
# File lib/base_parsers.rb, line 52
def many1(&wrapper)
  Parser.new do |input|
    matched   = ""
    output    = []
    remaining = input
    parser    = wrapper.call

    loop do
      result = parser.run(remaining)
      break if remaining.nil? || result.fail?
      matched   = matched + result.matched
      output += result.output unless (result.output.length == 1 and result.output[0].length == 1)
      remaining = result.remaining
    end

    #puts "output: " + output.to_s unless output.empty?
    output = nil if output.empty?

    ParserResult.new(!matched.empty?, remaining, matched, output)
  end
end
match(rule, between:) click to toggle source
# File lib/base_parsers.rb, line 129
def match(rule, between:)
  first, last = between
  Parser.new do |input|
    lhs = first.run(input)
    if lhs.ok?
      middle = rule.run(lhs.remaining)
      if middle.ok?
        rhs = last.run(middle.remaining)
        if rhs.ok?
          rhs
        else
          ParserResult.fail(input)
        end
      else
        ParserResult.fail(input)
      end
    else
      ParserResult.fail(input)
    end
  end
end
one(char) click to toggle source
# File lib/base_parsers.rb, line 24
def one(char)
  Parser.new do |input|
    if input[0] == char
      ParserResult.ok(matched: char, remaining: input[1..-1])
    else
      ParserResult.fail(input)
    end
  end
end
regex(re) click to toggle source
# File lib/base_parsers.rb, line 123
def regex(re)
  Parser.new do |input|
    test regex: re, with: input
  end
end
satisfy(&wrapper) click to toggle source

This is just an alias of lambda in the DSL. See specs for more on this.

# File lib/base_parsers.rb, line 117
def satisfy(&wrapper)
  Parser.new do |input|
    wrapper.call(input)
  end
end
seq(*args) click to toggle source
# File lib/base_parsers.rb, line 84
def seq(*args)
  callback = args[-1]
  parsers  = args[0..(args.length - 2)]

  raise "Seq expects at least a parser and a callback." if callback.nil? || parsers.empty?

  Parser.new do |input|
    remaining = input
    matched   = ""
    fail = nil

    new_args = parsers.map do |parser|
      result = parser.run(remaining)
      unless result.ok?
        fail = ParserResult.fail(input)
        break
      end
      remaining = result.remaining
      matched += result.matched
      result.output
    end

    if fail.nil?
      output = callback.call(*new_args)
      ParserResult.ok(output, matched: matched, remaining: remaining)
    else
      fail
    end
  end
end
str(string) click to toggle source
# File lib/base_parsers.rb, line 34
def str(string)
  Parser.new do |input|
    if input.start_with?(string)
      ParserResult.ok(matched: string, remaining: input[string.length..-1])
    else
      ParserResult.fail(input)
    end
  end
end
whitespace() click to toggle source
# File lib/base_parsers.rb, line 20
def whitespace
  many0 { anyChar([" ", "\b", "\f", "\n", "\r", "\t"]) }
end

Private Instance Methods

test(regex:, with:) click to toggle source

Test against a simple regex, no groups. It would be possible to pass a callback to the regex, in order to work with groups. #MAYBE #TODO

# File lib/base_parsers.rb, line 216
def test(regex:, with:)
  match = regex.match(with)
  return ParserResult.fail(with) if match.nil?
  matched = match[0]
  ParserResult.ok(matched: matched, remaining: with[matched.length..-1])
end