Skip to content

Instantly share code, notes, and snippets.

@zacharyvoase
Created January 8, 2010 01:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zacharyvoase/271792 to your computer and use it in GitHub Desktop.
Save zacharyvoase/271792 to your computer and use it in GitHub Desktop.
Tweed — Parser Combinators for Ruby
# -*- coding: utf-8 -*-
class Proc
alias :[] :call
end
module Tweed
class Parser < ::Proc
alias :parse :call
def bind(&parser_maker)
Parser.new do |input|
self.parse(input).
map { |value, string| parser_maker.call(value).parse(string) }.
reduce(:concat)
end
end
def +(other_parser)
self.bind do |value1|
other_parser.bind do |value2|
Parsers::RESULT[[value1, value2]]
end
end
end
def &(other_parser)
Parser.new do |input|
(self.parse(input) || []) + other_parser.parse(input)
end
end
end
# A collection of common parsers.
module Parsers
# RESULT :: value -> ( input -> [[value, input]] )
RESULT = lambda { |value| Parser.new { |input| [[value, input]] } }
# ZERO :: input -> []
ZERO = Parser.new { |input| [] } # Fail unconditionally.
# ITEM :: input -> [] if input.empty?
# [[first, rest]] otherwise.
ITEM = Parser.new do |input|
case input
when :empty? then []
else [[input[0, 1], input[1..input.length] || ""]]
end
end
# SAT :: (value -> Boolean) -> Parser
SAT = lambda do |&pred|
ITEM.bind { |value| pred.call(value) ? RESULT[value] : ZERO }
end
# CHAR :: char -> Parser
CHAR = lambda { |char| SAT.call(&char.method(:==)) }
DIGIT = SAT.call(&"0".."9".method(:include))
LOWER = SAT.call(&"a".."z".method(:include))
UPPER = SAT.call(&"A".."Z".method(:include))
LETTER = LOWER & UPPER
ALPHANUM = LETTER & DIGIT
WORD = begin
LETTER.bind do |first|
WORD.bind do |rest|
RESULT[first + rest]
end
end & RESULT[""]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment