rsec-1.0.0/0000755000004100000410000000000013602762172012502 5ustar www-datawww-datarsec-1.0.0/test/0000755000004100000410000000000013602762172013461 5ustar www-datawww-datarsec-1.0.0/test/test_repeat.rb0000644000004100000410000000221613602762172016326 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestRepeat < TC def test_maybe [:_?, :maybe].each do |m| p = seq('v', 'q').send m ase [], p.parse('') ase INVALID, p.eof.parse('v') ase [['v', 'q']], p.parse('vq') # with map block p = seq('v', 'q').maybe {|x| x.empty? ? 'bad' : 'good' } ase 'good', p.parse('vq') end end def test_multiply p = ('ce'.r * 3).eof ase ['ce','ce','ce'], (p.parse 'cecece') ase INVALID, (p.parse 'cece') ase INVALID, (p.parse 'cececece') p = ('ce'.r * 0).eof ase [], (p.parse '') ase INVALID, (p.parse 'ce') end def test_range p = ('ce'.r * (2..3)).eof ase INVALID, (p.parse 'ce') ase ['ce','ce'], (p.parse 'cece') ase INVALID, (p.parse 'cececece') end def test_inf p = ('ce'.r * (3..-1)).eof ase INVALID, (p.parse 'cece') ase ['ce','ce','ce'], (p.parse 'cecece') ase ['ce','ce','ce','ce','ce'], (p.parse 'cecececece') end def test_star p = '*'.r.star ase [], p.parse('') ase %w[* * *], p.parse('***') end end rsec-1.0.0/test/test_prim.rb0000644000004100000410000000316313602762172016017 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestPrim < TC def test_floating [:double].each do |ty| p = prim ty ase INVALID, p.parse('d') ase 3.2e5.round, p.parse('+3.2e5').round ase INVALID, p.parse(' 4.8') p = prim ty, allowed_sign: '' ase 1.5e-3.round(4), p.parse('1.5E-3').round(4) ase INVALID, p.parse('+3.0') p = prim ty, allowed_sign: '-' ase (-5.0).round, p.parse('-5').round ase INVALID, p.parse('-') ase INVALID, p.parse('+5') ase 5.0.round, p.parse('5').round # with map block p = prim(ty){|x| x * 2 } ase 100.0.round, p.parse('50').round end end def test_hex_floating return # NOTE Ruby 1.9.3 removed Float() from hex values [:hex_double].each do |ty| p = prim ty ase Float('0x3.2').round(4), p.parse('0x3.2').round(4) # with map block p = prim(ty){|x| x - 0.1 } ase (Float('0x3.2') - 0.1).round(4), p.parse('0x3.2').round(4) end end def test_integer [:int32, :unsigned_int32, :int64, :unsigned_int64].each do |ty| p = prim ty ase 432, p.parse('432') p = prim ty, base: 4 ase '120'.to_i(4), p.parse('120') p = prim ty, base: 35 ase '1ax'.to_i(35), p.parse('1ax') end p = prim :int32, allowed_signs: '-' ase INVALID, p.parse('+12') ase INVALID, p.parse('123333333333333333333333333333333333') ase INVALID, p.parse('-') ase -49, p.parse('-49') assert_raise RuntimeError do prim :unsigned_int32, allowed_signs: '+-' end end end rsec-1.0.0/test/test_lookahead.rb0000644000004100000410000000070413602762172016775 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestLookAhead < TC def test_lookahead p1 = 'a'.r & 'b' p2 = /\w/.r p = seq(p1, p2) ase ['a', 'b'], p.parse('ab') ase INVALID, p.parse('ac') p1 = 'a'.r ^ 'b' p = seq(p1, p2) ase ['a', 'c'], p.parse('ac') ase INVALID, p.parse('ab') end def test_negative_lookahead p = 'a'.r ^ 'b' ase 'a', p.parse('ac') ase INVALID, p.parse('ab') end end rsec-1.0.0/test/helpers.rb0000644000004100000410000000063413602762172015453 0ustar www-datawww-data# coding: utf-8 $:.unshift "#{File.dirname(__FILE__)}/../lib" require "rsec" include Rsec::Helpers require "test/unit" TC = Test::Unit::TestCase class TC INVALID = Rsec::INVALID end module Test::Unit::Assertions alias ase assert_equal def asr assert_raise(Rsec::SyntaxError) { yield } end # assert parse returns s def asp s, p assert_equal(s, p.parse(s)) end end rsec-1.0.0/test/test_one_of.rb0000644000004100000410000000141613602762172016314 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestOneOf < TC def test_one_of p = one_of('abcd') ase 'c', p.parse('c') ase INVALID, p.parse('e') p = one_of('+=') ase '=', p.parse('=') begin p = one_of('') assert false, "should raise exception for empty string" rescue end # with map block p = one_of('x+'){|v| v * 2} ase '++', p.parse('+') end def test_one_of_ p = one_of_('abcd') ase 'a', p.parse('a') ase INVALID, p.parse('e') ase 'd', p.parse(' d ') ase 'a', p.parse(' a') ase 'c', p.parse('c ') assert_raise(ArgumentError) { p = one_of_('') } # with map block p = one_of_('w'){'v'} ase 'v', p.parse('w') end end rsec-1.0.0/test/test_branch.rb0000644000004100000410000000045513602762172016306 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestBranch < TC def test_branch p = 'a'.r | /\d+/ | seq('c', 'd') ase ['c','d'], p.parse('cd') ase '3', p.parse('3') ase INVALID, p.parse('c') p = 'x'.r | 'y' ase INVALID, p.parse('') ase 'y', p.parse('y') end end rsec-1.0.0/test/test_join.rb0000644000004100000410000000261313602762172016006 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestJoin < TC def test_join p0 = /\w{1,3}/.r.join '+' ase ['abc'], p0.eof.parse('abc') ase ['a','+','bc','+','d'], p0.parse('a+bc+d') ase INVALID, p0.eof.parse('a+ bc+d') ase INVALID, p0.eof.parse('a+b+') p1 = seq(/[a-z]{1,3}/, '3')[0].join seq(/\s/.r, '+', /\s/)[1] ase ['abc'], p1.eof.parse('abc3') ase %w[a + bc + d], p1.parse('a3 + bc3 + d3') ase INVALID, p1.eof.parse('a+b+') end def test_nest_join p = 'a'.r.join(/\s*\*\s*/.r).join(/\s*\+\s*/.r) ase [['a'], ' + ', ['a', ' * ', 'a'], ' +', ['a']], p.parse('a + a * a +a') end def test_join_with_mapping_block p = 'a'.r.join('+'){|res| res.grep /\+/ } ase ['+', '+'], p.parse('a+a+a') ase [], p.parse('a') end def test_join_even p = 'a'.r.join('+').even ase %w[a a a], p.parse('a+a+a') ase %w[a], p.parse('a') ase INVALID, p.eof.parse('a+') ase INVALID, p.parse('b') ase INVALID, p.parse('') end def test_join_odd p = 'a'.r.join('+').odd ase %w[+ +], p.parse('a+a+a') ase [], p.parse('a') ase INVALID, p.parse('') ase INVALID, p.parse('+') ase INVALID, p.parse('b') end def test_nest_join_even_odd p = 'a'.r.join('+').odd.join('*') ase [['+'], '*', []], p.parse('a+a*a') p = 'a'.r.join('+').even.join('*') ase [['a','a'], '*', ['a']], p.parse('a+a*a') end end rsec-1.0.0/test/test_pattern.rb0000644000004100000410000000173713602762172016532 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestPattern < TC def test_create p1 = 'x'.r asp 'x', p1 p1 = 'abc'.r asp 'abc', p1 asr do p1.eof.parse! 'abcd' end ase INVALID, p1.eof.parse('abcd') asr do p1.eof.parse! 'xabc' end ase INVALID, p1.eof.parse('xabc') # with map block p = 'x'.r{ 'y' } ase INVALID, p.parse('y') ase 'y', p.parse('x') end def test_until p = 'ef'.r.until asp 'xef', p asp "x\nef", p p = 'e'.r.until asp 'xe', p asp "x\ne", p # with map block p = 'e'.r.until{|s| s*2} ase 'xexe', p.parse('xe') end def test_word p = word('abc') ase INVALID, p.parse('abcd') ase INVALID, seq_(p, 'd').parse('abcd') ase 'abc', p.parse('abc') ase ['abc', 'd'], seq_(p, 'd').parse('abc d') end def test_symbol p = symbol('*') ase '*', p.parse(' * ') end end rsec-1.0.0/test/test_rsec.rb0000644000004100000410000000042113602762172015776 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestRsec < TC def test_try_skip_pattern p = Rsec.try_skip_pattern 'abc'.r ase Rsec::SkipPattern, p.class p = Rsec.try_skip_pattern 'abc'.r.until ase Rsec::SkipUntilPattern, p.class end end rsec-1.0.0/test/test_misc.rb0000644000004100000410000000273613602762172016010 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestMisc < TC def test_lazy p1 = nil p2 = lazy{p1} p1 = '3'.r asp '3', p2 p1 = '4'.r asp '3', p2 p2 = lazy{p7} # don't have to define p7 before lazy p7 = '5'.r asp '5',p2 end def test_eof p = ''.r.eof asp '', p ase INVALID, p.parse('a') p = seq('a', 'b').eof ase INVALID, p.parse('abc') ase ['a', 'b'], p.parse('ab') end def test_cache p1 = seq('a', seq('b', 'c')) p = seq(p1.cached, 'd') ase [['a',['b','c']],'d'], p.parse('abcd') # with map block p = seq(p1.cached{ 'mapped' }, 'd') ase ['mapped', 'd'], p.parse('abcd') end def test_map p = /\w/.r.map{|n| n*2} ase 'bb', p.parse('b') ase INVALID, p.parse('.') end def test_seq_map p = seq('"'.r, /\w/.r, '"'.r).map{|(_, n, _)| n*2} ase 'bb', p.parse('"b"') ase INVALID, p.parse('.') end def test_seq_map_with_context p = seq('"'.r, /\w/.r, '"'.r).map{|(_, n, _), ctx| n*ctx.pos} ase 'bbb', p.parse('"b"') ase INVALID, p.parse('.') end def test_fail p = 'v'.r.fail 'omg!' p.eof.parse! 'u' assert false, "should raise syntax error" rescue Rsec::SyntaxError => e assert e.to_s.index 'omg!' end def test_fail_with_block p = 'v'.r.fail('omg!'){ 'should fail' } p.eof.parse! 'u' assert false, "should raise syntax error" rescue Rsec::SyntaxError => e assert e.to_s.index 'omg!' end end rsec-1.0.0/test/test_seq.rb0000644000004100000410000000220513602762172015634 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" class TestSeq < TC def test_seq p = seq('a', 'b', 'c') ase ['a','b','c'], p.parse('abc') ase INVALID, p.parse('a') ase INVALID, p.parse('b') ase INVALID, p.parse('c') ase INVALID, p.parse('bc') ase INVALID, p.parse('ab') end def test_seq_ p = seq_('abc', 'ef', 'vv') ase %w[abc ef vv], p.parse("abc ef vv") p = seq_('abc', 'ef', 'vv', skip: /\s+/) ase %w[abc ef vv], p.parse("abc ef vv") ase INVALID, p.parse("abcef vv") end def test_seq_mix p = seq('e', seq_('a','b','c'), 'd') ase ['e', ['a','b','c'], 'd'], p.parse('eabcd') end def test_seq_one p = seq('a', 'b', 'c')[1] ase 'b', p.parse('abc') p = seq('abc', /\s*/, 'd')[2] ase 'd', p.parse('abc d') end def test_seq_one_ p = seq_('a', 'b', 'c')[1] ase 'b', p.parse('a bc') p = seq_('abc', /\s*/, 'd')[2] ase 'd', p.parse('abc d') end def test_fall p = 'a'.r >> 'b' ase 'b', p.parse!('ab') p = p << 'c' ase 'b', p.parse!('abc') p = p._? ase ['b'], p.eof.parse!('abc') ase [], p.eof.parse!('') end end rsec-1.0.0/test/test_examples.rb0000644000004100000410000000151613602762172016666 0ustar www-datawww-datarequire "#{File.dirname(__FILE__)}/helpers.rb" $:.unshift "#{File.dirname __FILE__}/../examples" require "arithmetic" require "s_exp" class TestExamples < TC def initialize *xs super(*xs) @a = arithmetic() @s_exp = s_exp() end def test_arithmetic # step by step s = '1' ase eval(s), @a.parse(s) s = '3+ 2' ase eval(s), @a.parse(s) s = '5-2*1' ase eval(s), @a.parse(s) s = '(2)' ase eval(s), @a.parse(s) s = '1+(2- (3+ 4))/5 * 2*4 +1' ase eval(s), @a.parse(s) end def test_s_exp res = @s_exp.parse! '(a 3 4.3 (add 1 3) (minus (multi 4 5)))' expected = ['a', 3.0, 4.3, ['add', 1, 3], ['minus', ['multi', 4, 5]]] ase expected, res res = @s_exp.parse! '(a (3) ce2 (add 1 3))' expected = ['a', 3.0, 'ce2', ['add', 1, 3]] ase expected, res end end rsec-1.0.0/license.txt0000644000004100000410000000001113602762172014655 0ustar www-datawww-dataAs Ruby'srsec-1.0.0/examples/0000755000004100000410000000000013602762172014320 5ustar www-datawww-datarsec-1.0.0/examples/bnf.rb0000644000004100000410000000165613602762172015422 0ustar www-datawww-data# BNF grammar parser # http://en.wikipedia.org/wiki/Backus-Naur_form require "rsec" include Rsec::Helpers def bnf nbsp = /[\ \t]*/.r spacee = /\s*/.r # include \n literal = /".*?"|'.*?'/.r rule_name = /\<.*?\>/ term = literal | rule_name list = term.join(nbsp).even expr = list.join seq(nbsp, '|', nbsp)[1] rule = seq_ rule_name, '::=', expr spacee.join(rule).eof end require "pp" pp bnf.parse! DATA.read __END__ ::= | ::= "<" ">" "::=" ::= " " | "" ::= | "|" ::= | ::= | ::= | "<" ">" ::= '"' '"' | "'" "'" rsec-1.0.0/examples/hello.scm0000644000004100000410000000037413602762172016133 0ustar www-datawww-data(display 4) (define (fact x) (if (= x 0) 1 (* x (fact (- x 1))))) (display (fact 6)) (define (add x) (lambda (y) (+ x y))) (define add4 (add 4)) (define add5 (add 5)) (display (add4 3)) (display (add5 3)) rsec-1.0.0/examples/arithmetic.rb0000644000004100000410000000120513602762172016774 0ustar www-datawww-data# arithmetic parser require "rsec" include Rsec::Helpers def arithmetic calculate = proc do |(p, *ps)| ps.each_slice(2).inject(p) do |left, (op, right)| left.send op, right end end num = prim(:double).fail 'number' paren = '('.r >> lazy{expr} << ')' factor = num | paren term = factor.join(one_of_('*/%').fail 'operator').map &calculate expr = term.join(one_of_('+-').fail 'operator').map &calculate expr.eof end if __FILE__ == $PROGRAM_NAME print '1+ 2*4 = ' p arithmetic.parse! '1+ 2*4' #=> 9 print '1+ 2*/4 = ' p arithmetic.parse! '1+ 2*/4' #=> syntax error end rsec-1.0.0/examples/little_markdown.rb0000644000004100000410000001170013602762172020043 0ustar www-datawww-data# a markdown translator # # The differences between this and original markdown: # - markdown in inline tags are not processed # - every line-break in non-tag parts is translated into
# - nested list elements are not supported require "rsec" class LittleMarkdown include Rsec::Helper def initialize @markdown_line_translator = make_markdown_line_translator @parser = (make_xml_tag_parser | make_char_parser).star.eof end def translate src @stack = [] @charsbuf = '' @out = '' @parser.parse! src flush_chars @out end def flush_chars @out.<< translate_markdown @charsbuf @charsbuf = '' end def make_char_parser # care stringscanner's bug, see issues (/./.r | /\n/).fail('char'){|c| @charsbuf << c} end # make a single-line markdown parser def make_markdown_line_translator line_text = lazy{line}.map{|tokens| tokens.empty? ? Rsec::INVALID : tokens.join # filter out empty } title = /"[^"]*"|'[^']*'/.r._?{|(s)| s ? "title=#{s}" : '' } img = seq('!['.r >> /[^\]]+/ << '](', /[^\)"']+/, title, ')'){|(txt, path, title)| "#{txt}" } link = seq(('['.r >> /[^\]]+/ << ']('), /[^\)"']+/, title, ')'){|(txt, path, title)| "#{txt}" } # NOTE strong should be left of em strong = ('**'.r >> line_text << '**').map{|s| "#{s}" } em = ('*'.r >> line_text << '*').map{|s| "#{s}" } code = ('`'.r >> /[^`]+/ << '`').map{|s| "#{s}" } escape = '<'.r{'<'} | '&'.r{'&'} | /\\[\!\`\*\[\]]/.r{|s|s[1]} text = /[^\!\`\*\[\]]+/ id = seq_(('['.r >> /[^\]]+/ << ']:'), text){|(id, text)| "#{text}" } line = (img | link | strong | em | code | escape | id | text).star line.eof.map {|parts, _| parts.join } end # pseudo xml tag parser, except
and
and