Remove Hammer::Parser subclasses.
This commit is contained in:
parent
daeabb587d
commit
6e34e0c8fb
4 changed files with 38 additions and 99 deletions
|
|
@ -47,7 +47,7 @@ Also possible:
|
||||||
```ruby
|
```ruby
|
||||||
parser = Hammer::ParserBuilder.new
|
parser = Hammer::ParserBuilder.new
|
||||||
.token('Hello ')
|
.token('Hello ')
|
||||||
.choice(Hammer::Parser::Token.new('Mom'), Hammer::Parser::Token.new('Dad'))
|
.choice(Hammer::Parser.token('Mom'), Hammer::Parser.token('Dad'))
|
||||||
.token('!')
|
.token('!')
|
||||||
.build
|
.build
|
||||||
```
|
```
|
||||||
|
|
@ -56,7 +56,7 @@ More like hammer in C:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
h = Hammer::Parser
|
h = Hammer::Parser
|
||||||
parser = h.sequence(h.token('Hello'), h.choice(h.token('Mom'), h.token('Dad')), h.token('!'))
|
parser = h.sequence(h.token('Hello '), h.choice(h.token('Mom'), h.token('Dad')), h.token('!'))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Parsing
|
### Parsing
|
||||||
|
|
|
||||||
|
|
@ -32,16 +32,6 @@ if parser
|
||||||
p parser.parse 'blah'
|
p parser.parse 'blah'
|
||||||
end
|
end
|
||||||
|
|
||||||
parser = Hammer::Parser::Sequence.new(
|
|
||||||
Hammer::Parser::Token.new('Hello '),
|
|
||||||
Hammer::Parser::Choice.new(
|
|
||||||
Hammer::Parser::Token.new('Mom'),
|
|
||||||
Hammer::Parser::Token.new('Dad')
|
|
||||||
),
|
|
||||||
Hammer::Parser::Token.new('!')
|
|
||||||
)
|
|
||||||
p parser.parse 'Hello Mom!'
|
|
||||||
|
|
||||||
parser = Hammer::Parser.build {
|
parser = Hammer::Parser.build {
|
||||||
token 'Hello '
|
token 'Hello '
|
||||||
choice {
|
choice {
|
||||||
|
|
@ -54,12 +44,11 @@ p parser.parse 'Hello Mom!'
|
||||||
|
|
||||||
parser = Hammer::ParserBuilder.new
|
parser = Hammer::ParserBuilder.new
|
||||||
.token('Hello ')
|
.token('Hello ')
|
||||||
.choice(Hammer::Parser::Token.new('Mom'), Hammer::Parser::Token.new('Dad'))
|
.choice(Hammer::Parser.token('Mom'), Hammer::Parser.token('Dad'))
|
||||||
.token('!')
|
.token('!')
|
||||||
.build
|
.build
|
||||||
p parser.parse 'Hello Mom!'
|
p parser.parse 'Hello Mom!'
|
||||||
|
|
||||||
# not yet working
|
h = Hammer::Parser
|
||||||
#h = Hammer::Parser
|
parser = h.sequence(h.token('Hello '), h.choice(h.token('Mom'), h.token('Dad')), h.token('!'))
|
||||||
#parser = h.sequence(h.token('Hello'), h.choice(h.token('Mom'), h.token('Dad')), h.token('!'))
|
p parser.parse 'Hello Mom!'
|
||||||
#p parser.parse 'Hello Mom!'
|
|
||||||
|
|
|
||||||
|
|
@ -14,94 +14,50 @@ module Hammer
|
||||||
!result.null?
|
!result.null?
|
||||||
end
|
end
|
||||||
|
|
||||||
class Token < Parser
|
def self.token(string)
|
||||||
def initialize(string)
|
h_parser = Hammer::Internal.h_token(string, string.length)
|
||||||
@h_parser = Hammer::Internal.h_token(string, string.length)
|
|
||||||
end
|
parser = Hammer::Parser.new
|
||||||
|
parser.instance_variable_set :@h_parser, h_parser
|
||||||
|
return parser
|
||||||
end
|
end
|
||||||
|
|
||||||
class Ch < Parser
|
def self.ch(char)
|
||||||
def initialize(char)
|
|
||||||
# TODO: Really? Should probably accept Fixnum in appropriate range
|
# TODO: Really? Should probably accept Fixnum in appropriate range
|
||||||
# Also, char.ord gives unexptected results if you pass e.g. Japanese characters: '今'.ord == 20170; Hammer::Parser::Ch.new('今').parse(202.chr) == true
|
# Also, char.ord gives unexpected results if you pass e.g. Japanese characters: '今'.ord == 20170; Hammer::Parser::Ch.new('今').parse(202.chr) == true
|
||||||
# Not really unexpected though, since 20170 & 255 == 202.
|
# Not really unexpected though, since 20170 & 255 == 202.
|
||||||
# But probably it's better to use Ch for Fixnum in 0..255 only, and only Token for strings.
|
# But probably it's better to use Ch for Fixnum in 0..255 only, and only Token for strings.
|
||||||
raise ArgumentError, 'expecting a one-character String' unless char.is_a?(String) && char.length == 1
|
raise ArgumentError, 'expecting a one-character String' unless char.is_a?(String) && char.length == 1
|
||||||
@h_parser = Hammer::Internal.h_ch(char.ord)
|
h_parser = Hammer::Internal.h_ch(char.ord)
|
||||||
end
|
|
||||||
|
parser = Hammer::Parser.new
|
||||||
|
parser.instance_variable_set :@h_parser, h_parser
|
||||||
|
return parser
|
||||||
end
|
end
|
||||||
|
|
||||||
class Sequence < Parser
|
def self.sequence(*parsers)
|
||||||
def initialize(*parsers)
|
|
||||||
#args = []
|
|
||||||
#parsers.each { |p| args += [:pointer, p.h_parser] }
|
|
||||||
args = parsers.flat_map { |p| [:pointer, p.h_parser] }
|
args = parsers.flat_map { |p| [:pointer, p.h_parser] }
|
||||||
@h_parser = Hammer::Internal.h_sequence(*args, :pointer, nil)
|
h_parser = Hammer::Internal.h_sequence(*args, :pointer, nil)
|
||||||
@sub_parsers = parsers # store them so they don't get garbage-collected (probably not needed, though)
|
sub_parsers = parsers # store them so they don't get garbage-collected (probably not needed, though)
|
||||||
# TODO: Use (managed?) FFI struct instead of void pointers
|
# TODO: Use (managed?) FFI struct instead of void pointers
|
||||||
end
|
|
||||||
|
parser = Hammer::Parser.new
|
||||||
|
parser.instance_variable_set :@h_parser, h_parser
|
||||||
|
parser.instance_variable_set :@sub_parsers, sub_parsers
|
||||||
|
return parser
|
||||||
end
|
end
|
||||||
|
|
||||||
class Choice < Parser
|
def self.choice(*parsers)
|
||||||
def initialize(*parsers)
|
|
||||||
#args = []
|
|
||||||
#parsers.each { |p| args += [:pointer, p.h_parser] }
|
|
||||||
args = parsers.flat_map { |p| [:pointer, p.h_parser] }
|
args = parsers.flat_map { |p| [:pointer, p.h_parser] }
|
||||||
@h_parser = Hammer::Internal.h_choice(*args, :pointer, nil)
|
h_parser = Hammer::Internal.h_choice(*args, :pointer, nil)
|
||||||
@sub_parsers = parsers # store them so they don't get garbage-collected (probably not needed, though)
|
sub_parsers = parsers # store them so they don't get garbage-collected (probably not needed, though)
|
||||||
# TODO: Use (managed?) FFI struct instead of void pointers
|
# TODO: Use (managed?) FFI struct instead of void pointers
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Define parsers that take some number of other parsers
|
parser = Hammer::Parser.new
|
||||||
# TODO: Maybe use -1 for variable number, and use this for Sequence and Choice too
|
parser.instance_variable_set :@h_parser, h_parser
|
||||||
# TODO: Refactor this code as a method? And call it like: define_parser :Int64, :h_int64, 0
|
parser.instance_variable_set :@sub_parsers, sub_parsers
|
||||||
[
|
return parser
|
||||||
[:Int64, :h_int64, 0],
|
|
||||||
[:Int32, :h_int32, 0],
|
|
||||||
[:Int16, :h_int16, 0],
|
|
||||||
[:Int8, :h_int8, 0],
|
|
||||||
[:UInt64, :h_uint64, 0],
|
|
||||||
[:UInt32, :h_uint32, 0],
|
|
||||||
[:UInt16, :h_uint16, 0],
|
|
||||||
[:UInt8, :h_uint8, 0],
|
|
||||||
[:Whitespace, :h_whitespace, 1],
|
|
||||||
[:Left, :h_left, 2],
|
|
||||||
[:Right, :h_right, 2],
|
|
||||||
[:Middle, :h_middle, 3],
|
|
||||||
[:End, :h_end_p, 0],
|
|
||||||
[:Nothing, :h_nothing_p, 0],
|
|
||||||
[:ButNot, :h_butnot, 2],
|
|
||||||
[:Difference, :h_difference, 2],
|
|
||||||
[:Xor, :h_xor, 2],
|
|
||||||
[:Many, :h_many, 1],
|
|
||||||
[:Many1, :h_many1, 1]
|
|
||||||
].each do |class_name, h_function_name, parameter_count|
|
|
||||||
# Create new subclass of Hammer::Parser
|
|
||||||
klass = Class.new(Hammer::Parser) do
|
|
||||||
# Need to use define_method instead of def to be able to access h_function_name in the method's body
|
|
||||||
define_method :initialize do |*parsers|
|
|
||||||
# Checking parameter_count is not really needed, since the h_* methods will complain anyways
|
|
||||||
@h_parser = Hammer::Internal.send(h_function_name, *parsers.map(&:h_parser))
|
|
||||||
# TODO: Do we need to store sub-parsers to prevent them from getting garbage-collected?
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
# Register class with name Hammer::Parser::ClassName
|
|
||||||
Hammer::Parser.const_set class_name, klass
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO:
|
|
||||||
# Hammer::Parser::Token.new('...') is a bit too long. Find a shorter way to use the parsers.
|
|
||||||
# Maybe:
|
|
||||||
# class Hammer::Parser
|
|
||||||
# def self.token(*args)
|
|
||||||
# Hammer::Parser::Token.new(*args)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# Can create functions like that automatically. Usage:
|
|
||||||
# h = Hammer::Parser
|
|
||||||
# parser = h.sequence(h.token('blah'), h.token('other_token'))
|
|
||||||
# Looks almost like hammer in C!
|
|
||||||
|
|
||||||
# Defines a parser constructor with the given name.
|
# Defines a parser constructor with the given name.
|
||||||
# Options:
|
# Options:
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ module Hammer
|
||||||
|
|
||||||
def build
|
def build
|
||||||
if @parsers.length > 1
|
if @parsers.length > 1
|
||||||
Hammer::Parser::Sequence.new(*@parsers)
|
Hammer::Parser.sequence(*@parsers)
|
||||||
else
|
else
|
||||||
@parsers.first
|
@parsers.first
|
||||||
end
|
end
|
||||||
|
|
@ -40,14 +40,12 @@ module Hammer
|
||||||
|
|
||||||
|
|
||||||
def token(str)
|
def token(str)
|
||||||
#@h_parsers << Hammer::Internal.h_token(str, str.length)
|
@parsers << Hammer::Parser.token(str)
|
||||||
@parsers << Hammer::Parser::Token.new(str)
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
def ch(char)
|
def ch(char)
|
||||||
#@h_parsers << Hammer::Internal.h_ch(char.ord)
|
@parsers << Hammer::Parser.ch(char)
|
||||||
@parsers << Hammer::Parser::Ch.new(char)
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -57,17 +55,13 @@ module Hammer
|
||||||
@parsers += parsers
|
@parsers += parsers
|
||||||
@parsers << Docile.dsl_eval(ParserBuilder.new, &block).build if block_given?
|
@parsers << Docile.dsl_eval(ParserBuilder.new, &block).build if block_given?
|
||||||
return self
|
return self
|
||||||
#builder = Hammer::ParserBuilder.new
|
|
||||||
#builder.instance_eval &block
|
|
||||||
#@parsers << Hammer::Parser::Sequence.new(*builder.parsers)
|
|
||||||
## TODO: Save original receiver and redirect missing methods!
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def choice(*parsers, &block)
|
def choice(*parsers, &block)
|
||||||
if block_given?
|
if block_given?
|
||||||
parsers += Docile.dsl_eval(ParserBuilder.new, &block).parsers
|
parsers += Docile.dsl_eval(ParserBuilder.new, &block).parsers
|
||||||
end
|
end
|
||||||
@parsers << Hammer::Parser::Choice.new(*parsers)
|
@parsers << Hammer::Parser.choice(*parsers)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue