# usage: # ruby rx.rb # run the unit test # ruby -rrx -e 'p matchstr(...)' # use rx as library. don't run the unit test. def count_try(exp, str) $try_count = 0 matchstr(exp, str) $try_count end def matchstr(exp, str) result = [] try(exp, str.split(//), 0) {|pos| result << pos } result end def try(exp, seq, pos, &block) $try_count += 1 if defined? $try_count case exp[0] when :empseq try_empseq(seq, pos, &block) when :lit _, sym = exp try_lit(sym, seq, pos, &block) when :cat _, e1, e2 = exp try_cat(e1, e2, seq, pos, &block) when :alt _, e1, e2 = exp try_alt(e1, e2, seq, pos, &block) when :rep _, e = exp try_rep(e, seq, pos, &block) else raise "unexpected AST: #{exp.inspect}" end end def try_empseq(seq, pos) yield pos end def try_lit(sym, seq, pos) if pos < seq.length && seq[pos] == sym yield pos + 1 end end def try_cat(e1, e2, seq, pos, &block) try(e1, seq, pos) {|pos2| try(e2, seq, pos2, &block) } end def try_alt(e1, e2, seq, pos, &block) try(e1, seq, pos, &block) try(e2, seq, pos, &block) end def try_rep(exp, seq, pos, &block) try(exp, seq, pos) {|pos2| try_rep(exp, seq, pos2, &block) if pos < pos2 } yield pos end if $0 == __FILE__ # The trick to run the unit test only for non-library execution. require 'test/unit' class TestRX < Test::Unit::TestCase def test_empseq assert_equal([0], matchstr([:empseq], "")) end def test_lit assert_equal([], matchstr([:lit, "a"], "")) assert_equal([1], matchstr([:lit, "a"], "a")) assert_equal([1], matchstr([:lit, "a"], "aa")) assert_equal([], matchstr([:lit, "a"], "b")) end def test_cat assert_equal([], matchstr([:cat, [:lit, "a"], [:lit, "b"]], "")) assert_equal([], matchstr([:cat, [:lit, "a"], [:lit, "b"]], "a")) assert_equal([2], matchstr([:cat, [:lit, "a"], [:lit, "b"]], "ab")) assert_equal([2], matchstr([:cat, [:lit, "a"], [:lit, "b"]], "abc")) end def test_alt assert_equal([], matchstr([:alt, [:lit, "a"], [:lit, "b"]], "")) assert_equal([1], matchstr([:alt, [:lit, "a"], [:lit, "b"]], "a")) assert_equal([1], matchstr([:alt, [:lit, "a"], [:lit, "b"]], "b")) end def test_rep assert_equal([0], matchstr([:rep, [:lit, "a"]], "")) assert_equal([5,4,3,2,1,0], matchstr([:rep, [:lit, "a"]], "aaaaa")) end end end