Skip to content

Commit

Permalink
Simple string parser specs.
Browse files Browse the repository at this point in the history
  • Loading branch information
vic committed Jan 21, 2011
1 parent cd22554 commit b044ca1
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 15 deletions.
10 changes: 8 additions & 2 deletions lib/akin/parser/language.rb
Expand Up @@ -89,10 +89,10 @@ def fwd
end
end

module Number
module NumberLiteral
def self./(base)
Module.new do
include Number
include NumberLiteral
define_method(:base) { base }
end
end
Expand All @@ -107,6 +107,12 @@ def text
end
end

module StringLiteral
def parts
self[1]
end
end

module Language
class << self
attr_accessor :syntax
Expand Down
17 changes: 9 additions & 8 deletions lib/akin/parser/match.rb
Expand Up @@ -21,7 +21,7 @@ def clone

class Matcher

[ :name, :mixin, :block, :seq, :min, :max, :but, :true, :parse ].tap do |attrs|
[ :name, :mixin, :block, :seq, :min, :max, :pos, :true, :parse ].tap do |attrs|
attr_reader *attrs

define_method(:with) do |map|
Expand All @@ -43,15 +43,15 @@ class Matcher

def initialize(block)
@lookup = block
@true, @but, @min, @max = true, true, 1, 1
@true, @pos, @min, @max = true, true, 1, 1
end

def lookup(name)
@lookup.call(name)
end

def but
with :but => !@but
with :pos => !@pos
end

def not
Expand Down Expand Up @@ -120,7 +120,7 @@ def |(other)
private

def positive?(bool)
if @but
if @pos
bool
else
!bool
Expand All @@ -143,6 +143,7 @@ def match_new(positive, from, to, fwd, &block)
m.to = to
m.fwd = fwd
m.tap(&block) if block
m.expected = self unless positive
end
end

Expand Down Expand Up @@ -178,7 +179,7 @@ def match_all(input, ary)
match, at = nil, input
ary.each_with_index do |item, index|
match = match_single(at, item)
if match.positive?
if @true && match.positive?
at = match.fwd
all << match
named[match.name] = match if match.name
Expand All @@ -189,12 +190,12 @@ def match_all(input, ary)
end
if all.size == 1
match = all.first
match.extend @mixin if @mixin && match.positive? && @but == true
match.extend @mixin if @mixin && match.positive? && @pos
else
match = match_positive(input, all.last.to, all.last.fwd) do |match|
match.ary = all
match.map = named
match.extend @mixin if @mixin && @but == true
match.extend @mixin if @mixin && @pos
end
end
match
Expand Down Expand Up @@ -261,7 +262,7 @@ def match_single(input, rule)
class Match
include Enumerable

attr_accessor :name, :from, :to, :fwd, :ary, :map, :positive
attr_accessor :name, :from, :to, :fwd, :ary, :map, :positive, :expected

def text
buff = ""
Expand Down
27 changes: 23 additions & 4 deletions lib/akin/parser/syntax.rb
Expand Up @@ -5,10 +5,15 @@ module Parser
a(:eof) .is nil
a(:any) .is /./

a(:dollar) .is "$"
a(:dash) .is "-"
a(:slash) .is "/"
a(:underscore) .is "_"
a(:back_slash) .is "\\"
a(:dot) .is "."
a(:comma) .is ","
a(:collon) .is ":"
a(:semicollon) .is ";"

a(:unix_eol) .is "\n"
a(:win_eol) .is "\r\n"
Expand All @@ -34,13 +39,27 @@ module Parser
a(digit, a([ digit, a(:underscore, digit) ]).any)
end

a(:bin_int).as(Number/2) .is "0", /[bB]/, a(:digits).of(:oct_digit)
a(:oct_int).as(Number/8) .is "0", a(/[oO]/).opt, a(:digits).of(:oct_digit)
a(:dec_int).as(Number/10) .is a(:digits).of(:dec_digit)
a(:hex_int).as(Number/16) .is "0", /[xX]/, a(:digits).of(:hex_digit)
a(:bin_int).as(NumberLiteral/2) .is "0", /[bB]/, a(:digits).of(:oct_digit)
a(:oct_int).as(NumberLiteral/8) .is "0", a(/[oO]/).opt, a(:digits).of(:oct_digit)
a(:dec_int).as(NumberLiteral/10) .is a(:digits).of(:dec_digit)
a(:hex_int).as(NumberLiteral/16) .is "0", /[xX]/, a(:digits).of(:hex_digit)

a(:integer).is [:bin_int, :hex_int, :oct_int, :dec_int]

text = lambda do |left, right|
interpol = a(:dollar, :table)
escaped = a(:back_slash, [:dollar, right])
char = a(right.not, :any)
content = interpol | (escaped | char).many
a(left, content.any, right)
end

a(:string).as(StringLiteral).is text.call(a("\""), a("\""))
a(:mstring).as(StringLiteral).tap do |ms|
mq = a("\"", "\"", "\"")
ms.is text.call(mq, mq)
end

end
end
end
19 changes: 19 additions & 0 deletions spec/parser/match_spec.rb
Expand Up @@ -151,6 +151,14 @@ def input(text)
m.to.should == nil
m.fwd.should == i
end

it "does not consume input on invalid sequence match" do
m = a("a", "b", "c").not.match i = input("foo")
m.positive?.should be_true
m.from.should == i
m.to.should == nil
m.fwd.should == i
end
end

describe "+ operator" do
Expand Down Expand Up @@ -331,5 +339,16 @@ def input(text)
end
end


describe "composite rule" do
it "matches an enclosed text between abc" do
x = a("a", "b", "c")
y = a(x, a(x.not, /./).any, x)
y.match(input("abcabc")).positive?.should be_true
y.match(input("abc abc")).positive?.should be_true
y.match(input("abcdefabc")).positive?.should be_true
end
end

end

31 changes: 30 additions & 1 deletion spec/parser/syntax_spec.rb
Expand Up @@ -56,7 +56,7 @@ def parse(name, input)
m.fwd.position.logical.pos.should == [1, 9, 9]
end

it "leaves the physical fwd logical position intact" do
it "leaves the physical fwd position intact" do
m = parse a(:tab), "\thola"
m.fwd.position.physical.pos.should == [1, 2, 2]
end
Expand Down Expand Up @@ -165,4 +165,33 @@ def parse(name, input)
end
end # hex integer


describe "string" do
it "matches a simple string literal" do
m = parse(:string, %q("hello"))
m.parts.count.should == 1
m.to.position.logical.pos.should == [1, 7, 7]
end

it "matches a string literal with escaped quotes" do
m = parse(:string, %q("hell\"o"))
m.parts.count.should == 1
m.to.position.logical.pos.should == [1, 9, 9]
end
end

describe "multi string" do
it "matches a simple string literal" do
m = parse(:mstring, %q("""hello"""))
m.parts.count.should == 1
m.to.position.logical.pos.should == [1, 11, 11]
end

it "matches a string literal with escaped quotes" do
m = parse(:mstring, %q("""hell\"""o"""))
m.parts.count.should == 1
m.to.position.logical.pos.should == [1, 15, 15]
end
end

end

0 comments on commit b044ca1

Please sign in to comment.