pax_global_header00006660000000000000000000000064136222566110014516gustar00rootroot0000000000000052 comment=655f0c3c0bd2ebcf528a9a616597b41c1d148c77 parslet-2.0.0/000077500000000000000000000000001362225661100131675ustar00rootroot00000000000000parslet-2.0.0/.gitignore000066400000000000000000000001601362225661100151540ustar00rootroot00000000000000/vendor today Gemfile.lock *.gem website/.sass-cache .yardoc test.rb .rspec .bundle doc pkg rdoc .DS_Store tags parslet-2.0.0/.travis.yml000066400000000000000000000001171362225661100152770ustar00rootroot00000000000000sudo: false script: bundle exec rspec rvm: - "2.2.6" - "2.3.3" - "2.4.0" parslet-2.0.0/Gemfile000066400000000000000000000002241362225661100144600ustar00rootroot00000000000000source "https://rubygems.org" gemspec group :development do %w(rspec flexmock rdoc qed ae). each { |gem_name| gem gem_name } end parslet-2.0.0/Guardfile000066400000000000000000000006341362225661100150170ustar00rootroot00000000000000guard 'rspec', :version => 2 do watch(%r(^spec/(.*)_spec.rb)) watch(%r(^lib/(.*)\.rb)) { |m| "spec/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { "spec" } watch(%r'^lib/parslet/bytecode/(.*)\.rb') { 'spec/acceptance/vm_spec.rb' } watch(%r'^lib/parslet/pattern/(.*)\.rb') { 'spec/parslet/pattern_spec.rb' } end guard 'livereload' do watch %r(qed/.*) watch %r(lib/.*) endparslet-2.0.0/HISTORY.txt000066400000000000000000000237651362225661100151060ustar00rootroot00000000000000 = 2.0 / 16Feb2020 This release is essentially what is called a 'done' release, meaning I consider that parslet meets its goals and doesn't need (much) more evolution. - prsnt? and absnt? are now finally banned into oblivion. Wasting vocals for the win. - Because Ruby 2.6 broke Integer() conversion for non-Strings, we've removed to_int for slices; to assert Integer type, you must now 'Integer(slice.to_s)'. (Broken? How? Integer now (2.6) calls to_int - and when that fails - to_i. A class that has both will never raise an exception. Incientially, this makes String a unicorn class.) = 1.8.2 / 13Feb2018 ! Improvements to performance in cases where atoms are dynamically generated (Kevin Olbrich). = 1.8.1 / 19Nov2017 - Minor fixes for language compatibility. = 1.8 / 3Apr2017 + The `ignored` atom that allows to ignore a part of the matched text. `str('foo').ignore` will match 'foo', but not yield any parse output. Thanks to chrismwendt (Chris Wendt). + Infix expression parser (arithmetics, anyone?) now supports custom reducers in block form. Thanks to chrismwendt (Chris Wendt). ! Small patches to memory footprint (Christophe Bliard). - blankslate dependency removed. You should be good - but if things break, please let us know (Nikita Shilnikov). ! Parslet now has `parse_failure_cause`, replaces the earlier `cause`. ! Fixes all these interpreter warnings on modern rubies. = 1.7 / 12Mar2015 ! Small speed gains from improvements on the hot spots. + Contextual error reporter, a flavor of error reporting that emphasizes context. = 1.6 / 1May2014, 13Okt14 + EXPERIMENTAL: Parslet accelerators permit replacing parts of your parser with optimized atoms using pattern matching. Look at examples/optimized_erb.rb or the introduction to the feature in qed/accelerators.md. + infix_expression permits to declare an infix expression parser (think calculator) directly. This will solve many of the problems we have more elegantly. + Rspec 3 syntax, though hideous, should now work. - Drops 1.8.7 compatibility. ! A performance anomaly when parsing multibyte characters has been detected and fixed with the help of Zach Moazeni (@zmoazeni). ! A few small bug fixes and optimisations have been introduced. API should remain unchanged. + More lenient on the blankslate version. + Modernizes the test suite to run with rspec again. (!) = 1.5 / 27Dec2012 + Handles unconsumed input at end of parse completely differently. Instead of generating a toplevel error, it now raises an error in every branch of the parse. More information in the resulting exception ensues! Thanks again to John Mettraux for inspiration & acceptance specs. NOTE that this means that the UnconsumedInput exception is gone, since the unconsumed input case is nothing special anymore. * This history now finally reads like the Changelog of the linux kernel. Meaning that probably no one ever reads this. + Captures and parsing subsequent input based on captured values. This has been long overdue - finally you can parse HEREdocs with parslet! = 1.4.0 / 25May2012 + Revised documentation. A few new API features have finally made it into the documentation. Examples in the documentation are now curated and run against the current code so that they really really work. Also, the website generation tools have been replaced with 2012-style tools. Much less pain to update now. + Parslet::Source now doesn't hold a StringIO, it directly holds the buffer to be parsed. The api of Source has changed a tiny bit. This change has been made for speed optimisation reasons. + :reporter argument to parse, allowing to customize error reporting within wide boundaries. See issue #64 for a discussion. Included are two error reporters, one (default) with the existing error tree functionality, one reporting deepest errors as defined by the above ticket. + Optimistic parse: Parsing is two phase, with the first phase assuming there will be no errors. This yields ~ 20% speed improvement in the case where the parse succeeds. Also, internal error handling is now using tuples. This and other optimizations have yielded ~ 30% overall improvement. ! #error_tree and #cause removed from all of parslet. The Parslet::ParseFailed exception now contains a #cause field that can be asked for an #ascii_tree as before. Cleaner internal error handling, not stateful in atoms anymore. Some parsers will see correct error reporting for the first time. (issue #65) + Made it possible to pass a custom Parslet::Source implementor to #parse. (see #63) + #parse has now a second argument that is an options hash. See Parslet::Atoms::Base#parse for documentation. - VM engine on the way out. No benefit except for the intellectual challenge. = 1.3.0 / 5Mar2012 ! Parslet::Transform::Context is now much more well-behaved. It has #respond_to? and #method_missing; it now looks like a plain old Ruby object with instance variables and attribute readers. - Grammar transforms turned out to be a dead end and have been removed. ! A few problems in error message generation have been fixed. This will improve diagnostics further. + A VM driven parser engine: Removes the limitation that parsing needs a lot of stack space, something dearly missing from Ruby 1.9.3 fibers. This engine is experimental and might be removed in the future. ! Interaction with mathn fixed - Line number generation will terminate. . Internal reorganisation, removing cruft and bit rot. = 1.2.3 / 22Sep2011 + Transform#apply can now be called with a hash as second argument. This provides bindings and a way to inject context. ! Fixes a bug thar modified parslet atoms in place, defeating oop chaining. (#50) = 1.2.1 / 6Jun2011 ! FIX: Input at the end of a parse raises Parslet::UnconsumedInput. (see issue 18) ! FIX: Unicode parsing should now work as expected. (see issue 38) ! FIX: Slice#slice returned wrong bits at times (see issue 36). = 1.2.0 / 4Feb2011 + Parslet::Parser is now also a grammar atom, it can be composed freely with other atoms. (str('f') >> MiniLispParser.new >> str('b')) + No strings, only slices are returned as part of the parser result. Parslet::Slice is almost a string class, but one that remembers the source offset. This has also bought us a slight speedup. + require 'parslet/convenience' now brings #parse_with_debug to all parslets. This is a consequence of the above change. + Deprecates prsnt? and absnt? in favor of the more readable absent? and prsnt?. Uses 3 bytes more RAM. The old variants will exist until we release 2.0. INTERNALLY + Visitors now should have methods that all begin with 'visit_*'. #str becomes #visit_str. + Parslet::Atoms::Entity now takes only a block argument instead of context and block. = 1.1.1 / 4Feb2011 ! FIX: Line counting was broken by performance optimisations. + Squeezed out another few drops of performance. = 1.1.0 / 2Feb2011 + Uses return (fail/success), cached line counts, memoizing of parse results and other tricks internally for at least an order of magnitude increase in execution speed. + str('foo').maybe will now return an empty string again. Use .as(...) to name things and get back [] from #repeat and nil from #maybe. + If you require 'parslet/atoms/visitor', you'll get an accept method on all known Parslet::Atoms. + If you require 'parslet/export', you can call #to_citrus and #to_treetop to produce string versions of your grammar in those dialects. + Requiring 'parslet/convenience' will given you a parse_with_debug on your Parslet::Parser class. This prints some diagnostics on parse failure. (Thanks to Florian Hanke) = 1.0.1 / 17Jan2011 A happy new year! ! FIX: Parslet::Transform was wrongly fixed earlier - it now wont mangle hashes anymore. (Blake Sweeney) + parslet/rig/rspec.rb contains useful rspec matchers. (R. Konstantin Haase) = 1.0.0 / 29Dez2010 - #each_match was removed. There was some duplication of code that even confused me - and we should not have 2 methods of achieving the same goal. + Full documentation. Fixed sdoc. = 0.11.0 / 25Nov2010 ! Bugfixes to tree handling. Let's hope that was the last such significant change to the core. = 0.10.1 / 22Nov2010 + Allow match['a-z'], shortcut for match('[a-z]') ! Fixed output inconsistencies (behaviour in connection to 'maybe') = 0.10.0 / 22Nov2010 + Parslet::Transform now takes a block on initialisation, wherein you can define all the rules directly. + Parslet::Transform now only passes a hash to the block during transform when its arity is 1. Otherwise all hash contents as bound as local variables. + Both inline and other documentation have been improved. + You can now use 'subtree(:x)' to bind any subtree to x during tree pattern matching. + Transform classes can now include rules into class definition. This makes Parser and Transformer behave the same. = 0.9.0 / 28Oct2010 * More of everything: Examples, documentation, etc... * Breaking change: Ruby's binary or ('|') is now used for alternatives, instead of the division sign ('/') - this reduces the amount of parenthesis needed for a grammar overall. * parslet.maybe now yields the result or nil in case of parse failure. This is probably better than the array it did before; the jury is still out on that. * parslet.repeat(min, max) is now valid syntax = 0.1.0 / not released. * Initial version. Classes for parsing, matching in the resulting trees and transforming the trees into something more useful. * Parses and outputs intermediary trees * Matching of single elements and sequencesparslet-2.0.0/LICENSE000066400000000000000000000020721362225661100141750ustar00rootroot00000000000000 Copyright (c) 2010-2018 Kaspar Schiess Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. parslet-2.0.0/README000066400000000000000000000036031362225661100140510ustar00rootroot00000000000000INTRODUCTION Parslet makes developing complex parsers easy. It does so by * providing the best error reporting possible * not generating reams of code for you to debug Parslet takes the long way around to make your job easier. It allows for incremental language construction. Often, you start out small, implementing the atoms of your language first; _parslet_ takes pride in making this possible. Eager to try this out? Please see the associated web site: http://kschiess.github.io/parslet SYNOPSIS require 'parslet' include Parslet # parslet parses strings str('foo'). parse('foo') # => "foo"@0 # it matches character sets match['abc'].parse('a') # => "a"@0 match['abc'].parse('b') # => "b"@0 match['abc'].parse('c') # => "c"@0 # and it annotates its output str('foo').as(:important_bit). parse('foo') # => {:important_bit=>"foo"@0} # you can construct parsers with just a few lines quote = str('"') simple_string = quote >> (quote.absent? >> any).repeat >> quote simple_string. parse('"Simple Simple Simple"') # => "\"Simple Simple Simple\""@0 # or by making a fuss about it class Smalltalk < Parslet::Parser root :smalltalk rule(:smalltalk) { statements } rule(:statements) { # insert smalltalk parser here (outside of the scope of this readme) } end # and then Smalltalk.new.parse('smalltalk') FEATURES * Tools for every part of the parser chain * Transformers generate Abstract Syntax Trees * Accelerators transform parsers, making them quite a bit faster * Pluggable error reporters * Graphviz export for your parser * Rspec testing support rig * Simply Ruby, composable and hackable COMPATIBILITY This library is intended to work with Ruby variants >= 1.9. I've tested it on MRI 1.9, rbx-head, jruby. Please report as a bug if you encounter issues. STATUS Production worthy. (c) 2010-2018 Kaspar Schiess parslet-2.0.0/Rakefile000066400000000000000000000012111362225661100146270ustar00rootroot00000000000000require 'rdoc/task' require 'sdoc' require 'rspec/core/rake_task' require "rubygems/package_task" desc "Run all tests: Exhaustive." RSpec::Core::RakeTask.new namespace :spec do desc "Only run unit tests: Fast. " RSpec::Core::RakeTask.new(:unit) do |task| task.pattern = "spec/parslet/**/*_spec.rb" end end task :default => :spec # This task actually builds the gem. task :gem => :spec spec = eval(File.read('parslet.gemspec')) desc "Prints LOC stats" task :stat do %w(lib spec example).each do |dir| loc = %x(find #{dir} -name "*.rb" | xargs wc -l | grep 'total').split.first.to_i printf("%20s %d\n", dir, loc) end end parslet-2.0.0/example/000077500000000000000000000000001362225661100146225ustar00rootroot00000000000000parslet-2.0.0/example/big.erb000066400000000000000000000124031362225661100160550ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <%= erb tag %> Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. parslet-2.0.0/example/boolean_algebra.rb000066400000000000000000000035761362225661100202560ustar00rootroot00000000000000$:.unshift File.dirname(__FILE__) + "/../lib" require "parslet" require "pp" # Parses strings like "var1 and (var2 or var3)" respecting operator precedence # and parentheses. After that transforms the parse tree into an array of # arrays like this: # # [["1", "2"], ["1", "3"]] # # The array represents a DNF (disjunctive normal form). Elements of outer # array are connected with "or" operator, while elements of inner arrays are # joined with "and". # class Parser < Parslet::Parser rule(:space) { match[" "].repeat(1) } rule(:space?) { space.maybe } rule(:lparen) { str("(") >> space? } rule(:rparen) { str(")") >> space? } rule(:and_operator) { str("and") >> space? } rule(:or_operator) { str("or") >> space? } rule(:var) { str("var") >> match["0-9"].repeat(1).as(:var) >> space? } # The primary rule deals with parentheses. rule(:primary) { lparen >> or_operation >> rparen | var } # Note that following rules are both right-recursive. rule(:and_operation) { (primary.as(:left) >> and_operator >> and_operation.as(:right)).as(:and) | primary } rule(:or_operation) { (and_operation.as(:left) >> or_operator >> or_operation.as(:right)).as(:or) | and_operation } # We start at the lowest precedence rule. root(:or_operation) end class Transformer < Parslet::Transform rule(:var => simple(:var)) { [[String(var)]] } rule(:or => { :left => subtree(:left), :right => subtree(:right) }) do (left + right) end rule(:and => { :left => subtree(:left), :right => subtree(:right) }) do res = [] left.each do |l| right.each do |r| res << (l + r) end end res end end pp tree = Parser.new.parse("var1 and (var2 or var3)") # {:and=> # {:left=>{:var=>"1"@3}, # :right=>{:or=>{:left=>{:var=>"2"@13}, :right=>{:var=>"3"@21}}}}} pp Transformer.new.apply(tree) # [["1", "2"], ["1", "3"]] parslet-2.0.0/example/calc.rb000066400000000000000000000066451362225661100160640ustar00rootroot00000000000000# A simple integer calculator to answer the question about how to do # left and right associativity in parslet (PEG) once and for all. $:.unshift File.dirname(__FILE__) + "/../lib" require 'rspec' require 'parslet' require 'parslet/rig/rspec' # This is the parsing stage. It expresses left associativity by compiling # list of things that have the same associativity. class CalcParser < Parslet::Parser root :addition rule(:addition) { multiplication.as(:l) >> (add_op >> multiplication.as(:r)).repeat(1) | multiplication } rule(:multiplication) { integer.as(:l) >> (mult_op >> integer.as(:r)).repeat(1) | integer } rule(:integer) { digit.repeat(1).as(:i) >> space? } rule(:mult_op) { match['*/'].as(:o) >> space? } rule(:add_op) { match['+-'].as(:o) >> space? } rule(:digit) { match['0-9'] } rule(:space?) { match['\s'].repeat } end # Classes for the abstract syntax tree. Int = Struct.new(:int) { def eval; self end def op(operation, other) left = int right = other.int Int.new( case operation when '+' left + right when '-' left - right when '*' left * right when '/' left / right end) end def to_i int end } Seq = Struct.new(:sequence) { def eval sequence.reduce { |accum, operation| operation.call(accum) } end } LeftOp = Struct.new(:operation, :right) { def call(left) left = left.eval right = self.right.eval left.op(operation, right) end } # Transforming intermediary syntax tree into a real AST. class CalcTransform < Parslet::Transform rule(i: simple(:i)) { Int.new(Integer(i)) } rule(o: simple(:o), r: simple(:i)) { LeftOp.new(o, i) } rule(l: simple(:i)) { i } rule(sequence(:seq)) { Seq.new(seq) } end # And this calls everything in the right order. def calculate(str) intermediary_tree = CalcParser.new.parse(str) abstract_tree = CalcTransform.new.apply(intermediary_tree) result = abstract_tree.eval result.to_i end # A test suite for the above parser describe CalcParser do let(:p) { described_class.new } describe '#integer' do let(:i) { p.integer } it "parses integers" do i.should parse('1') i.should parse('123') end it "consumes trailing white space" do i.should parse('123 ') end it "doesn't parse floats" do i.should_not parse('1.3') end end describe '#multiplication' do let(:m) { p.multiplication } it "parses simple multiplication" do m.should parse('1*2') end it "parses division" do m.should parse('1/2') end end describe '#addition' do let(:a) { p.addition } it "parses simple addition" do a.should parse('1+2') a.should parse('1+2+3-4') end end end describe CalcTransform do def t(obj) described_class.new.apply(obj) end it "transforms integers" do t(i: '1').should == Int.new(1) end it "unwraps left operand" do t(l: :obj).should == :obj end end describe 'whole computation specs' do def self.result_of(str, int) it(str) { calculate(str).should == int } end result_of '1+1', 2 result_of '1-1-1', -1 result_of '1+1+3*5/2', 9 result_of '123*2', 246 end # Enable these if you want to change the code. # RSpec::Core::Runner.run([], $stderr, $stdout) str = ARGV.join str = '123*2' if str.match(/^\s*$/) print "#{str} (command line): -> " puts calculate(str) parslet-2.0.0/example/capture.rb000066400000000000000000000026241362225661100166160ustar00rootroot00000000000000 # This example demonstrates how pieces of input can be captured and matched # against later on. Without this, you cannot match here-documents and other # self-dependent grammars. $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' require 'parslet/convenience' require 'pp' class CapturingParser < Parslet::Parser root :document # Introduce a scope for each document. This ensures that documents can be # nested. rule(:document) { scope { doc_start >> text >> doc_end } } # Start of a document is a heredoc marker. This is captured in :marker rule(:doc_start) { str('<') >> marker >> newline } rule(:marker) { match['A-Z'].repeat(1).capture(:marker) } # The content of a document can be either lines of text or another # document, introduced by > any >> (newline.absent? >> any).repeat >> newline } # The end of the document is marked by the marker that was at the beginning # of the document, by itself on a line. rule(:doc_end) { captured_marker } rule(:captured_marker) { dynamic { |source, context| str(context.captures[:marker]) } } rule(:newline) { match["\n"] } end parser = CapturingParser.new pp parser.parse_with_debug %Q(> expression.repeat >> newline } rule(:newline) { str("\n") >> str("\r").maybe } rule(:expression) { (str('a').as(:a) >> spaces).as(:exp) } rule(:spaces) { space.repeat } rule(:space) { multiline_comment | line_comment | str(' ') } rule(:line_comment) { (str('//') >> (newline.absent? >> any).repeat).as(:line) } rule(:multiline_comment) { (str('/*') >> (str('*/').absent? >> any).repeat >> str('*/')).as(:multi) } end code = %q( a // line comment a a a // line comment a /* inline comment */ a /* multiline comment */ ) pp ALanguage.new.parse_with_debug(code) parslet-2.0.0/example/deepest_errors.rb000066400000000000000000000044001362225661100201720ustar00rootroot00000000000000$:.unshift File.dirname(__FILE__) + "/../lib" # This example demonstrates how to do deepest error reporting, as invented # by John Mettraux (issue #64). require 'parslet' require 'parslet/convenience' def prettify(str) puts " "*3 + " "*4 + "." + " "*4 + "10" + " "*3 + "." + " "*4 + "20" str.lines.each_with_index do |line, index| printf "%02d %s\n", index+1, line.chomp end end class Parser < Parslet::Parser # commons rule(:space) { match('[ \t]').repeat(1) } rule(:space?) { space.maybe } rule(:newline) { match('[\r\n]') } rule(:comment) { str('#') >> match('[^\r\n]').repeat } rule(:line_separator) { (space? >> ((comment.maybe >> newline) | str(';')) >> space?).repeat(1) } rule(:blank) { line_separator | space } rule(:blank?) { blank.maybe } rule(:identifier) { match('[a-zA-Z0-9_]').repeat(1) } # res_statement rule(:reference) { (str('@').repeat(1,2) >> identifier).as(:reference) } rule(:res_action_or_link) { str('.').as(:dot) >> (identifier >> str('?').maybe ).as(:name) >> str('()') } rule(:res_actions) { ( reference ).as(:resources) >> ( res_action_or_link.as(:res_action) ).repeat(0).as(:res_actions) } rule(:res_statement) { res_actions >> (str(':') >> identifier.as(:name)).maybe.as(:res_field) } # expression rule(:expression) { res_statement } # body rule(:body) { (line_separator >> (block | expression)).repeat(1).as(:body) >> line_separator } # blocks rule(:begin_block) { (str('concurrent').as(:type) >> space).maybe.as(:pre) >> str('begin').as(:begin) >> body >> str('end') } rule(:define_block) { str('define').as(:define) >> space >> identifier.as(:name) >> str('()') >> body >> str('end') } rule(:block) { define_block | begin_block } # root rule(:radix) { line_separator.maybe >> block >> line_separator.maybe } root(:radix) end ds = [ %{ define f() @res.name end }, %{ define f() begin @res.name end end } ] ds.each do |d| puts '-' * 80 prettify(d) parser = Parser.new begin parser.parse_with_debug(d, :reporter => Parslet::ErrorReporter::Deepest.new) end end puts '-' * 80parslet-2.0.0/example/documentation.rb000066400000000000000000000005251362225661100200220ustar00rootroot00000000000000# A small example that shows a really small parser and what happens on parser # errors. $:.unshift File.dirname(__FILE__) + "/../lib" require 'pp' require 'parslet' class MyParser < Parslet::Parser rule(:a) { str('a').repeat } def parse(str) a.parse(str) end end pp MyParser.new.parse('aaaa') pp MyParser.new.parse('bbbb') parslet-2.0.0/example/email_parser.rb000066400000000000000000000026411362225661100176150ustar00rootroot00000000000000#!/usr/bin/env ruby # Example contributed by Hal Brodigan (postmodern). Thanks! $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' require 'parslet/convenience' class EmailParser < Parslet::Parser rule(:space) { match('\s').repeat(1) } rule(:space?) { space.maybe } rule(:dash?) { match['_-'].maybe } rule(:at) { str('@') | (dash? >> (str('at') | str('AT')) >> dash?) } rule(:dot) { str('.') | (dash? >> (str('dot') | str('DOT')) >> dash?) } rule(:word) { match('[a-z0-9]').repeat(1).as(:word) >> space? } rule(:separator) { dot.as(:dot) >> space? | space } rule(:words) { word >> (separator >> word).repeat } rule(:email) { (words.as(:username) >> space? >> at >> space? >> words).as(:email) } root(:email) end class EmailSanitizer < Parslet::Transform rule(:dot => simple(:dot), :word => simple(:word)) { ".#{word}" } rule(:word => simple(:word)) { word } rule(:username => sequence(:username)) { username.join + "@" } rule(:username => simple(:username)) { username.to_s + "@" } rule(:email => sequence(:email)) { email.join } end parser = EmailParser.new sanitizer = EmailSanitizer.new input = ARGV[0] || begin default = "a.b.c.d@gmail.com" STDERR.puts "usage: #{$0} \"EMAIL_ADDR\"" STDOUT.puts "since you haven't specified any EMAIL_ADDR, for testing purposes we're using #{default}" default end p sanitizer.apply(parser.parse_with_debug(input)) parslet-2.0.0/example/empty.rb000066400000000000000000000004441362225661100163070ustar00rootroot00000000000000# Basically just demonstrates that you can leave rules empty and get a nice # NotImplementedError. A way to quickly spec out your parser rules? $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' class Parser < Parslet::Parser rule(:empty) { } end Parser.new.empty.parslet parslet-2.0.0/example/erb.rb000066400000000000000000000026461362225661100157270ustar00rootroot00000000000000# Example that demonstrates how a simple erb-like parser could be constructed. $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' class ErbParser < Parslet::Parser rule(:ruby) { (str('%>').absent? >> any).repeat.as(:ruby) } rule(:expression) { (str('=') >> ruby).as(:expression) } rule(:comment) { (str('#') >> ruby).as(:comment) } rule(:code) { ruby.as(:code) } rule(:erb) { expression | comment | code } rule(:erb_with_tags) { str('<%') >> erb >> str('%>') } rule(:text) { (str('<%').absent? >> any).repeat(1) } rule(:text_with_ruby) { (text.as(:text) | erb_with_tags).repeat.as(:text) } root(:text_with_ruby) end parser = ErbParser.new p parser.parse "The value of x is <%= x %>." p parser.parse "<% 1 + 2 %>" p parser.parse "<%# commented %>" evaluator = Parslet::Transform.new do erb_binding = binding rule(:code => { :ruby => simple(:ruby) }) { eval(ruby, erb_binding); '' } rule(:expression => { :ruby => simple(:ruby) }) { eval(ruby, erb_binding) } rule(:comment => { :ruby => simple(:ruby) }) { '' } rule(:text => simple(:text)) { text } rule(:text => sequence(:texts)) { texts.join } end puts evaluator.apply(parser.parse(<<-ERB The <% a = 2 %>not printed result of "a = 2". The <%# a = 1 %>not printed non-evaluated comment "a = 1", see the value of a below. The <%= 'nicely' %> printed result. The <% b = 3 %>value of a is <%= a %>, and b is <%= b %>. ERB )) parslet-2.0.0/example/ip_address.rb000066400000000000000000000061441362225661100172710ustar00rootroot00000000000000# This example is heavily inspired by citrus' ip.citrus. Have a look at both # of these to get some choice! # The grammars in this file conform to the ABNF given in Appendix A of RFC 3986 # Uniform Resource Identifier (URI): Generic Syntax. # # See http://tools.ietf.org/html/rfc3986#appendix-A for more information. $:.unshift File.dirname(__FILE__) + "/../lib" require 'pp' require 'parslet' module IPv4 include Parslet # A host identified by an IPv4 literal address is represented in # dotted-decimal notation (a sequence of four decimal numbers in the range 0 # to 255, separated by "."), as described in [RFC1123] by reference to # [RFC0952]. Note that other forms of dotted notation may be interpreted on # some platforms, as described in Section 7.4, but only the dotted-decimal # form of four octets is allowed by this grammar. rule(:ipv4) { (dec_octet >> str('.') >> dec_octet >> str('.') >> dec_octet >> str('.') >> dec_octet).as(:ipv4) } rule(:dec_octet) { str('25') >> match("[0-5]") | str('2') >> match("[0-4]") >> digit | str('1') >> digit >> digit | match('[1-9]') >> digit | digit } rule(:digit) { match('[0-9]') } end # Must be used in concert with IPv4 module IPv6 include Parslet rule(:colon) { str(':') } rule(:dcolon) { colon >> colon } # h16 : def h16r(times) (h16 >> colon).repeat(times, times) end # : h16 def h16l(times) (colon >> h16).repeat(0,times) end # A 128-bit IPv6 address is divided into eight 16-bit pieces. Each piece is # represented numerically in case-insensitive hexadecimal, using one to four # hexadecimal digits (leading zeroes are permitted). The eight encoded # pieces are given most-significant first, separated by colon characters. # Optionally, the least-significant two pieces may instead be represented in # IPv4 address textual format. A sequence of one or more consecutive # zero-valued 16-bit pieces within the address may be elided, omitting all # their digits and leaving exactly two consecutive colons in their place to # mark the elision. rule(:ipv6) { ( ( h16r(6) | dcolon >> h16r(5) | h16.maybe >> dcolon >> h16r(4) | (h16 >> h16l(1)).maybe >> dcolon >> h16r(3) | (h16 >> h16l(2)).maybe >> dcolon >> h16r(2) | (h16 >> h16l(3)).maybe >> dcolon >> h16r(1) | (h16 >> h16l(4)).maybe >> dcolon ) >> ls32 | (h16 >> h16l(5)).maybe >> dcolon >> h16 | (h16 >> h16l(6)).maybe >> dcolon ).as(:ipv6) } rule(:h16) { hexdigit.repeat(1,4) } rule(:ls32) { (h16 >> colon >> h16) | ipv4 } rule(:hexdigit) { digit | match("[a-fA-F]") } end class Parser include IPv4 include IPv6 def parse(str) (ipv4 | ipv6).parse(str) end end %W( 0.0.0.0 255.255.255.255 255.255.255 1:2:3:4:5:6:7:8 12AD:34FC:A453:1922:: 12AD::34FC 12AD:: :: 1:2 ).each do |address| parser = Parser.new printf "%30s -> ", address begin result = parser.parse(address) puts result.inspect rescue Parslet::ParseFailed => m puts "Failed: #{m}" end end parslet-2.0.0/example/json.rb000066400000000000000000000052071362225661100161240ustar00rootroot00000000000000$:.unshift File.dirname(__FILE__) + "/../lib" # # MIT License - (c) 2011 John Mettraux # require 'rubygems' require 'parslet' # gem install parslet module MyJson class Parser < Parslet::Parser rule(:spaces) { match('\s').repeat(1) } rule(:spaces?) { spaces.maybe } rule(:comma) { spaces? >> str(',') >> spaces? } rule(:digit) { match('[0-9]') } rule(:number) { ( str('-').maybe >> ( str('0') | (match('[1-9]') >> digit.repeat) ) >> ( str('.') >> digit.repeat(1) ).maybe >> ( match('[eE]') >> (str('+') | str('-')).maybe >> digit.repeat(1) ).maybe ).as(:number) } rule(:string) { str('"') >> ( str('\\') >> any | str('"').absent? >> any ).repeat.as(:string) >> str('"') } rule(:array) { str('[') >> spaces? >> (value >> (comma >> value).repeat).maybe.as(:array) >> spaces? >> str(']') } rule(:object) { str('{') >> spaces? >> (entry >> (comma >> entry).repeat).maybe.as(:object) >> spaces? >> str('}') } rule(:value) { string | number | object | array | str('true').as(:true) | str('false').as(:false) | str('null').as(:null) } rule(:entry) { ( string.as(:key) >> spaces? >> str(':') >> spaces? >> value.as(:val) ).as(:entry) } rule(:attribute) { (entry | value).as(:attribute) } rule(:top) { spaces? >> value >> spaces? } root(:top) end class Transformer < Parslet::Transform class Entry < Struct.new(:key, :val); end rule(:array => subtree(:ar)) { ar.is_a?(Array) ? ar : [ ar ] } rule(:object => subtree(:ob)) { (ob.is_a?(Array) ? ob : [ ob ]).inject({}) { |h, e| h[e.key] = e.val; h } } rule(:entry => { :key => simple(:ke), :val => simple(:va) }) { Entry.new(ke, va) } rule(:string => simple(:st)) { st.to_s } rule(:number => simple(:nb)) { nb.match(/[eE\.]/) ? Float(nb) : Integer(nb) } rule(:null => simple(:nu)) { nil } rule(:true => simple(:tr)) { true } rule(:false => simple(:fa)) { false } end def self.parse(s) parser = Parser.new transformer = Transformer.new tree = parser.parse(s) puts; p tree; puts out = transformer.apply(tree) out end end s = %{ [ 1, 2, 3, null, "asdfasdf asdfds", { "a": -1.2 }, { "b": true, "c": false }, 0.1e24, true, false, [ 1 ] ] } out = MyJson.parse(s) p out; puts out == [ 1, 2, 3, nil, "asdfasdf asdfds", { "a" => -1.2 }, { "b" => true, "c" => false }, 0.1e24, true, false, [ 1 ] ] || raise("MyJson is a failure") parslet-2.0.0/example/local.rb000066400000000000000000000016561362225661100162510ustar00rootroot00000000000000 # An exploration of two ideas: # a) Constructing a whole parser inline, without the artificial class around # it. # and: # b) Constructing non-greedy or non-blind parsers by transforming the # grammar. $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' include Parslet a = str('a').repeat >> str('aa') # E1% E2 # # S = E2 | E1 S def this(name, &block); return Parslet::Atoms::Entity.new(name, &block) end def epsilon; any.absent? end # Traditional repetition will try as long as the pattern can be matched and # then give up. This is greedy and blind. a = str('a').as(:e) >> this('a') { a }.as(:rec) | epsilon # Here's a pattern match that is greedy and non-blind. The first pattern # 'a'* will be tried as many times as possible, while still matching the # end pattern 'aa'. b = str('aa').as(:e2) >> epsilon | str('a').as(:e1) >> this('b') { b }.as(:rec) p a.parse('aaaa') p b p b.parse('aaaa') parslet-2.0.0/example/mathn.rb000066400000000000000000000022551362225661100162620ustar00rootroot00000000000000# Demonstrates that we have a compatibility fix to mathn's weird idea of # integer mathematics. # This was contributed by Jonathan Hinkle (https://github.com/hynkle). Thanks! $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' require 'parslet/convenience' include Parslet def attempt_parse possible_whitespace = match['\s'].repeat cephalopod = str('octopus') | str('squid') parenthesized_cephalopod = str('(') >> possible_whitespace >> cephalopod >> possible_whitespace >> str(')') parser = possible_whitespace >> parenthesized_cephalopod >> possible_whitespace # This parse fails, but that is not the point. When mathn is in the current # ruby environment, it modifies integer division in a way that makes # parslet loop indefinitely. parser.parse %{(\nsqeed)\n} rescue Parslet::ParseFailed end attempt_parse puts 'it terminates before we require mathn' puts "requiring mathn now" # mathn was deprecated as of Ruby 2.5 if RUBY_VERSION.gsub(/[^\d]/, '').to_i < 250 require 'mathn' end puts "and trying again (will hang without the fix)" attempt_parse # but it doesn't terminate after requiring mathn puts "okay!" parslet-2.0.0/example/minilisp.rb000066400000000000000000000042051362225661100167740ustar00rootroot00000000000000# Reproduces [1] using parslet. # [1] http://thingsaaronmade.com/blog/a-quick-intro-to-writing-a-parser-using-treetop.html $:.unshift File.dirname(__FILE__) + "/../lib" require 'pp' require 'parslet' require 'parslet/convenience' module MiniLisp class Parser < Parslet::Parser root :expression rule(:expression) { space? >> str('(') >> space? >> body >> str(')') >> space? } rule(:body) { (expression | identifier | float | integer | string).repeat.as(:exp) } rule(:space) { match('\s').repeat(1) } rule(:space?) { space.maybe } rule(:identifier) { (match('[a-zA-Z=*]') >> match('[a-zA-Z=*_]').repeat).as(:identifier) >> space? } rule(:float) { ( integer >> ( str('.') >> match('[0-9]').repeat(1) | str('e') >> match('[0-9]').repeat(1) ).as(:e) ).as(:float) >> space? } rule(:integer) { ((str('+') | str('-')).maybe >> match("[0-9]").repeat(1)).as(:integer) >> space? } rule(:string) { str('"') >> ( str('\\') >> any | str('"').absent? >> any ).repeat.as(:string) >> str('"') >> space? } end class Transform include Parslet attr_reader :t def initialize @t = Parslet::Transform.new # To understand these, take a look at what comes out of the parser. t.rule(:identifier => simple(:ident)) { ident.to_sym } t.rule(:string => simple(:str)) { str } t.rule(:integer => simple(:int)) { Integer(int) } t.rule(:float=>{:integer=> simple(:a), :e=> simple(:b)}) { Float(a + b) } t.rule(:exp => subtree(:exp)) { exp } end def do(tree) t.apply(tree) end end end parser = MiniLisp::Parser.new transform = MiniLisp::Transform.new result = parser.parse_with_debug %Q{ (define test (lambda () (begin (display "something") (display 1) (display 3.08)))) } # Transform the result pp transform.do(result) if result # Thereby reducing it to the earlier problem: # http://github.com/kschiess/toylisp parslet-2.0.0/example/modularity.rb000066400000000000000000000021611362225661100173400ustar00rootroot00000000000000$:.unshift File.dirname(__FILE__) + "/../lib" require 'pp' require "parslet" # Demonstrates modular parsers, split out over many classes. Please look at # ip_address.rb as well. module ALanguage include Parslet # Parslet rules are really a special kind of method. Mix them into your # classes! rule(:a_language) { str('aaa') } end # Parslet parsers are parslet atoms as well. Create an instance and chain them # to your other rules. # class BLanguage < Parslet::Parser root :blang rule(:blang) { str('bbb') } end # Parslet atoms are really Ruby values, pass them around. c_language = Parslet.str('ccc') class Language < Parslet::Parser def initialize(c_language) @c_language = c_language super() end root :root include ALanguage rule(:root) { str('a(') >> a_language >> str(')') >> space | str('b(') >> BLanguage.new >> str(')') >> space | str('c(') >> @c_language >> str(')') >> space } rule(:space) { str(' ').maybe } end Language.new(c_language).parse('a(aaa)') Language.new(c_language).parse('b(bbb)') Language.new(c_language).parse('c(ccc)')parslet-2.0.0/example/nested_errors.rb000066400000000000000000000043401362225661100200260ustar00rootroot00000000000000$:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' require 'parslet/convenience' # This example demonstrates tree error reporting in a real life example. # The parser code has been contributed by John Mettraux. def prettify(str) puts " "*3 + " "*4 + "." + " "*4 + "10" + " "*3 + "." + " "*4 + "20" str.lines.each_with_index do |line, index| printf "%02d %s\n", index+1, line.chomp end end class Parser < Parslet::Parser # commons rule(:space) { match('[ \t]').repeat(1) } rule(:space?) { space.maybe } rule(:newline) { match('[\r\n]') } rule(:comment) { str('#') >> match('[^\r\n]').repeat } rule(:line_separator) { (space? >> ((comment.maybe >> newline) | str(';')) >> space?).repeat(1) } rule(:blank) { line_separator | space } rule(:blank?) { blank.maybe } rule(:identifier) { match('[a-zA-Z0-9_]').repeat(1) } # res_statement rule(:reference) { (str('@').repeat(1,2) >> identifier).as(:reference) } rule(:res_action_or_link) { str('.').as(:dot) >> (identifier >> str('?').maybe ).as(:name) >> str('()') } rule(:res_actions) { ( reference ).as(:resources) >> ( res_action_or_link.as(:res_action) ).repeat(0).as(:res_actions) } rule(:res_statement) { res_actions >> (str(':') >> identifier.as(:name)).maybe.as(:res_field) } # expression rule(:expression) { res_statement } # body rule(:body) { (line_separator >> (block | expression)).repeat(1).as(:body) >> line_separator } # blocks rule(:begin_block) { (str('concurrent').as(:type) >> space).maybe.as(:pre) >> str('begin').as(:begin) >> body >> str('end') } rule(:define_block) { str('define').as(:define) >> space >> identifier.as(:name) >> str('()') >> body >> str('end') } rule(:block) { define_block | begin_block } # root rule(:radix) { line_separator.maybe >> block >> line_separator.maybe } root(:radix) end ds = [ %{ define f() @res.name end }, %{ define f() begin @res.name end end } ] ds.each do |d| puts '-' * 80 prettify(d) parser = Parser.new begin parser.parse_with_debug(d) end end puts '-' * 80parslet-2.0.0/example/optimized_erb.rb000066400000000000000000000025161362225661100200070ustar00rootroot00000000000000# Please also look at the more naive 'erb.rb'. This shows how to optimize an # ERB like parser using parslet. $:.unshift File.join(File.dirname(__FILE__), "/../lib") require 'parslet' require './qed/applique/gobbleup' require 'parslet/accelerator' class ErbParser < Parslet::Parser rule(:ruby) { (str('%>').absent? >> any).repeat.as(:ruby) } rule(:expression) { (str('=') >> ruby).as(:expression) } rule(:comment) { (str('#') >> ruby).as(:comment) } rule(:code) { ruby.as(:code) } rule(:erb) { expression | comment | code } rule(:erb_with_tags) { str('<%') >> erb >> str('%>') } rule(:text) { (str('<%').absent? >> any).repeat(1) } rule(:text_with_ruby) { (text.as(:text) | erb_with_tags).repeat.as(:text) } root(:text_with_ruby) end parser = ErbParser.new A = Parslet::Accelerator optimized = A.apply(parser, A.rule((A.str(:x).absent? >> A.any).repeat(1)) { GobbleUp.new(x, 1) }, A.rule((A.str(:x).absent? >> A.any).repeat(0)) { GobbleUp.new(x, 0) }) input = File.read(File.dirname(__FILE__) + "/big.erb") # Remove the comment marks here to see what difference the optimisation makes. # Commented out for the acceptance tests to run. # # require 'benchmark' # Benchmark.bm(7) do |bm| # bm.report('original') { parser.parse(input) } # bm.report('gobble') { optimized.parse(input) } # end p optimized.parse(input)parslet-2.0.0/example/output/000077500000000000000000000000001362225661100161625ustar00rootroot00000000000000parslet-2.0.0/example/output/boolean_algebra.out000066400000000000000000000001761362225661100220130ustar00rootroot00000000000000{:and=> {:left=>{:var=>"1"@3}, :right=>{:or=>{:left=>{:var=>"2"@13}, :right=>{:var=>"3"@21}}}}} [["1", "2"], ["1", "3"]] parslet-2.0.0/example/output/calc.out000066400000000000000000000000351362225661100176130ustar00rootroot00000000000000123*2 (command line): -> 246 parslet-2.0.0/example/output/capture.out000066400000000000000000000001521362225661100203540ustar00rootroot00000000000000[{:line=>"Text1\n"@9}, {:doc=>[{:line=>"Text3\n"@23}, {:line=>"Text4\n"@29}]}, {:line=>"\nText2\n"@41}] parslet-2.0.0/example/output/comments.out000066400000000000000000000004341362225661100205410ustar00rootroot00000000000000[{:exp=>{:a=>"a"@3}}, {:line=>"// line comment"@7}, {:exp=>{:a=>"a"@25}}, {:exp=>{:a=>"a"@27}}, {:exp=>[{:a=>"a"@29}, {:line=>"// line comment"@31}]}, {:exp=>[{:a=>"a"@49}, {:multi=>"/* inline comment */"@51}]}, {:exp=>{:a=>"a"@72}}, {:multi=>"/* multiline\n comment */"@77}] parslet-2.0.0/example/output/deepest_errors.out000066400000000000000000000075311362225661100217460ustar00rootroot00000000000000-------------------------------------------------------------------------------- . 10 . 20 01 02 define f() 03 @res.name 04 end 05 Failed to match sequence (LINE_SEPARATOR? BLOCK LINE_SEPARATOR?) at line 2 char 5. `- Expected one of [DEFINE_BLOCK, BEGIN_BLOCK] at line 2 char 5. |- Failed to match sequence (define:'define' SPACE name:IDENTIFIER '()' BODY 'end') at line 2 char 15. | `- Failed to match sequence (body:((LINE_SEPARATOR (BLOCK / EXPRESSION)){1, }) LINE_SEPARATOR) at line 3 char 11. | `- Expected at least 1 of SPACE? (COMMENT? NEWLINE / ';') SPACE? at line 3 char 11. | `- Failed to match sequence (SPACE? (COMMENT? NEWLINE / ';') SPACE?) at line 3 char 11. | `- Expected one of [COMMENT? NEWLINE, ';'] at line 3 char 11. | |- Failed to match sequence (COMMENT? NEWLINE) at line 3 char 11. | | `- Expected "()", but got "\n " at line 3 char 16. | `- Expected "()", but got "\n " at line 3 char 16. `- Failed to match sequence (pre:((type:'concurrent' SPACE)?) begin:'begin' BODY 'end') at line 2 char 5. `- Expected "()", but got "\n " at line 3 char 16. -------------------------------------------------------------------------------- . 10 . 20 01 02 define f() 03 begin 04 @res.name 05 end 06 end 07 Failed to match sequence (LINE_SEPARATOR? BLOCK LINE_SEPARATOR?) at line 2 char 5. `- Expected one of [DEFINE_BLOCK, BEGIN_BLOCK] at line 2 char 5. |- Failed to match sequence (define:'define' SPACE name:IDENTIFIER '()' BODY 'end') at line 2 char 15. | `- Failed to match sequence (body:((LINE_SEPARATOR (BLOCK / EXPRESSION)){1, }) LINE_SEPARATOR) at line 2 char 15. | `- Expected at least 1 of LINE_SEPARATOR (BLOCK / EXPRESSION) at line 2 char 15. | `- Failed to match sequence (LINE_SEPARATOR (BLOCK / EXPRESSION)) at line 3 char 7. | `- Expected one of [BLOCK, EXPRESSION] at line 3 char 7. | |- Expected one of [DEFINE_BLOCK, BEGIN_BLOCK] at line 3 char 7. | | |- Failed to match sequence (define:'define' SPACE name:IDENTIFIER '()' BODY 'end') at line 3 char 7. | | | `- Expected "define", but got "begin\n" at line 3 char 7. | | `- Failed to match sequence (pre:((type:'concurrent' SPACE)?) begin:'begin' BODY 'end') at line 3 char 12. | | `- Failed to match sequence (body:((LINE_SEPARATOR (BLOCK / EXPRESSION)){1, }) LINE_SEPARATOR) at line 4 char 13. | | `- Expected at least 1 of SPACE? (COMMENT? NEWLINE / ';') SPACE? at line 4 char 13. | | `- Failed to match sequence (SPACE? (COMMENT? NEWLINE / ';') SPACE?) at line 4 char 13. | | `- Expected one of [COMMENT? NEWLINE, ';'] at line 4 char 13. | | |- Failed to match sequence (COMMENT? NEWLINE) at line 4 char 13. | | | `- Expected "()", but got "\n " at line 4 char 18. | | `- Expected "()", but got "\n " at line 4 char 18. | `- Failed to match sequence (RES_ACTIONS res_field:((':' name:IDENTIFIER)?)) at line 3 char 7. | `- Failed to match sequence (resources:REFERENCE res_actions:(res_action:RES_ACTION_OR_LINK{0, })) at line 3 char 7. | `- Failed to match sequence ('@'{1, 2} IDENTIFIER) at line 3 char 7. | `- Expected at least 1 of '@' at line 3 char 7. | `- Expected "()", but got "\n " at line 4 char 18. `- Failed to match sequence (pre:((type:'concurrent' SPACE)?) begin:'begin' BODY 'end') at line 2 char 5. `- Expected "()", but got "\n " at line 4 char 18. -------------------------------------------------------------------------------- parslet-2.0.0/example/output/documentation.err000066400000000000000000000005111362225661100215420ustar00rootroot00000000000000/Users/kaspar/git_work/own/parslet/lib/parslet/atoms/base.rb:326:in `parse_failed': Don't know what to do with bbbb at line 1 char 1. (Parslet::ParseFailed) from /Users/kaspar/git_work/own/parslet/lib/parslet/atoms/base.rb:55:in `parse' from example/documentation.rb:13:in `parse' from example/documentation.rb:18:in `
' parslet-2.0.0/example/output/documentation.out000066400000000000000000000000111362225661100215540ustar00rootroot00000000000000"aaaa"@0 parslet-2.0.0/example/output/email_parser.out000066400000000000000000000001631362225661100213560ustar00rootroot00000000000000since you haven't specified any EMAIL_ADDR, for testing purposes we're using a.b.c.d@gmail.com "a.b.c.d@gmail.com" parslet-2.0.0/example/output/empty.err000066400000000000000000000002031362225661100200250ustar00rootroot00000000000000example/empty.rb:13:in `
': rule(:empty) { ... } returns nil. Still not implemented, but already used? (NotImplementedError) parslet-2.0.0/example/output/erb.out000066400000000000000000000005371362225661100174700ustar00rootroot00000000000000{:text=>[{:text=>"The value of x is "@0}, {:expression=>{:ruby=>" x "@21}}, {:text=>"."@26}]} {:text=>[{:code=>{:ruby=>" 1 + 2 "@2}}]} {:text=>[{:comment=>{:ruby=>" commented "@3}}]} The not printed result of "a = 2". The not printed non-evaluated comment "a = 1", see the value of a below. The nicely printed result. The value of a is 2, and b is 3. parslet-2.0.0/example/output/ignore.out000066400000000000000000000000071362225661100201730ustar00rootroot00000000000000"ac"@0 parslet-2.0.0/example/output/ignore_whitespace.out000066400000000000000000000000651362225661100224130ustar00rootroot00000000000000[{:a=>"a"@0}, {:a=>"a"@1}, {:a=>"a"@5}, {:a=>"a"@7}] parslet-2.0.0/example/output/ip_address.out000066400000000000000000000011161362225661100210270ustar00rootroot00000000000000 0.0.0.0 -> {:ipv4=>"0.0.0.0"@0} 255.255.255.255 -> {:ipv4=>"255.255.255.255"@0} 255.255.255 -> Failed: Expected one of [IPV4, IPV6] at line 1 char 1. 1:2:3:4:5:6:7:8 -> {:ipv6=>"1:2:3:4:5:6:7:8"@0} 12AD:34FC:A453:1922:: -> {:ipv6=>"12AD:34FC:A453:1922::"@0} 12AD::34FC -> {:ipv6=>"12AD::34FC"@0} 12AD:: -> {:ipv6=>"12AD::"@0} :: -> {:ipv6=>"::"@0} 1:2 -> Failed: Expected one of [IPV4, IPV6] at line 1 char 1. parslet-2.0.0/example/output/json.out000066400000000000000000000010271362225661100176640ustar00rootroot00000000000000 {:array=>[{:number=>"1"@5}, {:number=>"2"@8}, {:number=>"3"@11}, {:null=>"null"@14}, {:string=>"asdfasdf asdfds"@25}, {:object=>{:entry=>{:key=>{:string=>"a"@46}, :val=>{:number=>"-1.2"@50}}}}, {:object=>[{:entry=>{:key=>{:string=>"b"@61}, :val=>{:true=>"true"@65}}}, {:entry=>{:key=>{:string=>"c"@72}, :val=>{:false=>"false"@76}}}]}, {:number=>"0.1e24"@89}, {:true=>"true"@97}, {:false=>"false"@103}, {:array=>{:number=>"1"@112}}]} [1, 2, 3, nil, "asdfasdf asdfds", {"a"=>-1.2}, {"b"=>true, "c"=>false}, 1.0e+23, true, false, [1]] parslet-2.0.0/example/output/local.out000066400000000000000000000002401362225661100200010ustar00rootroot00000000000000{:e=>"a"@0, :rec=>{:e=>"a"@1, :rec=>{:e=>"a"@2, :rec=>{:e=>"a"@3, :rec=>nil}}}} e2:'aa' !. / e1:'a' rec:B {:e1=>"a"@0, :rec=>{:e1=>"a"@1, :rec=>{:e2=>"aa"@2}}} parslet-2.0.0/example/output/mathn.out000066400000000000000000000001551362225661100200230ustar00rootroot00000000000000it terminates before we require mathn requiring mathn now and trying again (will hang without the fix) okay! parslet-2.0.0/example/output/minilisp.out000066400000000000000000000001551362225661100205400ustar00rootroot00000000000000[:define, :test, [:lambda, [], [:begin, [:display, "something"@54], [:display, 1], [:display, 3.08]]]] parslet-2.0.0/example/output/modularity.out000066400000000000000000000000001362225661100210720ustar00rootroot00000000000000parslet-2.0.0/example/output/nested_errors.out000066400000000000000000000075171362225661100216030ustar00rootroot00000000000000-------------------------------------------------------------------------------- . 10 . 20 01 02 define f() 03 @res.name 04 end 05 Failed to match sequence (LINE_SEPARATOR? BLOCK LINE_SEPARATOR?) at line 2 char 5. `- Expected one of [DEFINE_BLOCK, BEGIN_BLOCK] at line 2 char 5. |- Failed to match sequence (define:'define' SPACE name:IDENTIFIER '()' BODY 'end') at line 2 char 15. | `- Failed to match sequence (body:((LINE_SEPARATOR (BLOCK / EXPRESSION)){1, }) LINE_SEPARATOR) at line 3 char 11. | `- Expected at least 1 of SPACE? (COMMENT? NEWLINE / ';') SPACE? at line 3 char 11. | `- Failed to match sequence (SPACE? (COMMENT? NEWLINE / ';') SPACE?) at line 3 char 11. | `- Expected one of [COMMENT? NEWLINE, ';'] at line 3 char 11. | |- Failed to match sequence (COMMENT? NEWLINE) at line 3 char 11. | | `- Failed to match [\\r\\n] at line 3 char 11. | `- Expected ";", but got "." at line 3 char 11. `- Failed to match sequence (pre:((type:'concurrent' SPACE)?) begin:'begin' BODY 'end') at line 2 char 5. `- Expected "begin", but got "defin" at line 2 char 5. -------------------------------------------------------------------------------- . 10 . 20 01 02 define f() 03 begin 04 @res.name 05 end 06 end 07 Failed to match sequence (LINE_SEPARATOR? BLOCK LINE_SEPARATOR?) at line 2 char 5. `- Expected one of [DEFINE_BLOCK, BEGIN_BLOCK] at line 2 char 5. |- Failed to match sequence (define:'define' SPACE name:IDENTIFIER '()' BODY 'end') at line 2 char 15. | `- Failed to match sequence (body:((LINE_SEPARATOR (BLOCK / EXPRESSION)){1, }) LINE_SEPARATOR) at line 2 char 15. | `- Expected at least 1 of LINE_SEPARATOR (BLOCK / EXPRESSION) at line 2 char 15. | `- Failed to match sequence (LINE_SEPARATOR (BLOCK / EXPRESSION)) at line 3 char 7. | `- Expected one of [BLOCK, EXPRESSION] at line 3 char 7. | |- Expected one of [DEFINE_BLOCK, BEGIN_BLOCK] at line 3 char 7. | | |- Failed to match sequence (define:'define' SPACE name:IDENTIFIER '()' BODY 'end') at line 3 char 7. | | | `- Expected "define", but got "begin\n" at line 3 char 7. | | `- Failed to match sequence (pre:((type:'concurrent' SPACE)?) begin:'begin' BODY 'end') at line 3 char 12. | | `- Failed to match sequence (body:((LINE_SEPARATOR (BLOCK / EXPRESSION)){1, }) LINE_SEPARATOR) at line 4 char 13. | | `- Expected at least 1 of SPACE? (COMMENT? NEWLINE / ';') SPACE? at line 4 char 13. | | `- Failed to match sequence (SPACE? (COMMENT? NEWLINE / ';') SPACE?) at line 4 char 13. | | `- Expected one of [COMMENT? NEWLINE, ';'] at line 4 char 13. | | |- Failed to match sequence (COMMENT? NEWLINE) at line 4 char 13. | | | `- Failed to match [\\r\\n] at line 4 char 13. | | `- Expected ";", but got "." at line 4 char 13. | `- Failed to match sequence (RES_ACTIONS res_field:((':' name:IDENTIFIER)?)) at line 3 char 7. | `- Failed to match sequence (resources:REFERENCE res_actions:(res_action:RES_ACTION_OR_LINK{0, })) at line 3 char 7. | `- Failed to match sequence ('@'{1, 2} IDENTIFIER) at line 3 char 7. | `- Expected at least 1 of '@' at line 3 char 7. | `- Expected "@", but got "b" at line 3 char 7. `- Failed to match sequence (pre:((type:'concurrent' SPACE)?) begin:'begin' BODY 'end') at line 2 char 5. `- Expected "begin", but got "defin" at line 2 char 5. -------------------------------------------------------------------------------- parslet-2.0.0/example/output/optimized_erb.out000066400000000000000000000126231362225661100215530ustar00rootroot00000000000000{:text=>[{:text=>"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\n"@0}, {:expression=>{:ruby=>" erb tag "@2685}}, {:text=>"\n\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n"@2696}]} parslet-2.0.0/example/output/parens.out000066400000000000000000000006601362225661100202050ustar00rootroot00000000000000 (): {:l=>"("@0, :m=>nil, :r=>")"@1} (1 parens) (()): {:l=>"("@0, :m=>{:l=>"("@1, :m=>nil, :r=>")"@2}, :r=>")"@3} (2 parens) ((((())))): {:l=>"("@0, :m=>{:l=>"("@1, :m=>{:l=>"("@2, :m=>{:l=>"("@3, :m=>{:l=>"("@4, :m=>nil, :r=>")"@5}, :r=>")"@6}, :r=>")"@7}, :r=>")"@8}, :r=>")"@9} (5 parens) ((()): Failed to match sequence (l:'(' m:(BALANCED?) r:')') at line 1 char 6. parslet-2.0.0/example/output/prec_calc.out000066400000000000000000000001051362225661100206220ustar00rootroot00000000000000a = 1 b = 2 c = 3 * 25 d = 100 + 3*4 {:a=>1, :b=>2, :c=>75, :d=>112} parslet-2.0.0/example/output/readme.out000066400000000000000000000001041362225661100201430ustar00rootroot00000000000000String contents: This is a \"String\" in which you can escape stuff parslet-2.0.0/example/output/scopes.out000066400000000000000000000000151362225661100202030ustar00rootroot00000000000000parses 'aba' parslet-2.0.0/example/output/seasons.out000066400000000000000000000010021362225661100203570ustar00rootroot00000000000000"And when Spring comes" {:bud=>{:stem=>[{:branch=>:leaf}]}} "And when Summer comes" {:bud=>{:stem=>[{:branch=>[:leaf, :flower]}]}} "And when Fall comes" Fruit! Falling Leaves! {:bud=>{:stem=>[{:branch=>[]}]}} "And when Winter comes" {:bud=>{:stem=>[]}} "And when Spring comes" {:bud=>{:stem=>[{:branch=>:leaf}]}} "And when Summer comes" {:bud=>{:stem=>[{:branch=>[:leaf, :flower]}]}} "And when Fall comes" Fruit! Falling Leaves! {:bud=>{:stem=>[{:branch=>[]}]}} "And when Winter comes" {:bud=>{:stem=>[]}} parslet-2.0.0/example/output/sentence.out000066400000000000000000000015741362225661100205260ustar00rootroot00000000000000["RubyKaigi2009ã®ãƒ†ãƒ¼ãƒžã¯ã€ã€Œå¤‰ã‚ã‚‹ï¼å¤‰ãˆã‚‹ã€ã§ã™ã€‚", " å‰å›žã®RubyKaigi2008ã®ãƒ†ãƒ¼ãƒžã§ã‚ã£ãŸã€Œå¤šæ§˜æ€§ã€ã®è¨€è‘‰ã®é€šã‚Šã€ 2008å¹´ã¯Rubyãã®ã‚‚ã®ã«é–¢ã—ã¦ã‚‚ã€ã¾ãŸRubyã®æ´»èºã™ã‚‹èˆžå°ã«é–¢ã—ã¦ã‚‚〠ã¾ã™ã¾ã™å¤šæ§˜åŒ–ãŒé€²ã¿ã¤ã¤ã‚りã¾ã™ã€‚", "RubyKaigi2008ã¯ã€ãã®ã‚ˆã†ãª Rubyã®ç”Ÿæ…‹ç³»ã‚’ã‚らãŸã‚ã¦èªè­˜ã™ã‚‹å ´ã¨ãªã‚Šã¾ã—ãŸã€‚", " ã—ã‹ã—ã€ã“ã†ã—ãŸå¤šæ§˜åŒ–ãŒé€²ã‚€ä¸­ã€ç•°ãªã‚‹è€…åŒå£«ãŒå˜ç´”ã«è·é›¢ã‚’ ç½®ã„ãŸã¾ã¾ã§ã¯ã€ãã®é•ã„ã‚’èªè­˜ã—ãŸã¨ã“ã‚ã§ã‚ã¾ã‚Šæ„味ãŒã‚りã¾ã›ã‚“。", " ç•°ãªã‚‹å®Ÿè£…ã€ç•°ãªã‚‹æ€æƒ³ã€ç•°ãªã‚‹èƒŒæ™¯ã¨ã„ã£ãŸã€æ§˜ã€…ãªå¤šæ§˜æ€§ã‚’ç†è§£ã—ã¤ã¤ã€ ã™ã‚Šåˆã‚ã›ã‚‹ã¹ãã‚‚ã®ã‚’ã™ã‚Šåˆã‚ã›ã€å¤‰ãˆã¦ã„ãã¹ãã¨ã“ã‚ã‚’ 変ãˆã¦ã„ãã“ã¨ãŒã€è±Šã‹ãªæœªæ¥ã¸ã¨ã¤ãªãŒã‚‹é“ã«é•ã„ã‚りã¾ã›ã‚“。"] parslet-2.0.0/example/output/simple_xml.out000066400000000000000000000001051362225661100210600ustar00rootroot00000000000000"verified" {:o=>{:name=>"b"@1}, :i=>"verified", :c=>{:name=>"a"@33}} parslet-2.0.0/example/output/string_parser.out000066400000000000000000000002041362225661100215710ustar00rootroot00000000000000[#, #, #] parslet-2.0.0/example/parens.rb000066400000000000000000000016611362225661100164430ustar00rootroot00000000000000# A small example that demonstrates the power of tree pattern matching. Also # uses '.as(:name)' to construct a tree that can reliably be matched # afterwards. $:.unshift File.dirname(__FILE__) + "/../lib" require 'pp' require 'parslet' module LISP # as in 'lots of insipid and stupid parenthesis' class Parser < Parslet::Parser rule(:balanced) { str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r) } root(:balanced) end class Transform < Parslet::Transform rule(:l => '(', :m => simple(:x), :r => ')') { # innermost :m will contain nil x.nil? ? 1 : x+1 } end end parser = LISP::Parser.new transform = LISP::Transform.new %w! () (()) ((((())))) ((()) !.each do |pexp| begin result = parser.parse(pexp) puts "#{"%20s"%pexp}: #{result.inspect} (#{transform.apply(result)} parens)" rescue Parslet::ParseFailed => m puts "#{"%20s"%pexp}: #{m}" end puts end parslet-2.0.0/example/prec_calc.rb000066400000000000000000000034761362225661100170740ustar00rootroot00000000000000 # A demonstration of the new precedence climbing infix expression parser. $:.unshift File.dirname(__FILE__) + "/../lib" require 'pp' require 'rspec' require 'parslet' require 'parslet/rig/rspec' require 'parslet/convenience' class InfixExpressionParser < Parslet::Parser root :variable_assignment_list rule(:space) { match[' '] } def cts atom atom >> space.repeat end def infix *args Infix.new(*args) end # This is the heart of the infix expression parser: real simple definitions # for all the pieces we need. rule(:mul_op) { cts match['*/'] } rule(:add_op) { cts match['+-'] } rule(:digit) { match['0-9'] } rule(:integer) { cts digit.repeat(1).as(:int) } rule(:expression) { infix_expression(integer, [mul_op, 2, :left], [add_op, 1, :right]) } # And now adding variable assignments to that, just to a) demonstrate this # embedded in a bigger parser, and b) make the example interesting. rule(:variable_assignment_list) { variable_assignment.repeat(1) } rule(:variable_assignment) { identifier.as(:ident) >> equal_sign >> expression.as(:exp) >> eol } rule(:identifier) { cts (match['a-z'] >> match['a-zA-Z0-9'].repeat) } rule(:equal_sign) { cts str('=') } rule(:eol) { cts(str("\n")) | any.absent? } end class InfixInterpreter < Parslet::Transform rule(int: simple(:int)) { Integer(int) } rule(ident: simple(:ident), exp: simple(:result)) { |d| d[:doc][d[:ident].to_s.strip.to_sym] = d[:result] } rule(l: simple(:l), o: /^\*/, r: simple(:r)) { l * r } rule(l: simple(:l), o: /^\+/, r: simple(:r)) { l + r } end input = <> ( str('\\') >> any | str('"').absent? >> any ).repeat.as(:string) >> str('"') # Parse the string and capture parts of the interpretation (:string above) tree = parser.parse('"This is a \\"String\\" in which you can escape stuff"') tree # => {:string=>"This is a \\\"String\\\" in which you can escape stuff"} # Here's how you can grab results from that tree: transform = Parslet::Transform.new do rule(:string => simple(:x)) { puts "String contents: #{x}" } end transform.apply(tree) parslet-2.0.0/example/scopes.rb000066400000000000000000000004221362225661100164410ustar00rootroot00000000000000 $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' include Parslet parser = str('a').capture(:a) >> scope { str('b').capture(:a) } >> dynamic { |s,c| str(c.captures[:a]) } begin parser.parse('aba') puts "parses 'aba'" rescue puts "exception!" endparslet-2.0.0/example/seasons.rb000066400000000000000000000016741362225661100166320ustar00rootroot00000000000000$:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' require 'pp' tree = {:bud => {:stem => []}} class Spring < Parslet::Transform rule(:stem => sequence(:branches)) { {:stem => (branches + [{:branch => :leaf}])} } end class Summer < Parslet::Transform rule(:stem => subtree(:branches)) { new_branches = branches.map { |b| {:branch => [:leaf, :flower]} } {:stem => new_branches} } end class Fall < Parslet::Transform rule(:branch => sequence(:x)) { x.each { |e| puts "Fruit!" if e==:flower } x.each { |e| puts "Falling Leaves!" if e==:leaf } {:branch => []} } end class Winter < Parslet::Transform rule(:stem => subtree(:x)) { {:stem => []} } end def do_seasons(tree) [Spring, Summer, Fall, Winter].each do |season| p "And when #{season} comes" tree = season.new.apply(tree) pp tree puts end tree end # What marvel of life! tree = do_seasons(tree) tree = do_seasons(tree) parslet-2.0.0/example/sentence.rb000066400000000000000000000027751362225661100167660ustar00rootroot00000000000000# encoding: UTF-8 # A small example contributed by John Mettraux (jmettraux) that demonstrates # working with Unicode. This only works on Ruby 1.9. $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' class Parser < Parslet::Parser rule(:sentence) { (match('[^。]').repeat(1) >> str("。")).as(:sentence) } rule(:sentences) { sentence.repeat } root(:sentences) end class Transformer < Parslet::Transform rule(:sentence => simple(:sen)) { sen.to_s } end string = "RubyKaigi2009ã®ãƒ†ãƒ¼ãƒžã¯ã€ã€Œå¤‰ã‚ã‚‹ï¼å¤‰ãˆã‚‹ã€ã§ã™ã€‚ å‰å›žã®" + "RubyKaigi2008ã®ãƒ†ãƒ¼ãƒžã§ã‚ã£ãŸã€Œå¤šæ§˜æ€§ã€ã®è¨€è‘‰ã®é€šã‚Šã€ " + "2008å¹´ã¯Rubyãã®ã‚‚ã®ã«é–¢ã—ã¦ã‚‚ã€ã¾ãŸRubyã®æ´»èºã™ã‚‹èˆžå°ã«é–¢ã—ã¦ã‚‚〠" + "ã¾ã™ã¾ã™å¤šæ§˜åŒ–ãŒé€²ã¿ã¤ã¤ã‚りã¾ã™ã€‚RubyKaigi2008ã¯ã€ãã®ã‚ˆã†ãª " + "Rubyã®ç”Ÿæ…‹ç³»ã‚’ã‚らãŸã‚ã¦èªè­˜ã™ã‚‹å ´ã¨ãªã‚Šã¾ã—ãŸã€‚ ã—ã‹ã—ã€" + "ã“ã†ã—ãŸå¤šæ§˜åŒ–ãŒé€²ã‚€ä¸­ã€ç•°ãªã‚‹è€…åŒå£«ãŒå˜ç´”ã«è·é›¢ã‚’ ç½®ã„ãŸã¾ã¾ã§ã¯ã€" + "ãã®é•ã„ã‚’èªè­˜ã—ãŸã¨ã“ã‚ã§ã‚ã¾ã‚Šæ„味ãŒã‚りã¾ã›ã‚“。 ç•°ãªã‚‹å®Ÿè£…ã€" + "ç•°ãªã‚‹æ€æƒ³ã€ç•°ãªã‚‹èƒŒæ™¯ã¨ã„ã£ãŸã€æ§˜ã€…ãªå¤šæ§˜æ€§ã‚’ç†è§£ã—ã¤ã¤ã€ " + "ã™ã‚Šåˆã‚ã›ã‚‹ã¹ãã‚‚ã®ã‚’ã™ã‚Šåˆã‚ã›ã€å¤‰ãˆã¦ã„ãã¹ãã¨ã“ã‚ã‚’ " + "変ãˆã¦ã„ãã“ã¨ãŒã€è±Šã‹ãªæœªæ¥ã¸ã¨ã¤ãªãŒã‚‹é“ã«é•ã„ã‚りã¾ã›ã‚“。" parser = Parser.new transformer = Transformer.new tree = parser.parse(string) p transformer.apply(tree) parslet-2.0.0/example/simple.lit000066400000000000000000000000521362225661100166220ustar00rootroot00000000000000123 12345 " Some String with \"escapes\"" parslet-2.0.0/example/simple_xml.rb000066400000000000000000000023471362225661100173260ustar00rootroot00000000000000# A simple xml parser. It is simple in the respect as that it doesn't address # any of the complexities of XML. This is ruby 1.9. $:.unshift File.dirname(__FILE__) + "/../lib" require 'pp' require 'parslet' class XML < Parslet::Parser root :document rule(:document) { tag(close: false).as(:o) >> document.as(:i) >> tag(close: true).as(:c) | text } # Perhaps we could have some syntax sugar to make this more easy? # def tag(opts={}) close = opts[:close] || false parslet = str('<') parslet = parslet >> str('/') if close parslet = parslet >> (str('>').absent? >> match("[a-zA-Z]")).repeat(1).as(:name) parslet = parslet >> str('>') parslet end rule(:text) { match('[^<>]').repeat(0) } end def check(xml) r = XML.new.parse(xml) # We'll validate the tree by reducing valid pairs of tags into simply the # string "verified". If the transformation ends on a string, then the # document was 'valid'. # t = Parslet::Transform.new do rule( o: {name: simple(:tag)}, c: {name: simple(:tag)}, i: simple(:t) ) { 'verified' } end t.apply(r) end pp check("some text in the tags") pp check("some text in the tags") parslet-2.0.0/example/string_parser.rb000066400000000000000000000026011362225661100200300ustar00rootroot00000000000000# A more complex parser that illustrates how a compiler might be constructed. # The parser recognizes strings and integer literals and constructs almost a # useful AST from the file contents. require 'pp' $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' include Parslet class LiteralsParser < Parslet::Parser rule :space do (match '[ ]').repeat(1) end rule :literals do (literal >> eol).repeat end rule :literal do (integer | string).as(:literal) >> space.maybe end rule :string do str('"') >> ( (str('\\') >> any) | (str('"').absent? >> any) ).repeat.as(:string) >> str('"') end rule :integer do match('[0-9]').repeat(1).as(:integer) end rule :eol do line_end.repeat(1) end rule :line_end do crlf >> space.maybe end rule :crlf do match('[\r\n]').repeat(1) end root :literals end input_name = File.join(File.dirname(__FILE__), 'simple.lit') file = File.read(input_name) parsetree = LiteralsParser.new.parse(file) class Lit < Struct.new(:text) def to_s text.inspect end end class StringLit < Lit end class IntLit < Lit def to_s text end end transform = Parslet::Transform.new do rule(:literal => {:integer => simple(:x)}) { IntLit.new(x) } rule(:literal => {:string => simple(:s)}) { StringLit.new(s) } end ast = transform.apply(parsetree) pp ast parslet-2.0.0/example/test.lit000066400000000000000000000002101362225661100163040ustar00rootroot00000000000000"THis is a string" "This is another string" "This string is escaped \"embedded quoted stuff \" " 12 // an integer literal and a comment parslet-2.0.0/experiments/000077500000000000000000000000001362225661100155325ustar00rootroot00000000000000parslet-2.0.0/experiments/error_reporter_variants.rb000066400000000000000000000003601362225661100230400ustar00rootroot00000000000000 class NopClass def nop end end nop = NopClass.new fal = nil n = 1000_000 require 'benchmark' Benchmark.bm(9) do |bm| bm.report(:unless) { n.times do method_call if fal end } bm.report(:nop) { n.times do nop.nop end } endparslet-2.0.0/experiments/graphviz.rb000066400000000000000000000052151362225661100177140ustar00rootroot00000000000000# A small experiment that demonstrates graphivz painting of parsers. The parser # here has been taken from the example/ip_parser.rb # $:.unshift File.dirname(__FILE__) + "/../lib" require 'parslet' module IPv4 include Parslet # A host identified by an IPv4 literal address is represented in # dotted-decimal notation (a sequence of four decimal numbers in the range 0 # to 255, separated by "."), as described in [RFC1123] by reference to # [RFC0952]. Note that other forms of dotted notation may be interpreted on # some platforms, as described in Section 7.4, but only the dotted-decimal # form of four octets is allowed by this grammar. rule(:ipv4) { (dec_octet >> str('.') >> dec_octet >> str('.') >> dec_octet >> str('.') >> dec_octet).as(:ipv4) } rule(:dec_octet) { str('25') >> match("[0-5]") | str('2') >> match("[0-4]") >> digit | str('1') >> digit >> digit | match('[1-9]') >> digit | digit } rule(:digit) { match('[0-9]') } end # Must be used in concert with IPv4 module IPv6 include Parslet rule(:colon) { str(':') } rule(:dcolon) { colon >> colon } # h16 : def h16r(times) (h16 >> colon).repeat(times, times) end # : h16 def h16l(times) (colon >> h16).repeat(0,times) end # A 128-bit IPv6 address is divided into eight 16-bit pieces. Each piece is # represented numerically in case-insensitive hexadecimal, using one to four # hexadecimal digits (leading zeroes are permitted). The eight encoded # pieces are given most-significant first, separated by colon characters. # Optionally, the least-significant two pieces may instead be represented in # IPv4 address textual format. A sequence of one or more consecutive # zero-valued 16-bit pieces within the address may be elided, omitting all # their digits and leaving exactly two consecutive colons in their place to # mark the elision. rule(:ipv6) { ( ( h16r(6) | dcolon >> h16r(5) | h16.maybe >> dcolon >> h16r(4) | (h16 >> h16l(1)).maybe >> dcolon >> h16r(3) | (h16 >> h16l(2)).maybe >> dcolon >> h16r(2) | (h16 >> h16l(3)).maybe >> dcolon >> h16r(1) | (h16 >> h16l(4)).maybe >> dcolon ) >> ls32 | (h16 >> h16l(5)).maybe >> dcolon >> h16 | (h16 >> h16l(6)).maybe >> dcolon ).as(:ipv6) } rule(:h16) { hexdigit.repeat(1,4) } rule(:ls32) { (h16 >> colon >> h16) | ipv4 } rule(:hexdigit) { digit | match("[a-fA-F]") } end class IPP < Parslet::Parser include IPv4 include IPv6 rule(:ipp) { ipv4 | ipv6 } root :ipp end require 'parslet/graphviz' IPP.graph(pdf: 'ip.pdf')parslet-2.0.0/experiments/heredoc.rb000066400000000000000000000055421362225661100174760ustar00rootroot00000000000000# An example that demonstrates how you would parse Ruby heredocs. # This is just a hack, however it toys around with some concepts that we # just might use later on. $:.unshift File.dirname(__FILE__) + "/../lib" require 'pp' require 'parslet' require 'parslet/convenience' class Parslet::Atoms::Base def space self >> Parslet.str(' ').repeat end def bind(name) BoundParslet.new(self, name) end def matches(name) BindCompare.new(self, name) end end class BoundParslet < Parslet::Atoms::Base attr_reader :parslet, :name def initialize(parslet, name) super() @parslet, @name = parslet, name end def try(source, context) parslet.try(source, context).tap { |result| set_binding(context, name, flatten(result.result)) } end def set_binding(context, name, value) b = context.instance_variable_get('@bindings') || {} b.store name, value p b context.instance_variable_set('@bindings', b) end def to_s_inner(prec) parslet.to_s(prec) end end class BindCompare < Parslet::Atoms::Base attr_reader :parslet, :name def initialize(parslet, name) super() @parslet, @name = parslet, name end def try(source, context) parslet.try(source, context).tap { |result| unless result.error? value = flatten(result.result) p [value, bound_value(context, name), value == bound_value(context, name)] unless value == bound_value(context, name) p :error_return return error(source, "Bound value doesn't match.") end end } end def bound_value(context, name) b = context.instance_variable_get('@bindings') || {} b[name] end def to_s_inner(prec) parslet.to_s(prec) end end class HereDocs < Parslet::Parser root(:document) # a document of heredocs rule(:document) { heredoc.repeat } # a whole heredoc led by 'a' or 'b' rule(:heredoc) { space? >> intro.as(:intro) >> backticks >> tag.bind(:tag) >> doc.as(:doc) | space? >> eol } # essentially just 'a' or 'b' rule(:intro) { (str('a') | str('b')).space } # the tag that delimits the heredoc rule(:tag) { match['A-Z'].repeat(1) } # the doc itself, ends when tag is found at start of line rule(:doc) { gobble_eol >> doc_line } # a doc_line is either the stop tag followed by nothing # or just any kind of line. rule(:doc_line) { (end_tag.absent? >> gobble_eol).repeat >> end_tag } rule(:end_tag) { tag.matches(:tag) >> space? >> eol } # eats anything until an end of line is found rule(:gobble_eol) { (eol.absent? >> any).repeat >> eol } rule(:eol) { match['\n\r'].repeat(1) } rule(:space?) { str(' ').repeat } rule(:backticks) { str('<<').space } end code = %q( a <').absent? >> any).repeat.as(:ruby) } rule(:expression) { (str('=') >> ruby).as(:expression) } rule(:comment) { (str('#') >> ruby).as(:comment) } rule(:code) { ruby.as(:code) } rule(:erb) { expression | comment | code } rule(:erb_with_tags) { str('<%') >> erb >> str('%>') } rule(:text) { (str('<%').absent? >> any).repeat(1) } rule(:text_with_ruby) { (text.as(:text) | erb_with_tags).repeat.as(:text) } root(:text_with_ruby) end class Parslet::Source def match_excluding str slice_str = @str.check_until(Regexp.new(Regexp.escape(str))) return @str.rest_size unless slice_str return slice_str.size - str.size end end class AbsentParser < Parslet::Atoms::Base def initialize absent @absent = absent end def try(source, context, consume_all) excluding_length = source.match_excluding(@absent) if excluding_length >= 1 return succ(source.consume(excluding_length)) else return context.err(self, source, "Failed absence #{@absent.inspect}.") end end end class Parslet::Optimizer module DSL def >> other Match::Sequence.new(self, other) end def absent? Match::Lookahead.new(false, self) end def repeat(min=0, max=nil) Match::Repetition.new(self, min, max) end end module Match class Base include DSL def visit_parser(root) false end def visit_entity(name, block) false end def visit_named(name, atom) false end def visit_repetition(tag, min, max, atom) false end def visit_alternative(alternatives) false end def visit_sequence(sequence) false end def visit_lookahead(positive, atom) false end def visit_re(regexp) false end def visit_str(str) false end def match(other, bindings) @bindings = bindings other.accept(self) end end class Str < Base def initialize(variable) @variable = variable end def visit_str(str) if bound_value=@bindings[@variable] return bound_value == str else @bindings[@variable] = str return true end end end class Lookahead < Base def initialize(positive, expression) @positive, @expression = positive, expression end def visit_lookahead(positive, atom) positive == @positive && @expression.match(atom, @bindings) end end class Sequence < Base def initialize(*parslets) @parslets = parslets end def visit_sequence(sequence) sequence.zip(@parslets).all? { |atom, expr| expr.match(atom, @bindings) } end end class Repetition < Base def initialize(expression, min, max) @min, @max, @expression = min, max, expression end def visit_repetition(tag, min, max, atom) @min == min && @max == max && @expression.match(atom, @bindings) end end class Re < Base def initialize(variable) @variable = variable end def visit_re(regexp) case @variable when Symbol p [@variable, regexp] fail else @variable == regexp end end end end def self.str(var) Match::Str.new(var) end def self.any Match::Re.new('.') end class Rule def initialize(expression, replacement) @expression, @replacement = expression, replacement end class Context def initialize(bindings) @bindings = bindings end def method_missing(sym, *args, &block) if args.size == 0 && !block && @bindings.has_key?(sym) return @bindings[sym] end super end def call(callable) instance_eval(&callable) end end def match other bindings = {} if @expression.match(other, bindings) return bindings end end def call(bindings) context = Context.new(bindings) context.call(@replacement) end end def self.rule(expression, &replacement) rules << Rule.new(expression, replacement) end def self.rules @rules ||= [] end def rules self.class.rules end class Transform def initialize(rules) @rules = rules @candidates = [] end def default_parser(root) root.accept(self) end def default_entity(name, block) Parslet::Atoms::Entity.new(name) { block.call.accept(self) } end def default_named(name, atom) Parslet::Atoms::Named.new(atom.accept(self), name) end def default_repetition(tag, min, max, atom) Parslet::Atoms::Repetition.new(atom.accept(self), min, max, tag) end def default_alternative(alternatives) Parslet::Atoms::Alternative.new( *alternatives.map { |atom| atom.accept(self) }) end def default_sequence(sequence) Parslet::Atoms::Sequence.new( *sequence.map { |atom| atom.accept(self) }) end def default_lookahead(positive, atom) Parslet::Atoms::Lookahead.new(atom, positive) end def default_re(regexp) Parslet::Atoms::Re.new(regexp) end def default_str(str) Parslet::Atoms::Str.new(str) end def method_missing(sym, *args, &block) if (md=sym.to_s.match(/visit_([a-z]+)/)) && !block # Obtain the default, which is a completely transformed new parser default = self.send("default_#{md[1]}", *args) # Try transforming this parser again at the current level return transform(default) end super end def transform(atom) # Try to match one of the rules against the newly constructed tree. @rules.each do |rule| if bindings=rule.match(atom) return rule.call(bindings) end end # No match, returning new atom. return atom end end def apply(parser) parser.accept(Transform.new(rules)) end end class Optimizer < Parslet::Optimizer rule((str(:x).absent? >> any).repeat(1)) { AbsentParser.new(x) } end parser = ErbParser.new optimized_parser = Optimizer.new.apply(parser) # p optimized_parser.parse(File.read(ARGV.first)) p parser.parse_with_debug(File.read(ARGV.first)) parslet-2.0.0/experiments/return_values.rb000066400000000000000000000007541362225661100207630ustar00rootroot00000000000000def pair [true, "123"] end Success = Struct.new(:value) def struct Success.new("123") end class SuccessO def initialize(value) @value = value end end def klass SuccessO.new("123") end def raise_ex fail "123" end n = 1000_00 require 'benchmark' Benchmark.bm(9) do |bm| bm.report(:pair) { n.times do pair end } bm.report(:struct) { n.times do struct end } bm.report(:klass) { n.times do klass end } bm.report(:throw) { n.times do raise_ex rescue nil end } endparslet-2.0.0/lib/000077500000000000000000000000001362225661100137355ustar00rootroot00000000000000parslet-2.0.0/lib/parslet.rb000066400000000000000000000242341362225661100157410ustar00rootroot00000000000000# A simple parser generator library. Typical usage would look like this: # # require 'parslet' # # class MyParser < Parslet::Parser # rule(:a) { str('a').repeat } # root(:a) # end # # pp MyParser.new.parse('aaaa') # => 'aaaa'@0 # pp MyParser.new.parse('bbbb') # => Parslet::Atoms::ParseFailed: # # Don't know what to do with bbbb at line 1 char 1. # # The simple DSL allows you to define grammars in PEG-style. This kind of # grammar construction does away with the ambiguities that usually comes with # parsers; instead, it allows you to construct grammars that are easier to # debug, since less magic is involved. # # Parslet is typically used in stages: # # # * Parsing the input string; this yields an intermediary tree, see # Parslet.any, Parslet.match, Parslet.str, Parslet::ClassMethods#rule and # Parslet::ClassMethods#root. # * Transformation of the tree into something useful to you, see # Parslet::Transform, Parslet.simple, Parslet.sequence and Parslet.subtree. # # The first stage is traditionally intermingled with the second stage; output # from the second stage is usually called the 'Abstract Syntax Tree' or AST. # # The stages are completely decoupled; You can change your grammar around and # use the second stage to isolate the rest of your code from the changes # you've effected. # # == Further reading # # All parslet atoms are subclasses of {Parslet::Atoms::Base}. You might want to # look at all of those: {Parslet::Atoms::Re}, {Parslet::Atoms::Str}, # {Parslet::Atoms::Repetition}, {Parslet::Atoms::Sequence}, # {Parslet::Atoms::Alternative}. # # == When things go wrong # # A parse that fails will raise {Parslet::ParseFailed}. This exception contains # all the details of what went wrong, including a detailed error trace that # can be printed out as an ascii tree. ({Parslet::Cause}) # module Parslet # Extends classes that include Parslet with the module # {Parslet::ClassMethods}. # def self.included(base) base.extend(ClassMethods) end # Raised when the parse failed to match. It contains the message that should # be presented to the user. More details can be extracted from the # exceptions #parse_failure_cause member: It contains an instance of {Parslet::Cause} that # stores all the details of your failed parse in a tree structure. # # begin # parslet.parse(str) # rescue Parslet::ParseFailed => failure # puts failure.parse_failure_cause.ascii_tree # end # # Alternatively, you can just require 'parslet/convenience' and call the # method #parse_with_debug instead of #parse. This method will never raise # and print error trees to stdout. # # require 'parslet/convenience' # parslet.parse_with_debug(str) # class ParseFailed < StandardError def initialize(message, parse_failure_cause=nil) super(message) @parse_failure_cause = parse_failure_cause end # Why the parse failed. # # @return [Parslet::Cause] attr_reader :parse_failure_cause end module ClassMethods # Define an entity for the parser. This generates a method of the same # name that can be used as part of other patterns. Those methods can be # freely mixed in your parser class with real ruby methods. # # class MyParser # include Parslet # # rule(:bar) { str('bar') } # rule(:twobar) do # bar >> bar # end # # root :twobar # end # def rule(name, opts={}, &definition) undef_method name if method_defined? name define_method(name) do @rules ||= {} # memoization return @rules[name] if @rules.has_key?(name) # Capture the self of the parser class along with the definition. definition_closure = proc { self.instance_eval(&definition) } @rules[name] = Atoms::Entity.new(name, opts[:label], &definition_closure) end end end # Allows for delayed construction of #match. See also Parslet.match. # # @api private class DelayedMatchConstructor def [](str) Atoms::Re.new("[" + str + "]") end end # Returns an atom matching a character class. All regular expressions can be # used, as long as they match only a single character at a time. # # match('[ab]') # will match either 'a' or 'b' # match('[\n\s]') # will match newlines and spaces # # There is also another (convenience) form of this method: # # match['a-z'] # synonymous to match('[a-z]') # match['\n'] # synonymous to match('[\n]') # # @overload match(str) # @param str [String] character class to match (regexp syntax) # @return [Parslet::Atoms::Re] a parslet atom # def match(str=nil) return DelayedMatchConstructor.new unless str return Atoms::Re.new(str) end module_function :match # Returns an atom matching the +str+ given: # # str('class') # will match 'class' # # @param str [String] string to match verbatim # @return [Parslet::Atoms::Str] a parslet atom # def str(str) Atoms::Str.new(str) end module_function :str # Returns an atom matching any character. It acts like the '.' (dot) # character in regular expressions. # # any.parse('a') # => 'a' # # @return [Parslet::Atoms::Re] a parslet atom # def any Atoms::Re.new('.') end module_function :any # Introduces a new capture scope. This means that all old captures stay # accessible, but new values stored will only be available during the block # given and the old values will be restored after the block. # # Example: # # :a will be available until the end of the block. Afterwards, # # :a from the outer scope will be available again, if such a thing # # exists. # scope { str('a').capture(:a) } # def scope(&block) Parslet::Atoms::Scope.new(block) end module_function :scope # Designates a piece of the parser as being dynamic. Dynamic parsers can # either return a parser at runtime, which will be applied on the input, or # return a result from a parse. # # Dynamic parse pieces are never cached and can introduce performance # abnormalitites - use sparingly where other constructs fail. # # Example: # # Parses either 'a' or 'b', depending on the weather # dynamic { rand() < 0.5 ? str('a') : str('b') } # def dynamic(&block) Parslet::Atoms::Dynamic.new(block) end module_function :dynamic # Returns a parslet atom that parses infix expressions. Operations are # specified as a list of tuples, where # atom is simply the parslet atom that matches an operator, precedence is # a number and associativity is either :left or :right. # # Higher precedence indicates that the operation should bind tighter than # other operations with lower precedence. In common algebra, '+' has # lower precedence than '*'. So you would have a precedence of 1 for '+' and # a precedence of 2 for '*'. Only the order relation between these two # counts, so any number would work. # # Associativity is what decides what interpretation to take for strings that # are ambiguous like '1 + 2 + 3'. If '+' is specified as left associative, # the expression would be interpreted as '(1 + 2) + 3'. If right # associativity is chosen, it would be interpreted as '1 + (2 + 3)'. Note # that the hash trees output reflect that choice as well. # # An optional block can be provided in order to manipulate the generated tree. # The block will be called on each operator and passed 3 arguments: the left # operand, the operator, and the right operand. # # Examples: # infix_expression(integer, [add_op, 1, :left]) # # would parse things like '1 + 2' # # infix_expression(integer, [add_op, 1, :left]) { |l,o,r| { :plus => [l, r] } } # # would parse '1 + 2 + 3' as: # # { :plus => [1, { :plus => [2, 3] }] } # # @param element [Parslet::Atoms::Base] elements that take the NUMBER position # in the expression # @param operations [Array<(Parslet::Atoms::Base, Integer, {:left, :right})>] # # @see Parslet::Atoms::Infix # def infix_expression(element, *operations, &reducer) Parslet::Atoms::Infix.new(element, operations, &reducer) end module_function :infix_expression # A special kind of atom that allows embedding whole treetop expressions # into parslet construction. # # # the same as str('a') >> str('b').maybe # exp(%Q("a" "b"?)) # # @param str [String] a treetop expression # @return [Parslet::Atoms::Base] the corresponding parslet parser # def exp(str) Parslet::Expression.new(str).to_parslet end module_function :exp # Returns a placeholder for a tree transformation that will only match a # sequence of elements. The +symbol+ you specify will be the key for the # matched sequence in the returned dictionary. # # # This would match a body element that contains several declarations. # { :body => sequence(:declarations) } # # The above example would match :body => ['a', 'b'], but not # :body => 'a'. # # see {Parslet::Transform} # def sequence(symbol) Pattern::SequenceBind.new(symbol) end module_function :sequence # Returns a placeholder for a tree transformation that will only match # simple elements. This matches everything that #sequence # doesn't match. # # # Matches a single header. # { :header => simple(:header) } # # see {Parslet::Transform} # def simple(symbol) Pattern::SimpleBind.new(symbol) end module_function :simple # Returns a placeholder for tree transformation patterns that will match # any kind of subtree. # # { :expression => subtree(:exp) } # def subtree(symbol) Pattern::SubtreeBind.new(symbol) end module_function :subtree autoload :Expression, 'parslet/expression' end require 'parslet/slice' require 'parslet/cause' require 'parslet/source' require 'parslet/atoms' require 'parslet/pattern' require 'parslet/pattern/binding' require 'parslet/transform' require 'parslet/parser' require 'parslet/error_reporter' require 'parslet/scope' parslet-2.0.0/lib/parslet/000077500000000000000000000000001362225661100154075ustar00rootroot00000000000000parslet-2.0.0/lib/parslet/accelerator.rb000066400000000000000000000106411362225661100202220ustar00rootroot00000000000000 # Optimizes the parsers by pattern matching on the parser atoms and replacing # matches with better versions. See the file qed/accelerators.md for a more # in-depth description. # # Example: # quote = str('"') # parser = quote >> (quote.absent? >> any).repeat >> quote # # A = Accelerator # for making what follows a bit shorter # optimized_parser = A.apply(parser, # A.rule( (A.str(:x).absent? >> A.any).repeat ) { GobbleUp.new(x) }) # # optimized_parser.parse('"Parsing is now fully optimized! (tm)"') # module Parslet::Accelerator # An expression to match against a tree of parser atoms. Normally, an # expression is produced by Parslet::Accelerator.any, # Parslet::Accelerator.str or Parslet::Accelerator.re. # # Expressions can be chained much like parslet atoms can be: # # expr.repeat(1) # matching repetition # expr.absent? # matching absent? # expr.present? # matching present? # expr1 >> expr2 # matching a sequence # expr1 | expr2 # matching an alternation # # @see Parslet::Accelerator.str # @see Parslet::Accelerator.re # @see Parslet::Accelerator.any # # @see Parslet::Accelerator # class Expression attr_reader :type attr_reader :args def initialize(type, *args) @type = type @args = args end # @return [Expression] def >> other_expr join_or_new :seq, other_expr end # @return [Expression] def | other_expr join_or_new :alt, other_expr end # @return [Expression] def absent? Expression.new(:absent, self) end # @return [Expression] def present? Expression.new(:present, self) end # @return [Expression] def repeat min=0, max=nil Expression.new(:rep, min, max, self) end # @return [Expression] def as name Expression.new(:as, name) end # @api private # @return [Expression] def join_or_new tag, other_expr if type == tag @args << other_expr self else Expression.new(tag, self, other_expr) end end end module_function # Returns a match expression that will match `str` parslet atoms. # # @return [Parslet::Accelerator::Expression] # def str variable, *constraints Expression.new(:str, variable, *constraints) end # Returns a match expression that will match `match` parslet atoms. # # @return [Parslet::Accelerator::Expression] # def re variable, *constraints Expression.new(:re, variable, *constraints) end # Returns a match expression that will match `any` parslet atoms. # # @return [Parslet::Accelerator::Expression] # def any Expression.new(:re, ".") end # Given a parslet atom and an expression, will determine if the expression # matches the atom. If successful, returns the bindings into the pattern # that were made. If no bindings had to be made to make the match successful, # the empty hash is returned. # # @param atom [Parslet::Atoms::Base] parslet atom to match against # @param expr [Parslet::Accelerator::Expression] expression to match # @return [nil, Hash] bindings for the match, nil on failure # def match atom, expr engine = Engine.new return engine.bindings if engine.match(atom, expr) end # Constructs an accelerator rule. A rule is a matching expression and the # code that should be executed once the expression could be bound to a # parser. # # Example: # Accelerator.rule(Accelerator.any) { Parslet.match('.') } # def rule expression, &action [expression, action] end # Given a parslet atom and a set of rules, tries to match the rules # recursively through the parslet atom. Once a rule could be matched, # its action block will be called. # # Example: # quote = str('"') # parser = quote >> (quote.absent? >> any).repeat >> quote # # A = Accelerator # for making what follows a bit shorter # optimized_parser = A.apply(parser, # A.rule( (A.str(:x).absent? >> A.any).repeat ) { GobbleUp.new(x) }) # # optimized_parser.parse('"Parsing is now fully optimized! (tm)"') # # @param atom [Parslet::Atoms::Base] a parser to optimize # @param *rules [Parslet::Accelerator::Rule] rules produced by .rule # @return [Parslet::Atoms::Base] optimized parser # def apply atom, *rules Application.new(atom, rules).call end end require 'parslet/accelerator/engine' require 'parslet/accelerator/application'parslet-2.0.0/lib/parslet/accelerator/000077500000000000000000000000001362225661100176735ustar00rootroot00000000000000parslet-2.0.0/lib/parslet/accelerator/application.rb000066400000000000000000000033121362225661100225220ustar00rootroot00000000000000 # @api private module Parslet::Accelerator class Application def initialize atom, rules @atom = atom @rules = rules end def call @atom.accept(self) end def visit_parser(root) transform root.accept(self) end def visit_entity(name, block) transform Parslet::Atoms::Entity.new(name) { block.call.accept(self) } end def visit_named(name, atom) transform Parslet::Atoms::Named.new(atom.accept(self), name) end def visit_repetition(tag, min, max, atom) transform Parslet::Atoms::Repetition.new(atom.accept(self), min, max, tag) end def visit_alternative(alternatives) transform Parslet::Atoms::Alternative.new( *alternatives.map { |atom| atom.accept(self) }) end def visit_sequence(sequence) transform Parslet::Atoms::Sequence.new( *sequence.map { |atom| atom.accept(self) }) end def visit_lookahead(positive, atom) transform Parslet::Atoms::Lookahead.new(atom, positive) end def visit_re(regexp) transform Parslet::Atoms::Re.new(regexp) end def visit_str(str) transform Parslet::Atoms::Str.new(str) end def transform atom @rules.each do |expr, action| # Try and match each rule in turn binding = Parslet::Accelerator.match(atom, expr) if binding # On a successful match, allow the rule action to transform the # parslet into something new. ctx = Parslet::Context.new(binding) return ctx.instance_eval(&action) end end # rules.each # If no rule matches, this is the fallback - a clean new parslet atom. return atom end end end require 'parslet/context'parslet-2.0.0/lib/parslet/accelerator/engine.rb000066400000000000000000000047531362225661100214760ustar00rootroot00000000000000 require 'parslet/atoms/visitor' module Parslet::Accelerator # @api private class Apply def initialize(engine, expr) @engine = engine @expr = expr end def visit_parser(root) false end def visit_entity(name, block) false end def visit_named(name, atom) match(:as) do |key| @engine.try_bind(key, name) end end def visit_repetition(tag, min, max, atom) match(:rep) do |e_min, e_max, expr| e_min == min && e_max == max && @engine.match(atom, expr) end end def visit_alternative(alternatives) match(:alt) do |*expressions| return false if alternatives.size != expressions.size alternatives.zip(expressions).all? do |atom, expr| @engine.match(atom, expr) end end end def visit_sequence(sequence) match(:seq) do |*expressions| return false if sequence.size != expressions.size sequence.zip(expressions).all? do |atom, expr| @engine.match(atom, expr) end end end def visit_lookahead(positive, atom) match(:absent) do |expr| return positive == false && @engine.match(atom, expr) end match(:present) do |expr| return positive == true && @engine.match(atom, expr) end end def visit_re(regexp) match(:re) do |*bind_conditions| bind_conditions.all? { |bind_cond| @engine.try_bind(bind_cond, regexp) } end end def visit_str(str) match(:str) do |*bind_conditions| bind_conditions.all? { |bind_cond| @engine.try_bind(bind_cond, str) } end end def match(type_tag) expr_tag = @expr.type if expr_tag == type_tag yield *@expr.args end end end # @api private class Engine attr_reader :bindings def initialize @bindings = {} end def match(atom, expr) atom.accept( Apply.new(self, expr)) end def try_bind(variable, value) if bound? variable return value == lookup(variable) else case variable when Symbol bind(variable, value) else # This does not look like a variable - let's try matching it against # the value: variable === value end end end def bound? var @bindings.has_key? var end def lookup var @bindings[var] end def bind var, val @bindings[var] = val end end endparslet-2.0.0/lib/parslet/atoms.rb000066400000000000000000000020431362225661100170560ustar00rootroot00000000000000 # This is where parslets name comes from: Small parser atoms. # module Parslet::Atoms # The precedence module controls parenthesis during the #inspect printing # of parslets. It is not relevant to other aspects of the parsing. # module Precedence BASE = 1 # everything else LOOKAHEAD = 2 # &SOMETHING REPETITION = 3 # 'a'+, 'a'? SEQUENCE = 4 # 'a' 'b' ALTERNATE = 5 # 'a' | 'b' OUTER = 6 # printing is done here. end require 'parslet/atoms/can_flatten' require 'parslet/atoms/context' require 'parslet/atoms/dsl' require 'parslet/atoms/base' require 'parslet/atoms/ignored' require 'parslet/atoms/named' require 'parslet/atoms/lookahead' require 'parslet/atoms/alternative' require 'parslet/atoms/sequence' require 'parslet/atoms/repetition' require 'parslet/atoms/re' require 'parslet/atoms/str' require 'parslet/atoms/entity' require 'parslet/atoms/capture' require 'parslet/atoms/dynamic' require 'parslet/atoms/scope' require 'parslet/atoms/infix' end parslet-2.0.0/lib/parslet/atoms/000077500000000000000000000000001362225661100165325ustar00rootroot00000000000000parslet-2.0.0/lib/parslet/atoms/alternative.rb000066400000000000000000000025051362225661100213770ustar00rootroot00000000000000 # Alternative during matching. Contains a list of parslets that is tried each # one in turn. Only fails if all alternatives fail. # # Example: # # str('a') | str('b') # matches either 'a' or 'b' # class Parslet::Atoms::Alternative < Parslet::Atoms::Base attr_reader :alternatives # Constructs an Alternative instance using all given parslets in the order # given. This is what happens if you call '|' on existing parslets, like # this: # # str('a') | str('b') # def initialize(*alternatives) super() @alternatives = alternatives end #--- # Don't construct a hanging tree of Alternative parslets, instead store them # all here. This reduces the number of objects created. #+++ def |(parslet) self.class.new(*@alternatives + [parslet]) end def error_msg @error_msg ||= "Expected one of #{alternatives.inspect}" end def try(source, context, consume_all) errors = alternatives.map { |a| success, value = result = a.apply(source, context, consume_all) return result if success # Aggregate all errors value } # If we reach this point, all alternatives have failed. context.err(self, source, error_msg, errors) end precedence ALTERNATE def to_s_inner(prec) alternatives.map { |a| a.to_s(prec) }.join(' / ') end end parslet-2.0.0/lib/parslet/atoms/base.rb000066400000000000000000000117001362225661100177700ustar00rootroot00000000000000# Base class for all parslets, handles orchestration of calls and implements # a lot of the operator and chaining methods. # # Also see Parslet::Atoms::DSL chaining parslet atoms together. # class Parslet::Atoms::Base include Parslet::Atoms::Precedence include Parslet::Atoms::DSL include Parslet::Atoms::CanFlatten # Parslet label as provided in grammar attr_accessor :label # Given a string or an IO object, this will attempt a parse of its contents # and return a result. If the parse fails, a Parslet::ParseFailed exception # will be thrown. # # @param io [String, Source] input for the parse process # @option options [Parslet::ErrorReporter] :reporter error reporter to use, # defaults to Parslet::ErrorReporter::Tree # @option options [Boolean] :prefix Should a prefix match be accepted? # (default: false) # @return [Hash, Array, Parslet::Slice] PORO (Plain old Ruby object) result # tree # def parse(io, options={}) source = io.respond_to?(:line_and_column) ? io : Parslet::Source.new(io) # Try to cheat. Assuming that we'll be able to parse the input, don't # run error reporting code. success, value = setup_and_apply(source, nil, !options[:prefix]) # If we didn't succeed the parse, raise an exception for the user. # Stack trace will be off, but the error tree should explain the reason # it failed. unless success # Cheating has not paid off. Now pay the cost: Rerun the parse, # gathering error information in the process. reporter = options[:reporter] || Parslet::ErrorReporter::Tree.new source.bytepos = 0 success, value = setup_and_apply(source, reporter, !options[:prefix]) fail "Assertion failed: success was true when parsing with reporter" \ if success # Value is a Parslet::Cause, which can be turned into an exception: value.raise fail "NEVER REACHED" end # assert: success is true # Extra input is now handled inline with the rest of the parsing. If # really we have success == true, prefix: false and still some input # is left dangling, that is a BUG. if !options[:prefix] && source.chars_left > 0 fail "BUG: New error strategy should not reach this point." end return flatten(value) end # Creates a context for parsing and applies the current atom to the input. # Returns the parse result. # # @return [] Result of the parse. If the first member is # true, the parse has succeeded. def setup_and_apply(source, error_reporter, consume_all) context = Parslet::Atoms::Context.new(error_reporter) apply(source, context, consume_all) end # Calls the #try method of this parslet. Success consumes input, error will # rewind the input. # # @param source [Parslet::Source] source to read input from # @param context [Parslet::Atoms::Context] context to use for the parsing # @param consume_all [Boolean] true if the current parse must consume # all input by itself. def apply(source, context, consume_all=false) old_pos = source.bytepos success, _ = result = context.try_with_cache(self, source, consume_all) if success # Notify context context.succ(source) # If a consume_all parse was made and doesn't result in the consumption # of all the input, that is considered an error. if consume_all && source.chars_left>0 # Read 10 characters ahead. Why ten? I don't know. offending_pos = source.pos offending_input = source.consume(10) # Rewind input (as happens always in error case) source.bytepos = old_pos return context.err_at( self, source, "Don't know what to do with #{offending_input.to_s.inspect}", offending_pos ) end # Looks like the parse was successful after all. Don't rewind the input. return result end # We only reach this point if the parse has failed. Rewind the input. source.bytepos = old_pos return result end # Override this in your Atoms::Base subclasses to implement parsing # behaviour. # def try(source, context, consume_all) raise NotImplementedError, \ "Atoms::Base doesn't have behaviour, please implement #try(source, context)." end # Returns true if this atom can be cached in the packrat cache. Most parslet # atoms are cached, so this always returns true, unless overridden. # def cached? true end # Debug printing - in Treetop syntax. # def self.precedence(prec) define_method(:precedence) { prec } end precedence BASE def to_s(outer_prec=OUTER) str = label || to_s_inner(precedence) if outer_prec < precedence "(#{str})" else str end end def inspect to_s(OUTER) end private # Produces an instance of Success and returns it. # def succ(result) [true, result] end end parslet-2.0.0/lib/parslet/atoms/can_flatten.rb000066400000000000000000000110231362225661100213320ustar00rootroot00000000000000 module Parslet::Atoms # A series of helper functions that have the common topic of flattening # result values into the intermediary tree that consists of Ruby Hashes and # Arrays. # # This module has one main function, #flatten, that takes an annotated # structure as input and returns the reduced form that users expect from # Atom#parse. # # NOTE: Since all of these functions are just that, functions without # side effects, they are in a module and not in a class. Its hard to draw # the line sometimes, but this is beyond. # module CanFlatten # Takes a mixed value coming out of a parslet and converts it to a return # value for the user by dropping things and merging hashes. # # Named is set to true if this result will be embedded in a Hash result from # naming something using .as(...). It changes the folding # semantics of repetition. # def flatten(value, named=false) # Passes through everything that isn't an array of things return value unless value.instance_of? Array # Extracts the s-expression tag tag, *tail = value # Merges arrays: result = tail. map { |e| flatten(e) } # first flatten each element case tag when :sequence return flatten_sequence(result) when :maybe return named ? result.first : result.first || '' when :repetition return flatten_repetition(result, named) end fail "BUG: Unknown tag #{tag.inspect}." end # Lisp style fold left where the first element builds the basis for # an inject. # def foldl(list, &block) return '' if list.empty? list[1..-1].inject(list.first, &block) end # Flatten results from a sequence of parslets. # # @api private # def flatten_sequence(list) foldl(list.compact) { |r, e| # and then merge flat elements merge_fold(r, e) } end # @api private def merge_fold(l, r) # equal pairs: merge. ---------------------------------------------------- if l.class == r.class if l.is_a?(Hash) warn_about_duplicate_keys(l, r) return l.merge(r) else return l + r end end # unequal pairs: hoist to same level. ------------------------------------ # Maybe classes are not equal, but both are stringlike? if l.respond_to?(:to_str) && r.respond_to?(:to_str) # if we're merging a String with a Slice, the slice wins. return r if r.respond_to? :to_slice return l if l.respond_to? :to_slice fail "NOTREACHED: What other stringlike classes are there?" end # special case: If one of them is a string/slice, the other is more important return l if r.respond_to? :to_str return r if l.respond_to? :to_str # otherwise just create an array for one of them to live in return l + [r] if r.class == Hash return [l] + r if l.class == Hash fail "Unhandled case when foldr'ing sequence." end # Flatten results from a repetition of a single parslet. named indicates # whether the user has named the result or not. If the user has named # the results, we want to leave an empty list alone - otherwise it is # turned into an empty string. # # @api private # def flatten_repetition(list, named) if list.any? { |e| e.instance_of?(Hash) } # If keyed subtrees are in the array, we'll want to discard all # strings inbetween. To keep them, name them. return list.select { |e| e.instance_of?(Hash) } end if list.any? { |e| e.instance_of?(Array) } # If any arrays are nested in this array, flatten all arrays to this # level. return list. select { |e| e.instance_of?(Array) }. flatten(1) end # Consistent handling of empty lists, when we act on a named result return [] if named && list.empty? # If there are only strings, concatenate them and return that. foldl(list.compact) { |s,e| s+e } end # That annoying warning 'Duplicate subtrees while merging result' comes # from here. You should add more '.as(...)' names to your intermediary tree. # def warn_about_duplicate_keys(h1, h2) d = h1.keys & h2.keys unless d.empty? warn "Duplicate subtrees while merging result of \n #{self.inspect}\nonly the values"+ " of the latter will be kept. (keys: #{d.inspect})" end end end endparslet-2.0.0/lib/parslet/atoms/capture.rb000066400000000000000000000017001362225661100205200ustar00rootroot00000000000000 # Stores the result of matching an atom against input in the #captures in # parse context. Doing so will allow you to pull parts of the ongoing parse # out later and use them to match other pieces of input. # # Example: # # After this, context.captures[:an_a] returns 'a' # str('a').capture(:an_a) # # # Capture and use of the capture: (matches either 'aa' or 'bb') # match['ab'].capture(:first) >> # dynamic { |src, ctx| str(ctx.captures[:first]) } # class Parslet::Atoms::Capture < Parslet::Atoms::Base attr_reader :parslet, :name def initialize(parslet, name) super() @parslet, @name = parslet, name end def apply(source, context, consume_all) success, value = result = parslet.apply(source, context, consume_all) if success context.captures[name.to_sym] = flatten(value) end return result end def to_s_inner(prec) "(#{name.inspect} = #{parslet.to_s(prec)})" end end parslet-2.0.0/lib/parslet/atoms/context.rb000066400000000000000000000057601362225661100205530ustar00rootroot00000000000000module Parslet::Atoms # Helper class that implements a transient cache that maps position and # parslet object to results. This is used for memoization in the packrat # style. # # Also, error reporter is stored here and error reporting happens through # this class. This makes the reporting pluggable. # class Context # @param reporter [#err, #err_at] Error reporter (leave empty for default # reporter) def initialize(reporter=Parslet::ErrorReporter::Tree.new) @cache = Hash.new { |h, k| h[k] = {} } @reporter = reporter @captures = Parslet::Scope.new end # Caches a parse answer for obj at source.pos. Applying the same parslet # at one position of input always yields the same result, unless the input # has changed. # # We need the entire source here so we can ask for how many characters # were consumed by a successful parse. Imitation of such a parse must # advance the input pos by the same amount of bytes. # def try_with_cache(obj, source, consume_all) beg = source.bytepos # Not in cache yet? Return early. unless entry = lookup(obj, beg) result = obj.try(source, self, consume_all) if obj.cached? set obj, beg, [result, source.bytepos-beg] end return result end # the condition in unless has returned true, so entry is not nil. result, advance = entry # The data we're skipping here has been read before. (since it is in # the cache) PLUS the actual contents are not interesting anymore since # we know obj matches at beg. So skip reading. source.bytepos = beg + advance return result end # Report an error at a given position. # @see ErrorReporter # def err_at(*args) return [false, @reporter.err_at(*args)] if @reporter return [false, nil] end # Report an error. # @see ErrorReporter # def err(*args) return [false, @reporter.err(*args)] if @reporter return [false, nil] end # Report a successful parse. # @see ErrorReporter::Contextual # def succ(*args) return [true, @reporter.succ(*args)] if @reporter return [true, nil] end # Returns the current captures made on the input (see # Parslet::Atoms::Base#capture). Use as follows: # # context.captures[:foobar] # => returns capture :foobar # attr_reader :captures # Starts a new scope. Use the #scope method of Parslet::Atoms::DSL # to call this. # def scope captures.push yield ensure captures.pop end private # NOTE These methods use #object_id directly, since that seems to bring the # most performance benefit. This is a hot spot; going through # Atoms::Base#hash doesn't yield as much. # def lookup(obj, pos) @cache[pos][obj.object_id] end def set(obj, pos, val) @cache[pos][obj.object_id] = val end end endparslet-2.0.0/lib/parslet/atoms/dsl.rb000066400000000000000000000057621362225661100176530ustar00rootroot00000000000000 # A mixin module that defines operations that can be called on any subclass # of Parslet::Atoms::Base. These operations make parslets atoms chainable and # allow combination of parslet atoms to form bigger parsers. # # Example: # # str('foo') >> str('bar') # str('f').repeat # any.absent? # also called The Epsilon # module Parslet::Atoms::DSL # Construct a new atom that repeats the current atom min times at least and # at most max times. max can be nil to indicate that no maximum is present. # # Example: # # match any number of 'a's # str('a').repeat # # # match between 1 and 3 'a's # str('a').repeat(1,3) # def repeat(min=0, max=nil) Parslet::Atoms::Repetition.new(self, min, max) end # Returns a new parslet atom that is only maybe present in the input. This # is synonymous to calling #repeat(0,1). Generated tree value will be # either nil (if atom is not present in the input) or the matched subtree. # # Example: # str('foo').maybe # def maybe Parslet::Atoms::Repetition.new(self, 0, 1, :maybe) end # Returns a new parslet atom that will not show up in the output. This # is synonymous to calling #repeat(0,1). Generated tree value will always be # nil. # # Example: # str('foo').ignore # def ignore Parslet::Atoms::Ignored.new(self) end # Chains two parslet atoms together as a sequence. # # Example: # str('a') >> str('b') # def >>(parslet) Parslet::Atoms::Sequence.new(self, parslet) end # Chains two parslet atoms together to express alternation. A match will # always be attempted with the parslet on the left side first. If it doesn't # match, the right side will be tried. # # Example: # # matches either 'a' OR 'b' # str('a') | str('b') # def |(parslet) Parslet::Atoms::Alternative.new(self, parslet) end # Tests for absence of a parslet atom in the input stream without consuming # it. # # Example: # # Only proceed the parse if 'a' is absent. # str('a').absent? # def absent? Parslet::Atoms::Lookahead.new(self, false) end # Tests for presence of a parslet atom in the input stream without consuming # it. # # Example: # # Only proceed the parse if 'a' is present. # str('a').present? # def present? Parslet::Atoms::Lookahead.new(self, true) end # Marks a parslet atom as important for the tree output. This must be used # to achieve meaningful output from the #parse method. # # Example: # str('a').as(:b) # will produce {:b => 'a'} # def as(name) Parslet::Atoms::Named.new(self, name) end # Captures a part of the input and stores it under the name given. This # is very useful to create self-referential parses. A capture stores # the result of its parse (may be complex) on a successful parse action. # # Example: # str('a').capture(:b) # will store captures[:b] == 'a' # def capture(name) Parslet::Atoms::Capture.new(self, name) end endparslet-2.0.0/lib/parslet/atoms/dynamic.rb000066400000000000000000000013041362225661100205010ustar00rootroot00000000000000# Evaluates a block at parse time. The result from the block must be a parser # (something which implements #apply). In the first case, the parser will then # be applied to the input, creating the result. # # Dynamic parses are never cached. # # Example: # dynamic { rand < 0.5 ? str('a') : str('b') } # class Parslet::Atoms::Dynamic < Parslet::Atoms::Base attr_reader :block def initialize(block) @block = block end def cached? false end def try(source, context, consume_all) result = block.call(source, context) # Result is a parslet atom. return result.apply(source, context, consume_all) end def to_s_inner(prec) "dynamic { ... }" end end parslet-2.0.0/lib/parslet/atoms/entity.rb000066400000000000000000000024611362225661100203760ustar00rootroot00000000000000# This wraps pieces of parslet definition and gives them a name. The wrapped # piece is lazily evaluated and cached. This has two purposes: # # * Avoid infinite recursion during evaluation of the definition # * Be able to print things by their name, not by their sometimes # complicated content. # # You don't normally use this directly, instead you should generate it by # using the structuring method Parslet.rule. # class Parslet::Atoms::Entity < Parslet::Atoms::Base attr_reader :name, :block def initialize(name, label=nil, &block) super() @name = name @label = label @block = block @parslet = nil end def try(source, context, consume_all) parslet.apply(source, context, consume_all) end def parslet return @parslet unless @parslet.nil? @parslet = @block.call raise_not_implemented if @parslet.nil? @parslet.label = @label @parslet end def to_s_inner(prec) name.to_s.upcase end private def raise_not_implemented trace = caller.reject {|l| l =~ %r{#{Regexp.escape(__FILE__)}}} # blatantly stolen from dependencies.rb in activesupport exception = NotImplementedError.new("rule(#{name.inspect}) { ... } returns nil. Still not implemented, but already used?") exception.set_backtrace(trace) raise exception end end parslet-2.0.0/lib/parslet/atoms/ignored.rb000066400000000000000000000010151362225661100205030ustar00rootroot00000000000000# Ignores the result of a match. # # Example: # # str('foo') # will return 'foo', # str('foo').ignore # will return nil # class Parslet::Atoms::Ignored < Parslet::Atoms::Base attr_reader :parslet def initialize(parslet) super() @parslet = parslet end def apply(source, context, consume_all) success, _ = result = parslet.apply(source, context, consume_all) return result unless success succ(nil) end def to_s_inner(prec) "ignored(#{parslet.to_s(prec)})" end end parslet-2.0.0/lib/parslet/atoms/infix.rb000066400000000000000000000060621362225661100202000ustar00rootroot00000000000000class Parslet::Atoms::Infix < Parslet::Atoms::Base attr_reader :element, :operations, :reducer def initialize(element, operations, &reducer) super() @element = element @operations = operations @reducer = reducer || lambda { |left, op, right| {l: left, o: op, r: right} } end def try(source, context, consume_all) return catch(:error) { return succ( produce_tree( precedence_climb(source, context, consume_all))) } end # Turns an array of the form ['1', '+', ['2', '*', '3']] into a hash that # reflects the same structure. # def produce_tree(ary) return ary unless ary.kind_of? Array left = ary.shift until ary.empty? op, right = ary.shift(2) # p [left, op, right] if right.kind_of? Array # Subexpression -> Subhash left = reducer.call(left, op, produce_tree(right)) else left = reducer.call(left, op, right) end end left end # A precedence climbing algorithm married to parslet, as described here # http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing/ # # @note Error handling in this routine is done by throwing :error and # as a value the error to return to parslet. This avoids cluttering # the recursion logic here with parslet error handling. # def precedence_climb(source, context, consume_all, current_prec=1, needs_element=false) result = [] # To even begin parsing an arithmetic expression, there needs to be # at least one @element. success, value = @element.apply(source, context, false) unless success throw :error, context.err(self, source, "#{@element.inspect} was expected", [value]) end result << flatten(value, true) # Loop until we fail on operator matching or until input runs out. loop do op_pos = source.bytepos op_match, prec, assoc = match_operation(source, context, false) # If no operator could be matched here, one of several cases # applies: # # - end of file # - end of expression # - syntax error # # We abort matching the expression here. break unless op_match if prec >= current_prec next_prec = (assoc == :left) ? prec+1 : prec result << op_match result << precedence_climb( source, context, consume_all, next_prec, true) else source.bytepos = op_pos return unwrap(result) end end return unwrap(result) end def unwrap expr expr.size == 1 ? expr.first : expr end def match_operation(source, context, consume_all) errors = [] @operations.each do |op_atom, prec, assoc| success, value = op_atom.apply(source, context, consume_all) return flatten(value, true), prec, assoc if success # assert: this was in fact an error, accumulate errors << value end return nil end def to_s_inner(prec) ops = @operations.map { |o, _, _| o.inspect }.join(', ') "infix_expression(#{@element.inspect}, [#{ops}])" end endparslet-2.0.0/lib/parslet/atoms/lookahead.rb000066400000000000000000000026151362225661100210120ustar00rootroot00000000000000# Either positive or negative lookahead, doesn't consume its input. # # Example: # # str('foo').present? # matches when the input contains 'foo', but leaves it # class Parslet::Atoms::Lookahead < Parslet::Atoms::Base attr_reader :positive attr_reader :bound_parslet def initialize(bound_parslet, positive=true) super() # Model positive and negative lookahead by testing this flag. @positive = positive @bound_parslet = bound_parslet end def error_msgs @error_msgs ||= { :positive => ["Input should start with ", bound_parslet], :negative => ["Input should not start with ", bound_parslet] } end def try(source, context, consume_all) rewind_pos = source.bytepos error_pos = source.pos success, _ = bound_parslet.apply(source, context, consume_all) if positive return succ(nil) if success return context.err_at(self, source, error_msgs[:positive], error_pos) else return succ(nil) unless success return context.err_at(self, source, error_msgs[:negative], error_pos) end # This is probably the only parslet that rewinds its input in #try. # Lookaheads NEVER consume their input, even on success, that's why. ensure source.bytepos = rewind_pos end precedence LOOKAHEAD def to_s_inner(prec) @char = positive ? '&' : '!' "#{@char}#{bound_parslet.to_s(prec)}" end end parslet-2.0.0/lib/parslet/atoms/named.rb000066400000000000000000000012771362225661100201520ustar00rootroot00000000000000# Names a match to influence tree construction. # # Example: # # str('foo') # will return 'foo', # str('foo').as(:foo) # will return :foo => 'foo' # class Parslet::Atoms::Named < Parslet::Atoms::Base attr_reader :parslet, :name def initialize(parslet, name) super() @parslet, @name = parslet, name end def apply(source, context, consume_all) success, value = result = parslet.apply(source, context, consume_all) return result unless success succ( produce_return_value( value)) end def to_s_inner(prec) "#{name}:#{parslet.to_s(prec)}" end private def produce_return_value(val) { name => flatten(val, true) } end end parslet-2.0.0/lib/parslet/atoms/re.rb000066400000000000000000000020021362225661100174570ustar00rootroot00000000000000# Matches a special kind of regular expression that only ever matches one # character at a time. Useful members of this family are: character # ranges, \\w, \\d, \\r, \\n, ... # # Example: # # match('[a-z]') # matches a-z # match('\s') # like regexps: matches space characters # class Parslet::Atoms::Re < Parslet::Atoms::Base attr_reader :match, :re def initialize(match) super() @match = match.to_s @re = Regexp.new(self.match, Regexp::MULTILINE) end def error_msgs @error_msgs ||= { premature: 'Premature end of input', failed: "Failed to match #{match.inspect[1..-2]}" } end def try(source, context, consume_all) return succ(source.consume(1)) if source.matches?(@re) # No string could be read return context.err(self, source, error_msgs[:premature]) \ if source.chars_left < 1 # No match return context.err(self, source, error_msgs[:failed]) end def to_s_inner(prec) match.inspect[1..-2] end end parslet-2.0.0/lib/parslet/atoms/repetition.rb000066400000000000000000000044341362225661100212460ustar00rootroot00000000000000 # Matches a parslet repeatedly. # # Example: # # str('a').repeat(1,3) # matches 'a' at least once, but at most three times # str('a').maybe # matches 'a' if it is present in the input (repeat(0,1)) # class Parslet::Atoms::Repetition < Parslet::Atoms::Base attr_reader :min, :max, :parslet def initialize(parslet, min, max, tag=:repetition) super() raise ArgumentError, "Asking for zero repetitions of a parslet. (#{parslet.inspect} repeating #{min},#{max})" \ if max == 0 @parslet = parslet @min = min @max = max @tag = tag end def error_msgs @error_msgs ||= { minrep: "Expected at least #{min} of #{parslet.inspect}", unconsumed: 'Extra input after last repetition' } end def try(source, context, consume_all) occ = 0 accum = [@tag] # initialize the result array with the tag (for flattening) start_pos = source.pos break_on = nil loop do success, value = parslet.apply(source, context, false) break_on = value break unless success occ += 1 accum << value # If we're not greedy (max is defined), check if that has been reached. return succ(accum) if max && occ>=max end # Last attempt to match parslet was a failure, failure reason in break_on. # Greedy matcher has produced a failure. Check if occ (which will # contain the number of successes) is >= min. return context.err_at( self, source, error_msgs[:minrep], start_pos, [break_on]) if occ < min # consume_all is true, that means that we're inside the part of the parser # that should consume the input completely. Repetition failing here means # probably that we didn't. # # We have a special clause to create an error here because otherwise # break_on would get thrown away. It turns out, that contains very # interesting information in a lot of cases. # return context.err( self, source, error_msgs[:unconsumed], [break_on]) if consume_all && source.chars_left>0 return succ(accum) end precedence REPETITION def to_s_inner(prec) minmax = "{#{min}, #{max}}" minmax = '?' if min == 0 && max == 1 parslet.to_s(prec) + minmax end end parslet-2.0.0/lib/parslet/atoms/scope.rb000066400000000000000000000007601362225661100201730ustar00rootroot00000000000000# Starts a new scope in the parsing process. Please also see the #captures # method. # class Parslet::Atoms::Scope < Parslet::Atoms::Base attr_reader :block def initialize(block) super() @block = block end def cached? false end def apply(source, context, consume_all) context.scope do parslet = block.call return parslet.apply(source, context, consume_all) end end def to_s_inner(prec) "scope { #{block.call.to_s(prec)} }" end end parslet-2.0.0/lib/parslet/atoms/sequence.rb000066400000000000000000000020461362225661100206710ustar00rootroot00000000000000# A sequence of parslets, matched from left to right. Denoted by '>>' # # Example: # # str('a') >> str('b') # matches 'a', then 'b' # class Parslet::Atoms::Sequence < Parslet::Atoms::Base attr_reader :parslets def initialize(*parslets) super() @parslets = parslets end def error_msgs @error_msgs ||= { failed: "Failed to match sequence (#{inspect})" } end def >>(parslet) self.class.new(* @parslets+[parslet]) end def try(source, context, consume_all) # Presize an array result = Array.new(parslets.size + 1) result[0] = :sequence parslets.each_with_index do |p, idx| child_consume_all = consume_all && (idx == parslets.size-1) success, value = p.apply(source, context, child_consume_all) unless success return context.err(self, source, error_msgs[:failed], [value]) end result[idx+1] = value end return succ(result) end precedence SEQUENCE def to_s_inner(prec) parslets.map { |p| p.to_s(prec) }.join(' ') end end parslet-2.0.0/lib/parslet/atoms/str.rb000066400000000000000000000016241362225661100176720ustar00rootroot00000000000000# Matches a string of characters. # # Example: # # str('foo') # matches 'foo' # class Parslet::Atoms::Str < Parslet::Atoms::Base attr_reader :str def initialize(str) super() @str = str.to_s @pat = Regexp.new(Regexp.escape(str)) @len = str.size end def error_msgs @error_msgs ||= { premature: 'Premature end of input', failed: "Expected #{str.inspect}, but got " } end def try(source, context, consume_all) return succ(source.consume(@len)) if source.matches?(@pat) # Input ending early: return context.err(self, source, error_msgs[:premature]) \ if source.chars_left<@len # Expected something, but got something else instead: error_pos = source.pos return context.err_at( self, source, [error_msgs[:failed], source.consume(@len)], error_pos) end def to_s_inner(prec) "'#{str}'" end end parslet-2.0.0/lib/parslet/atoms/visitor.rb000066400000000000000000000036141362225661100205620ustar00rootroot00000000000000# Augments all parslet atoms with an accept method that will call back # to the visitor given. # module Parslet::Atoms class Base def accept(visitor) raise NotImplementedError, "No #accept method on #{self.class.name}." end end class Str # Call back visitors #visit_str method. See parslet/export for an example. # def accept(visitor) visitor.visit_str(str) end end class Entity # Call back visitors #visit_entity method. See parslet/export for an # example. # def accept(visitor) visitor.visit_entity(name, block) end end class Named # Call back visitors #visit_named method. See parslet/export for an # example. # def accept(visitor) visitor.visit_named(name, parslet) end end class Sequence # Call back visitors #visit_sequence method. See parslet/export for an # example. # def accept(visitor) visitor.visit_sequence(parslets) end end class Repetition # Call back visitors #visit_repetition method. See parslet/export for an # example. # def accept(visitor) visitor.visit_repetition(@tag, min, max, parslet) end end class Alternative # Call back visitors #visit_alternative method. See parslet/export for an # example. # def accept(visitor) visitor.visit_alternative(alternatives) end end class Lookahead # Call back visitors #visit_lookahead method. See parslet/export for an # example. # def accept(visitor) visitor.visit_lookahead(positive, bound_parslet) end end class Re # Call back visitors #visit_re method. See parslet/export for an example. # def accept(visitor) visitor.visit_re(match) end end end class Parslet::Parser # Call back visitors #visit_parser method. # def accept(visitor) visitor.visit_parser(root) end end parslet-2.0.0/lib/parslet/cause.rb000066400000000000000000000064411362225661100170410ustar00rootroot00000000000000module Parslet # Represents a cause why a parse did fail. A lot of these objects are # constructed - not all of the causes turn out to be failures for the whole # parse. # class Cause def initialize(message, source, pos, children) @message, @source, @pos, @children, @context = message, source, pos, children, nil end # @return [String, Array] A string or an array of message pieces that # provide failure information. Use #to_s to get a formatted string. attr_reader :message # @return [Parslet::Source] Source that was parsed when this error # happend. Mainly used for line number information. attr_reader :source # Location of the error. # # @return [Fixnum] Position where the error happened. (character offset) attr_reader :pos # When this cause is part of a tree of error causes: child nodes for this # node. Very often carries the reasons for this cause. # # @return [Array] A list of reasons for this cause. def children @children ||= [] end # Appends 'at line LINE char CHAR' to the string given. Use +pos+ to # override the position of the +source+. This method returns an object # that can be turned into a string using #to_s. # # @param source [Parslet::Source] source that was parsed when this error # happened # @param pos [Fixnum] position of error # @param str [String, Array] message parts # @param children [Array] child nodes for this error tree # @return [Parslet::Cause] a new instance of {Parslet::Cause} # def self.format(source, pos, str, children=[]) self.new(str, source, pos, children) end # Update error message to include context provided by label # Update all child causes too (the same context applies to all causes) def set_label(l) @context = " when parsing #{l}" children.each { |c| c.set_label(l) } end def to_s line, column = source.line_and_column(pos) # Allow message to be a list of objects. Join them here, since we now # really need it. Array(message).map { |o| o.respond_to?(:to_slice) ? o.str.inspect : o.to_s }.join + " at line #{line} char #{column}#{@context}." end # Signals to the outside that the parse has failed. Use this in # conjunction with .format for nice error messages. # def raise(exception_klass=Parslet::ParseFailed) exception = exception_klass.new(self.to_s, self) Kernel.raise exception end # Returns an ascii tree representation of the causes of this node and its # children. # def ascii_tree StringIO.new.tap { |io| recursive_ascii_tree(self, io, [true]) }. string end private def recursive_ascii_tree(node, stream, curved) append_prefix(stream, curved) stream.puts node.to_s node.children.each do |child| last_child = (node.children.last == child) recursive_ascii_tree(child, stream, curved + [last_child]) end end def append_prefix(stream, curved) return if curved.size < 2 curved[1..-2].each do |c| stream.print c ? " " : "| " end stream.print curved.last ? "`- " : "|- " end end endparslet-2.0.0/lib/parslet/context.rb000066400000000000000000000007461362225661100174270ustar00rootroot00000000000000# Provides a context for tree transformations to run in. The context allows # accessing each of the bindings in the bindings hash as local method. # # Example: # # ctx = Context.new(:a => :b) # ctx.instance_eval do # a # => :b # end # # @api private class Parslet::Context include Parslet def initialize(bindings) bindings.each do |key, value| singleton_class.send(:define_method, key) { value } instance_variable_set("@#{key}", value) end end endparslet-2.0.0/lib/parslet/convenience.rb000066400000000000000000000012761362225661100202360ustar00rootroot00000000000000class Parslet::Atoms::Base # Packages the common idiom # # begin # tree = parser.parse('something') # rescue Parslet::ParseFailed => error # puts parser.parse_failure_cause.ascii_tree # end # # into a convenient method. # # Usage: # # require 'parslet' # require 'parslet/convenience' # # class FooParser < Parslet::Parser # rule(:foo) { str('foo') } # root(:foo) # end # # FooParser.new.parse_with_debug('bar') # # @see Parslet::Atoms::Base#parse # def parse_with_debug str, opts={} parse str, opts rescue Parslet::ParseFailed => error puts error.parse_failure_cause.ascii_tree end endparslet-2.0.0/lib/parslet/error_reporter.rb000066400000000000000000000003071362225661100210070ustar00rootroot00000000000000# A namespace for all error reporters. # module Parslet::ErrorReporter end require 'parslet/error_reporter/tree' require 'parslet/error_reporter/deepest' require 'parslet/error_reporter/contextual' parslet-2.0.0/lib/parslet/error_reporter/000077500000000000000000000000001362225661100204625ustar00rootroot00000000000000parslet-2.0.0/lib/parslet/error_reporter/contextual.rb000066400000000000000000000077731362225661100232130ustar00rootroot00000000000000module Parslet module ErrorReporter # A reporter that tries to improve on the deepest error reporter by # using heuristics to find the most relevant error and provide more # context. # The heuristic chooses the deepest error when parsing a sequence for which # no alternative parsed successfully. # # Given the following parser: # # root(:call) # # rule(:call, label: 'call') { # identifier >> str('.') >> method # } # # rule(:method, label: 'method call') { # identifier >> str('(') >> arguments.maybe >> str(')') # } # # rule(:identifier, label: 'identifier') { # match['[:alnum:]'].repeat(1) # } # # rule(:arguments, label: 'method call arguments') { # argument >> str(',') >> arguments | argument # } # # rule(:argument) { # call | identifier # } # # and the following source: # # foo.bar(a,goo.baz(),c,) # # The contextual reporter returns the following causes: # # 0: Failed to match sequence (identifier '.' method call) at line 1 char 5 # when parsing method call arguments. # 1: Failed to match sequence (identifier '(' method call arguments? ')') at # line 1 char 22 when parsing method call arguments. # 2: Failed to match [[:alnum:]] at line 1 char 23 when parsing method call # arguments. # # (where 2 is a child cause of 1 and 1 a child cause of 0) # # The last piece used by the reporter is the (newly introduced) ability # to attach a label to rules that describe a sequence in the grammar. The # labels are used in two places: # - In the "to_s" of Atom::Base so that any error message uses labels to # refer to atoms # - In the cause error messages to give information about which expression # failed to parse # class Contextual < Deepest def initialize @last_reset_pos = 0 reset end # A sequence expression successfully parsed, reset all errors reported # for previous expressions in the sequence (an alternative matched) # Only reset errors if the position of the source that matched is higher # than the position of the source that was last successful (so we keep # errors that are the "deepest" but for which no alternative succeeded) # def succ(source) source_pos = source.pos.bytepos return if source_pos < @last_reset_pos @last_reset_pos = source_pos reset end # Reset deepest error and its position and sequence index # def reset @deepest_cause = nil @label_pos = -1 end # Produces an error cause that combines the message at the current level # with the errors that happened at a level below (children). # Compute and set label used by Cause to produce error message. # # @param atom [Parslet::Atoms::Base] parslet that failed # @param source [Source] Source that we're using for this parse. (line # number information...) # @param message [String, Array] Error message at this level. # @param children [Array] A list of errors from a deeper level (or nil). # @return [Cause] An error tree combining children with message. # def err(atom, source, message, children=nil) cause = super(atom, source, message, children) if (label = atom.respond_to?(:label) && atom.label) update_label(label, source.pos.bytepos) cause.set_label(@label) end cause end # Update error message label if given label is more relevant. # A label is more relevant if the position of the matched source is # bigger. # # @param label [String] label to apply if more relevant # @param bytepos [Integer] position in source code of matched source # def update_label(label, bytepos) if bytepos >= @label_pos @label_pos = bytepos @label = label end end end end end parslet-2.0.0/lib/parslet/error_reporter/deepest.rb000066400000000000000000000072421362225661100224450ustar00rootroot00000000000000module Parslet module ErrorReporter # Instead of reporting the latest error that happens like {Tree} does, # this class reports the deepest error. Depth is defined here as how # advanced into the input an error happens. The errors close to the # greatest depth tend to be more relevant to the end user, since they # specify what could be done to make them go away. # # More specifically, errors produced by this reporter won't be related to # the structure of the grammar at all. The positions of the errors will # be advanced and convey at every grammar level what the deepest rule # was to fail. # class Deepest def initialize @deepest_cause = nil end # Produces an error cause that combines the message at the current level # with the errors that happened at a level below (children). # # @param atom [Parslet::Atoms::Base] parslet that failed # @param source [Source] Source that we're using for this parse. (line # number information...) # @param message [String, Array] Error message at this level. # @param children [Array] A list of errors from a deeper level (or nil). # @return [Cause] An error tree combining children with message. # def err(atom, source, message, children=nil) position = source.pos cause = Cause.format(source, position, message, children) return deepest(cause) end # Produces an error cause that combines the message at the current level # with the errors that happened at a level below (children). # # @param atom [Parslet::Atoms::Base] parslet that failed # @param source [Source] Source that we're using for this parse. (line # number information...) # @param message [String, Array] Error message at this level. # @param pos [Fixnum] The real position of the error. # @param children [Array] A list of errors from a deeper level (or nil). # @return [Cause] An error tree combining children with message. # def err_at(atom, source, message, pos, children=nil) position = pos cause = Cause.format(source, position, message, children) return deepest(cause) end # Returns the cause that is currently deepest. Mainly for specs. # Notification that an expression successfully parsed # not used, see ErrorReporter::Contextual def succ(source) end # attr_reader :deepest_cause # Checks to see if the lineage of the cause given includes a cause with # an error position deeper than the current deepest cause stored. If # yes, it passes the cause through to the caller. If no, it returns the # current deepest error that was saved as a reference. # def deepest(cause) _, leaf = deepest_child(cause) if !deepest_cause || leaf.pos >= deepest_cause.pos # This error reaches deeper into the input, save it as reference. @deepest_cause = leaf return cause end return deepest_cause end private # Returns the leaf from a given error tree with the biggest rank. # def deepest_child(cause, rank=0) max_child = cause max_rank = rank if cause.children && !cause.children.empty? cause.children.each do |child| c_rank, c_cause = deepest_child(child, rank+1) if c_rank > max_rank max_rank = c_rank max_child = c_cause end end end return max_rank, max_child end end end endparslet-2.0.0/lib/parslet/error_reporter/tree.rb000066400000000000000000000054301362225661100217500ustar00rootroot00000000000000module Parslet module ErrorReporter # An error reporter has two central methods, one for reporting errors at # the current parse position (#err) and one for reporting errors at a # given parse position (#err_at). The reporter can return an object (a # 'cause') that will be returned to the caller along with the information # that the parse failed. # # When reporting errors on the outer levels of your parser, these methods # get passed a list of error objects ('causes') from the inner levels. In # this default implementation, the inner levels are considered error # subtrees and are appended to the generated tree node at each level, # thereby constructing an error tree. # # This error tree will report in parallel with the grammar structure that # failed. A one-to-one correspondence exists between each error in the # tree and the parslet atom that produced that error. # # The implementor is really free to use these return values as he sees # fit. One example would be to return an error state object from these # methods that is then updated as errors cascade up the parse derivation # tree. # class Tree # Produces an error cause that combines the message at the current level # with the errors that happened at a level below (children). # # @param atom [Parslet::Atoms::Base] parslet that failed # @param source [Source] Source that we're using for this parse. (line # number information...) # @param message [String, Array] Error message at this level. # @param children [Array] A list of errors from a deeper level (or nil). # @return [Cause] An error tree combining children with message. # def err(atom, source, message, children=nil) position = source.pos Cause.format(source, position, message, children) end # Produces an error cause that combines the message at the current level # with the errors that happened at a level below (children). # # @param atom [Parslet::Atoms::Base] parslet that failed # @param source [Source] Source that we're using for this parse. (line # number information...) # @param message [String, Array] Error message at this level. # @param pos [Fixnum] The real position of the error. # @param children [Array] A list of errors from a deeper level (or nil). # @return [Cause] An error tree combining children with message. # def err_at(atom, source, message, pos, children=nil) position = pos Cause.format(source, position, message, children) end # Notification that an expression successfully parsed # not used, see ErrorReporter::Contextual def succ(source) end end end end parslet-2.0.0/lib/parslet/export.rb000066400000000000000000000076471362225661100172730ustar00rootroot00000000000000# Allows exporting parslet grammars to other lingos. require 'set' require 'parslet/atoms/visitor' class Parslet::Parser module Visitors class Citrus attr_reader :context, :output def initialize(context) @context = context end def visit_str(str) "\"#{str.inspect[1..-2]}\"" end def visit_re(match) match.to_s end def visit_entity(name, block) context.deferred(name, block) "(#{context.mangle_name(name)})" end def visit_named(name, parslet) parslet.accept(self) end def visit_sequence(parslets) '(' << parslets. map { |el| el.accept(self) }. join(' ') << ')' end def visit_repetition(tag, min, max, parslet) parslet.accept(self) << "#{min}*#{max}" end def visit_alternative(alternatives) '(' << alternatives. map { |el| el.accept(self) }. join(' | ') << ')' end def visit_lookahead(positive, bound_parslet) (positive ? '&' : '!') << bound_parslet.accept(self) end end class Treetop < Citrus def visit_repetition(tag, min, max, parslet) parslet.accept(self) << "#{min}..#{max}" end def visit_alternative(alternatives) '(' << alternatives. map { |el| el.accept(self) }. join(' / ') << ')' end end end # A helper class that formats Citrus and Treetop grammars as a string. # class PrettyPrinter attr_reader :visitor def initialize(visitor_klass) @visitor = visitor_klass.new(self) end # Pretty prints the given parslet using the visitor that has been # configured in initialize. Returns the string representation of the # Citrus or Treetop grammar. # def pretty_print(name, parslet) output = "grammar #{name}\n" output << rule('root', parslet) seen = Set.new loop do # @todo is constantly filled by the visitor (see #deferred). We # keep going until it is empty. break if @todo.empty? name, block = @todo.shift # Track what rules we've already seen. This breaks loops. next if seen.include?(name) seen << name output << rule(name, block.call) end output << "end\n" end # Formats a rule in either dialect. # def rule(name, parslet) " rule #{mangle_name name}\n" << " " << parslet.accept(visitor) << "\n" << " end\n" end # Whenever the visitor encounters an rule in a parslet, it defers the # pretty printing of the rule by calling this method. # def deferred(name, content) @todo ||= [] @todo << [name, content] end # Mangles names so that Citrus and Treetop can live with it. This mostly # transforms some of the things that Ruby allows into other patterns. If # there is collision, we will not detect it for now. # def mangle_name(str) str.to_s.sub(/\?$/, '_p') end end # Exports the current parser instance as a string in the Citrus dialect. # # Example: # # require 'parslet/export' # class MyParser < Parslet::Parser # root(:expression) # rule(:expression) { str('foo') } # end # # MyParser.new.to_citrus # => a citrus grammar as a string # def to_citrus PrettyPrinter.new(Visitors::Citrus). pretty_print(self.class.name, root) end # Exports the current parser instance as a string in the Treetop dialect. # # Example: # # require 'parslet/export' # class MyParser < Parslet::Parser # root(:expression) # rule(:expression) { str('foo') } # end # # MyParser.new.to_treetop # => a treetop grammar as a string # def to_treetop PrettyPrinter.new(Visitors::Treetop). pretty_print(self.class.name, root) end end parslet-2.0.0/lib/parslet/expression.rb000066400000000000000000000022311362225661100201310ustar00rootroot00000000000000 # Allows specifying rules as strings using the exact same grammar that treetop # does, minus the actions. This is on one hand a good example of a fully # fledged parser and on the other hand might even turn out really useful. # # This can be viewed as an extension to parslet and might even be hosted in # its own gem one fine day. # class Parslet::Expression include Parslet autoload :Treetop, 'parslet/expression/treetop' # Creates a parslet from a foreign language expression. # # Example: # # Parslet::Expression.new("'a' 'b'") # def initialize(str, opts={}, context=self) @type = opts[:type] || :treetop @exp = str @parslet = transform( parse(str)) end # Transforms the parse tree into a parslet expression. # def transform(tree) transform = Treetop::Transform.new # pp tree transform.apply(tree) rescue warn "Could not transform: " + tree.inspect raise end # Parses the string and returns a parse tree. # def parse(str) parser = Treetop::Parser.new parser.parse(str) end # Turns this expression into a parslet. # def to_parslet @parslet end endparslet-2.0.0/lib/parslet/expression/000077500000000000000000000000001362225661100176065ustar00rootroot00000000000000parslet-2.0.0/lib/parslet/expression/treetop.rb000066400000000000000000000047551362225661100216300ustar00rootroot00000000000000class Parslet::Expression::Treetop class Parser < Parslet::Parser root(:expression) rule(:expression) { alternatives } # alternative 'a' / 'b' rule(:alternatives) { (simple >> (spaced('/') >> simple).repeat).as(:alt) } # sequence by simple concatenation 'a' 'b' rule(:simple) { occurrence.repeat(1).as(:seq) } # occurrence modifiers rule(:occurrence) { atom.as(:repetition) >> spaced('*').as(:sign) | atom.as(:repetition) >> spaced('+').as(:sign) | atom.as(:repetition) >> repetition_spec | atom.as(:maybe) >> spaced('?') | atom } rule(:atom) { spaced('(') >> expression.as(:unwrap) >> spaced(')') | dot | string | char_class } # a character class rule(:char_class) { (str('[') >> (str('\\') >> any | str(']').absent? >> any).repeat(1) >> str(']')).as(:match) >> space? } # anything at all rule(:dot) { spaced('.').as(:any) } # recognizing strings rule(:string) { str('\'') >> ( (str('\\') >> any) | (str("'").absent? >> any) ).repeat.as(:string) >> str('\'') >> space? } # repetition specification like {1, 2} rule(:repetition_spec) { spaced('{') >> integer.maybe.as(:min) >> spaced(',') >> integer.maybe.as(:max) >> spaced('}') } rule(:integer) { match['0-9'].repeat(1) } # whitespace handling rule(:space) { match("\s").repeat(1) } rule(:space?) { space.maybe } def spaced(str) str(str) >> space? end end class Transform < Parslet::Transform rule(:repetition => simple(:rep), :sign => simple(:sign)) { min = sign=='+' ? 1 : 0 Parslet::Atoms::Repetition.new(rep, min, nil) } rule(:repetition => simple(:rep), :min => simple(:min), :max => simple(:max)) { Parslet::Atoms::Repetition.new(rep, Integer(min || 0), max && Integer(max) || nil) } rule(:alt => subtree(:alt)) { Parslet::Atoms::Alternative.new(*alt) } rule(:seq => sequence(:s)) { Parslet::Atoms::Sequence.new(*s) } rule(:unwrap => simple(:u)) { u } rule(:maybe => simple(:m)) { |d| d[:m].maybe } rule(:string => simple(:s)) { Parslet::Atoms::Str.new(s) } rule(:match => simple(:m)) { Parslet::Atoms::Re.new(m) } rule(:any => simple(:a)) { Parslet::Atoms::Re.new('.') } end end parslet-2.0.0/lib/parslet/graphviz.rb000066400000000000000000000037031362225661100175710ustar00rootroot00000000000000 # Paints a graphviz graph of your parser. begin require 'ruby-graphviz' rescue LoadError puts "Please install the 'ruby-graphviz' gem first." fail end require 'set' require 'parslet/atoms/visitor' module Parslet class GraphvizVisitor def initialize g @graph = g @known_links = Set.new @visited = Set.new end attr_reader :parent def visit_parser(root) recurse root, node('parser') end def visit_entity(name, block) s = node(name) downwards s return if @visited.include?(name) @visited << name recurse block.call, s end def visit_named(name, atom) recurse atom, parent end def visit_repetition(tag, min, max, atom) recurse atom, parent end def visit_alternative(alternatives) p = parent alternatives.each do |atom| recurse atom, p end end def visit_sequence(sequence) p = parent sequence.each do |atom| recurse atom, p end end def visit_lookahead(positive, atom) recurse atom, parent end def visit_re(regexp) # downwards node(regexp.object_id, label: escape("re(#{regexp.inspect})")) end def visit_str(str) # downwards node(str.object_id, label: escape("#{str.inspect}")) end def escape str str.gsub('"', "'") end def node name, opts={} @graph.add_nodes name.to_s, opts end def downwards child if @parent && !@known_links.include?([@parent, child]) @graph.add_edges(@parent, child) @known_links << [@parent, child] end end def recurse node, current @parent = current node.accept(self) end end module Graphable def graph opts g = GraphViz.new(:G, type: :digraph) visitor = GraphvizVisitor.new(g) new.accept(visitor) g.output opts end end class Parser # reopen for introducing the .graph method extend Graphable end endparslet-2.0.0/lib/parslet/parser.rb000066400000000000000000000035441362225661100172360ustar00rootroot00000000000000 # The base class for all your parsers. Use as follows: # # require 'parslet' # # class MyParser < Parslet::Parser # rule(:a) { str('a').repeat } # root(:a) # end # # pp MyParser.new.parse('aaaa') # => 'aaaa' # pp MyParser.new.parse('bbbb') # => Parslet::Atoms::ParseFailed: # # Don't know what to do with bbbb at line 1 char 1. # # Parslet::Parser is also a grammar atom. This means that you can mix full # fledged parsers freely with small parts of a different parser. # # Example: # class ParserA < Parslet::Parser # root :aaa # rule(:aaa) { str('a').repeat(3,3) } # end # class ParserB < Parslet::Parser # root :expression # rule(:expression) { str('b') >> ParserA.new >> str('b') } # end # # In the above example, ParserB would parse something like 'baaab'. # class Parslet::Parser < Parslet::Atoms::Base include Parslet class << self # class methods # Define the parsers #root function. This is the place where you start # parsing; if you have a rule for 'file' that describes what should be # in a file, this would be your root declaration: # # class Parser # root :file # rule(:file) { ... } # end # # #root declares a 'parse' function that works just like the parse # function that you can call on a simple parslet, taking a string as input # and producing parse output. # # In a way, #root is a shorthand for: # # def parse(str) # your_parser_root.parse(str) # end # def root(name) undef_method :root if method_defined? :root define_method(:root) do self.send(name) end end end def try(source, context, consume_all) root.try(source, context, consume_all) end def to_s_inner(prec) root.to_s(prec) end end parslet-2.0.0/lib/parslet/pattern.rb000066400000000000000000000067641362225661100174260ustar00rootroot00000000000000# Matches trees against expressions. Trees are formed by arrays and hashes # for expressing membership and sequence. The leafs of the tree are other # classes. # # A tree issued by the parslet library might look like this: # # { # :function_call => { # :name => 'foobar', # :args => [1, 2, 3] # } # } # # A pattern that would match against this tree would be: # # { :function_call => { :name => simple(:name), :args => sequence(:args) }} # # Note that Parslet::Pattern only matches at a given subtree; it wont try # to match recursively. To do that, please use Parslet::Transform. # class Parslet::Pattern def initialize(pattern) @pattern = pattern end # Decides if the given subtree matches this pattern. Returns the bindings # made on a successful match or nil if the match fails. If you specify # bindings to be a hash, the mappings in it will be treated like bindings # made during an attempted match. # # Pattern.new('a').match('a', :foo => 'bar') # => { :foo => 'bar' } # # @param subtree [String, Hash, Array] poro subtree returned by a parse # @param bindings [Hash] variable bindings to be verified # @return [Hash, nil] On success: variable bindings that allow a match. On # failure: nil # def match(subtree, bindings=nil) bindings = bindings && bindings.dup || Hash.new return bindings if element_match(subtree, @pattern, bindings) end # Returns true if the tree element given by +tree+ matches the expression # given by +exp+. This match must respect bindings already made in # +bindings+. Note that bindings is carried along and modified. # # @api private # def element_match(tree, exp, bindings) # p [:elm, tree, exp] if tree.is_a?(Hash) && exp.is_a?(Hash) return element_match_hash(tree, exp, bindings) elsif tree.is_a?(Array) && exp.is_a?(Array) return element_match_ary_single(tree, exp, bindings) else # If elements match exactly, then that is good enough in all cases return true if exp === tree # If exp is a bind variable: Check if the binding matches if exp.respond_to?(:can_bind?) && exp.can_bind?(tree) return element_match_binding(tree, exp, bindings) end # Otherwise: No match (we don't know anything about the element # combination) return false end end # @api private # def element_match_binding(tree, exp, bindings) var_name = exp.variable_name # TODO test for the hidden :_ feature. if var_name && bound_value = bindings[var_name] return bound_value == tree end # New binding: bindings.store var_name, tree return true end # @api private # def element_match_ary_single(sequence, exp, bindings) return false if sequence.size != exp.size return sequence.zip(exp).all? { |elt, subexp| element_match(elt, subexp, bindings) } end # @api private # def element_match_hash(tree, exp, bindings) # Early failure when one hash is bigger than the other return false unless exp.size == tree.size # We iterate over expected pattern, since we demand that the keys that # are there should be in tree as well. exp.each do |expected_key, expected_value| return false unless tree.has_key? expected_key # Recurse into the value and stop early on failure value = tree[expected_key] return false unless element_match(value, expected_value, bindings) end return true end endparslet-2.0.0/lib/parslet/pattern/000077500000000000000000000000001362225661100170645ustar00rootroot00000000000000parslet-2.0.0/lib/parslet/pattern/binding.rb000066400000000000000000000023521362225661100210250ustar00rootroot00000000000000 # Used internally for representing a bind placeholder in a Parslet::Transform # pattern. This is the superclass for all bindings. # # It defines the most permissive kind of bind, the one that matches any subtree # whatever it looks like. # class Parslet::Pattern::SubtreeBind < Struct.new(:symbol) def variable_name symbol end def inspect "#{bind_type_name}(#{symbol.inspect})" end def can_bind?(subtree) true end private def bind_type_name if md=self.class.name.match(/(\w+)Bind/) md.captures.first.downcase else # This path should never be used, but since this is for inspection only, # let's not raise. 'unknown_bind' end end end # Binds a symbol to a simple subtree, one that is not either a sequence of # elements or a collection of attributes. # class Parslet::Pattern::SimpleBind < Parslet::Pattern::SubtreeBind def can_bind?(subtree) not [Hash, Array].include?(subtree.class) end end # Binds a symbol to a sequence of simple leafs ([element1, element2, ...]) # class Parslet::Pattern::SequenceBind < Parslet::Pattern::SubtreeBind def can_bind?(subtree) subtree.kind_of?(Array) && (not subtree.any? { |el| [Hash, Array].include?(el.class) }) end endparslet-2.0.0/lib/parslet/position.rb000066400000000000000000000005071362225661100176020ustar00rootroot00000000000000 # Encapsules the concept of a position inside a string. # class Parslet::Position attr_reader :bytepos include Comparable def initialize string, bytepos @string = string @bytepos = bytepos end def charpos @string.byteslice(0, @bytepos).size end def <=> b self.bytepos <=> b.bytepos end endparslet-2.0.0/lib/parslet/rig/000077500000000000000000000000001362225661100161705ustar00rootroot00000000000000parslet-2.0.0/lib/parslet/rig/rspec.rb000066400000000000000000000031641362225661100176350ustar00rootroot00000000000000RSpec::Matchers.define(:parse) do |input, opts| as = block = nil result = trace = nil match do |parser| begin result = parser.parse(input) block ? block.call(result) : (as == result || as.nil?) rescue Parslet::ParseFailed => ex trace = ex.parse_failure_cause.ascii_tree if opts && opts[:trace] false end end public_send(respond_to?(:failure_message) ? :failure_message : :failure_message_for_should) do |is| if block "expected output of parsing #{input.inspect}" << " with #{is.inspect} to meet block conditions, but it didn't" else "expected " << (as ? "output of parsing #{input.inspect}"<< " with #{is.inspect} to equal #{as.inspect}, but was #{result.inspect}" : "#{is.inspect} to be able to parse #{input.inspect}") << (trace ? "\n"+trace : '') end end public_send(respond_to?(:failure_message_when_negated) ? :failure_message_when_negated : :failure_message_for_should_not) do |is| if block "expected output of parsing #{input.inspect} with #{is.inspect} not to meet block conditions, but it did" else "expected " << (as ? "output of parsing #{input.inspect}"<< " with #{is.inspect} not to equal #{as.inspect}" : "#{is.inspect} to not parse #{input.inspect}, but it did") end end # NOTE: This has a nodoc tag since the rdoc parser puts this into # Object, a thing I would never allow. chain :as do |expected_output=nil, &my_block| as = expected_output block = my_block end end parslet-2.0.0/lib/parslet/scope.rb000066400000000000000000000012461362225661100170500ustar00rootroot00000000000000class Parslet::Scope # Raised when the accessed slot has never been assigned a value. # class NotFound < StandardError end class Binding attr_reader :parent def initialize(parent=nil) @parent = parent @hash = Hash.new end def [](k) @hash.has_key?(k) && @hash[k] || parent && parent[k] or raise NotFound end def []=(k,v) @hash.store(k,v) end end def [](k) @current[k] end def []=(k,v) @current[k] = v end def initialize @current = Binding.new end def push @current = Binding.new(@current) end def pop @current = @current.parent end endparslet-2.0.0/lib/parslet/slice.rb000066400000000000000000000047141362225661100170410ustar00rootroot00000000000000 # A slice is a small part from the parse input. A slice mainly behaves like # any other string, except that it remembers where it came from (offset in # original input). # # == Extracting line and column # # Using the #line_and_column method, you can extract the line and column in # the original input where this slice starts. # # Example: # slice.line_and_column # => [1, 13] # slice.offset # => 12 # # == Likeness to strings # # Parslet::Slice behaves in many ways like a Ruby String. This likeness # however is not complete - many of the myriad of operations String supports # are not yet in Slice. You can always extract the internal string instance by # calling #to_s. # # These omissions are somewhat intentional. Rather than maintaining a full # delegation, we opt for a partial emulation that gets the job done. # class Parslet::Slice attr_reader :str attr_reader :position attr_reader :line_cache # Construct a slice using a string, an offset and an optional line cache. # The line cache should be able to answer to the #line_and_column message. # def initialize(position, string, line_cache=nil) @position = position @str = string @line_cache = line_cache end def offset @position.charpos end # Compares slices to other slices or strings. # def == other str == other end # Match regular expressions. # def match(regexp) str.match(regexp) end # Returns the slices size in characters. # def size str.size end alias length size # Concatenate two slices; it is assumed that the second slice begins # where the first one ends. The offset of the resulting slice is the same # as the one of this slice. # def +(other) self.class.new(@position, str + other.to_s, line_cache) end # Returns a tuple referring to the original input. # def line_and_column raise ArgumentError, "No line cache was given, cannot infer line and column." \ unless line_cache line_cache.line_and_column(@position.bytepos) end # Conversion operators ----------------------------------------------------- def to_str str end alias to_s to_str def to_slice self end def to_sym str.to_sym end def to_i self.str.to_i end def to_f str.to_f end # Inspection & Debugging --------------------------------------------------- # Prints the slice as "string"@offset. def inspect str.inspect + "@#{offset}" end end parslet-2.0.0/lib/parslet/source.rb000066400000000000000000000045711362225661100172430ustar00rootroot00000000000000 require 'stringio' require 'strscan' require 'parslet/position' require 'parslet/source/line_cache' module Parslet # Wraps the input string for parslet. # class Source def initialize(str) raise( ArgumentError, "Must construct Source with a string like object." ) unless str.respond_to?(:to_str) @str = StringScanner.new(str) # maps 1 => /./m, 2 => /../m, etc... @re_cache = Hash.new { |h,k| h[k] = /(.|$){#{k}}/m } @line_cache = LineCache.new @line_cache.scan_for_line_endings(0, str) end # Checks if the given pattern matches at the current input position. # # @param pattern [Regexp] pattern to check for # @return [Boolean] true if the pattern matches at #pos # def matches?(pattern) @str.match?(pattern) end alias match matches? # Consumes n characters from the input, returning them as a slice of the # input. # def consume(n) position = self.pos slice_str = @str.scan(@re_cache[n]) slice = Parslet::Slice.new( position, slice_str, @line_cache) return slice end # Returns how many chars remain in the input. # def chars_left @str.rest_size end # Returns how many chars there are between current position and the # string given. If the string given doesn't occur in the source, then # the remaining chars (#chars_left) are returned. # # @return [Fixnum] count of chars until str or #chars_left # def chars_until str slice_str = @str.check_until(Regexp.new(Regexp.escape(str))) return chars_left unless slice_str return slice_str.size - str.size end # Position of the parse as a character offset into the original string. # # @note Please be aware of encodings at this point. # def pos Position.new(@str.string, @str.pos) end def bytepos @str.pos end # @note Please be aware of encodings at this point. # def bytepos=(n) @str.pos = n rescue RangeError end # Returns a tuple for the given position. If no position is # given, line/column information is returned for the current position # given by #pos. # def line_and_column(position=nil) @line_cache.line_and_column(position || self.bytepos) end end end parslet-2.0.0/lib/parslet/source/000077500000000000000000000000001362225661100167075ustar00rootroot00000000000000parslet-2.0.0/lib/parslet/source/line_cache.rb000066400000000000000000000055311362225661100213120ustar00rootroot00000000000000 class Parslet::Source # A cache for line start positions. # class LineCache def initialize # Stores line endings as a simple position number. The first line always # starts at 0; numbers beyond the biggest entry are on any line > size, # but probably make a scan to that position neccessary. @line_ends = [] @line_ends.extend RangeSearch @last_line_end = nil end # Returns a tuple for the given input position. Input # position must be given as byte offset into original string. # def line_and_column(pos) pos = pos.bytepos if pos.respond_to? :bytepos eol_idx = @line_ends.lbound(pos) if eol_idx # eol_idx points to the offset that ends the current line. # Let's try to find the offset that starts it: offset = eol_idx>0 && @line_ends[eol_idx-1] || 0 return [eol_idx+1, pos-offset+1] else # eol_idx is nil, that means that we're beyond the last line end that # we know about. Pretend for now that we're just on the last line. offset = @line_ends.last || 0 return [@line_ends.size+1, pos-offset+1] end end def scan_for_line_endings(start_pos, buf) return unless buf buf = StringScanner.new(buf) return unless buf.exist?(/\n/) ## If we have already read part or all of buf, we already know about ## line ends in that portion. remove it and correct cur (search index) if @last_line_end && start_pos < @last_line_end # Let's not search the range from start_pos to last_line_end again. buf.pos = @last_line_end - start_pos end ## Scan the string for line endings; store the positions of all endings ## in @line_ends. while buf.skip_until(/\n/) @last_line_end = start_pos + buf.pos @line_ends << @last_line_end end end end # Mixin for arrays that implicitly give a number of ranges, where one range # begins where the other one ends. # # Example: # # [10, 20, 30] # # would describe [0, 10], (10, 20], (20, 30] # module RangeSearch def find_mid(left, right) # NOTE: Jonathan Hinkle reported that when mathn is required, just # dividing and relying on the integer truncation is not enough. left + ((right - left) / 2).floor end # Scans the array for the first number that is > than bound. Returns the # index of that number. # def lbound(bound) return nil if empty? return nil unless last > bound left = 0 right = size - 1 loop do mid = find_mid(left, right) if self[mid] > bound right = mid else # assert: self[mid] <= bound left = mid+1 end if right <= left return right end end end end end parslet-2.0.0/lib/parslet/transform.rb000066400000000000000000000173551362225661100177620ustar00rootroot00000000000000 require 'parslet/pattern' # Transforms an expression tree into something else. The transformation # performs a depth-first, post-order traversal of the expression tree. During # that traversal, each time a rule matches a node, the node is replaced by the # result of the block associated to the rule. Otherwise the node is accepted # as is into the result tree. # # This is almost what you would generally do with a tree visitor, except that # you can match several levels of the tree at once. # # As a consequence of this, the resulting tree will contain pieces of the # original tree and new pieces. Most likely, you will want to transform the # original tree wholly, so this isn't a problem. # # You will not be able to create a loop, given that each node will be replaced # only once and then left alone. This means that the results of a replacement # will not be acted upon. # # Example: # # class Example < Parslet::Transform # rule(:string => simple(:x)) { # (1) # StringLiteral.new(x) # } # end # # A tree transform (Parslet::Transform) is defined by a set of rules. Each # rule can be defined by calling #rule with the pattern as argument. The block # given will be called every time the rule matches somewhere in the tree given # to #apply. It is passed a Hash containing all the variable bindings of this # pattern match. # # In the above example, (1) illustrates a simple matching rule. # # Let's say you want to parse matching parentheses and distill a maximum nest # depth. You would probably write a parser like the one in example/parens.rb; # here's the relevant part: # # rule(:balanced) { # str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r) # } # # If you now apply this to a string like '(())', you get a intermediate parse # tree that looks like this: # # { # l: '(', # m: { # l: '(', # m: nil, # r: ')' # }, # r: ')' # } # # This parse tree is good for debugging, but what we would really like to have # is just the nesting depth. This transformation rule will produce that: # # rule(:l => '(', :m => simple(:x), :r => ')') { # # innermost :m will contain nil # x.nil? ? 1 : x+1 # } # # = Usage patterns # # There are four ways of using this class. The first one is very much # recommended, followed by the second one for generality. The other ones are # omitted here. # # Recommended usage is as follows: # # class MyTransformator < Parslet::Transform # rule(...) { ... } # rule(...) { ... } # # ... # end # MyTransformator.new.apply(tree) # # Alternatively, you can use the Transform class as follows: # # transform = Parslet::Transform.new do # rule(...) { ... } # end # transform.apply(tree) # # = Execution context # # The execution context of action blocks differs depending on the arity of # said blocks. This can be confusing. It is however somewhat intentional. You # should not create fat Transform descendants containing a lot of helper methods, # instead keep your AST class construction in global scope or make it available # through a factory. The following piece of code illustrates usage of global # scope: # # transform = Parslet::Transform.new do # rule(...) { AstNode.new(a_variable) } # rule(...) { Ast.node(a_variable) } # modules are nice # end # transform.apply(tree) # # And here's how you would use a class builder (a factory): # # transform = Parslet::Transform.new do # rule(...) { builder.add_node(a_variable) } # rule(...) { |d| d[:builder].add_node(d[:a_variable]) } # end # transform.apply(tree, :builder => Builder.new) # # As you can see, Transform allows you to inject local context for your rule # action blocks to use. # class Parslet::Transform # FIXME: Maybe only part of it? Or maybe only include into constructor # context? include Parslet class << self # FIXME: Only do this for subclasses? include Parslet # Define a rule for the transform subclass. # def rule(expression, &block) @__transform_rules ||= [] # Prepend new rules so they have higher precedence than older rules @__transform_rules.unshift([Parslet::Pattern.new(expression), block]) end # Allows accessing the class' rules # def rules @__transform_rules ||= [] end def inherited(subclass) super subclass.instance_variable_set(:@__transform_rules, rules.dup) end end def initialize(raise_on_unmatch=false, &block) @raise_on_unmatch = raise_on_unmatch @rules = [] if block instance_eval(&block) end end # Defines a rule to be applied whenever apply is called on a tree. A rule # is composed of two parts: # # * an *expression pattern* # * a *transformation block* # def rule(expression, &block) # Prepend new rules so they have higher precedence than older rules @rules.unshift([Parslet::Pattern.new(expression), block]) end # Applies the transformation to a tree that is generated by Parslet::Parser # or a simple parslet. Transformation will proceed down the tree, replacing # parts/all of it with new objects. The resulting object will be returned. # # Using the context parameter, you can inject bindings for the transformation. # This can be used to allow access to the outside world from transform blocks, # like so: # # document = # some class that you act on # transform.apply(tree, document: document) # # The above will make document available to all your action blocks: # # # Variant A # rule(...) { document.foo(bar) } # # Variant B # rule(...) { |d| d[:document].foo(d[:bar]) } # # @param obj PORO ast to transform # @param context start context to inject into the bindings. # def apply(obj, context=nil) transform_elt( case obj when Hash recurse_hash(obj, context) when Array recurse_array(obj, context) else obj end, context ) end # Executes the block on the bindings obtained by Pattern#match, if such a match # can be made. Depending on the arity of the given block, it is called in # one of two environments: the current one or a clean toplevel environment. # # If you would like the current environment preserved, please use the # arity 1 variant of the block. Alternatively, you can inject a context object # and call methods on it (think :ctx => self). # # # the local variable a is simulated # t.call_on_match(:a => :b) { a } # # no change of environment here # t.call_on_match(:a => :b) { |d| d[:a] } # def call_on_match(bindings, block) if block if block.arity == 1 return block.call(bindings) else context = Context.new(bindings) return context.instance_eval(&block) end end end # Allow easy access to all rules, the ones defined in the instance and the # ones predefined in a subclass definition. # def rules self.class.rules + @rules end # @api private # def transform_elt(elt, context) rules.each do |pattern, block| if bindings=pattern.match(elt, context) # Produces transformed value return call_on_match(bindings, block) end end # No rule matched - element is not transformed if @raise_on_unmatch && elt.is_a?(Hash) elt_types = elt.map do |key, value| [ key, value.class ] end.to_h raise NotImplementedError, "Failed to match `#{elt_types.inspect}`" else return elt end end # @api private # def recurse_hash(hsh, ctx) hsh.inject({}) do |new_hsh, (k,v)| new_hsh[k] = apply(v, ctx) new_hsh end end # @api private # def recurse_array(ary, ctx) ary.map { |elt| apply(elt, ctx) } end end require 'parslet/context'parslet-2.0.0/parslet.gemspec000066400000000000000000000010301362225661100162000ustar00rootroot00000000000000# -*- encoding: utf-8 -*- Gem::Specification.new do |s| s.name = 'parslet' s.version = '2.0.0' s.authors = ['Kaspar Schiess'] s.email = 'kaspar.schiess@absurd.li' s.extra_rdoc_files = ['README'] s.files = %w(HISTORY.txt LICENSE Rakefile README parslet.gemspec) + Dir.glob("{lib,spec,example}/**/*") s.homepage = 'http://kschiess.github.io/parslet' s.license = 'MIT' s.rdoc_options = ['--main', 'README'] s.require_paths = ['lib'] s.summary = 'Parser construction library with great error reporting in Ruby.' end parslet-2.0.0/qed/000077500000000000000000000000001362225661100137405ustar00rootroot00000000000000parslet-2.0.0/qed/accelerators.md000066400000000000000000000273531362225661100167430ustar00rootroot00000000000000# Parslet Accelerator ## Synopsis Reading this all the way is worth it. But don't despair; if your attention span is short, read this and zap away! The TLDR: Parslet is slow because of the way it is constructed internally. Optimisation helps, but makes your parser harder to read. Don't go there. Use parser accelerators instead - optimize parts of your parser without changing its definition. This is what it looks like: slow_parser = something >> slow_part >> other include Parslet::Accelerator optimized_parser = apply( rule( slow_part ) { faster_part }) optimized_parser.parse('"Parsing is now fully optimized! (tm)"') Thus parslet allows you to write cristal clear parsers that are also as fast as you can make them. (But still slower than C!) ## Introduction The goals of parslet are simple: make writing PEG parsers a predictable and straightforward endeavour. Some people have since claimed the word 'fun' for writing parsers, a connotation that we don't entirely oppose - otherwise why would we spend our time extending parslet? ### Dark Clouds ahead Writing your first thousand line parser that works is easy – IF you use parslet. But very often, the resulting parser is rather slow - having execution times in the second range instead of the subsecond range. You fire up your email client and drop us a mail to the mailing list, asking: "Why is parslet so slow?" You'll receive the following answers: * Parslet is not a parser generator, but a parser engine based on Ruby. As such, it will be slower than parsers written in languages such as C. * Parslet's internal structure is simple and clear. We've invested a lot of effort in making everything it does obvious and extendable. The downside of this somewhat OO-heavy approach is that we've got many objects juggling data and deep callstacks. Read: Bad use of caches and CPU time. * Very few big languages have parsers written in high level languages such as Ruby. For good reasons. Depending on how serious you are about writing a new language (as opposed to fiddling around), you might want to _not start with parslet at all_. It's not like we haven't done anything to fix the above reasons; rather, we're doing everything we can, provided the main goal of *simplicity and understandability* is not in danger! If you look up what has been done over the years you will find a lot of small and large optimisations. But we have always refused to sacrifice simplicity of design to the god of optimisation, especially when it came to making a single parser faster. We want parslet to be fast in general, and frankly, your parser of language X is not our concern - only insofar as it uses parslet. But parslet needs to be useful for something. If not, what is the point? We would like to make parslet as useful as possible for smaller languages and for places where execution speed isn't your only concern. A lot of languages have rapidly evolving grammars and are developed by programmers that don't have the time for hand-writing parsers in C. Still, what should you do once you've written your parser and speed becomes the issue? Until now, you had no real options short of rewriting the damn thing in C. That has changed: we've come up with Parslet Accelerator. The Accelerator will allow you to pattern match bits of your _parser_ and replace them with bits that do the same work, but faster. Really just hot spot optimisation, but without sacrificing readability of the original parser grammar. ### An Example Let's consider the parser for quoted strings as an example, usually written to look something like this: quote = str('"') quote >> (quote.absent? >> any).repeat >> quote If we spell out the work parslet needs to do when matching a 1000 character string using this method, the performance problems will become obvious to you. Parslet will: * Match a quote * In a loop: * Try to match a quote * If that fails, continue, otherwise break from the loop * Gobble up a single char * Match the final quote The inner loop will be executed a 1000 times; that's a 1000 times reading a char, checking to see if it's a quote, then reading it again, etc... As a programmer, this should disturb you deeply. And the fix, pseudo-code wise, should be obvious to you. Let's look at this revised flow: * Match a quote * Gobble up as many chars as possible, until we hit a quote * Match the final quote Ok, we've sort of cheated there in the middle - we've transformed something into a single step that is really still a loop. But as a Ruby programmer, you will not see this as a loop, but rather as a call to a more efficient library like `StringScanner`, which underlies `Parslet::Source`. So we're pretty confident that this new parser will work faster; maybe fast even. Let's assume that we've got a `GobbleUp` atom that gobbles up chars until it hits a stop char. Our faster parser would have to look something like this: quote = str('"') quote >> GobbleUp.new('"') >> quote And all is fine, right? We don't think so. You've chosen to use parslet, so you don't want to end up sprinkling your grammar which is as much specification as it is implementation with things like `GobbleUp`. Wouldn't it be nice if you could keep the parser as it is, but somehow replace the pattern of `(quote.absent? >> any).repeat` with `GobbleUp.new('"')` before doing any work with your parser? Well, you can. quote = str('"') parser = quote >> (quote.absent? >> any).repeat >> quote A = Accelerator # for making what follows a bit shorter optimized_parser = A.apply(parser, A.rule( (A.str(:x).absent? >> A.any).repeat ) { GobbleUp.new(x) }) optimized_parser.parse('"Parsing is now fully optimized! (tm)"') (If you're interested in a bit of history, the example that triggered the discussion around accelerators is preserved in [optimizer.rb](https://github.com/kschiess/parslet/blob/master/experiments/optimizer.rb). If you look past the hacks and the optimism, you'll recognize some of the things we talk about in this document.) ### About this Document Now that the goal is defined, let us expose the details of the system proposed above. We'll start with explaining what these `Accelerator.rule` things are, how they match against your parser and how binding of variables work. (*Parslet Pattern Matching*) Then we'll explain what actions you can take once you've matched part of your parser. (*Binding and Actions*) ## Parser Pattern Matching We'll demonstrate how pattern detection is constructed by showing what the smallest parts do first. Let's require needed libraries. require 'parslet' require 'parslet/accelerator' include Parslet The whole optimizer code is inside the `Parslet::Accelerator` module. If you read that, read 'particle accelerator', not 'will make my code fast'. It is very possible to make things worse using `Parslet::Accelerator`. The simplest parser I can think of would be the one matching a simple string. atom = str('foo') expression = Accelerator.str(:x) binding = Accelerator.match(atom, expression) binding[:x].assert == 'foo' Note that the above was somewhat verbose, with all these variables and all that. We'll write shorter examples from now on. Another simple parser is the one that matches against variants of the `match(...)` atom. Multiple forms of this exist, so we'll go through all of them. First comes a simple character range match. binding = Accelerator.match( match['a-z'], Accelerator.re(:x)) binding[:x].assert == '[a-z]' Note how the internal regular expression value used for the match atom is really bound to :x – we'll keep this as a convention. This also means that some parslet internas are leaked through the Accelerator API here. We consider that a feature, since working with accelerators will bring you into close contact with the atoms internas. Also the Accelerator matcher for `Parslet.match` is called `Accelerator.re` - the reason for this should be obvious from what stands above. Just to make this complete, a special case for the `match(...)` atom is the `any` atom. binding = Accelerator.match(any, Accelerator.any) binding.assert != nil ## Composite Parsers Let's start assembling these simple parsers into more complex patterns and match those. As our first pattern, we'll consider sequences. binding = Accelerator.match( str('a') >> str('b'), Accelerator.str(:x) >> Accelerator.str(:y)) binding.values_at(:x, :y).assert == %w(a b) Also, alternatives should correctly bind. binding = Accelerator.match( str('a') | str('b'), Accelerator.str(:x) | Accelerator.str(:y)) binding.values_at(:x, :y).assert == %w(a b) Note that this means that we bind to the whole alternative subtree, not either to the left or to the right. We're matching the parslet tree, so the meaning of the alternative is irrelevant. Let's quickly skip through the list of other composite parsers here: First off is repetition. Accelerator.match( str('a').repeat(1,2), A.str('a').repeat(1,2)).assert != nil And then there is positive and negative lookahead. Accelerator.match( str('a').present?, A.str('a').present?).assert != nil Accelerator.match( str('a').absent?, A.str('a').absent?).assert != nil And named values. Accelerator.match( str('a').as(:a), A.str('a').as(:a)).assert != nil Which we also want to be able to bind. Accelerator.match( str('a').as(:a), A.str('a').as(:b)).assert == {:b => :a} ## Binding to Values As a side note, our parser should also respect literal value matches in the pattern and only bind to subsequent locations when the values match up. binding = Accelerator.match( str('a') >> str('b'), Accelerator.str(:x) >> Accelerator.str(:x)) binding.assert == nil Another property should be that literal strings passed to the pattern should be matched using ===. binding = Accelerator.match( str('abc') >> str('bcd'), Accelerator.str(/b/) >> Accelerator.str('bcd')) binding.assert == {} The binding is empty here, since no variables were given. But lets also implement constrained variable bindings, that seems useful. The way this works is that you specify a variable you want to bind to first, and then a list of constraints that are matched by `#===`. A.match(str('abc'), A.str(:x, /c/))[:x].assert == 'abc' A.match(str('abc'), A.str(:x, /d/)).assert == nil A.match(str('abc'), A.str(:x, /a/, /c/))[:x].assert == 'abc' A.match(str('abc'), A.str(:x, /a/, /d/)).assert == nil Here's a quick demonstration that demonstrates that this feature equally applies to both `Accelerator.re` and `Parslet.match`. A.match(match['abc'], A.re(:x, /d/)).assert == nil A.match(match['abc'], A.re(:x, /c/))[:x].assert == '[abc]' ## Bindings and Actions Matching parsers is only useful if we can take action and create a new parser that is different in some way. Let's say we have the lofty goal of creating an 'accelerator' that reverses the language understood by a parser. Where the first parser would parse something like: aaaaabbbb we want the second parser to parse something like: bbbaaaaa Here's how the first parser would look like: parser = str('a').repeat(1) >> str('b').repeat(1) And here's how we turn this around to parse the second kind of input. include Parslet parser = str('a').repeat(1) >> str('b').repeat(1) new_parser = A.apply(parser, A.rule(A.str(:x).repeat(1) >> A.str(:y).repeat(1)) { str(y).repeat(1) >> str(x).repeat(1) } ) new_parser.parse('bbbaaaa').assert == 'bbbaaaa' The `Accelerator.apply` method should be called with the following schema: Accelerator.apply(PARSER, RULE1, RULE2, ...) This is a more modern syntax than `Parslet::Transform` uses - maybe we'll update that class in a future version. ## Closing Note * not a panacea parslet-2.0.0/qed/applique/000077500000000000000000000000001362225661100155605ustar00rootroot00000000000000parslet-2.0.0/qed/applique/ae.rb000066400000000000000000000000141362225661100164650ustar00rootroot00000000000000require 'ae'parslet-2.0.0/qed/applique/gobbleup.rb000066400000000000000000000012011362225661100176760ustar00rootroot00000000000000 require 'parslet' # This is where the famous GobbleUp atom is defined. This parslet atom consumes # all chars until a given string (absent) is encountered. # class GobbleUp < Parslet::Atoms::Base def initialize absent, min_chars=0 @absent = absent @min_chars = min_chars end def try(source, context, consume_all) excluding_length = source.chars_until(@absent) if excluding_length >= @min_chars return succ(source.consume(excluding_length)) else return context.err(self, source, "No such string in input: #{@absent.inspect}.") end end def to_s_inner(prec) "until('#{@absent}')" end endparslet-2.0.0/qed/applique/parslet.rb000066400000000000000000000001001362225661100175460ustar00rootroot00000000000000require 'parslet' require 'parslet/accelerator' include Parsletparslet-2.0.0/qed/regressions.md000066400000000000000000000055571362225661100166410ustar00rootroot00000000000000 # Intro This document collects all sorts of weird bugs that parslet had over the years and asserts that we don't accidentially reintroduce one of these. There's not much to be learnt here but a few war stories to be had. # Regresssions ## Redefinition of Ruby core methods This [bug](https://github.com/kschiess/parslet/issues/101) came up at least twice. When constructing a parser like this class HashParser < Parslet::Parser rule(:str_hash) { str('hash') } rule(:hash) { str_hash } rule(:expr) { hash } root(:expr) end We're essentially redefining the parser object's #hash method here. So the parse might work, but the packrat caching doesn't - because it used to stick parsers into a hash. Now this should be impossible, because we're using the `#object_id` of the parser - a speed gain HashParser.new.parse('hash') # did raise a TypeError, but doesn't now Looking back, getting this error was not only probable, but inevitable: Everyone constructs parsers for programming languages, and almost all modern programming languages know the hash as a construct. Hope this one stays buried. ## i18n and the Difference between Byte and Character Position This is hard to get right, especially when considering the differences between the 1.8 and 1.9 branches of Ruby. By dropping 1.8 support, we got rid of a special branch of problems, now really the only thing that remains is treating character and byte offsets consistently. Ruby itself doesn't really: | method | seeks by/returns | |-------------------------|-------------------| | `String#[]` | character | | `StringScanner#pos` | byte | | `StringScanner#pos=` | byte | | `StringScanner#charpos` | character | In parslet, we adopt the following conventions: * `Source#bytepos` and `Source#bytepos=` only deal in byte positions. We don't have a choice here, since StringScanner only seeks to byte locations, probably for speed reasons. These methods are used internally for parsing, caching and resetting the parse. * `Source#pos` returns character positions. This method is used for returning positions associated to slices. So let's test if we get this right by using input composed of the unicode chars 'öäü', mainly because I can type those easily on this keyboard. class I18NParser < Parslet::Parser rule(:nl) { str("\n") } rule(:ouml) { str("ö") } rule(:auml) { str("ä") } rule(:expr) { ((ouml | auml).repeat(1).as(:line) >> nl).repeat(1) } root(:expr) end result = I18NParser.new.parse("äö\nä\nö\n") [{ofs: 0, line: 1, col: 1}, {ofs: 3, line: 2, col: 1}, {ofs: 5, line: 3, col: 1}].zip(result).each do |expect, capture| capture[:line].offset.assert == expect[:ofs] capture[:line].line_and_column.assert == expect.values_at(:line, :col) end parslet-2.0.0/readme.rb000066400000000000000000000016771362225661100147640ustar00rootroot00000000000000require 'parslet' include Parslet # parslet parses strings str('foo'). parse('foo') # => "foo"@0 # it matches character sets match['abc'].parse('a') # => "a"@0 match['abc'].parse('b') # => "b"@0 match['abc'].parse('c') # => "c"@0 # and it annotates its output str('foo').as(:important_bit). parse('foo') # => {:important_bit=>"foo"@0} # you can construct parsers with just a few lines quote = str('"') simple_string = quote >> (quote.absent? >> any).repeat >> quote simple_string. parse('"Simple Simple Simple"') # => "\"Simple Simple Simple\""@0 # or by making a fuss about it class Smalltalk < Parslet::Parser root :smalltalk rule(:smalltalk) { statements } rule(:statements) { # insert smalltalk parser here (outside of the scope of this readme) str('smalltalk') } end # and then Smalltalk.new.parse('smalltalk') # be sure to read our 'Get started' tutorial at # http://kschiess.github.com/parslet/get-started.htmlparslet-2.0.0/spec/000077500000000000000000000000001362225661100141215ustar00rootroot00000000000000parslet-2.0.0/spec/acceptance/000077500000000000000000000000001362225661100162075ustar00rootroot00000000000000parslet-2.0.0/spec/acceptance/examples_spec.rb000066400000000000000000000017041362225661100213660ustar00rootroot00000000000000require 'spec_helper' require 'open3' describe "Regression on" do Dir["example/*.rb"].each do |example| context example do # Generates a product path for a given example file. def product_path(str, ext) str. gsub('.rb', ".#{ext}"). gsub('example/','example/output/') end it "runs successfully" do _, stdout, stderr = Open3.popen3("ruby #{example}") handle_map = { stdout => :out, stderr => :err } expectation_found = handle_map.any? do |io, ext| name = product_path(example, ext) if File.exist?(name) io.read.strip.should == File.read(name).strip true end end unless expectation_found fail "Example doesn't have either an .err or an .out file. "+ "Please create in examples/output!" end end end end end parslet-2.0.0/spec/acceptance/infix_parser_spec.rb000066400000000000000000000064271362225661100222500ustar00rootroot00000000000000require 'spec_helper' describe 'Infix expression parsing' do class InfixExpressionParser < Parslet::Parser rule(:space) { match['\s'] } def cts atom atom >> space.repeat end def infix *args Infix.new(*args) end rule(:mul_op) { match['*/'] >> str(' ').maybe } rule(:add_op) { match['+-'] >> str(' ').maybe } rule(:digit) { match['0-9'] } rule(:integer) { cts digit.repeat(1) } rule(:expression) { infix_expression(integer, [mul_op, 2, :left], [add_op, 1, :right]) } end let(:p) { InfixExpressionParser.new } describe '#integer' do let(:i) { p.integer } it "parses integers" do i.should parse('1') i.should parse('123') end it "consumes trailing white space" do i.should parse('1 ') i.should parse('134 ') end it "doesn't parse floats" do i.should_not parse('1.3') end end describe '#multiplication' do let(:m) { p.expression } it "parses simple multiplication" do m.should parse('1*2').as(l: '1', o: '*', r: '2') end it "parses simple multiplication with spaces" do m.should parse('1 * 2').as(l: '1 ', o: '* ', r: '2') end it "parses division" do m.should parse('1/2') end end describe '#addition' do let(:a) { p.expression } it "parses simple addition" do a.should parse('1+2') end it "parses complex addition" do a.should parse('1+2+3-4') end it "parses a single element" do a.should parse('1') end end describe 'mixed operations' do let(:mo) { p.expression } describe 'inspection' do it 'produces useful expressions' do p.expression.parslet.inspect.should == "infix_expression(INTEGER, [MUL_OP, ADD_OP])" end end describe 'right associativity' do it 'produces trees that lean right' do mo.should parse('1+2+3').as( l: '1', o: '+', r: {l: '2', o: '+', r: '3'}) end end describe 'left associativity' do it 'produces trees that lean left' do mo.should parse('1*2*3').as( l: {l:'1', o:'*', r:'2'}, o:'*', r:'3') end end describe 'error handling' do describe 'incomplete expression' do it 'produces the right error' do cause = catch_failed_parse { mo.parse('1+') } cause.ascii_tree.to_s.should == <<-ERROR INTEGER was expected at line 1 char 3. `- Failed to match sequence (DIGIT{1, } SPACE{0, }) at line 1 char 3. `- Expected at least 1 of DIGIT at line 1 char 3. `- Premature end of input at line 1 char 3. ERROR end end describe 'invalid operator' do it 'produces the right error' do cause = catch_failed_parse { mo.parse('1%') } cause.ascii_tree.to_s.should == <<-ERROR Don't know what to do with "%" at line 1 char 2. ERROR end end end end describe "providing a reducer block" do class InfixExpressionReducerParser < Parslet::Parser rule(:top) { infix_expression(str('a'), [str('-'), 1, :right]) { |l,o,r| {:and=>[l,r]} } } end it "applies the reducer" do InfixExpressionReducerParser.new.top.parse("a-a-a").should == {:and=>["a", {:and=>["a", "a"]}]} end end endparslet-2.0.0/spec/acceptance/regression_spec.rb000066400000000000000000000230021362225661100217230ustar00rootroot00000000000000# Encoding: UTF-8 require 'spec_helper' require 'parslet' describe "Regressions from real examples" do # This parser piece produces on the left a subtree that is keyed (a hash) # and on the right a subtree that is a repetition of such subtrees. I've # for now decided that these would merge into the repetition such that the # return value is an array. This avoids maybe loosing keys/values in a # hash merge. # class ArgumentListParser include Parslet rule :argument_list do expression.as(:argument) >> (comma >> expression.as(:argument)).repeat end rule :expression do string end rule :string do str('"') >> ( str('\\') >> any | str('"').absent? >> any ).repeat.as(:string) >> str('"') >> space? end rule :comma do str(',') >> space? end rule :space? do space.maybe end rule :space do match("[ \t]").repeat(1) end def parse(str) argument_list.parse(str) end end describe ArgumentListParser do let(:instance) { ArgumentListParser.new } it "should have method expression" do instance.should respond_to(:expression) end it 'should parse "arg1", "arg2"' do result = ArgumentListParser.new.parse('"arg1", "arg2"') result.size.should == 2 result.each do |r| r[:argument] end end it 'should parse "arg1", "arg2", "arg3"' do result = ArgumentListParser.new.parse('"arg1", "arg2", "arg3"') result.size.should == 3 result.each do |r| r[:argument] end end end class ParensParser < Parslet::Parser rule(:balanced) { str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r) } root(:balanced) end describe ParensParser do let(:instance) { ParensParser.new } context "statefulness: trying several expressions in sequence" do it "should not be stateful" do # NOTE: Since you've come here to read this, I'll explain why # this is broken and not fixed: You're looking at the tuning branch, # which rewrites a bunch of stuff - so I have failing tests to # remind me of what is left to be done. And to remind you not to # trust this code. instance.parse('(())') lambda { instance.parse('((()))') instance.parse('(((())))') }.should_not raise_error end end context "expression '(())'" do let(:result) { instance.parse('(())') } it "should yield a doubly nested hash" do result.should be_a(Hash) result.should have_key(:m) result[:m].should be_a(Hash) # This was an array earlier end context "inner hash" do let(:inner) { result[:m] } it "should have nil as :m" do inner[:m].should be_nil end end end end class ALanguage < Parslet::Parser root(:expressions) rule(:expressions) { (line >> eol).repeat(1) | line } rule(:line) { space? >> an_expression.as(:exp).repeat } rule(:an_expression) { str('a').as(:a) >> space? } rule(:eol) { space? >> match["\n\r"].repeat(1) >> space? } rule(:space?) { space.repeat } rule(:space) { multiline_comment.as(:multi) | line_comment.as(:line) | str(' ') } rule(:line_comment) { str('//') >> (match["\n\r"].absent? >> any).repeat } rule(:multiline_comment) { str('/*') >> (str('*/').absent? >> any).repeat >> str('*/') } end describe ALanguage do def remove_indent(s) s.to_s.lines.map { |l| l.chomp.strip }.join("\n") end it "should count lines correctly" do cause = catch_failed_parse { subject.parse('a a a a aaa // ff /* a */ b ') } remove_indent(cause.ascii_tree).should == remove_indent(%q( Expected one of [(LINE EOL){1, }, LINE] at line 1 char 1. |- Extra input after last repetition at line 7 char 11. | `- Failed to match sequence (LINE EOL) at line 7 char 11. | `- Failed to match sequence (SPACE? [\n\r]{1, } SPACE?) at line 7 char 11. | `- Expected at least 1 of [\n\r] at line 7 char 11. | `- Failed to match [\n\r] at line 7 char 11. `- Don't know what to do with "\n " at line 1 char 2.).strip) end end class BLanguage < Parslet::Parser root :expression rule(:expression) { b.as(:one) >> b.as(:two) } rule(:b) { str('b') } end describe BLanguage do it "should parse 'bb'" do subject.should parse('bb').as(:one => 'b', :two => 'b') end it "should transform with binding constraint" do transform = Parslet::Transform.new do |t| t.rule(:one => simple(:b), :two => simple(:b)) { :ok } end transform.apply(subject.parse('bb')).should == :ok end end class UnicodeLanguage < Parslet::Parser root :gobble rule(:gobble) { any.repeat } end describe UnicodeLanguage do it "should parse UTF-8 strings" do subject.should parse('éèäöü').as('éèäöü') subject.should parse('RubyKaigi2009ã®ãƒ†ãƒ¼ãƒžã¯ã€ã€Œå¤‰ã‚ã‚‹ï¼å¤‰ãˆã‚‹ã€ã§ã™ã€‚ å‰å›žã®').as('RubyKaigi2009ã®ãƒ†ãƒ¼ãƒžã¯ã€ã€Œå¤‰ã‚ã‚‹ï¼å¤‰ãˆã‚‹ã€ã§ã™ã€‚ å‰å›žã®') end end class UnicodeSentenceLanguage < Parslet::Parser rule(:sentence) { (match('[^。]').repeat(1) >> str("。")).as(:sentence) } rule(:sentences) { sentence.repeat } root(:sentences) end describe UnicodeSentenceLanguage do let(:string) { "RubyKaigi2009ã®ãƒ†ãƒ¼ãƒžã¯ã€ã€Œå¤‰ã‚ã‚‹ï¼å¤‰ãˆã‚‹ã€ã§ã™ã€‚ å‰å›žã®" + "RubyKaigi2008ã®ãƒ†ãƒ¼ãƒžã§ã‚ã£ãŸã€Œå¤šæ§˜æ€§ã€ã®è¨€è‘‰ã®é€šã‚Šã€ " + "2008å¹´ã¯Rubyãã®ã‚‚ã®ã«é–¢ã—ã¦ã‚‚ã€ã¾ãŸRubyã®æ´»èºã™ã‚‹èˆžå°ã«é–¢ã—ã¦ã‚‚〠" + "ã¾ã™ã¾ã™å¤šæ§˜åŒ–ãŒé€²ã¿ã¤ã¤ã‚りã¾ã™ã€‚RubyKaigi2008ã¯ã€ãã®ã‚ˆã†ãª " + "Rubyã®ç”Ÿæ…‹ç³»ã‚’ã‚らãŸã‚ã¦èªè­˜ã™ã‚‹å ´ã¨ãªã‚Šã¾ã—ãŸã€‚ ã—ã‹ã—ã€" + "ã“ã†ã—ãŸå¤šæ§˜åŒ–ãŒé€²ã‚€ä¸­ã€ç•°ãªã‚‹è€…åŒå£«ãŒå˜ç´”ã«è·é›¢ã‚’ ç½®ã„ãŸã¾ã¾ã§ã¯ã€" + "ãã®é•ã„ã‚’èªè­˜ã—ãŸã¨ã“ã‚ã§ã‚ã¾ã‚Šæ„味ãŒã‚りã¾ã›ã‚“。 ç•°ãªã‚‹å®Ÿè£…ã€" + "ç•°ãªã‚‹æ€æƒ³ã€ç•°ãªã‚‹èƒŒæ™¯ã¨ã„ã£ãŸã€æ§˜ã€…ãªå¤šæ§˜æ€§ã‚’ç†è§£ã—ã¤ã¤ã€ " + "ã™ã‚Šåˆã‚ã›ã‚‹ã¹ãã‚‚ã®ã‚’ã™ã‚Šåˆã‚ã›ã€å¤‰ãˆã¦ã„ãã¹ãã¨ã“ã‚ã‚’ " + "変ãˆã¦ã„ãã“ã¨ãŒã€è±Šã‹ãªæœªæ¥ã¸ã¨ã¤ãªãŒã‚‹é“ã«é•ã„ã‚りã¾ã›ã‚“。" } it "should parse sentences" do subject.should parse(string) end end class TwoCharLanguage < Parslet::Parser root :twochar rule(:twochar) { any >> str('2') } end describe TwoCharLanguage do def di(s) s.strip.to_s.lines.map { |l| l.chomp.strip }.join("\n") end it "should raise an error" do error = catch_failed_parse { subject.parse('123') } di(error.ascii_tree).should == di(%q( Failed to match sequence (. '2') at line 1 char 2. `- Don't know what to do with "3" at line 1 char 3. )) end end # Issue #68: Extra input reporting, written by jmettraux class RepetitionParser < Parslet::Parser rule(:nl) { match('[\s]').repeat(1) } rule(:nl?) { nl.maybe } rule(:sp) { str(' ').repeat(1) } rule(:sp?) { str(' ').repeat(0) } rule(:line) { sp >> str('line') } rule(:body) { ((line | block) >> nl).repeat(0) } rule(:block) { sp? >> str('begin') >> sp >> match('[a-z]') >> nl >> body >> sp? >> str('end') } rule(:blocks) { nl? >> block >> (nl >> block).repeat(0) >> nl? } root(:blocks) end describe RepetitionParser do def di(s) s.strip.to_s.lines.map { |l| l.chomp.strip }.join("\n") end it 'parses a block' do subject.parse(%q{ begin a end }) end it 'parses nested blocks' do subject.parse(%q{ begin a begin b end end }) end it 'parses successive blocks' do subject.parse(%q{ begin a end begin b end }) end it 'fails gracefully on a missing end' do error = catch_failed_parse { subject.parse(%q{ begin a begin b end }) } di(error.ascii_tree).should == di(%q( Failed to match sequence (NL? BLOCK (NL BLOCK){0, } NL?) at line 2 char 11. `- Failed to match sequence (SP? 'begin' SP [a-z] NL BODY SP? 'end') at line 5 char 9. `- Premature end of input at line 5 char 9. )) end it 'fails gracefully on a missing end (2)' do error = catch_failed_parse { subject.parse(%q{ begin a end begin b begin c end }) } di(error.ascii_tree).should == di(%q( Failed to match sequence (NL? BLOCK (NL BLOCK){0, } NL?) at line 3 char 14. `- Don't know what to do with "begin b\n " at line 4 char 11. )) end it 'fails gracefully on a missing end (deepest reporter)' do error = catch_failed_parse { subject.parse(%q{ begin a end begin b begin c li end end }, :reporter => Parslet::ErrorReporter::Deepest.new) } di(error.ascii_tree).should == di(%q( Failed to match sequence (NL? BLOCK (NL BLOCK){0, } NL?) at line 3 char 16. `- Expected "end", but got "li\n" at line 6 char 17. )) end end end parslet-2.0.0/spec/acceptance/repetition_and_maybe_spec.rb000066400000000000000000000022731362225661100237330ustar00rootroot00000000000000require 'spec_helper' describe "Tree output" do extend Parslet def self.hash_examples(h) h.each do |atom, expected| it "should convert #{atom} to #{expected.inspect}" do atom.parse(input).should == expected end end end context "when parsing the empty string" do let(:input) { '' } hash_examples( # No naming str('a').maybe => '', str('a').repeat => '', # Named contents: maybe yields nil str('a').maybe.as(:f) => {:f => nil}, str('a').repeat.as(:f) => {:f => []}, # Contents that aren't simple strings (str('a') >> str('b')).maybe.as(:f) => {:f => nil}, (str('a') >> str('b')).repeat.as(:f) => {:f => []}, # The other way around: Contents would be tagged, but nil result isn't (str('a') >> str('b')).as(:f).maybe => '', (str('a') >> str('b')).as(:f).repeat => '' ) end context "when parsing 'aa'" do let(:input) { 'aa' } hash_examples( # since they're not named, repetitions get merged together. str('a').as(:a).repeat >> str('a').as(:a).repeat => [{:a=>'a'},{:a=>'a'}] ) end endparslet-2.0.0/spec/acceptance/unconsumed_input_spec.rb000066400000000000000000000011451362225661100231460ustar00rootroot00000000000000require 'spec_helper' describe "Unconsumed input:" do class RepeatingBlockParser < Parslet::Parser root :expressions rule(:expressions) { expression.repeat } rule(:expression) { str('(') >> aab >> str(')') } rule(:aab) { str('a').repeat(1) >> str('b') } end describe RepeatingBlockParser do let(:parser) { described_class.new } it "throws annotated error" do error = catch_failed_parse { parser.parse('(aaac)') } end it "doesn't error out if prefix is true" do expect { parser.parse('(aaac)', :prefix => true) }.not_to raise_error end end endparslet-2.0.0/spec/parslet/000077500000000000000000000000001362225661100155735ustar00rootroot00000000000000parslet-2.0.0/spec/parslet/atom_results_spec.rb000066400000000000000000000023241362225661100216540ustar00rootroot00000000000000require 'spec_helper' describe 'Result of a Parslet#parse' do include Parslet; extend Parslet describe "regression" do [ # Behaviour with maybe-nil [str('foo').maybe >> str('bar'), "bar", "bar"], [str('bar') >> str('foo').maybe, "bar", 'bar'], # These might be hard to understand; look at the result of # str.maybe >> str # and # str.maybe >> str first. [(str('f').maybe >> str('b')).repeat, "bb", "bb"], [(str('b') >> str('f').maybe).repeat, "bb", 'bb'], [str('a').as(:a) >> (str('b') >> str('c').as(:a)).repeat, 'abc', [{:a=>'a'}, {:a=>'c'}]], [str('a').as(:a).repeat >> str('b').as(:b).repeat, 'ab', [{:a=>'a'}, {:b=>'b'}]], # Repetition behaviour / named vs. unnamed [str('f').repeat, '', ''], [str('f').repeat.as(:f), '', {:f => []}], # Maybe behaviour / named vs. unnamed [str('f').maybe, '', ''], [str('f').maybe.as(:f), '', {:f => nil}], ].each do |parslet, input, result| context "#{parslet.inspect}" do it "should parse \"#{input}\" into \"#{result}\"" do parslet.parse(input).should == result end end end end endparslet-2.0.0/spec/parslet/atoms/000077500000000000000000000000001362225661100167165ustar00rootroot00000000000000parslet-2.0.0/spec/parslet/atoms/alternative_spec.rb000066400000000000000000000012001362225661100225640ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Alternative do include Parslet describe '| shortcut' do let(:alternative) { str('a') | str('b') } context "when chained with different atoms" do before(:each) { # Chain something else to the alternative parslet. If it modifies the # parslet atom in place, we'll notice: alternative | str('d') } let!(:chained) { alternative | str('c') } it "is side-effect free" do chained.should parse('c') chained.should parse('a') chained.should_not parse('d') end end end endparslet-2.0.0/spec/parslet/atoms/base_spec.rb000066400000000000000000000075321362225661100211760ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Base do let(:parslet) { Parslet::Atoms::Base.new } let(:context) { Parslet::Atoms::Context.new } describe "<- #try(io)" do it "should raise NotImplementedError" do lambda { parslet.try(flexmock(:io), context, false) }.should raise_error(NotImplementedError) end end describe "<- #flatten_sequence" do [ # 9 possibilities for making a word of 2 letters from the alphabeth of # A(rray), H(ash) and S(tring). Make sure that all results are valid. # ['a', 'b'], 'ab', # S S [['a'], ['b']], ['a', 'b'], # A A [{:a=>'a'}, {:b=>'b'}], {:a=>'a',:b=>'b'}, # H H [{:a=>'a'}, ['a']], [{:a=>'a'}, 'a'], # H A [{:a=>'a'}, 's'], {:a=>'a'}, # H S [['a'], {:a=>'a'}], ['a', {:a=>'a'}], # A H (symmetric to H A) [['a'], 'b'], ['a'], # A S ['a', {:b=>'b'}], {:b=>'b'}, # S H (symmetric to H S) ['a', ['b']], ['b'], # S A (symmetric to A S) [nil, ['a']], ['a'], # handling of lhs nil [nil, {:a=>'a'}], {:a=>'a'}, [['a'], nil], ['a'], # handling of rhs nil [{:a=>'a'}, nil], {:a=>'a'} ].each_slice(2) do |sequence, result| context "for " + sequence.inspect do it "should equal #{result.inspect}" do parslet.flatten_sequence(sequence).should == result end end end end describe "<- #flatten_repetition" do def unnamed(obj) parslet.flatten_repetition(obj, false) end it "should give subtrees precedence" do unnamed([[{:a=>"a"}, {:m=>"m"}], {:a=>"a"}]).should == [{:a=>"a"}] end end describe '#parse(source)' do context "when given something that looks like a source" do let(:source) { flexmock("source lookalike", :line_and_column => [1,2], :bytepos => 1, :chars_left => 0) } it "should not rewrap in a source" do flexmock(Parslet::Source). should_receive(:new => :source_created).never begin parslet.parse(source) rescue NotImplementedError end end end end context "when the parse fails, the exception" do it "should contain a string" do begin Parslet.str('foo').parse('bar') rescue Parslet::ParseFailed => ex ex.message.should be_kind_of(String) end end end context "when not all input is consumed" do let(:parslet) { Parslet.str('foo') } it "should raise with a proper error message" do error = catch_failed_parse { parslet.parse('foobar') } error.to_s.should == "Don't know what to do with \"bar\" at line 1 char 4." end end context "when only parsing string prefix" do let(:parslet) { Parslet.str('foo') >> Parslet.str('bar') } it "returns the first half on a prefix parse" do parslet.parse('foobarbaz', :prefix => true).should == 'foobar' end end describe ':reporter option' do let(:parslet) { Parslet.str('test') >> Parslet.str('ing') } let(:reporter) { flexmock(:reporter) } it "replaces the default reporter" do cause = flexmock(:cause) # Two levels of the parse, calling two different error reporting # methods. reporter. should_receive(:err_at).once reporter. should_receive(:err => cause).once reporter. should_receive(:succ).once # The final cause will be sent the #raise method. cause. should_receive(:raise).once.and_throw(:raise) catch(:raise) { parslet.parse('testung', :reporter => reporter) fail "NEVER REACHED" } end end endparslet-2.0.0/spec/parslet/atoms/capture_spec.rb000066400000000000000000000010071362225661100217160ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Capture do include Parslet let(:context) { Parslet::Atoms::Context.new(nil) } def inject string, parser source = Parslet::Source.new(string) parser.apply(source, context, true) end it "should capture simple results" do inject 'a', str('a').capture(:a) context.captures[:a].should == 'a' end it "should capture complex results" do inject 'a', str('a').as(:b).capture(:a) context.captures[:a].should == {:b => 'a'} end endparslet-2.0.0/spec/parslet/atoms/combinations_spec.rb000066400000000000000000000001171362225661100227410ustar00rootroot00000000000000require 'spec_helper' describe "Parslet combinations" do include Parslet endparslet-2.0.0/spec/parslet/atoms/dsl_spec.rb000066400000000000000000000002141362225661100210340ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::DSL do describe "deprecated methods" do let(:parslet) { Parslet.str('foo') } end endparslet-2.0.0/spec/parslet/atoms/entity_spec.rb000066400000000000000000000040401362225661100215670ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Entity do context "when constructed with str('bar') inside" do let(:named) { Parslet::Atoms::Entity.new('name', &proc { Parslet.str('bar') }) } it "should parse 'bar' without raising exceptions" do named.parse('bar') end it "should raise when applied to 'foo'" do lambda { named.parse('foo') }.should raise_error(Parslet::ParseFailed) end describe "#inspect" do it "should return the name of the entity" do named.inspect.should == 'NAME' end end end context "when constructed with empty block" do let(:entity) { Parslet::Atoms::Entity.new('name', &proc { }) } it "should raise NotImplementedError" do lambda { entity.parse('some_string') }.should raise_error(NotImplementedError) end end context "recursive definition parser" do class RecDefParser include Parslet rule :recdef do str('(') >> atom >> str(')') end rule :atom do str('a') | str('b') | recdef end end let(:parser) { RecDefParser.new } it "should parse balanced parens" do parser.recdef.parse("(((a)))") end it "should not throw 'stack level too deep' when printing errors" do cause = catch_failed_parse { parser.recdef.parse('(((a))') } cause.ascii_tree end end context "when constructed with a label" do let(:named) { Parslet::Atoms::Entity.new('name', 'label', &proc { Parslet.str('bar') }) } it "should parse 'bar' without raising exceptions" do named.parse('bar') end it "should raise when applied to 'foo'" do lambda { named.parse('foo') }.should raise_error(Parslet::ParseFailed) end describe "#inspect" do it "should return the label of the entity" do named.inspect.should == 'label' end end describe "#parslet" do it "should set the label on the cached parslet" do named.parslet.label.should == 'label' end end end end parslet-2.0.0/spec/parslet/atoms/ignored_spec.rb000066400000000000000000000010211362225661100216760ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Ignored do include Parslet describe "ignore" do it "ignores parts of the input" do str('a').ignore.parse('a').should == nil (str('a') >> str('b').ignore >> str('c')).parse('abc').should == 'ac' (str('a') >> str('b').as(:name).ignore >> str('c')).parse('abc').should == 'ac' (str('a') >> str('b').maybe.ignore >> str('c')).parse('abc').should == 'ac' (str('a') >> str('b').maybe.ignore >> str('c')).parse('ac').should == 'ac' end end endparslet-2.0.0/spec/parslet/atoms/infix_spec.rb000066400000000000000000000000751362225661100213740ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Infix do endparslet-2.0.0/spec/parslet/atoms/lookahead_spec.rb000066400000000000000000000013701362225661100222050ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Lookahead do include Parslet describe 'negative lookahead' do it "influences the error tree" do parser = str('f').absent? >> str('b') cause = catch_failed_parse { parser.parse('f') } cause.ascii_tree.should == "Failed to match sequence (!'f' 'b') at line 1 char 1.\n`- Input should not start with 'f' at line 1 char 1.\n" end end describe 'positive lookahead' do it "influences the error tree" do parser = str('f').present? >> str('b') cause = catch_failed_parse { parser.parse('b') } cause.ascii_tree.should == "Failed to match sequence (&'f' 'b') at line 1 char 1.\n`- Input should start with 'f' at line 1 char 1.\n" end end endparslet-2.0.0/spec/parslet/atoms/named_spec.rb000066400000000000000000000000741362225661100213420ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Named do endparslet-2.0.0/spec/parslet/atoms/re_spec.rb000066400000000000000000000004751362225661100206710ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Re do describe "construction" do include Parslet it "should allow match(str) form" do match('[a]').should be_a(Parslet::Atoms::Re) end it "should allow match[str] form" do match['a'].should be_a(Parslet::Atoms::Re) end end endparslet-2.0.0/spec/parslet/atoms/repetition_spec.rb000066400000000000000000000007611362225661100224430ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Repetition do include Parslet describe "repeat" do let(:parslet) { str('a') } describe "(min, max)" do subject { parslet.repeat(1,2) } it { should_not parse("") } it { should parse("a") } it { should parse("aa") } end describe "0 times" do it "raises an ArgumentError" do expect { parslet.repeat(0,0) }.to raise_error(ArgumentError) end end end endparslet-2.0.0/spec/parslet/atoms/scope_spec.rb000066400000000000000000000011011362225661100213570ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Scope do include Parslet include Parslet::Atoms::DSL let(:context) { Parslet::Atoms::Context.new(nil) } let(:captures) { context.captures } def inject string, parser source = Parslet::Source.new(string) parser.apply(source, context, true) end let(:aabb) { scope { match['ab'].capture(:f) >> dynamic { |s,c| str(c.captures[:f]) } } } it "keeps values of captures outside" do captures[:f] = 'old_value' inject 'aa', aabb captures[:f].should == 'old_value' end endparslet-2.0.0/spec/parslet/atoms/sequence_spec.rb000066400000000000000000000012121362225661100220610ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms::Sequence do include Parslet let(:sequence) { described_class.new } describe '>> shortcut' do let(:sequence) { str('a') >> str('b') } context "when chained with different atoms" do before(:each) { # Chain something else to the sequence parslet. If it modifies the # parslet atom in place, we'll notice: sequence >> str('d') } let!(:chained) { sequence >> str('c') } it "is side-effect free" do chained.should parse('abc') chained.should_not parse('abdc') end end end end parslet-2.0.0/spec/parslet/atoms/str_spec.rb000066400000000000000000000004471362225661100210720ustar00rootroot00000000000000# Encoding: UTF-8 require 'spec_helper' describe Parslet::Atoms::Str do def str(s) described_class.new(s) end describe 'regression #1: multibyte characters' do it "parses successfully (length check works)" do str('ã‚ã‚ã‚').should parse('ã‚ã‚ã‚') end end endparslet-2.0.0/spec/parslet/atoms/visitor_spec.rb000066400000000000000000000044041362225661100217560ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Atoms do include Parslet let(:visitor) { flexmock(:visitor) } describe Parslet::Atoms::Str do let(:parslet) { str('foo') } it "should call back visitor" do visitor.should_receive(:visit_str).with('foo').once parslet.accept(visitor) end end describe Parslet::Atoms::Re do let(:parslet) { match['abc'] } it "should call back visitor" do visitor.should_receive(:visit_re).with('[abc]').once parslet.accept(visitor) end end describe Parslet::Atoms::Sequence do let(:parslet) { str('a') >> str('b') } it "should call back visitor" do visitor.should_receive(:visit_sequence).with(Array).once parslet.accept(visitor) end end describe Parslet::Atoms::Repetition do let(:parslet) { str('a').repeat(1,2) } it "should call back visitor" do visitor.should_receive(:visit_repetition).with(:repetition, 1, 2, Parslet::Atoms::Base).once parslet.accept(visitor) end end describe Parslet::Atoms::Alternative do let(:parslet) { str('a') | str('b') } it "should call back visitor" do visitor.should_receive(:visit_alternative).with(Array).once parslet.accept(visitor) end end describe Parslet::Atoms::Named do let(:parslet) { str('a').as(:a) } it "should call back visitor" do visitor.should_receive(:visit_named).with(:a, Parslet::Atoms::Base).once parslet.accept(visitor) end end describe Parslet::Atoms::Entity do let(:parslet) { Parslet::Atoms::Entity.new('foo', &lambda {}) } it "should call back visitor" do visitor.should_receive(:visit_entity).with('foo', Proc).once parslet.accept(visitor) end end describe Parslet::Atoms::Lookahead do let(:parslet) { str('a').absent? } it "should call back visitor" do visitor.should_receive(:visit_lookahead).with(false, Parslet::Atoms::Base).once parslet.accept(visitor) end end describe "< Parslet::Parser" do let(:parslet) { Parslet::Parser.new } it "calls back to visitor" do visitor.should_receive(:visit_parser).with(:root).once flexmock(parslet, :root => :root) parslet.accept(visitor) end end endparslet-2.0.0/spec/parslet/atoms_spec.rb000066400000000000000000000314671362225661100202700ustar00rootroot00000000000000require 'spec_helper' require 'timeout' require 'parslet' describe Parslet do def not_parse raise_error(Parslet::ParseFailed) end include Parslet extend Parslet def src(str); Parslet::Source.new str; end let(:context) { Parslet::Atoms::Context.new } describe "match('[abc]')" do attr_reader :parslet before(:each) do @parslet = match('[abc]') end it "should parse {a,b,c}" do parslet.parse('a') parslet.parse('b') parslet.parse('c') end it "should not parse d" do cause = catch_failed_parse { parslet.parse('d') } cause.to_s.should == "Failed to match [abc] at line 1 char 1." end it "should print as [abc]" do parslet.inspect.should == "[abc]" end end describe "match(['[a]').repeat(3)" do attr_reader :parslet before(:each) do @parslet = match('[a]').repeat(3) end context "when failing on input 'aa'" do let!(:cause) { catch_failed_parse { parslet.parse('aa') } } it "should have a relevant cause" do cause.to_s.should == "Expected at least 3 of [a] at line 1 char 1." end it "should have a tree with 2 nodes" do cause.children.size.should == 1 end end it "should succeed on 'aaa'" do parslet.parse('aaa') end it "should succeed on many 'a'" do parslet.parse('a'*100) end it "should inspect as [a]{3, }" do parslet.inspect.should == "[a]{3, }" end end describe "str('foo')" do attr_reader :parslet before(:each) do @parslet = str('foo') end it "should parse 'foo'" do parslet.parse('foo') end it "should not parse 'bar'" do cause = catch_failed_parse { parslet.parse('bar') } cause.to_s.should == "Expected \"foo\", but got \"bar\" at line 1 char 1." end it "should inspect as 'foo'" do parslet.inspect.should == "'foo'" end end describe "str('foo').maybe" do let(:parslet) { str('foo').maybe } it "should parse a foo" do parslet.parse('foo') end it "should leave pos untouched if there is no foo" do source = src('bar') parslet.apply(source, context) source.pos.charpos.should == 0 end it "should inspect as 'foo'?" do parslet.inspect.should == "'foo'?" end context "when parsing 'foo'" do subject { parslet.parse('foo') } it { should == 'foo' } end context "when parsing ''" do subject { parslet.parse('') } it { should == '' } end end describe "str('foo') >> str('bar')" do let(:parslet) { str('foo') >> str('bar') } context "when it fails on input 'foobaz'" do let!(:cause) { catch_failed_parse { parslet.parse('foobaz') } } it "should not parse 'foobaz'" do cause.to_s.should == "Failed to match sequence ('foo' 'bar') at line 1 char 4." end it "should have 2 nodes in error tree" do cause.children.size.should == 1 end end it "should parse 'foobar'" do parslet.parse('foobar') end it "should inspect as ('foo' 'bar')" do parslet.inspect.should == "'foo' 'bar'" end end describe "str('foo') | str('bar')" do attr_reader :parslet before(:each) do @parslet = str('foo') | str('bar') end context "when failing on input 'baz'" do let!(:cause) { catch_failed_parse { parslet.parse('baz') } } it "should have a sensible cause" do cause.to_s.should == "Expected one of ['foo', 'bar'] at line 1 char 1." end it "should have an error tree with 3 nodes" do cause.children.size.should == 2 end end it "should accept 'foo'" do parslet.parse('foo') end it "should accept 'bar'" do parslet.parse('bar') end it "should inspect as ('foo' / 'bar')" do parslet.inspect.should == "'foo' / 'bar'" end end describe "str('foo').present? (positive lookahead)" do attr_reader :parslet before(:each) do @parslet = str('foo').present? end it "should inspect as &'foo'" do parslet.inspect.should == "&'foo'" end context "when fed 'foo'" do it "should parse" do success, _ = parslet.apply(src('foo'), context) success.should == true end it "should not change input position" do source = src('foo') parslet.apply(source, context) source.pos.charpos.should == 0 end end context "when fed 'bar'" do it "should not parse" do lambda { parslet.parse('bar') }.should not_parse end end describe "<- #parse" do it "should return nil" do parslet.apply(src('foo'), context).should == [true, nil] end end end describe "str('foo').absent? (negative lookahead)" do attr_reader :parslet before(:each) do @parslet = str('foo').absent? end it "should inspect as !'foo'" do parslet.inspect.should == "!'foo'" end context "when fed 'bar'" do it "should parse" do parslet.apply(src('bar'), context).should == [true, nil] end it "should not change input position" do source = src('bar') parslet.apply(source, context) source.pos.charpos.should == 0 end end context "when fed 'foo'" do it "should not parse" do lambda { parslet.parse('foo') }.should not_parse end end end describe "non greedy matcher combined with greedy matcher (possible loop)" do attr_reader :parslet before(:each) do # repeat will always succeed, since it has a minimum of 0. It will not # modify input position in that case. absent? will, depending on # implementation, match as much as possible and call its inner element # again. This leads to an infinite loop. This example tests for the # absence of that loop. @parslet = str('foo').repeat.maybe end it "should not loop infinitely" do lambda { Timeout.timeout(1) { parslet.parse('bar') } }.should raise_error(Parslet::ParseFailed) end end describe "any" do attr_reader :parslet before(:each) do @parslet = any end it "should match" do parslet.parse('.') end it "should consume one char" do source = src('foo') parslet.apply(source, context) source.pos.charpos.should == 1 end end describe "eof behaviour" do context "when the pattern just doesn't consume the input" do let (:parslet) { any } it "should fail the parse" do cause = catch_failed_parse { parslet.parse('..') } cause.to_s.should == "Don't know what to do with \".\" at line 1 char 2." end end context "when the pattern doesn't match the input" do let (:parslet) { (str('a')).repeat(1) } attr_reader :exception before(:each) do begin parslet.parse('a.') rescue => @exception end end it "raises Parslet::ParseFailed" do # ParseFailed here, because the input doesn't match the parser grammar. exception.should be_kind_of(Parslet::ParseFailed) end it "has the correct error message" do exception.message.should == \ "Extra input after last repetition at line 1 char 2." end end end describe "<- #as(name)" do context "str('foo').as(:bar)" do it "should return :bar => 'foo'" do str('foo').as(:bar).parse('foo').should == { :bar => 'foo' } end end context "match('[abc]').as(:name)" do it "should return :name => 'b'" do match('[abc]').as(:name).parse('b').should == { :name => 'b' } end end context "match('[abc]').repeat.as(:name)" do it "should return collated result ('abc')" do match('[abc]').repeat.as(:name). parse('abc').should == { :name => 'abc' } end end context "(str('a').as(:a) >> str('b').as(:b)).as(:c)" do it "should return a hash of hashes" do (str('a').as(:a) >> str('b').as(:b)).as(:c). parse('ab').should == { :c => { :a => 'a', :b => 'b' } } end end context "(str('a').as(:a) >> str('ignore') >> str('b').as(:b))" do it "should correctly flatten (leaving out 'ignore')" do (str('a').as(:a) >> str('ignore') >> str('b').as(:b)). parse('aignoreb').should == { :a => 'a', :b => 'b' } end end context "(str('a') >> str('ignore') >> str('b')) (no .as(...))" do it "should return simply the original string" do (str('a') >> str('ignore') >> str('b')). parse('aignoreb').should == 'aignoreb' end end context "str('a').as(:a) >> str('b').as(:a)" do attr_reader :parslet before(:each) do @parslet = str('a').as(:a) >> str('b').as(:a) end it "should issue a warning that a key is being overwritten in merge" do flexmock(parslet). should_receive(:warn).once parslet.parse('ab').should == { :a => 'b' } end it "should return :a => 'b'" do flexmock(parslet). should_receive(:warn) parslet.parse('ab').should == { :a => 'b' } end end context "str('a').absent?" do it "should return something in merge, even though it is nil" do (str('a').absent? >> str('b').as(:b)). parse('b').should == {:b => 'b'} end end context "str('a').as(:a).repeat" do it "should return an array of subtrees" do str('a').as(:a).repeat. parse('aa').should == [{:a=>'a'}, {:a=>'a'}] end end end describe "<- #flatten(val)" do def call(val) dummy = str('a') flexmock(dummy, :warn => nil) dummy.flatten(val) end [ # In absence of named subtrees: ---------------------------------------- # Sequence or Repetition [ [:sequence, 'a', 'b'], 'ab' ], [ [:repetition, 'a', 'a'], 'aa' ], # Nested inside another node [ [:sequence, [:sequence, 'a', 'b']], 'ab' ], # Combined with lookahead (nil) [ [:sequence, nil, 'a'], 'a' ], # Including named subtrees --------------------------------------------- # Atom: A named subtree [ {:a=>'a'}, {:a=>'a'} ], # Composition of subtrees [ [:sequence, {:a=>'a'},{:b=>'b'}], {:a=>'a',:b=>'b'} ], # Mixed subtrees :sequence of :repetition yields [] [ [:sequence, [:repetition, {:a => 'a'}], {:a => 'a'} ], [{:a=>'a'}, {:a=>'a'}]], [ [:sequence, {:a => 'a'},[:repetition, {:a => 'a'}] ], [{:a=>'a'}, {:a=>'a'}]], [ [:sequence, [:repetition, {:a => 'a'}],[:repetition, {:a => 'a'}] ], [{:a=>'a'}, {:a=>'a'}]], # Repetition [ [:repetition, [:repetition, {:a=>'a'}], [:repetition, {:a=>'a'}]], [{:a => 'a'}, {:a => 'a'}]], [ [:repetition, {:a=>'a'}, 'a', {:a=>'a'}], [{:a=>'a'}, {:a=>'a'}]], [ [:repetition, {:a=>'a'}, [:repetition, {:b=>'b'}]], [{:a=>'a'}] ], # Some random samples -------------------------------------------------- [ [:sequence, {:a => :b, :b => :c}], {:a=>:b, :b=>:c} ], [ [:sequence, {:a => :b}, 'a', {:c=>:d}], {:a => :b, :c=>:d} ], [ [:repetition, {:a => :b}, 'a', {:c=>:d}], [{:a => :b}, {:c=>:d}] ], [ [:sequence, {:a => :b}, {:a=>:d}], {:a => :d} ], [ [:sequence, {:a=>:b}, [:sequence, [:sequence, "\n", nil]]], {:a=>:b} ], [ [:sequence, nil, " "], ' ' ], ].each do |input, output| it "should transform #{input.inspect} to #{output.inspect}" do call(input).should == output end end end describe "combinations thereof (regression)" do [ [(str('a').repeat >> str('b').repeat), 'aaabbb'] ].each do |(parslet, input)| describe "#{parslet.inspect} applied to #{input.inspect}" do it "should parse successfully" do parslet.parse(input) end end end [ [str('a'), "'a'" ], [(str('a') | str('b')).maybe, "('a' / 'b')?" ], [(str('a') >> str('b')).maybe, "('a' 'b')?" ], [str('a').maybe.maybe, "'a'??" ], [(str('a')>>str('b')).maybe.maybe, "('a' 'b')??" ], [(str('a') >> (str('b') | str('c'))), "'a' ('b' / 'c')"], [str('a') >> str('b').repeat, "'a' 'b'{0, }" ], [(str('a')>>str('b')).repeat, "('a' 'b'){0, }" ] ].each do |(parslet, inspect_output)| context "regression for #{parslet.inspect}" do it "should inspect correctly as #{inspect_output}" do parslet.inspect.should == inspect_output end end end end endparslet-2.0.0/spec/parslet/convenience_spec.rb000066400000000000000000000024171362225661100214320ustar00rootroot00000000000000require 'spec_helper' describe 'parslet/convenience' do require 'parslet/convenience' include Parslet class FooParser < Parslet::Parser rule(:foo) { str('foo') } root(:foo) end describe 'parse_with_debug' do let(:parser) { flexmock FooParser.new } context 'internal' do before(:each) do # Suppress output. # parser.should_receive(:puts) end it 'should exist' do lambda { parser.parse_with_debug('anything') }.should_not raise_error end it 'should catch ParseFailed exceptions' do lambda { parser.parse_with_debug('bar') }.should_not raise_error end it 'should parse correct input like #parse' do lambda { parser.parse_with_debug('foo') }.should_not raise_error end end context 'output' do it 'should puts once for tree output' do parser.should_receive(:puts).once parser.parse_with_debug('incorrect') end it "should puts once for the error on unconsumed input" do parser.should_receive(:puts).once parser.parse_with_debug('foobar') end end it "should work for all parslets" do str('foo').parse_with_debug('foo') match['bar'].parse_with_debug('a') end end end parslet-2.0.0/spec/parslet/error_reporter/000077500000000000000000000000001362225661100206465ustar00rootroot00000000000000parslet-2.0.0/spec/parslet/error_reporter/contextual_spec.rb000066400000000000000000000065351362225661100244040ustar00rootroot00000000000000require 'spec_helper' describe Parslet::ErrorReporter::Contextual do let(:reporter) { described_class.new } let(:fake_source) { flexmock('source') } let(:fake_atom) { flexmock('atom') } let(:fake_cause) { flexmock('cause') } describe '#err' do before(:each) { fake_source.should_receive( :pos => 13, :line_and_column => [1,1]) } it "returns the deepest cause" do flexmock(reporter). should_receive(:deepest).and_return(:deepest) reporter.err('parslet', fake_source, 'message'). should == :deepest end end describe '#err_at' do before(:each) { fake_source.should_receive( :pos => 13, :line_and_column => [1,1]) } it "returns the deepest cause" do flexmock(reporter). should_receive(:deepest).and_return(:deepest) reporter.err('parslet', fake_source, 'message', 13). should == :deepest end end describe '#deepest(cause)' do def fake_cause(pos=13, children=nil) flexmock('cause' + pos.to_s, :pos => pos, :children => children) end context "when there is no deepest cause yet" do let(:cause) { fake_cause } it "returns the given cause" do reporter.deepest(cause).should == cause end end context "when the previous cause is deeper (no relationship)" do let(:previous) { fake_cause } before(:each) { reporter.deepest(previous) } it "returns the previous cause" do reporter.deepest(fake_cause(12)). should == previous end end context "when the previous cause is deeper (child)" do let(:previous) { fake_cause } before(:each) { reporter.deepest(previous) } it "returns the given cause" do given = fake_cause(12, [previous]) reporter.deepest(given).should == given end end context "when the previous cause is shallower" do before(:each) { reporter.deepest(fake_cause) } it "stores the cause as deepest" do deeper = fake_cause(14) reporter.deepest(deeper) reporter.deepest_cause.should == deeper end end end describe '#reset' do before(:each) { fake_source.should_receive( :pos => Parslet::Position.new('source', 13), :line_and_column => [1,1]) } it "resets deepest cause on success of sibling expression" do flexmock(reporter). should_receive(:deepest).and_return(:deepest) reporter.err('parslet', fake_source, 'message'). should == :deepest flexmock(reporter). should_receive(:reset).once reporter.succ(fake_source) end end describe 'label' do before(:each) { fake_source.should_receive( :pos => Parslet::Position.new('source', 13), :line_and_column => [1,1]) } it "sets label if atom has one" do fake_atom.should_receive(:label).once.and_return('label') fake_cause.should_receive(:set_label).once flexmock(reporter). should_receive(:deepest).and_return(fake_cause) reporter.err(fake_atom, fake_source, 'message'). should == fake_cause end it 'does not set label if atom does not have one' do flexmock(reporter). should_receive(:deepest).and_return(:deepest) fake_atom.should_receive(:update_label).never reporter.err(fake_atom, fake_source, 'message'). should == :deepest end end end parslet-2.0.0/spec/parslet/error_reporter/deepest_spec.rb000066400000000000000000000040751362225661100236440ustar00rootroot00000000000000require 'spec_helper' describe Parslet::ErrorReporter::Deepest do let(:reporter) { described_class.new } let(:fake_source) { flexmock('source') } describe '#err' do before(:each) { fake_source.should_receive( :pos => 13, :line_and_column => [1,1]) } it "returns the deepest cause" do flexmock(reporter). should_receive(:deepest).and_return(:deepest) reporter.err('parslet', fake_source, 'message'). should == :deepest end end describe '#err_at' do before(:each) { fake_source.should_receive( :pos => 13, :line_and_column => [1,1]) } it "returns the deepest cause" do flexmock(reporter). should_receive(:deepest).and_return(:deepest) reporter.err('parslet', fake_source, 'message', 13). should == :deepest end end describe '#deepest(cause)' do def fake_cause(pos=13, children=nil) flexmock('cause' + pos.to_s, :pos => pos, :children => children) end context "when there is no deepest cause yet" do let(:cause) { fake_cause } it "returns the given cause" do reporter.deepest(cause).should == cause end end context "when the previous cause is deeper (no relationship)" do let(:previous) { fake_cause } before(:each) { reporter.deepest(previous) } it "returns the previous cause" do reporter.deepest(fake_cause(12)). should == previous end end context "when the previous cause is deeper (child)" do let(:previous) { fake_cause } before(:each) { reporter.deepest(previous) } it "returns the given cause" do given = fake_cause(12, [previous]) reporter.deepest(given).should == given end end context "when the previous cause is shallower" do before(:each) { reporter.deepest(fake_cause) } it "stores the cause as deepest" do deeper = fake_cause(14) reporter.deepest(deeper) reporter.deepest_cause.should == deeper end end end end parslet-2.0.0/spec/parslet/error_reporter/tree_spec.rb000066400000000000000000000001501362225661100231400ustar00rootroot00000000000000require 'spec_helper' require 'parslet/error_reporter' describe Parslet::ErrorReporter::Tree do endparslet-2.0.0/spec/parslet/export_spec.rb000066400000000000000000000032761362225661100204630ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Parser, "exporting to other lingos" do class MiniLisp < Parslet::Parser root :expression rule(:expression) { space? >> str('(') >> space? >> body >> str(')') } rule(:body) { (expression | identifier | float | integer | string).repeat.as(:exp) } rule(:space) { match('\s').repeat(1) } rule(:space?) { space.maybe } rule(:identifier) { (match('[a-zA-Z=*]') >> match('[a-zA-Z=*_]').repeat).as(:identifier) >> space? } rule(:float) { ( integer >> ( str('.') >> match('[0-9]').repeat(1) | str('e') >> match('[0-9]').repeat(1) ).as(:e) ).as(:float) >> space? } rule(:integer) { ((str('+') | str('-')).maybe >> match("[0-9]").repeat(1)).as(:integer) >> space? } rule(:string) { str('"') >> ( str('\\') >> any | str('"').absent? >> any ).repeat.as(:string) >> str('"') >> space? } end # I only update the files once I've verified the new syntax to work with # the respective tools. This is more an acceptance test than a real spec. describe "<- #to_citrus" do let(:citrus) { File.read( File.join(File.dirname(__FILE__), 'minilisp.citrus')) } it "should be valid citrus syntax" do # puts MiniLisp.new.to_citrus MiniLisp.new.to_citrus.should == citrus end end describe "<- #to_treetop" do let(:treetop) { File.read( File.join(File.dirname(__FILE__), 'minilisp.tt')) } it "should be valid treetop syntax" do # puts MiniLisp.new.to_treetop MiniLisp.new.to_treetop.should == treetop end end endparslet-2.0.0/spec/parslet/expression/000077500000000000000000000000001362225661100177725ustar00rootroot00000000000000parslet-2.0.0/spec/parslet/expression/treetop_spec.rb000066400000000000000000000034611362225661100230170ustar00rootroot00000000000000require 'spec_helper' require 'parslet' describe Parslet::Expression::Treetop do include Parslet describe "positive samples" do [ # pattern # input "'abc'", 'abc', "...", 'abc', "[1-4]", '3', "'abc'?", 'abc', "'abc'?", '', "('abc')", 'abc', "'a' 'b'", 'ab', "'a' ('b')", 'ab', "'a' / 'b'", 'a', "'a' / 'b'", 'b', "'a'*", 'aaa', "'a'*", '', "'a'+", 'aa', "'a'+", 'a', "'a'{1,2}", 'a', "'a'{1,2}", 'aa', "'a'{1,}", 'a', "'a'{1,}", 'aa', "'a'{,2}", '', "'a'{,2}", 'a', "'a'{,2}", 'aa', ].each_slice(2) do |pattern, input| context "exp(#{pattern.inspect})" do let(:parslet) { exp(pattern) } subject { parslet } it { should parse(input) } context "string representation" do subject { exp(parslet.to_s) } it { should parse(input, :trace => true) } end end end end describe "negative samples" do [ # pattern # input "'abc'", 'cba', "[1-4]", '5', "'a' / 'b'", 'c', "'a'+", '', "'a'{1,2}", '', "'a'{1,2}", 'aaa', "'a'{1,}", '', "'a'{,2}", 'aaa', ].each_slice(2) do |pattern, input| context "exp(#{pattern.inspect})" do subject { exp(pattern) } it { should_not parse(input) } end end end endparslet-2.0.0/spec/parslet/minilisp.citrus000066400000000000000000000010471362225661100206540ustar00rootroot00000000000000grammar MiniLisp rule root (expression) end rule expression ((space_p) "(" (space_p) (body) ")") end rule space_p (space)0*1 end rule body ((expression) | (identifier) | (float) | (integer) | (string))0* end rule space \s1* end rule identifier (([a-zA-Z=*] [a-zA-Z=*_]0*) (space_p)) end rule float (((integer) (("." [0-9]1*) | ("e" [0-9]1*))) (space_p)) end rule integer ((("+" | "-")0*1 [0-9]1*) (space_p)) end rule string ("\"" (("\\" .) | (!"\"" .))0* "\"" (space_p)) end end parslet-2.0.0/spec/parslet/minilisp.tt000066400000000000000000000010601362225661100177650ustar00rootroot00000000000000grammar MiniLisp rule root (expression) end rule expression ((space_p) "(" (space_p) (body) ")") end rule space_p (space)0..1 end rule body ((expression) / (identifier) / (float) / (integer) / (string))0.. end rule space \s1.. end rule identifier (([a-zA-Z=*] [a-zA-Z=*_]0..) (space_p)) end rule float (((integer) (("." [0-9]1..) / ("e" [0-9]1..))) (space_p)) end rule integer ((("+" / "-")0..1 [0-9]1..) (space_p)) end rule string ("\"" (("\\" .) / (!"\"" .))0.. "\"" (space_p)) end end parslet-2.0.0/spec/parslet/parser_spec.rb000066400000000000000000000014151362225661100204270ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Parser do include Parslet class FooParser < Parslet::Parser rule(:foo) { str('foo') } root(:foo) end describe "<- .root" do parser = Class.new(Parslet::Parser) parser.root :root_parslet it "should have defined a 'root' method, returning the root" do parser_instance = parser.new flexmock(parser_instance).should_receive(:root_parslet => :answer) parser_instance.root.should == :answer end end it "should parse 'foo'" do FooParser.new.parse('foo').should == 'foo' end context "composition" do let(:parser) { FooParser.new } it "should allow concatenation" do composite = parser >> str('bar') composite.should parse('foobar') end end endparslet-2.0.0/spec/parslet/parslet_spec.rb000066400000000000000000000015631362225661100206110ustar00rootroot00000000000000require 'spec_helper' describe Parslet do include Parslet describe Parslet::ParseFailed do it "should be caught by an empty rescue" do begin raise Parslet::ParseFailed rescue # Success! Ignore this. end end end describe "<- .rule" do # Rules define methods. This can be easily tested by defining them right # here. context "empty rule" do rule(:empty) { } it "should raise a NotImplementedError" do lambda { empty.parslet }.should raise_error(NotImplementedError) end end context "containing 'any'" do rule(:any_rule) { any } subject { any_rule } it { should be_a Parslet::Atoms::Entity } it "should memoize the returned instance" do any_rule.object_id.should == any_rule.object_id end end end endparslet-2.0.0/spec/parslet/pattern_spec.rb000066400000000000000000000202231362225661100206060ustar00rootroot00000000000000require 'spec_helper' require 'parslet' describe Parslet::Pattern do include Parslet # These two factory methods help make the specs more robust to interface # changes. They also help to label trees (t) and patterns (p). def p(pattern) Parslet::Pattern.new(pattern) end def t(obj) obj end # Tries to match pattern to the tree, and verifies the bindings hash. Don't # use this for new examples. # RSpec::Matchers.define :match_with_bind do |pattern, exp_bindings| unless respond_to?(:failure_message) alias_method :failure_message_for_should, :failure_message end failure_message do |tree| "expected #{pattern.inspect} to match #{tree.inspect}, but didn't. (block wasn't called or not correctly)" end match do |tree| bindings = Parslet::Pattern.new(pattern).match(tree) bindings && bindings == exp_bindings end end # This is the more modern version of verifying a match: (uses 'exp' # implicitly). Checks for a match of pattern in +exp+ and yields the # matched variables. # def with_match_locals(pattern, &block) bindings = p(pattern).match(exp) bindings.should_not be_nil block.call(bindings) if block end # Can't use #match here, so I went to the Thesaurus. # RSpec::Matchers.define :detect do |pattern| match do |tree| bindings = Parslet::Pattern.new(pattern).match(tree) bindings ? true : false end end describe "<- #match" do context "injecting bindings" do let(:pattern) { p(simple(:x)) } it "should not modify the original bindings hash" do h = {} b=pattern.match('a', h) h.size.should == 0 b.size.should == 1 end it "should return nil when no match succeeds" do pattern.match([], :foo => :bar).should be_nil end context "when matching simple(:x) against 'a'" do let(:bindings) { pattern.match(t('a'), :foo => :bar) } before(:each) { bindings.should_not be_nil } it "should return the injected bindings" do bindings[:foo].should == :bar end it "should return the new bindings" do bindings[:x].should == 'a' end end end context "simple strings" do let(:exp) { 'aaaa' } it "should match simple strings" do exp.should match_with_bind(simple(:x), :x => 'aaaa') end end context "simple hash {:a => 'b'}" do attr_reader :exp before(:each) do @exp = t(:a => 'b') end it "should not match {:a => simple(:x), :b => simple(:y)}" do exp.should_not detect(:a => simple(:x), :b => simple(:y)) end it "should match {:a => simple(:x)}, binding 'x' to the first argument" do exp.should match_with_bind({:a => simple(:x)}, :x => 'b') end it "should match {:a => 'b'} with no binds" do exp.should match_with_bind({:a => 'b'}, {}) end end context "a more complex hash {:a => {:b => 'c'}}" do attr_reader :exp before(:each) do @exp = t(:a => {:b => 'c'}) end it "should match wholly with {:a => {:b => simple(:x)}}" do exp.should match_with_bind({:a => {:b => simple(:x)}}, :x => 'c') end it "should match wholly with {:a => subtree(:t)}" do with_match_locals(:a => subtree(:t)) do |dict| dict[:t].should == {:b => 'c'} end end it "should not bind subtrees to variables in {:a => simple(:x)}" do p(:a => simple(:x)).should_not detect(exp) end end context "a more complex hash {:a => 'a', :b => 'b'}" do attr_reader :exp before(:each) do @exp = t({:a => 'a', :b => 'b'}) end it "should not match partially" do Parslet::Pattern.new(:a => simple(:x)).match(exp).should be_nil end it "should match completely" do exp.should match_with_bind({:a => simple(:x), :b => simple(:y)}, :x => 'a', :y => 'b') end end context "an array of 'a', 'b', 'c'" do let(:exp) { ['a', 'b', 'c'] } it "should match all elements at once" do exp.should match_with_bind( [simple(:x), simple(:y), simple(:z)], :x => 'a', :y => 'b', :z => 'c') end end context "{:a => 'a', :b => 'b'}" do attr_reader :exp before(:each) do @exp = t(:a => 'a', :b => 'b') end it "should match both elements simple(:x), simple(:y)" do exp.should match_with_bind( {:a => simple(:x), :b => simple(:y)}, :x => 'a', :y => 'b') end it "should not match a constrained match (simple(:x) != simple(:y))" do exp.should_not detect({:a => simple(:x), :b => simple(:x)}) end end context "{:a => 'a', :b => 'a'}" do attr_reader :exp before(:each) do @exp = t(:a => 'a', :b => 'a') end it "should match constrained pattern" do exp.should match_with_bind( {:a => simple(:x), :b => simple(:x)}, :x => 'a') end end context "{:sub1 => {:a => 'a'}, :sub2 => {:a => 'a'}}" do attr_reader :exp before(:each) do @exp = t({ :sub1 => {:a => 'a'}, :sub2 => {:a => 'a'} }) end it "should verify constraints over several subtrees" do exp.should match_with_bind({ :sub1 => {:a => simple(:x)}, :sub2 => {:a => simple(:x)} }, :x => 'a') end it "should return both bind variables simple(:x), simple(:y)" do exp.should match_with_bind({ :sub1 => {:a => simple(:x)}, :sub2 => {:a => simple(:y)} }, :x => 'a', :y => 'a') end end context "{:sub1 => {:a => 'a'}, :sub2 => {:a => 'b'}}" do attr_reader :exp before(:each) do @exp = t({ :sub1 => {:a => 'a'}, :sub2 => {:a => 'b'} }) end it "should verify constraints over several subtrees" do exp.should_not match_with_bind({ :sub1 => {:a => simple(:x)}, :sub2 => {:a => simple(:x)} }, :x => 'a') end it "should return both bind variables simple(:x), simple(:y)" do exp.should match_with_bind({ :sub1 => {:a => simple(:x)}, :sub2 => {:a => simple(:y)} }, :x => 'a', :y => 'b') end end context "[{:a => 'x'}, {:a => 'y'}]" do attr_reader :exp before(:each) do @exp = t([{:a => 'x'}, {:a => 'y'}]) end it "should not match sequence(:x) (as a whole)" do exp.should_not detect(sequence(:x)) end end context "['x', 'y', 'z']" do attr_reader :exp before(:each) do @exp = t(['x', 'y', 'z']) end it "should match [simple(:x), simple(:y), simple(:z)]" do with_match_locals([simple(:x), simple(:y), simple(:z)]) do |dict| dict[:x].should == 'x' dict[:y].should == 'y' dict[:z].should == 'z' end end it "should match %w(x y z)" do exp.should match_with_bind(%w(x y z), { }) end it "should not match [simple(:x), simple(:y), simple(:x)]" do exp.should_not detect([simple(:x), simple(:y), simple(:x)]) end it "should not match [simple(:x), simple(:y)]" do exp.should_not detect([simple(:x), simple(:y), simple(:x)]) end it "should match sequence(:x) (as array)" do exp.should match_with_bind(sequence(:x), :x => ['x', 'y', 'z']) end end context "{:a => [1,2,3]}" do attr_reader :exp before(:each) do @exp = t(:a => [1,2,3]) end it "should match :a => sequence(:x) (binding x to the whole array)" do exp.should match_with_bind({:a => sequence(:x)}, {:x => [1,2,3]}) end end context "with differently ordered hashes" do it "should still match" do t(:a => 'a', :b => 'b').should detect(:a => 'a', :b => 'b') t(:a => 'a', :b => 'b').should detect(:b => 'b', :a => 'a') t(:b => 'b', :a => 'a').should detect(:b => 'b', :a => 'a') t(:b => 'b', :a => 'a').should detect(:a => 'a', :b => 'b') end end end endparslet-2.0.0/spec/parslet/position_spec.rb000066400000000000000000000004351362225661100210000ustar00rootroot00000000000000# Encoding: UTF-8 require 'spec_helper' describe Parslet::Position do slet(:position) { described_class.new('öäüö', 4) } it 'should have a charpos of 2' do position.charpos.should == 2 end it 'should have a bytepos of 4' do position.bytepos.should == 4 end endparslet-2.0.0/spec/parslet/rig/000077500000000000000000000000001362225661100163545ustar00rootroot00000000000000parslet-2.0.0/spec/parslet/rig/rspec_spec.rb000066400000000000000000000035431362225661100210340ustar00rootroot00000000000000require 'spec_helper' require 'parslet/rig/rspec' describe 'rspec integration' do include Parslet subject { str('example') } it { should parse('example') } it { should_not parse('foo') } it { should parse('example').as('example') } it { should_not parse('foo').as('example') } it { should_not parse('example').as('foo') } it { str('foo').as(:bar).should parse('foo').as({:bar => 'foo'}) } it { str('foo').as(:bar).should_not parse('foo').as({:b => 'f'}) } it 'accepts a block to assert more specific details about the parsing output' do str('foo').as(:bar).should(parse('foo').as { |output| output.should have_key(:bar) output.values.first.should == 'foo' }) end # Uncomment to test error messages manually: # it { str('foo').should parse('foo', :trace => true).as('bar') } # it { str('foo').should parse('food', :trace => true) } # it { str('foo').should_not parse('foo', :trace => true).as('foo') } # it { str('foo').should_not parse('foo', :trace => true) } # it 'accepts a block to assert more specific details about the parsing output' do # str('foo').as(:bar).should(parse('foo', :trace => true).as { |output| # output.should_not have_key(:bar) # }) # end end describe 'rspec3 syntax' do include Parslet let(:s) { str('example') } it { expect(s).to parse('example') } it { expect(s).not_to parse('foo') } it { expect(s).to parse('example').as('example') } it { expect(s).not_to parse('foo').as('example') } it { expect(s).not_to parse('example').as('foo') } # Uncomment to test error messages manually: # it { expect(str('foo')).to parse('foo', :trace => true).as('bar') } # it { expect(str('foo')).to parse('food', :trace => true) } # it { expect(str('foo')).not_to parse('foo', :trace => true).as('foo') } # it { expect(str('foo')).not_to parse('foo', :trace => true) } end parslet-2.0.0/spec/parslet/scope_spec.rb000066400000000000000000000017611362225661100202500ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Scope do let(:scope) { described_class.new } describe 'simple store/retrieve' do before(:each) { scope[:foo] = :bar } it "allows storing objects" do scope[:obj] = 42 end it "raises on access of empty slots" do expect { scope[:empty] }.to raise_error(Parslet::Scope::NotFound) end it "allows retrieval of stored values" do scope[:foo].should == :bar end end describe 'scoping' do before(:each) { scope[:depth] = 1 } before(:each) { scope.push } let(:depth) { scope[:depth] } subject { depth } it { should == 1 } describe 'after a push' do before(:each) { scope.push } it { should == 1 } describe 'and reassign' do before(:each) { scope[:depth] = 2 } it { should == 2 } describe 'and a pop' do before(:each) { scope.pop } it { should == 1 } end end end end endparslet-2.0.0/spec/parslet/slice_spec.rb000066400000000000000000000101771362225661100202370ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Slice do def cslice string, offset, cache=nil described_class.new( Parslet::Position.new(string, offset), string, cache) end describe "construction" do it "should construct from an offset and a string" do cslice('foobar', 40) end end context "('foobar', 40, 'foobar')" do let(:slice) { cslice('foobar', 40) } describe "comparison" do it "should be equal to other slices with the same attributes" do other = cslice('foobar', 40) slice.should == other other.should == slice end it "should be equal to other slices (offset is irrelevant for comparison)" do other = cslice('foobar', 41) slice.should == other other.should == slice end it "should be equal to a string with the same content" do slice.should == 'foobar' end it "should be equal to a string (inversed operands)" do 'foobar'.should == slice end it "should not be equal to a string" do slice.should_not equal('foobar') end it "should not be eql to a string" do slice.should_not eql('foobar') end it "should not hash to the same number" do slice.hash.should_not == 'foobar'.hash end end describe "offset" do it "should return the associated offset" do slice.offset.should == 6 end it "should fail to return a line and column" do lambda { slice.line_and_column }.should raise_error(ArgumentError) end context "when constructed with a source" do let(:slice) { cslice( 'foobar', 40, flexmock(:cache, :line_and_column => [13, 14])) } it "should return proper line and column" do slice.line_and_column.should == [13, 14] end end end describe "string methods" do describe "matching" do it "should match as a string would" do slice.should match(/bar/) slice.should match(/foo/) md = slice.match(/f(o)o/) md.captures.first.should == 'o' end end describe "<- #size" do subject { slice.size } it { should == 6 } end describe "<- #length" do subject { slice.length } it { should == 6 } end describe "<- #+" do let(:other) { cslice('baz', 10) } subject { slice + other } it "should concat like string does" do subject.size.should == 9 subject.should == 'foobarbaz' subject.offset.should == 6 end end end describe "conversion" do describe "<- #to_slice" do it "should return self" do slice.to_slice.should eq(slice) end end describe "<- #to_sym" do it "should return :foobar" do slice.to_sym.should == :foobar end end describe "cast to Float" do it "should return a float" do Float(cslice('1.345', 11)).should == 1.345 end end describe "cast to Integer" do it "should cast to integer as a string would" do s = cslice('1234', 40) Integer(s).should == 1234 s.to_i.should == 1234 end it "should fail when Integer would fail on a string" do lambda { Integer(slice.to_s) }.should raise_error(ArgumentError, /invalid value/) end it "should turn into zero when a string would" do slice.to_i.should == 0 end end end describe "inspection and string conversion" do describe "#inspect" do subject { slice.inspect } it { should == '"foobar"@6' } end describe "#to_s" do subject { slice.to_s } it { should == 'foobar' } end end describe "serializability" do it "should serialize" do Marshal.dump(slice) end context "when storing a line cache" do let(:slice) { cslice('foobar', 40, Parslet::Source::LineCache.new()) } it "should serialize" do Marshal.dump(slice) end end end end end parslet-2.0.0/spec/parslet/source/000077500000000000000000000000001362225661100170735ustar00rootroot00000000000000parslet-2.0.0/spec/parslet/source/line_cache_spec.rb000066400000000000000000000044201362225661100225040ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Source::RangeSearch do describe "<- #lbound" do context "for a simple array" do let(:ary) { [10, 20, 30, 40, 50] } before(:each) { ary.extend Parslet::Source::RangeSearch } it "should return correct answers for numbers not in the array" do ary.lbound(5).should == 0 ary.lbound(15).should == 1 ary.lbound(25).should == 2 ary.lbound(35).should == 3 ary.lbound(45).should == 4 end it "should return correct answers for numbers in the array" do ary.lbound(10).should == 1 ary.lbound(20).should == 2 ary.lbound(30).should == 3 ary.lbound(40).should == 4 end it "should cover right edge case" do ary.lbound(50).should be_nil ary.lbound(51).should be_nil end it "should cover left edge case" do ary.lbound(0).should == 0 end end context "for an empty array" do let(:ary) { [] } before(:each) { ary.extend Parslet::Source::RangeSearch } it "should return nil" do ary.lbound(1).should be_nil end end end end describe Parslet::Source::LineCache do describe "<- scan_for_line_endings" do context "calculating the line_and_columns" do let(:str) { "foo\nbar\nbazd" } it "should return the first line if we have no line ends" do subject.scan_for_line_endings(0, nil) subject.line_and_column(3).should == [1, 4] subject.scan_for_line_endings(0, "") subject.line_and_column(5).should == [1, 6] end it "should find the right line starting from pos 0" do subject.scan_for_line_endings(0, str) subject.line_and_column(5).should == [2, 2] subject.line_and_column(9).should == [3, 2] end it "should find the right line starting from pos 5" do subject.scan_for_line_endings(5, str) subject.line_and_column(11).should == [2, 3] end it "should find the right line if scannning the string multiple times" do subject.scan_for_line_endings(0, str) subject.scan_for_line_endings(0, "#{str}\nthe quick\nbrown fox") subject.line_and_column(10).should == [3,3] subject.line_and_column(24).should == [5,2] end end end end parslet-2.0.0/spec/parslet/source_spec.rb000066400000000000000000000107461362225661100204420ustar00rootroot00000000000000# Encoding: UTF-8 require 'spec_helper' describe Parslet::Source do describe "using simple input" do let(:str) { "a"*100 + "\n" + "a"*100 + "\n" } let(:source) { described_class.new(str) } describe "<- #read(n)" do it "should not raise error when the return value is nil" do described_class.new('').consume(1) end it "should return 100 'a's when reading 100 chars" do source.consume(100).should == 'a'*100 end end describe "<- #chars_left" do subject { source.chars_left } it { should == 202 } context "after depleting the source" do before(:each) { source.consume(10000) } it { should == 0 } end end describe "<- #pos" do subject { source.pos.charpos } it { should == 0 } context "after reading a few bytes" do it "should still be correct" do pos = 0 10.times do pos += (n = rand(10)+1) source.consume(n) source.pos.charpos.should == pos end end end end describe "<- #pos=(n)" do subject { source.pos.charpos } 10.times do pos = rand(200) context "setting position #{pos}" do before(:each) { source.bytepos = pos } it { should == pos } end end end describe '#chars_until' do it 'should return 100 chars before line end' do source.chars_until("\n").should == 100 end end describe "<- #column & #line" do subject { source.line_and_column } it { should == [1,1] } context "on the first line" do it "should increase column with every read" do 10.times do |i| source.line_and_column.last.should == 1+i source.consume(1) end end end context "on the second line" do before(:each) { source.consume(101) } it { should == [2, 1]} end context "after reading everything" do before(:each) { source.consume(10000) } context "when seeking to 9" do before(:each) { source.bytepos = 9 } it { should == [1, 10] } end context "when seeking to 100" do before(:each) { source.bytepos = 100 } it { should == [1, 101] } end context "when seeking to 101" do before(:each) { source.bytepos = 101 } it { should == [2, 1] } end context "when seeking to 102" do before(:each) { source.bytepos = 102 } it { should == [2, 2] } end context "when seeking beyond eof" do it "should not throw an error" do source.bytepos = 1000 end end end context "reading char by char, storing the results" do attr_reader :results before(:each) { @results = {} while source.chars_left>0 pos = source.pos.charpos @results[pos] = source.line_and_column source.consume(1) end @results.entries.size.should == 202 @results } context "when using pos argument" do it "should return the same results" do results.each do |pos, result| source.line_and_column(pos).should == result end end end it "should give the same results when seeking" do results.each do |pos, result| source.bytepos = pos source.line_and_column.should == result end end it "should give the same results when reading" do cur = source.bytepos = 0 while source.chars_left>0 source.line_and_column.should == results[cur] cur += 1 source.consume(1) end end end end end describe "reading encoded input" do let(:source) { described_class.new("éö変ã‚ã‚‹") } def r str Regexp.new(Regexp.escape(str)) end it "should read characters, not bytes" do source.should match(r("é")) source.consume(1) source.pos.charpos.should == 1 source.bytepos.should == 2 source.should match(r("ö")) source.consume(1) source.pos.charpos.should == 2 source.bytepos.should == 4 source.should match(r("変")) source.consume(1) source.consume(2) source.chars_left.should == 0 source.chars_left.should == 0 end end end parslet-2.0.0/spec/parslet/transform/000077500000000000000000000000001362225661100176065ustar00rootroot00000000000000parslet-2.0.0/spec/parslet/transform/context_spec.rb000066400000000000000000000023751362225661100226400ustar00rootroot00000000000000require 'spec_helper' describe Parslet::Context do def context(*args) described_class.new(*args) end it "binds hash keys as variable like things" do context(:a => 'value').instance_eval { a }. should == 'value' end it "one contexts variables aren't the next ones" do ca = context(:a => 'b') cb = context(:b => 'c') ca.methods.should_not include(:b) cb.methods.should_not include(:a) end describe 'works as a Ruby object should' do let(:obj) { context(a: 1) } it 'responds_to? :a' do assert obj.respond_to?(:a) end it 'includes :a in #methods' do obj.methods.assert.include?(:a) end it 'allows inspection' do obj.inspect.assert.match(/@a=1/) end it 'allows conversion to string' do obj.to_s.assert.match(/Parslet::Context:0x/) end context 'when the context is enhanced' do before(:each) do class << obj def foo 'foo' end end end it 'responds_to correctly' do assert obj.respond_to?(:foo) end it 'includes :foo also in methods' do obj.methods.assert.include?(:foo) end it 'allows calling #foo' do obj.foo.assert == 'foo' end end end endparslet-2.0.0/spec/parslet/transform_spec.rb000066400000000000000000000124041362225661100211460ustar00rootroot00000000000000require 'spec_helper' require 'parslet' describe Parslet::Transform do include Parslet let(:transform) { Parslet::Transform.new } class A < Struct.new(:elt); end class B < Struct.new(:elt); end class C < Struct.new(:elt); end class Bi < Struct.new(:a, :b); end describe "delayed construction" do context "given simple(:x) => A.new(x)" do before(:each) do transform.rule(simple(:x)) { |d| A.new(d[:x]) } end it "should transform 'a' into A.new('a')" do transform.apply('a').should == A.new('a') end it "should transform ['a', 'b'] into [A.new('a'), A.new('b')]" do transform.apply(['a', 'b']).should == [A.new('a'), A.new('b')] end end context "given rules on {:a => simple(:x)} and {:b => :_x}" do before(:each) do transform.rule(:a => simple(:x)) { |d| A.new(d[:x]) } transform.rule(:b => simple(:x)) { |d| B.new(d[:x]) } end it "should transform {:d=>{:b=>'c'}} into d => B('c')" do transform.apply({:d=>{:b=>'c'}}).should == {:d => B.new('c')} end it "should transform {:a=>{:b=>'c'}} into A(B('c'))" do transform.apply({:a=>{:b=>'c'}}).should == A.new(B.new('c')) end end describe "pulling out subbranches" do before(:each) do transform.rule(:a => {:b => simple(:x)}, :d => {:e => simple(:y)}) { |d| Bi.new(*d.values_at(:x, :y)) } end it "should yield Bi.new('c', 'f')" do transform.apply(:a => {:b => 'c'}, :d => {:e => 'f'}).should == Bi.new('c', 'f') end end end describe "dsl construction" do let(:transform) { Parslet::Transform.new do rule(simple(:x)) { A.new(x) } end } it "should still evaluate rules correctly" do transform.apply('a').should == A.new('a') end end describe "class construction" do class OptimusPrime < Parslet::Transform rule(:a => simple(:x)) { A.new(x) } rule(:b => simple(:x)) { B.new(x) } end let(:transform) { OptimusPrime.new } it "should evaluate rules" do transform.apply(:a => 'a').should == A.new('a') end context "optionally raise when no match found" do class BumbleBee < Parslet::Transform def initialize(&block) super(raise_on_unmatch: true, &block) end rule(:a => simple(:x)) { A.new(x) } end let(:transform) { BumbleBee.new } it "should evaluate rules" do transform.apply(:a => 'a').should == A.new('a') end it "should raise when no rules are matched" do lambda { transform.apply(:z => 'z') }.should raise_error(NotImplementedError, /Failed to match/) end end context "with inheritance" do class OptimusPrimeJunior < OptimusPrime rule(:b => simple(:x)) { B.new(x.upcase) } rule(:c => simple(:x)) { C.new(x) } end let(:transform) { OptimusPrimeJunior.new } it "should inherit rules from its parent" do transform.apply(:a => 'a').should == A.new('a') end it "should be able to override rules from its parent" do transform.apply(:b => 'b').should == B.new('B') end it "should be able to define new rules" do transform.apply(:c => 'c').should == C.new('c') end end end describe "<- #call_on_match" do let(:bindings) { { :foo => 'test' } } context "when given a block of arity 1" do it "should call the block" do called = false transform.call_on_match(bindings, lambda do |dict| called = true end) called.should == true end it "should yield the bindings" do transform.call_on_match(bindings, lambda do |dict| dict.should == bindings end) end it "should execute in the current context" do foo = 'test' transform.call_on_match(bindings, lambda do |dict| foo.should == 'test' end) end end context "when given a block of arity 0" do it "should call the block" do called = false transform.call_on_match(bindings, proc do called = true end) called.should == true end it "should have bindings as local variables" do transform.call_on_match(bindings, proc do foo.should == 'test' end) end it "should execute in its own context" do @bar = 'test' transform.call_on_match(bindings, proc do if instance_variable_defined?("@bar") instance_variable_get("@bar").should_not == 'test' end end) end end end context "various transformations (regression)" do context "hashes" do it "are matched completely" do transform.rule(:a => simple(:x)) { fail } transform.apply(:a => 'a', :b => 'b') end end end context "when not using the bindings as hash, but as local variables" do it "should access the variables" do transform.rule(simple(:x)) { A.new(x) } transform.apply('a').should == A.new('a') end it "should allow context as local variable" do transform.rule(simple(:x)) { foo } transform.apply('a', :foo => 'bar').should == 'bar' end end endparslet-2.0.0/spec/spec_helper.rb000066400000000000000000000014621362225661100167420ustar00rootroot00000000000000 require 'parslet' require 'parslet/rig/rspec' require 'parslet/atoms/visitor' require 'parslet/export' require 'ae' RSpec.configure do |config| config.mock_with :flexmock begin # Here's to the worst idea ever, rspec. This is why we'll be leaving you soon. config.expect_with :rspec do |c| c.syntax = [:should, :expect] end rescue NoMethodError # If the feature is missing, ignore it. end # Exclude other ruby versions by giving :ruby => 1.8 or :ruby => 1.9 # config.filter_run_excluding :ruby => lambda { |version| RUBY_VERSION.to_s !~ /^#{Regexp.escape(version.to_s)}/ } end def catch_failed_parse begin yield rescue Parslet::ParseFailed => exception end exception.parse_failure_cause end def slet name, &block let(name, &block) subject(&block) endparslet-2.0.0/website/000077500000000000000000000000001362225661100146315ustar00rootroot00000000000000parslet-2.0.0/website/Gemfile000066400000000000000000000002371362225661100161260ustar00rootroot00000000000000source "https://rubygems.org" gem "middleman" gem 'rb-fsevent' gem 'RedCloth' gem 'slim' gem 'cod' gem 'case' gem 'text-highlight' gem 'parslet' gem 'rspec'parslet-2.0.0/website/Gemfile.lock000066400000000000000000000054641362225661100170640ustar00rootroot00000000000000GEM remote: https://rubygems.org/ specs: RedCloth (4.3.2) activesupport (5.0.7.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) backports (3.16.0) case (0.5.2.1) cod (0.6.0) coffee-script (2.4.1) coffee-script-source execjs coffee-script-source (1.12.2) concurrent-ruby (1.1.6) contracts (0.13.0) diff-lcs (1.3) dotenv (2.7.5) erubis (2.7.0) execjs (2.7.0) fast_blank (1.0.0) fastimage (2.1.7) ffi (1.12.2) haml (5.1.2) temple (>= 0.8.0) tilt hamster (3.0.0) concurrent-ruby (~> 1.0) hashie (3.6.0) i18n (0.9.5) concurrent-ruby (~> 1.0) kramdown (1.17.0) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) memoist (0.16.2) middleman (4.3.5) coffee-script (~> 2.2) haml (>= 4.0.5) kramdown (~> 1.2) middleman-cli (= 4.3.5) middleman-core (= 4.3.5) middleman-cli (4.3.5) thor (>= 0.17.0, < 2.0) middleman-core (4.3.5) activesupport (>= 4.2, < 5.1) addressable (~> 2.3) backports (~> 3.6) bundler contracts (~> 0.13.0) dotenv erubis execjs (~> 2.0) fast_blank fastimage (~> 2.0) hamster (~> 3.0) hashie (~> 3.4) i18n (~> 0.9.0) listen (~> 3.0.0) memoist (~> 0.14) padrino-helpers (~> 0.13.0) parallel rack (>= 1.4.5, < 3) sassc (~> 2.0) servolux tilt (~> 2.0.9) uglifier (~> 3.0) minitest (5.14.0) padrino-helpers (0.13.3.4) i18n (~> 0.6, >= 0.6.7) padrino-support (= 0.13.3.4) tilt (>= 1.4.1, < 3) padrino-support (0.13.3.4) activesupport (>= 3.1) parallel (1.19.1) parslet (1.8.2) public_suffix (4.0.3) rack (2.2.2) rb-fsevent (0.10.3) rb-inotify (0.10.1) ffi (~> 1.0) rspec (3.9.0) rspec-core (~> 3.9.0) rspec-expectations (~> 3.9.0) rspec-mocks (~> 3.9.0) rspec-core (3.9.1) rspec-support (~> 3.9.1) rspec-expectations (3.9.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-support (3.9.2) sassc (2.2.1) ffi (~> 1.9) servolux (0.13.0) slim (4.0.1) temple (>= 0.7.6, < 0.9) tilt (>= 2.0.6, < 2.1) temple (0.8.2) text-highlight (1.0.2) thor (1.0.1) thread_safe (0.3.6) tilt (2.0.10) tzinfo (1.2.6) thread_safe (~> 0.1) uglifier (3.2.0) execjs (>= 0.3.0, < 3) PLATFORMS ruby DEPENDENCIES RedCloth case cod middleman parslet rb-fsevent rspec slim text-highlight BUNDLED WITH 1.17.1 parslet-2.0.0/website/README_website000066400000000000000000000006341362225661100172360ustar00rootroot00000000000000Parslets website is built with middleman. Just do a 'bundle install' in this directory and you should be good to go. 'bundle exec middleman server' will give you a development server. Once you want to publish the website to github, you need to set the gh-pages branch to the build directory below this website directory. The script publish-gh-pages.sh will do this using a little black git magic. (Panic!) parslet-2.0.0/website/build/000077500000000000000000000000001362225661100157305ustar00rootroot00000000000000parslet-2.0.0/website/build/contribute.html000066400000000000000000000132641362225661100210020ustar00rootroot00000000000000 parslet - Contribute

Contribute

Find parslet to be really useful? Or just found a bug that is really ruining the day for you? Please contribute! Find the code on github.

Contact

Join us on IRC in #parslet on irc.freenode.net.

Discussion and patches (or the odd cry for ‘Help! How can I parse X?’) should go to our mailing list at ruby.parslet@librelist.com. Just write a short message to that address and librelist will subscribe you. NNTP/web interface can be had through gmane.org: gmane.comp.lang.ruby.parslet.

Bugs

Log in to github and open a bug ticket here. Please be sure to include the version of parslet and Ruby; maybe you can even provide some code that exhibits the bug?

And of course if you provide a properly tested patch, you’ll be our hero and get a place in the space below for lifetime.

Thanks for all the fish — Contributions

  • Chris Wendt (chrismwendt) for the ‘ignore’ atom and other improvements!
  • Raphaël Simon (raphael) for a nice contextual error reporter!
  • Dan Freeman (dfreeman) for allowing rules to be inherited!
  • Rory O’Kane (roryokane) for a careful code review!
  • Zach Moazeni (zmoazeni) for his work on Unicode performance.
  • rogerbraun (rogerbraun) for being my unicode tester.
  • meh (meh) for taking a real close look.
  • John Mettraux (jmettraux) for the really nice JSON example and for pushing parslet beyond its limits.
  • Josep M. Bach (txus) for minding the small things that make a big difference.
  • Matthew Draper (matthewd) for bothering with my broken CSS.
  • Hal Brodigan (postmodern) for solving our email parsing needs!
  • R. Konstantin Haase (rhk) for rspec matchers that help stamp out, eliminate and abolish redundancy.
  • Florian Hanke (floere) has given a lot of very inspiring input for parslet. His questions have been key to rounding off the corners and making the library as aesthetic as it is. And just look at the logo.
  • Kaspar Schiess (absurd.li) for being brave enough to actually add another parser library to a field that’s already bursting at the seams.
parslet-2.0.0/website/build/documentation.html000066400000000000000000000145101362225661100214700ustar00rootroot00000000000000 parslet - Documentation

Documentation

Getting Started

Are you brand new to parslet? Well then let’s introduce you guys. This is what you should read and try out first.

There’s also a video version!

Examples

Parslet comes with a lot of examples that explain how to use various aspects. Take a look at those.

In depth

This is the real technical documentation, showing you how to use all aspects of parslet. Especially:

Projects

There is a Projects page with everything from JSON through Java to Ruby parsed with parslet.

Presentations

Videos

Blogs

  • Parslet Intro explains quite a few things on how parsers work and on parser metaprogramming. Besides, Florian Hanke also explains how to create an ERB parser in just a few lines!
  • Parslet and JSON shows how to construct a JSON parser in a few lines. John does a great job of explaining how parslet ties back in with railroad diagrams.
  • Build a query parser a step-by-step tutorial showing how to take user input and generate Elasticsearch queries by Luke Francl.

YARD Class Documentation

The YARD documentation will help you with the nitty gritty. This documentation is real important too. It will be constantly improved! (Thanks linode.com and DockYard for sponsoring this tool.)

parslet-2.0.0/website/build/get-started.html000066400000000000000000000362151362225661100210500ustar00rootroot00000000000000 parslet - Get Started

Get Started

Let’s develop a small language that allows for simple computation together. Here’s a valid input file for that language:

 
  puts(1 + 2)
  puts(4 + 2)

To install the parslet library, please do a

 
  gem install parslet

Now let’s write the first part of our parser. For now, we’ll just recognize simple numbers like ‘1’ or ‘42’.

 
  require 'parslet' 

  class Mini < Parslet::Parser
    rule(:integer) { match('[0-9]').repeat(1) }
    root(:integer)
  end

  Mini.new.parse("132432")  # => "132432"@0

Running this example will print “132432@0”. Congratulations! You just have written your first parser. Running it on the input ‘puts(1)’ will not work yet. Let’s see what happens in case of a failure:

 
  Mini.new.parse("puts(1)") # raises Parslet::ParseFailed

Here’s the error message provided by that exception: “Expected at least 1 of [0-9] at line 1 char 1.” parslet tries to find a number there, but can’t find one.

There are just two lines to the definition of this parser, let’s go through them:

 
  rule(:integer) { match('[0-9]').repeat(1) }

rule lets you create a new parser rule. Inside the block of that :integer rule, you find match('[0-9]').repeat(1). This says: “match a character that is in the range 0-9, then match any number of those, but at least match one.”

 
  root(:integer)

That second line just says: Start parsing at the rule called :integer.

Addition

Let’s go for simple addition. We’ll have to allow for spaces in our input, since those help make code readable.

 
  rule(:space)  { match('\s').repeat(1) }
  rule(:space?) { space.maybe }

Two things are new here: (and both in the second line)

  • you can use (‘call’) other rules in your rules
  • .maybe, the same as .repeat(0,1)1, indicating that the thing before it is maybe present once in the input.

Essentially, you can think about parslet rules as instructing Ruby to “parse this” and “parse that”. Calling other rules can be looked at in the same way; you tell Ruby to go off, parse that subrule and then come back with the results. This helps when thinking about rule recursion. For example, a self-recursive rule like this one will of course create an endless loop:

 
  rule(:infinity) {
    infinity >> str(';')
  }

Even though infinity seems to be delimited by ‘;’, in reality, infinity is very long, especially towards the end. There is no way of knowing for the parser when to stop processing infinity and start reading semicolons. Ergo, we need to make sure we talk about concrete items that consume input first, and then do recursion. This way we ensure that our grammar terminates, since in a way, it is like a normal program.

Here’s the full parser:

  
  class Mini < Parslet::Parser
    rule(:integer)    { match('[0-9]').repeat(1) >> space? }
  
    rule(:space)      { match('\s').repeat(1) }
    rule(:space?)     { space.maybe }
  
    rule(:operator)   { match('[+]') >> space? }
  
    rule(:sum)        { integer >> operator >> expression }
    rule(:expression) { sum | integer }

    root :expression
  end

  def parse(str)
    mini = Mini.new
  
    mini.parse(str)
  rescue Parslet::ParseFailed => failure
    puts failure.parse_failure_cause.ascii_tree
  end

  parse "1 + 2 + 3"  # => "1 + 2 + 3"@0
  parse "a + 2"      # fails, see below

As you can see, the parser got decorated with the space? idiom. Every atom of our language consumes the space right after it. This is a useful convention that makes top level rules (the important ones) look cleaner.

Note also the addition of :operator, :sum and :expression. The runner code has been extended a bit, so as to throw nice explanations of what went wrong when a parse failure is encountered. Running the code on ‘a + 2’ for example outputs:

  
Expected one of [SUM, INTEGER] at line 1 char 1.
|- Failed to match sequence (INTEGER OPERATOR EXPRESSION) at line 1 char 1.
|  `- Failed to match sequence ([0-9]{1, } SPACE?) at line 1 char 1.
|     `- Expected at least 1 of [0-9] at line 1 char 1.
|        `- Failed to match [0-9] at line 1 char 1.
`- Failed to match sequence ([0-9]{1, } SPACE?) at line 1 char 1.
   `- Expected at least 1 of [0-9] at line 1 char 1.
      `- Failed to match [0-9] at line 1 char 1.

This is what parslet calls an #error_tree. Not only the output of your parser, but also its grammar is constructed like a tree. When things go wrong, every branch of the tree has its own reasons for not accepting a given input. The #parse_failure_cause method returns those reasons.

Our grammar has essentially two branches, SUM and INTEGER. Can you see why all rules expect a number as the first character?

Tree output (and what to do about it)

But if we leave the negative examples for a second; what happens if the parse succeeds? It turns out, not much:


  parse "1 + 2 + 3"  # => "1 + 2 + 3"@0

The only notable difference between input and output is that the output has an extra ‘@0’ appended to it. This is related to line number tracking and will be explained later on (or you can skip ahead and look up Parslet::Slice).

The code we now have parses the input successfully, but doesn’t do much else. Parslet hasn’t got its own opinion on what to do with your input. By default, it will just play it back to you. But parslet provides also a method of structuring its output:

 
  # Without structure: just strings.
  str('ooo').parse('ooo')                           # => "ooo"@0
  str('o').repeat.parse('ooo')                      # => "ooo"@0

  # Added structure: .as(...)
  str('ooo').as(:ex1).parse('ooo')                  # => {:ex1=>"ooo"@0}
  
  long = str('o').as(:ex2a).repeat.as(:ex2b).parse('ooo')  
  long # => {:ex2b=>[{:ex2a=>"o"@0}, {:ex2a=>"o"@1}, {:ex2a=>"o"@2}]}

You get to name things the way you want! This is also free. Seriously: parslet requires you to add all the structure to its output. Annotate important parts of your grammar with .as(:symbol) and get back a tree-like structure composed of hashes (sequence), arrays (repetition) and strings (like we had initially).

Once you start naming things, you’ll notice that what you don’t name, disappears. Parslet assumes that what you don’t name is unimportant.

 
parser =  str('a').as(:a) >> str(' ').maybe >> 
          str('+').as(:o) >> str(' ').maybe >> 
          str('b').as(:b)
parser.parse('a + b') # => {:a=>"a"@0, :o=>"+"@2, :b=>"b"@4}

Think of this like using a highlighter on your input: What is there not to like about neon yellow?

Making the parser complete

Let’s look at the complete parser definition that also allows for function calls:

 
class MiniP < Parslet::Parser
  # Single character rules
  rule(:lparen)     { str('(') >> space? }
  rule(:rparen)     { str(')') >> space? }
  rule(:comma)      { str(',') >> space? }

  rule(:space)      { match('\s').repeat(1) }
  rule(:space?)     { space.maybe }

  # Things
  rule(:integer)    { match('[0-9]').repeat(1).as(:int) >> space? }
  rule(:identifier) { match['a-z'].repeat(1) }
  rule(:operator)   { match('[+]') >> space? }
  
  # Grammar parts
  rule(:sum)        { integer.as(:left) >> operator.as(:op) >> expression.as(:right) }
  rule(:arglist)    { expression >> (comma >> expression).repeat }
  rule(:funcall)    { identifier.as(:funcall) >> lparen >> arglist.as(:arglist) >> rparen }
  
  rule(:expression) { funcall | sum | integer }
  root :expression
end

require 'pp'
pp MiniP.new.parse("puts(1 + 2 + 3, 45)")

That’s really all there is to it — our language is a really simple language. When fed with a string like ’puts(1 + 2 + 3, 45), our parser outputs the following:

 
{:funcall=>"puts"@0,
 :arglist=>
  [{:left=>{:int=>"1"@5},
    :op=>"+ "@7,
    :right=>{:left=>{:int=>"2"@9}, :op=>"+ "@11, :right=>{:int=>"3"@13}}},
   {:int=>"45"@16}]}

Parslet calls this the intermediary tree. There are three types of nodes in this tree:

  • Hashes: a node that has named subtrees
  • Arrays: a node storing a collection of sub-nodes
  • Strings are the leaves, containing the accepted source

The format of this tree is easy to work with and to read. Here’s what the above tree would look like as a graphic:

Where to go from here: An Interpreter

As nice as the format above is for printing and looking at – it may be difficult at times to get the information out of it again. Let’s look at how to transform the tree:

 
class SimpleTransform < Parslet::Transform
  rule(funcall: 'puts', arglist: sequence(:args)) {
    "puts(#{args.inspect})"
  }
  # ... other rules
end

tree = {funcall: 'puts', arglist: [1,2,3]}
SimpleTransform.new.apply(tree) # => "puts([1, 2, 3])"

Transformation is an entire topic by itself; this will be covered in detail later on. To whet your appetite, let me just give you a few teasers:

  • Transformations match portions of your tree at any depth, replacing them with whatever you decide.
  • In addition to sequence(sym), there is also simple(sym) and subtree(sym). Those match simple strings and entire subtrees respectively. Caution with the latter.

Here’s how you would write a somewhat classical interpreter for our little language by using a transformation. Note that from this point on, there is not one way to go about this, but thousands; you are really free (and on your own):

 
class MiniP < Parslet::Parser
  # Single character rules
  rule(:lparen)     { str('(') >> space? }
  rule(:rparen)     { str(')') >> space? }
  rule(:comma)      { str(',') >> space? }

  rule(:space)      { match('\s').repeat(1) }
  rule(:space?)     { space.maybe }

  # Things
  rule(:integer)    { match('[0-9]').repeat(1).as(:int) >> space? }
  rule(:identifier) { match['a-z'].repeat(1) }
  rule(:operator)   { match('[+]') >> space? }
  
  # Grammar parts
  rule(:sum)        { 
    integer.as(:left) >> operator.as(:op) >> expression.as(:right) }
  rule(:arglist)    { expression >> (comma >> expression).repeat }
  rule(:funcall)    { 
    identifier.as(:funcall) >> lparen >> arglist.as(:arglist) >> rparen }
  
  rule(:expression) { funcall | sum | integer }
  root :expression
end

IntLit = Struct.new(:int) do
  def eval; int.to_i; end
end
Addition = Struct.new(:left, :right) do
  def eval; left.eval + right.eval; end
end
FunCall = Struct.new(:name, :args) do
  def eval; p args.map { |s| s.eval }; end
end

class MiniT < Parslet::Transform
  rule(:int => simple(:int))        { IntLit.new(int) }
  rule(
    :left => simple(:left), 
    :right => simple(:right), 
    :op => '+')                     { Addition.new(left, right) }
  rule(
    :funcall => 'puts', 
    :arglist => subtree(:arglist))  { FunCall.new('puts', arglist) }
end

parser = MiniP.new
transf = MiniT.new

ast = transf.apply(
  parser.parse(
    'puts(1,2,3, 4+5)'))
        
ast.eval # => [1, 2, 3, 9]

That’s a bunch of code for printing [1, 2, 3, 9]. Welcome to the fantastic world of compiler and interpreter writing!

1 As far as parsing goes. There is a subtle difference between #repeat(0,1) and #maybe. Can you figure it out?

parslet-2.0.0/website/build/images/000077500000000000000000000000001362225661100171755ustar00rootroot00000000000000parslet-2.0.0/website/build/images/ast.png000066400000000000000000000656741362225661100205140ustar00rootroot00000000000000‰PNG  IHDRý»oºGMWiCCPICC ProfilexÕYgXK³î™ 첤%眳d$ç$QX2’³ &@”  9"ˆ‚ŠQPDE”(wÐã9ß½ßýþÝ?wžgzÞ©ª®ª™ªéîê€cž3ag¢Ï¿ÓÅ•?€P€$ÀQ½"Ãôll,Á<¾h‹ùLfK×ûߌÞ>‘^@6(ÛÓ;Ò+Å×€õ½Â"¢À,£ôþب0#=(fŽ@DñØöû¶°ç/ŒE~É8Ø€e€@K¥Fø@Féü1^~¨²!8¦ï€(;Q¬íåOõ€£•‘ÝÂ](÷ü=~ÿ‚©TÏ¿uR©~ãßÏ‚öD D†Qã~Ýü_6ÁAÑèûúu0¡-mHÐŽ­Ø°¢ç¬7Õнr£çϰ _1Ce NŸG{”¶…¥Cá"¡ð”ðްFÃ@#B£AcMãMG“ISIÓJó„fšfÈH#jˆ{‰Gˆ…Ä:â=âq‘D" ’ÔI¶¤ÒaR!é ©‡4IZ¡e¢•¤5 u£¦=I{¶ö%í"™L%ë’]ÉQä“är'yœ¼LG¡“¥3£ó¦;DWB×D÷”î= ½½ýúxú|úú'ôó 4 ¢  T†ƒ % 7ž3,1R­ƒ3/2>`œeÂ3‰21y3%3U0u2MQ0!ŠÅ‹’D©¤Ü£L3ã˜Å˜Í˜÷2§3_fîc^`abQbqbÙÇRÂr›e‚Ã*ÊjÆÄšÉzu˜u•‡M͇-•­Ží)Ûv.v]vö4özö!öU~#Ž@ŽSͯ8NIN[ÎXÎ3œ÷8繘¹4¹¼¸Ò¸®qpÃÜ’ÜvÜû¹+¸{¹—xxyLxÂxŠx:yæyYyuy÷òæò¶ñÎñQø´ùørùîð½çgá×ãâ/äïâ_à0ˆ(èXtL¬|%DRòÊêZæ¶N®¡Qñ)éù!*&ê,zL´YtVŒ]ÌL,^¬VlLœ,®#.^.>(“P“”8-Ñ/ K*KúK–H>‘‚¥T¤¤NK Hc¥Õ¥C¤Ë¥ŸËÐÊèÉÄÈÔÊLʲÊZÊ&Ê6Ë~’–s•;%×-÷S^Y>H¾R~TIÁ\!Q¡Uá«¢¤¢—b‰âà6ò6ãm‡¶µlû¢$¥ä£tFé…2EÙJù˜r‡ò†ŠªJ„JÊœª°ª‡j©ês5f5µ µu¬º¾ú!õ[ê+*Q×4>kÊhj^ÔœÝ.¶Ýg{åö)-A-ªV™Ö„6¿¶‡ö9í ªN¹Î]!]oÝ*Ýwzz{õ.é}Ò—×ÐoÔÿa apÀ Ýchb˜fØgÄdähTl4n,hìg\k¼`¢l²ß¤ÝkjazÊô¹™—YÙ‚¹ªùó. Z {‹b‹7–’––­V°•¹UŽÕØ‘!;š­µ™uŽõ+1›p››¶8[ÛÛ;»»n{н»ýEûïú™£ŽâŽÑŽNôNnN5N?œ ³'vÊí<°ó‘ §K€K‹+ÞÕɵÊui—Ñ®¼]ÓnÊn)nûÅvïÛý`çž =·ÝéÝ©î Xg‹ëTkj9uÉÓ̳ÔsÁËÀ«À냷®w®÷œ–O¶Ï;_-ßlßY?-¿¿9ÿ|ÿùƒ€â€/{M÷žÝû#Ð:ðBàfsP}0!Ø#øFSH`HW(oè¾Ð0©°”°‰pð¼ð…‹ˆªH(rwdK3º8ì>=£S³ëÛ°q_ȾÞ8ɸԸwñÆñç÷#û½öw$$I˜< w ì tÐó`Ç!¡Cɇ¦›®>B<xäq¢|bvâ·$ç¤ÖdžäÃÉSGMŽÖ¦Ð¥D¤•¾¦'ªOZúÕû[¶´=Õyz÷™á³ûƒfƒ†v ;¿xîö|â…÷‹Ù—A/¿ŒÄŒ¬ÃŽ¥½bx•?Î=^þZâuý„ÊÄíIÃÉÞ7öoF§¼¦>¼|»>¨VkÑöâŒlb[q‡ðÖv"#‰žV˜¬MçC_ÀÐÏÄNÙËÜÆ*À–ʾÂÊõž'”w‘?A(T,".zSÜBb\*XzUöWÓ³GÀRÅÊG©uŸ-d§læÐì„wöÜÙâJÞåâVº{ÌÓÉšíùØ›à£ï{Äï~ó^ßÀÆ !r¡a§ÂïGlD)GÇTÆŽÇ ÆGísÀçà§Ã¾Gº“’mŽf¦<9N—ªŸæ™î—áxbûI©LÉ,•S†Ù;rlsmòlò ¼ c‹N×”t—NŸgYω•)–«T(UÊž—¬’¼ P­YczqgmÈ¥ôËõuýõóW6¯‘دó5 5 7 ·ˆÝnU¼©|Kî6_ÒöîÎ@ûý»÷:tövõÞ{p¿«»­§͵æGW{˧õ…>qî7P~*öŒ};8?Ô?|ýùéi/÷xŽŒqŽÍ½º1žõ:dÂfrÛæ7_§ž¼­›>9öÎzVpvrÎonþ}àû÷ æ->â?v~Jül°€]èþ’úÕ|‘°ØòMçÛÕ%®¥¸¥ßí¾¿üáûcqùð ÝJɪÄjËšåÚÌzú†ÊÆÌÏ¢MËÍÍ_ñ§‚èú0œ¯ A(ê€áS"& Dîa£q ¸U|?¡ž¦˜Iʦ­$ߦ›` 02%RºYØYƒÙpÈpsãyây?òû L y¿uë—°’l—V’)“#É*tncU¢*ŸQRé+h8hÆl/ÔjÕ~¥ é‰ëÛÄž5ê6þhJ1S7wµ8`YfÕ¹cƆl«jçk_èðÐqÝYvçn—t×»fvSöh»xäSïy.yKø¸ùžð»á?µ—.P=È'8+äfèL8c„vdxT]ôb¬æ¾qÍñï¸X<|¨áðt"c’J²Ãѽ) ÇN?›z9­5½+£÷ÄÐÉ×™óYëÙL9Ò¹–yAùÇ J kŠ®7–4–^;}åÌÕ³çn•u–?®«üREº ‚fƒåE—Z¯KA—#êb뮹züZVCáõŠÆ«M̓-oo|i]»…Ü&µ1Þamç¸ËÝÁ×)Ü%yoÛ}n˧»î|dßkñX¿Oó‰R¿ì€ØS¾g,ƒÄÁ¡Ã#Ï»^Ô½,IM zå:núZyBp’aróÍç©Ñ·Óe3±ïÌg¹g?Ïu¼Ïûà?¿ý#ÓǹOíŸ B¾|eý:±xú›Í·¥êï?àW—ÝWÈ+7WÖX×Ú×=7ÀFÚOÊÏÌMÒæÑ_ñ‡ÑŠ­ÌÑʵÞ™z+­1½>£æDùÉ’ÌܬŒSÉÙ 9¹^yfù‚ù‹]…åE¹Å9%E¥çN_8S¶ ÍžŽò]盪Ê/œªN­9y±¸¶öÒÍËêFêß_Y¹Fh`½.Ü(פ֬ÕbxöÕ÷fâ­s·ï´ßY¿ËÙ¡ÚéØy/ë~m÷Ýž“×z;ô|òp÷TïYüàõ¡ùçÂ/Ü^æŒ<¯ÔÆ÷¿~4)ù&÷-4236k6×øAp>ûíçê/ñ‹9K3ËÔÕ¹¨­øÿÞ[Úšp*Òà<€ º§”€èZgÑ{2êž4pQ€ª—þž?¸ÐêÒ­$óЪ±|BóG2CkÀè<Ô½…X6„}àcp5üžÇÐc1N˜xÌL'f¡GTÝÈQä2ò ¡u˜36 {;Ž#ã´p¡¸2Üžˆ×ÅÇáðŸÒ„ B=áÍvšš§Db±‹ÄN &uÒrÒFÐ>$ ““Çé´èÎÒÃô~ô} * çi÷1Î0Ù3µQÄ(Y”ÌTæ^ –K¬¬El¶Lv2{™#‹“™³˜‹«’[Œ»†Gšç ¯ïC>w¾eþliû‚TÁŸB¥ÂÛ…_‰$ˆ²‹Ö‹éŠõˆÛˆKP%%Ó¥ä¤F¥Ód4d>Èž–³—§È¿QèQìÞ֧ԣܠR¤š¨¡¨ é»Ý]ËAÛDGMWBCŸ¨¿i°b¸j ›0™J›Ù˜ÇZœ¶l³ݱlÃb+ogl¿ÓÁÏ1Î)Û¹qç¤+e—…[êîÇîlTêEÏEoŸ<ßuÿ„½4A›!¾¡Âå"J¢è£Scqû’â~î?pw°ü°M"’ÔpÔõ8^“æ–Á{âCæSGr òðùc…O‹§K×ÎlœûR>U9V5Qýþâ·KkõÈUÞ†SÍÜ­#·Ïµ«vôÞÛÕ=÷0î1ý“º§¹ÏéÇr&bߪÌB†>W,j~O]iXþküàZ`78JÁM0VÐ]ut *‚Z¡Qhæ…ua/8®Ã_ÐÚ]ãIÁ\Æ bÖd‹”#‘¬(Ö}v Ç‚3ÃÀ5àÞã…ð{ð%øaá<á3&MÍQxœ8IÒ$¾ÑÚÑ^%3’#ÉÃtšteôDú(ú k†;ŒòŒ•LL™,%ò•9€y’eË0«=ë36¶çìnìShù3‘‹žë4·4÷M žW¼á|4|•üºüãyï ù“„DÜD±¢—Åìľ‰çK¨KLJžÒ‘ú&}IÆ[–[ö¹\޼½‡Â´b˶l¥0e[%U.5¬Ú’ú¬Æ¸æàöGZwµu.èèÕ11 6Š2N4)4½n6l¾jÉc¥³ÃÃ:Éæ¼í=»IûUG&'9gû]ê]§ÝøvïÙsÎ}š*íéuÓë»Ã/Ïd¯@`PÐíæÐа¾…È¢hlLtìLœK|o‚öúC¼‡O%’’rŽŠ¦´wOéå'ŒOÎgd›æläÝ(8XdU"\º~fàÜùòý•NUªÕ<qµ —Gë{®¶5t5Nµð¶zݪnûrW§óä½±‰‡q½}OÄ2‡^ö½z=õf~z}Ž2¯øÙ÷ëø÷ñµ=¿â/ìÑè_èÎd퇪 §Ð&, »ÂÇáxEÌ10W0Ó'b‹¤"ÈVƒmÂþÀiàáºñLxwüBØMh¥á¡I¦Y R‰ƒ$ R­í5²¹’Ž®”ž›þ,ƒC£:c“ ÓGJ3óuk–yÖ 6Y¶gì bCœÇ¸´¹Ö¸Ûx’xù0|·ø£d>^ –^é=%æ-®!Á 1-Ù"•.í!£&Ë,»$7.?¬ðZñë¶U¥å•;ªj©êá»4M·«j‰k³ëÀ:Óºzgõ“ b ÷2n01Ù+[øY–Z=³¦³±´Í°ës`ttp*pup ØÕ¸Ùãè~‘ {îöºí#æ{Ñß*`3° ˜>$:t(\%¢(òg´oÌÀ>¸+ûÅjêš>r<‰+ùt Û±ÌT𴔠܉ã™ä¬¼l¾œËyÚùÃ…1Å\%·N;žy{.¤l¹"éü<ñEø«ÃbÔ·Ø¥¨ïž?,—eWèW>®>Z«^OÙðú©¿É¿ÿHßmŠ[³€hõÑíÇñÍÍEQtÿ2€S››kå››h‘þiúý¿bK‡î¹—Fn¡Sô7Êÿ8þ Ã8vY¹× pHYs  šœ IDATxí½ ¼eUy¦¿©‰*ŠIPDdpÅ £b4qB‘PЭ±ýÿÛ)qˆQHg°£iM4Q¡Q‰#j@pj±‘€D‘yŽ È\ÔtéçÙç|·ö=÷ÜáŒwŸSßwï]kg}ï·×z×Z{Ÿ}¶xðÁ‹´d H’d ¿‹éa2 $É@2 È@Š~^É@2 $ÉÀfÂ@Šþfèt3H’d HÑÏk H’d ØLHÑßLn&É@2 $)úy $É@2 $› )ú›I ÓÍd H’d E?¯d H’d`3a E3 tº™ $É@2 ¤èç5 $É@2 l& ¤èo&N7“d H’ý¼’d H’Í„%›‰Ÿéf2 $c` ¬yòHöY]ž¸üe5~`-a­KÇå°-ò—P¦É@20,š"o°Õ|,›ÃZEÝåXùÉåì #$õúŒýzÅ#K“ $5f "ö!즋iÜ.5u9¬šuƒJCÐãüU¡7?ÑD¬w¹@üËÔ|Úx3¢?ÞñMï’d O T_Q(øæ—=ÀRpp¦à/”èW…=#å á¯æË}säoÈÆÛòžþxÇ7½K’>0PüySÛOÓ£ŠåÅ{ÁÊbË·«“-bz«b e»¬¸­8ž¢]64¡ðOŠ?~:ê·6¦ äHL›n%É@h|E>)ãú÷;Çï(¶.öëÏçõý,Jømà|pj±–qþ»é|–%…}3­Š¿ºŸÂ1ãh)úãÕô)HúÆ¢Sù1²w*_Á£ç§0µ¿eß>n°'ºƒÓY–þ8äþëäÖ~;å¨ÍÏ{ü1Ž–¢?ŽQMŸ’d / 4Gùо£{ï×+øâñà{ŧø¿3%»’¾½x¹¹kÀ *þ!ü9Ú‡˜q³xØdÜüJ’d è>„¢ïH_áß’þ‡‹?f¢|Ô_FöÇâÇŠâCäVŸAÐ/ý‹Ya>|ÈǦ ‹ýa1Ÿ“ $#Å@˽üM‚_ûó<þ>Åá“Oè”_eagæâAü(ŠÇoN8{¡!üºŸÂ!ãf)úãÑô'HúÅ@Œòm'Cô½ÿ¦âFÆŽGÕ”ùW#üËŠ7’s´ïšíC¸[Šþ¸G8ýK’ލŒòm#ãi}GÃË!?«xf¹®ãóÖê€CèÈlQZúÔ˜âѾþæ(¿VÁê_aRôûÇež)HÆ‹ÛÇ}GÁâÀb;Öí4Ž>V–Óúû’Sð…‚/JßsŠ&ÆÌRôÇ, éN2 ô…éÚF:µ¯*Šôq”ÿ-ÎøBp-ó9ú?ÿ'VžÐDeU_²>PO1½oÇ&„ßmÁÙ´q` E¢˜>$ÉÀ °} lŒô·ä<{õñ{»6‹½=éðKðÏÀ/ÒÝÂî##úm{ã˲òa>;4ú}OÁï7ß58ŸNK’d ˜Ê@ŒrCøò--ö.vŸºcOKñÂ^ÇÙ7·Îp¶CXö ÛzY­/Ëði]9‹¡!úúŸÂß ·5=Ö ¦%É@2 4hÞǶm Äh ñmà wûgž9lO2ß+*éVä«,÷+» 'z°ô¨1“±é~¾%‹ŽO¿>-ÏSìÕ¥%É@2 Lg F»Š¿"¸„—Ô®àá·þ™ÓûgUNç§´ëT<¯²O?³v&&ÊÛÕQ¾¥ˆ} ßÜšoçë'ï x.›– $É@2°‰á†ð…è/Fô—ñ‹uãcú¢O›¦õCðå m HÑà¦KÉ@2Ц‹ÿbî~⺾·‹“ÜÏ1‹ÊÜ©vpÂïþ.(­û!)úuP–/H‚¼ª.b’m¡PŽ‹é‹>mzhOÕ…ªßÁ«ÓFýQ`–?HÉ@«øÝWÜ;È«œÛGëm÷”àÜE«Ÿ±<èäù‡Ì@>È7dÂëúqÍ'–-^]{õåƒDù@Q]¯ ±,×ôº°oÐ_SìÆ zg»qêêÃ}ƒû¤ÆûÖ×ò#òÜõb E¿^ñZiš"šiÀ2Tó±l: k}JØåX÷ W—³0Œl¶ŸQ½Ö$¬-®.®àÍû·ó>1—óŒÂúIÑz~µ.ÇúLG˜ý^7E¯ˆ}»©OìšÆíž¸§á¶aYkC o¤D”Ëøc¾@üËÔ|Z2ÐG¼Î&¯7ò——Ë}üˆ<Õ%e=ººé£Ñ×ð×eMÒÆ„ý1 ä|ܨ¾¢ˆ¯èøµ=€w¯hn'™6êwÝ ­ÚÀ”·E#dº±¹y;$ø¦?ü«Ç³*-èŠÖë(®·«‹»¹o䜻tuÞútE¹¿ØÀÿ@øu®šÖ§ÌY’žHÑï™ÂÑ8AEðCäM¿éQ¼žã½`%¯Y\ÜR¾¬£>Ž-b¤±o&_Q\VÜVOÁ.6VÂNÀdG?Kåg]Z2Ð ŠžBbXÍ_\œWT¼¦¬;½|ÆÂ{õf‚:ÕR‡š>‡è/lóÓûÎ@Š~ß)­ß [_‘Á_ʸþ}ÅűÅ;xØ~õ+{Y¢™m¸ Ù?¿xZqjñ%ªwÓølÓõ¤Žö~ÍQ ƒ‹üß žÂWüí¸ ~gq:WáË©G΢ù%½/àßúâÛäZ;ÐÑá)ýÏÙ³Q ðÌevJ4müPu¾MÕ–þ«=ǯzÕWð¥x)øʽ¦ø{Êþ2–ü™}ñ–DÌZè§G¤%½0‚ogÒkìpð÷à£Àëëúâ›å,Ù´¯–e¿’ß ì8· ttä!mŒHÑ£`¶s¥2ÊÑ·ûRÕÿ‰flY)í®ãº(T£Ùý¹=Á  ?SD¿é7«Ó’ÎhŽl»ƒÀ‡ÁÁ‹Á—Á«Á‰t6DâÀb;ÖíDnÔí18°’ñËZJ,ÊéJb¯hlÑpÛneÃ)›«q (ÄV§€Ïƒ#¹,.$˼v}ÑëJØn†¨“-g”ÜÇkOă¥Q÷XUÚ0„?®uÓ†è7:7vVÑ+ðн?µ£ðWGú­¢Ïæ´qc EÜ"ºÉŸh`lx¢²1: ±ášÛNhîòçsï:ëW²õNð$0Û'‹í~)êã`70óé„ïOd×_6;6Ñ@ÛËC4„dÓ6ûCðõ0mú~ž„p*žÎ Äu¥pZ¯âÚò:s»‚ﶘðjw÷ÕÌÚ,s *ú–-D_¡WøÛ‰¾¾xœÈz ãf)úãÑ©þØØØðˆÆHK^Á³×<߸g³Ðû$'ññ¨oÌq²]›Û·Ÿc¿êæ½ñå|æ[7ÙÐF'åêÞ™sú]pñh° (Ä'iÓ÷¬›Óœ!r¦ˆC<ý¸®BÀÝæú|ïõ[×¼£þ‘Šàû9še®–;:,ÕѾÂoEذüú¢Ÿ/¸‚Œq´ýqŒjÃ'¥e(úÆÚ‡÷ö.voì0çÿCØãìæ^ÿNúð4ð¯`pð-~Þ ýx9xzeùÈ{·]Á×þ ü7p38œü‰Ò‚ßî«ÙlÎ×ôe>5D_…þ†ï¦icÌ@Ó÷s1£x*„š×Rõzr[ŒðT¯Ü|¯A²Cµªè[þè˜XÎõ+ö. ·ëKˆ¾Ç§!)úcTÁ< ÐúÿÛðþ½ùÙVìvXs×;H¿ÛÄî¤ ù;ÁÇæ²Í‚XÞ®\Óøw#‰ÍÍ›šëŽ#ýpßÝ€¥ìÔì|R¤x?œ:1;&埭‚~Û*Ÿæëä´uÛWšeŠ´nE,EbÐb »àøÑ`P¨N]Mßsܼ ŸâWCäõUTÅTm½£þÍësú¼S”Ñ2G¹CÜMcd©¾¸Ÿ!ôر²¬?›ÂYmž7­@.I©3ŸÒÆ&œ?…p1ÍÔ2~º¦{{róÐý»8…ÍŠÏQ¾ Þ´g€8o¹¢ƒú¢OÓ¿®â8/çšõ%|ˆx¶.ǹb},2m„?3îÇ>}¸èåéû~q¡O¥(êg® Á4UDmW£Þ‘å#N“ÜûÁ˜eŠòUEßu.W·/ýyËú3s&úIṳ́qKˆE4@‹ýuL¿//GÖÝħðîšå`ï¶3¥Ùdž¼×¯Ð{õœØ8IÛ©ù8Ò¢²¡Ýäc£‘ ß;=ã‚î_©7Q~S;l¦ú¨™ºVÍǺA¥!*qþªšW4&ÅJéFL8öÎu Øô=çž—©†”'ö ŸLÍø˜w‡à'båºÈ“¸ÅçGM#&!ð‘ºÞ|¯nb4po:ü€¬?sÖwÑOÒç&}H{D ePô×–ßÌí¶Gsà[€SûÚcIùßûó޼ýÚ]Õâ[g³ò à­`wð6ðT èw;û èëÓ&!Ô×ÅðÛ4C²õ´Jݱüż]¦=€÷¯®ÓÂÇÆÒàÿWy4/JÑh¦!"!(–/¦ÇÕÎêñlšjp° kŽ«€#Ñ“À@§ï9·¦oÆAŸlKqº ¸MkMkûßòÄçV?ÉuULÙÏëo®øTOV·|ÖŸùE¤¯¢Ÿ¤Ïô!ïU­äæïãÛ¹ïº ŽÐCðÿ yŸÞ×^NUÁWž4Gö>¬çq_GSíðÌ2×ù¿{ÊCìV´úËŸsލÔySë§éQHý{‘“•ÌÑ,æíoñ=‡(i›ÜÑ[ÎΖÅe¼áxö¸ÄT²€ÉŽ~Nû*ëê0}߯±M«æŒÏrⳜø¬ >·Ô,>¾†w+¾–·‚7ÞVœ€WÆGSôf'ÍÎÙ´ø”[kþo Ⳇø\F|:®?†Æž]§Ç´ÝNÒ—P)–R)–R)î®Y¥°ÑZF£µ Òï™tœ·3ÜÒÚ2ÙûJbáèCÉUCû¼þ®àIøx¿P÷Ôò·éY1o‹÷ÎàYRŠlª«v/ ŽsD«yŒÛœæw”®¬UŸðg±#³±šG×—]ŠÛYòÄÝÀÒø)ÞlØ@ÈlÔji-uGfCð—ÂÏûŠm‰Øk‰¡ãÈ:šµAÖ/g3ë2Q¼›ÿŸeIá¯>(â_Ö!ün¾_Íþg°Ñ™›ÚجñYJ|v >ï >Ñ®MÉ›1>·gÔNmÆgÍÜñi]û$ãÓYˆú2ÒŸ•ôETŠ­©¿Çÿ;+ÜÐö~Q⽈ãU| ýÂâKe£µ¾¬6¾6ZŽ'{Äø[ÛÞ0eSàm~|¦Þ1ôþàñÀÀ5öML+Á!¿dça² É‚ŸÐÎfZï¾­Ÿf‡¡µÓÐîœs­»_Ö×7wk팵.Ïu¶…ÚîõeG-_¶|§Â+ÀqÅŸÀ^Ìš,T gû\Kÿp(8ñþñÌ÷,¢K7Q|5n -[£¸N&_÷é{˫͟Ä矉ϖkùßÒ?¼JI$>K‰Ïú¶ñÑ…¸]c~,ãÓA”ú2Ò§Û`‰˜Žl4Z‹h´–ñÚ–cøß—îEžu»«Å'‚ETæF£¥@ÆhÅFª­,ôÈΕ}€¯°›ŠÁ/À€ËÁ•ààˆ×¦ùÉÅØâ;µÃÙ–k9Rذ+Mu±×1rùmñAŠs1¸ü8Ò7’Žô5®whIZ;#ŽÕ˘Zw„µï•ßpèþf §X»‘Ïü×Ûúâä®i–ÀükÁ“Àéà$Br!i­mÎø|Šâ×u03³¶o'>ë&ãcÛPm碫e©º•ñ©²1¿|ÏRÜ$½Úpmj´&xÛ«(HÏŸ2?gú²—ßa?|‘ÎÊD)ž6Zú§Y Jè÷0„¤Éï|n«¸ïɺ«“ª—€“šù«H5Ywtïxzk° °2_$N7’ÛÌ×vcGQ'»ŽÂÜ_ŽíÔ8:Ñ¿ÉUòdkkÕºcÌþ-ùû0?Ö:ÁÓv¦G˼®^„ß,>„´\ÁÒïƒSÀË1c5 Ö>>+ˆÏ1ÄgçŒ-DZÄçdⳆqâÞÄ(?–료O‡²‘éÕÚ“¾”Jñ *Åö#X)YB¥8ŸJ±~x•Ÿ\µ+ô>ïî¹w§Lÿü‚N‡½óiÆy +­8İš¿¸8¯8ˆ‡ïê4fŸæÇœ+ÎC8&x£! ŠHøhZûF«Ù¡³þ‡M‚ï5°³8`ÝÁ‘ÒžŽOg–ײp¾\Q¯OJÇ™ÏêÊæŒÏá#ŸÃ‰Ïê2>ƒg«õ§l;2>]]6ý9h€ñéi1g¥Øo„+ÅʆX¶Rl œÁ°aò¦û6؇m~>¾nçDNQÿ)x 8üÿà‘ˆûnàÅàÝà4p1h+øì¯Yiµ¾C+ö&ò¾ÎëD9¡Wî6‚ÿœ´ÿþ­/¾]úÔ¸?¬¢l´š)TÕsjŸòE‡Ùë)DWÚ›xƇøFל¯8Œ:´¤¼›ìC•Ö!×Fý)ëËu¶öñÙ’ø/z3ªf4^M|–o$çl`ƧN±`|zmVÚWŠ%TЧQ%Fy)3OÁƒQ)6ïa)E5o£¢Jvº!äN¯ï ±W§ç·gÙ‘{ÀgÐ/E›n%í—EÙ,«eÞPÁõä¯gúu÷â%#Ú1ûjé“Óú7ƒÖg.Âgý–‡Ú׆u'êOŒôíX.§ÄÏb 6ʵ§Á÷þüwʧ@¬qý󦵵9ãóÌ1ˆÏ!ÄçôŒOm/ÂŧkÑŸµR4Z{ŒA¥x,•âÇsW ¸°Þ T…Ýü£Á¯@ˆû?6ó× rôé¹)SŒv£“bC«8ŠuÜË;‘¿PâEµý*mkW²vuÙYù9ÇüÎz„ð+&!úÑñaU-ÍŽ£ðúqÜ(ä‹­‹xØrô푸°œ±äú²ã{KvjŒ•þ–¾{².ð9½XûølGÙwêå´59ö1”c%1Y›ñ©ID¦c@ññ¢îÅÚWŠT Dz£n;à€“­Ñº©PÈï§ÐhýŒô^à½ö#£7ŸNö¥mhÐG‚¿ß×ÅhЂ§èÇ(«!ø ¡¼‰µŸ)ÞÌVŸw³¬ï`”¿¸8ܯ¢_~}¬Š>‹µ4¯Í:dç[!ôú:°Ø³Ì“Û³¬OÄ“˜>¶c£¯"f;ÈÖÎfŽÏ}ˆ]ŸtðýóµnŽ™ëÜ>=RŸ™xúŒÓµÍ~IúÇÀ›°«-¿vBåBÿ >]ôqkæJ±S*E¯¼y‡Ü&t›O´3~^Q¼Œ³¼ <Üü:œOÌüpjÞ'’ëf ~ëH_ôªÆ àhkn`¼¼x=ÂótÖú}Þˆ,ÙZ˜ø(ãÁitU6rcbcqK÷y×ýRðcêØ£¢ãC¶–¦à‡*†K‰Ç~|«Âéðñ0}¹„çb6”µQ£ƒ£ïu»ÒZ9ŸŸ-‰Ï^}ŒWl§ÖÍ13}ÆÞør>ñY—ñiKѮ͵ۓÞÞÚ\ö (íÄYà>0@|z}]œ^)–P)ÑÇJÑ-‘ŸåÀg‚'w{‚æq;âËÕx´±øÖ(47CBS×·½…àÅt·M…cÅQ¡t䵌5ç‚;x(îEêŽx¶„nB½c_#º²”[)ß9”ûr` }±Ð¿ªè+üu59Q‡¬‹>³w_¦Žõü;à›@{1xpýçÁîà‹ÀFÌíkà'àeÀníA ·vâú²{Šk­ÔF„ùDÏÉàùàÀXYIÛSÙÜUV_–áSCô3>­$F÷ÛÖòÛÍAúTðQp&¸Îý¶ÄÇ wk[p ˜Þh=¼ÛSrÜ]àçÀG’Žò¾4ï”K°AÐ.ç–¹F£æö_5—Ï#ÝìÙ\î&±KÃå×Z9³ßMkgÞBàÖƒM¼¨ ˆ¾ó ޼Œß¥¼ÏýZÒM´ŽÈX]úk: ³Ã¢5Ê>dÜSvVwcpo!üvbÖ;6UÑ—†8›êaÄ%®¹úÓ–™›2 ½š×¾‚¯¨ýÿ vŠ»°# à¿XGÜ÷[àRàH¦¶Uy’­ùï5%ô3|–‡ÚÕŸ9ã£7½š1–ãê¹¼²¿ öíD¿Ý1ìÚ“mÃÑ–¥Èø´#Ò«5ì`2»'4WÄmÑ•,{Ö\ßÏdñéJôg­N¨+'ÝšM¶ãiíànð%ð°+°gõ/à- ÿUä•÷³Û h{€Ë\÷ÿ‹‘j´ZœUôdEAônˆË¦Î «Êip÷Q8…ôڈƙliÃh C¤M¢ßr;+о¯Ø;Uüm*]ïöVÑgU­M>å8xv–eE_æÉBÄz{¸ì´c×-.`«€¢òzð.Ð/s¤´±ôÈëIÄ5~ÛœÔõûúÓã³–läûalžä.R¯bã£ý8ê·F·ªÅ1Õu½ä«‰ŒÏŒîÊ–³š[Í m58ß fŒZãTnèÿÄÇJØ‹µ¯ ¡ìå¼Eñ_9ÜFè§`°Ðþ(ú¿ç‚W€Gíà'À^²ÝcÀ6 SþF·R„p*žv§Bø•]ãç>6/nWðݦ×1*‹³ªÜßtfyUÑ·l!ú6‘ ;Ñמ§Ž&§Ákˆ_C 'à>¦{-ùç9Aˆºçªžw‡æÉeË˺¦õ~šŸ9QÊä7f;-º?ÛæúlÛH=±!î§9»òéÊ ¿B^h!8¥þÿ×—“üj‡¬Î™¹þèK¿ãSeývìk»ƒÿUæ÷oñéVôg&Ýß«ëe¤ôEÉ®cÅ^±’ÔËRS¾4›Ì°‡“±3`s¯Ù õjv`lˆ§Rä ÖæÔ¶ƒ( â)k!öQ~·¹>_mú€ûã±?«níDß2®Uá·ƒ"\gùÝ'D¿¶?ŠDÃäT„ð/"ç×)—÷ÜpÆY÷o úÇݺ°jݰcàU`)¢îÄ~½¦FÆßiÌ%ÜAz'plk§ÍØSŸ‹©M:#^ÿvYdÏ™¾mÁöÔ†¯Ròm{Lp²Is@ó6p88íÿBm Ù™‘YTÖ›M×`㚌ks`݇G7•}1õç>âÖë`¯]álaBð#ÿÒv;õyÝâÓëe5t­µ^Uôƒ÷u<,š†"å)Ì^ØÃ€—@¿Ì&ÉJ±±½ˆ~ûJa£e“Ü/Û91¸xø1Ðvÿ^lêÏ-ä ±Ôý}Ñ'ÿojªùhêÖh•>7Gû–14âæv×)”!¨v¡¼&„¢ßê'«†br0²Ëe52}î§Oº]ËxP¶V‹ë§‘nÁ8e ?³Ó«=›|¾‰G‘> XZtš·&ïlÀ?‚ÓÀž Ÿf£ÕÕOõsS]êç§ â\­å¾›J½Ç§]I½zÛ‚é IDATµê@¦±fpÿ}2&ã3?~Ÿ ;32¤ªÃ lñéEôÃÍé•b]*…gÕvŽ@>ãBÓ^×LŸOz"pêRÛ<¹Ì5~¶æ{äõ0Ö57u”4zÇ´úËn!vFÿüq6ð½ØôJ±‘*q•bç.OûPŽ{K˱‡²üT`!CæÝWIÖ\³ÁÛôêáo8dž‘­Á†©"ªx†˜FêºLSe@Öd9˜Ž8Gʦ™åÒ"µLQ>¯€€ëÌW·;ÀןQ±ˆåµ£µ'ì¯eš×ŒÞŸŒqj¿j ~«ù©1Âw–̼u§_¢¯/›êOÄ”(­u9Ö×%­Æ§Q¦µÜ,¹¢|Hïñ©ƒ——ŸM²Öx´.סÄÕ2d|ªlÌ3ß«$N'}•âÖTŠÖ¬ê`Uì«ë{õÎsýšJÑøÊ‘müüÜ ´ºWŠF)-(VyB:â¦`j6ýæõð©*ø‘góÀ->?ÊhªGç$DÞ4Ö™ŽšàïA™Ÿ”Ú§»ûOp÷ßõg8fd—?ýà,A¿ìªòzºšÓ…O¦"âì'Uó.×Éâú3—óUáð¥Nåì®,—d|º#nHG >ýÅé•âÆ1ª7••âRBìSÇ€¿¿??4Ѹû™"o³o ½"G0W·i­icí`ÿ[žøÜê'¹®Š)ûÑ©©å÷½)ÖÃ(·wÓxSáü”ÈŸƒ¿×ÇâåÁÕ”ßÓöІm͇Øýè·Ù‰x œ¹œBiG͘µ‚Uµ3ËXµ(ÿÕtù'ÊïÔïRÝ<‚ùë(óýŸÚFn@ñéEôg®k©¿…J¼e»ƒÂ;9Ùx ƒý·GùO'€iØo$uÀŽ€éOzÞKZ S)ˆpT¯Ð›Ó£¸Ÿû^ä~%ߺXŒ—ËYWÛ‘XŽ|lÉ»âî,ù¶£¥y *$š²Ï.,èWõø|礹‡¸›î~,÷ÇÁ±ÀëI‘w窶ÞUן‹yT<·Œ ‹#j—à˃Åe”^ŸD£fÂÏb-ÍòEy«eoÄç<âóšÏyÄd"ãSË«ÏB (>½ŠþÌ•â**ÅSG¼R\նѲÒÿ ø%§5Àû¯òðxà“6ø¯v쫵vîgÝPrT_‘Á_Šì¿¹9¶x-{9ί£=HgåNÄñR&žÏæñÅ»¹óýYŠª>ƒ ÆEšðC«';~!ðŽä÷N–_þ ¼üÊ{¤¥5ãaÝ á«Ö£ xôu¾:÷Tž¸_<Ô'¹›åëKbT¾‹Ê(mÃ’ã~c$BPKÿ«Ü°­±jŸuÄçtâórâ3ªwö}@ù Äa}ù²óês2£Ÿíh‘ï$>ŸöU©[ÑŸ½Rl Rü˜JñD*E·ŸÐ¾¼Ã[k5ø •bã¼*…Å[b5(hØ$!:¯%ë¯&µ#ŸÑèÙy¤)Š1º·\6UKù{8®ø–cÖÕ,½³F‡‚Ït¸/|™(Î8:4¦aŠh\£±®ç”Ø=š“(ì!òvðn ¼ø¸˜xÚœËBP¼v,o4¼¦×—¸€ï®üN7GÌÎŧ-Êož{½yKì—à+àk |Hœ8?lîø|“ø¼dDãóÕòš»¢nvÑâú36£۳Ã-_çëù퉌D´Z/’B„~Aî‚פ-ötŸÃíf< „À›z~§èø¿?"Nw‘vdS}S¢Âçµb£kã+|ÁÕ‰|¯þ̾,ê˯îqÒ¡™Ý 3Ëzÿ9rÆçã`_ð"ð§à p2ø¿@NkeóŠÏâs"ñÙ?÷¨Uñç.Œ­ÍêÉø8æ·“Â_ý¨OsŸsˆ{Tâc\^œyý2¸lÃì^˜hc6ŒmVϽŠFѼrá}É­@Ü—´±ôæ^Œò?À÷—öá[ûœnˆv+ŸuzùyÅ-ïî ¿Ñ|¸¬V˜õphÃÝ•Á£î¢#àèÑÞª·âùSG~^GÆùCì—p bo¼–óÿ šàøž…ÛGÏ~€P~“‡)(ޤðÆBØx‰²ášo\àHNž!ò$/ï | ÎçȨ/ÖŒ‹±7&>CaýÙl¬CažìÙxr µ¨÷ßàC1kÇ_ÒÛX|ŽÿòæÓ=ÖÓ»À ðð`gMñ_ ·7‘ÖÆ:ŠÏg)¶3 Ç™ ÄgcÛøÁûƒëR­~:œ¸ìB™Ž«€õü3À¤û@ûú“ñšMÖ‹è+³7Z‹h´&¸Äþ {nµéCkór?™ROÌØh ¼R4EèJbÀ΀éã€S¤ QÌ \BciÅlkœÇF5âd¬7Ý™/2M¾|do¿8.ùs:]ëÊ)½_°Tm¨bÔ2?SzµMNöaÿwSoÃÈí DþÚugŽóÌj”#:cѳ†l ¶!üÛSÞKŒ^ÎóüKÊR*.FµN&Kv‡/ç 'ëèŽm,¾ß\ë¥ÆÔºSí0?‘å£ÀàG`5ø2¼+8 jóŽÏ2Ⳍø¼žø<"?Ô1>·Q®‚ÓˆÏZâ³a^ñ)ëQkâ,C5ba›õ‡`°-ü<8üÌ^2>P4ÕzýÅœnvÒ7­ƒ©»³·}±ºU eÁæèjpa³Ñz°^•‚ ßÑàÀ‹>fö$ÿ Ú¸”JjeõvBK¾çØŠˆýCñ|î=‡±ä(Û·ˆÕ·Ëì=¸¡˜Üœ N—;ØÄÞÔ)û[Aˆ»©ß´ªÐkü;Ó˜}Ù4ÚáßžmÛýøÿ"&`wd>c âõªA‹(Ñrø^ ¯w!û~—½ñ-Göн©ˆɵU¯S» ¶!/«€Òùp19ŸtA¬ãø<”ø¬#>÷Ÿf¨“Ÿ•Ägñ¹½óø‡)ça¹F ᳎¯^ «ÁgmÆgSýé4>½ˆ~gÖJ*Å*ź6Z~%l¥[B¥¸o¤*…=`GKÕŽÀ,_ œ °'|ð.žÂßM.¥½ƒî×ÃX3ÊöŸþ#ˆÉúâyä§}ÀàI@·£#ø2¥’ÜÁº7.;eо¢g9çJàˆ_±v“…ëÜ.Œ¥ÇÙiðÚ0…&D@Á¶ceËŽ–bnÇËXؾ(ö¦®s v pßEzŽIƒ“Xpôoc¯o«Á)ÄìFÒ¡ZÆgøSûp^¾÷º: œJüoi ~Ƨ»øØpteö. Ý ßÊoE¶B[±mÈlœ¬¸—Rݯ%­g£å‹üm³µS­h¤lÄô'F&^ˆúªßÒ ‹Ã5>ÚrÙ¥…]Á{*x6x;x øpVà*deÑÈ >Ž˹ÎÖÿ¹gãçÀ©â/w‚«2F|þ\æõuGшºÇÇ>^ƒÂºåvë–û‡ Cø-‹fu¿*ú^“Š»¢o²þؘzòQÈN5bu3k>(¸–&]~NÞNìjð•æuOv(ÜÛ¶möñãÄV]h¾?’8; 7—e|æb¨e{×¢_9O’^!c¡³TÙó¨Hvl¤ˆ‡G½âé¼á½*¬ ý€sÛ\ ÞÞ ìË·3%äZ°ˆñ+ÙYmOF¸?+g0ÞÏ~?Ž*å@‘Q B¯Ñ:šåÒk¡p†°(œ2eW$Ý®?n‹µqtŸ`Ëü Í2ª¢oÙwË'ÿ ;ÑÁ÷ØYãµ|û\Àµü?Hcúÿc,‘eþû!é -ã3@v‰ã!œþðJ`{õIðbâjÝe|æÃRË>½Š~’ÞBhÍc$h%R¯D.öGtJ®9çóG`û9>öÙþ7 dlŽÝK_.aiCq»*‚1BÑœë ¶Æ­úÕ=ÅPÑr‡€+Ž®Áß’¼l¶Žôc6 ܬ÷Q÷£Cb½ÎªÂo@¸Îò»Oˆþ¼ßœØÓ9öt„b'Ò£À§ÉËÁjp2ûÜHÚWËøô“˜Ùå?¬vdOûÂõ-¤YƧ»øXiº¶$½;Ò»&¼³Âo¬—"‹{Ïû{߿ሯƒKÁ£À‹Á>Àõç›ßσ¿7/í`à]ó?p¡i6õׂ7 ¾¾´#ÀSÀ?»€ýx PÞæ2˰Ÿ|L©!„úYS ç*m»íЧìh³ÆRCXc„¯ V_Etâ˜a¥UÑ·üÑ1±œ ¼B/\n×÷s”Ï>m6çf6ÄôÿAäW§ÿBºøô¿~YƧG&‰M/Ó÷s}zÆg.†Z¶÷$úÍs%é-¤.ô"•,Ä^AÑW |ò{›òÉŠùò¯ÙI1~7¸|(ð6ßçíHð[àÝ ØG‚OíðFRþ·¹÷ÏwPðßîîÿPð,p%x>X æcv ¹y„!4mQVÕËšgË©*ˆæÃ\'s2®êŸu6:6Æ6|¬Çêšu>`™–3Êjyú ܯ|ý&ß“q ïù^ÈåîM£—‚U ¦ÿObûYîÉ8Gù¢'N’ñéIâr‡^ ΟLß³ûì–ñ™Ÿv[{ý$½­µY§( ¢!úHþòy–ï ì· PXmÊ[íxVl ~žšÕýï2·éŸ%»³™±D{Åß2í´ý@uÿrå ÿ<®ñÓÇU1ôÌá·} ¿¥Ò³ÈÌP‚žWS4/Àò†È· ji«ßžËÐÅ ¢Œ–9Êânj™«i_¿Z^øs&á_<>Šô(Pþ÷éÿXוe|æOüÛb VãèjúžãæeŸyÑ4¹SÏ¢ï™:$Ý9P§FËFÉ‹40´F‹Ïì·Ékpâ×à|‚iðùоc½–¢9îÔ. uزÈÌÀzq’V{x.0šÌÏgjß}õEŸ6~õSŸõ”LUÓiâzøôýôOm¿&ãÓž—vkû&ú-¤[ɵ¨ô6Zöú£a®6TæÅ°--?·l”H£r¹匆;ˆ§Á¯é"þ|ÁÜ"¾#»½ï4±ëÖj»³âdp °3ña0›]ÀÆ‹ÁÛÀ^ÀûüšŒk>(è$í|Ìr..=ªŠ}5þ‡XÍç¬uÙ§³†Ð[W÷Îj\ܦµ¦µƒý/—ñ¹ÕOr]SöC$ä6 m“Wü¿ Šà•uø$y¹\ |úÿÒNM_½Öôs³ˆœí‚¯GƒUÀ6ý$0Ðé{Îß­mvñ™Q}}?Lá7áÂló¦6åŠi»†˜Õ“‡ûË,›VM˲7×Yf—C†t-öeõH™¼nÂ|zÍ<~éå”ýÿlúú R›KGåG6×QÍéúƒ˜®w?'Y—‚V³ñTðMðÎÊÆ?#ïþûwà¯À|¦ø½ÑøNø&«þ–›ëÿ¯YgôA†dÖÔúizs3ï+y"c1ÜÎ÷ ‡Á|ÍëVÌõ¬à¦ÍmÅ |¢Ý:Í:cÝ׬K>»íD¹rØÿ¨ÆÕéÿ§ñù«€Óÿ?#] ¾Ä>ÓžþŸ3>K‰Ï2ⳜøÜ^³øøÆÑåÄgKâsgçñÁ÷:Lßš™mÎødý™$Ïž÷äB¿3Í@xZ3­Ú07Ö´ÿû·ßÚÝÚÙm¦Õü‚6Pݹ ɇ”PÅÁ±÷ÖÀ ô‡ ¬ïá…ÈÏ™|莕³Ú½lUv¬öÑ}ks›Ñ;À€v5ðiþ¹0‹ý–mžÛ’UÍÙ„ÖϨn¯æÿ g—1æø%9q ø9°D6Þ€ZýRå™b- –Œ)ïã7+åaÇ­§<71å ¼`¹ œNeÖe‚ï{¬)>Ë’#A£iªø ¯"š6<|@'ÿv/_Vg‚/‚“(ây¤Ö§h·"6›â³„ølK|^K|öpïšññáYŸ£9»ŸµsÇ·[§ïWs†òÝ÷¤µ±Yã“õgZœ*úÓ>­¹¢¤™6/Èú:5B½¿Ž•M3E ´>ÁtúQÜOwj³?v#§qJÿ¹ÀO°Ét6àÉ`Ðv*Ï(\TÎ(î»6aWäಠüå<»1µ²f¼ŒYŒì‚ÿ :[)N!VFrÌ®–±_Z‡ÜÜ:Ââï¯*þµ3bñH u8‡ÕàTp˜Ÿ¥ÄçÏÙÏÚ6 æ³:ǃEÄg¢m|Âÿ£ÙËxÚ¾ûžõµ°¬?…aAD¿³"æÞ2Ð"úŽÑéo”ä'“;†ª¿SÄJì/Êþ`X£ž0r¹§¼±àSJŽó »7(öû‚}ÀoÀ´Š/²ixF¬E*øŽ•…F<|¯øÿw£dWRØ·3òŽn¼áÁYUüËÝ;Ú„&¦ÿ_Cù½¾> Ζ¿Ÿw‘³«9Jf'ýcÄgýd|,½±z-x8œD|.$­µeýé<<)úsVû#¨1jTØCôíoŠ´œÂCt[•²X{of)à²í#<£°¾øäóÛÁ]À®Ç}ÀéýµÀéýxÖd7–í´ÂŽB»Î€×³f¬bºX±ß,'jg0Ö<€·Ëѳ3Æ“yõóšrÜo„Â/Í„¤–£}Ê6iÍøX‡^þ Î Jp›ìnŒf|~@ ¾I|(®ÀŸßþHÕgÀW€±¥ødý!`ó5§«ÒÆ“ïäÙ¨ŠrdUÉ_Ìïƒ˜Ž·²Œ®]‚_–Ó÷¥ˆàHøkªÿbÒ~®iâß&7¡qß…$:Žvœ¤Þ—õž«Úø…ËœÊéÞžŒsWï[é+úûóôË>¼Ñp4EVçÚZ]Îù<Ž%9«^ƒÆÁ‡útL‰ëkc•øXö¯³Á£Áqe|áø<øœYÆççøóBppFÆëÝën”⢟õ‡ÀÍe)ús14šÛ£!5-XR+³×†ò^ëwx†þYT|«É(šw‰¿‹oŠo“ÛÐDˆø\ú?aaŸ9‡8L ÿŽ,DgÀÔŸGÑ(;Íåëæóyì«ÅÔ¾lˆ¾÷ïßÄ“ÞÓ]³ëòj®¯Ïodlÿ–"6qês\Ÿuõ³]|îâæËNÅïQn=½¶#>ßf¾oCy;̈£QOÖŸ9jÓ(_¶s¸¶ÙoŽ5*r£éõ%.(vç…86¾£gçÒ8mQÜ@Áov¿ª¸Ø€ÉC׆€ÿšƒÅ÷ª'Aôw`9:'ÿ¼æòl‹N@¤v®æ\–­4ö‰Q¾üÇHÅéý唸YLö,Œ^‚,ž^~OÄÛLÕø˜ŸäÂ]ëfsÆgÿ1ˆÏþÄç;cŸ¬?3V©ý©Ý Ž4i´;E/zî6´Š£XÇîùÑ›ðÀÝ"Æ-£e7RÜ3ËÎÊçÈyÏÞûÄ!üUÑŽ›ûkPìCƒÿ·‰É“ûÏNT;oh.?’mW¯v~Åò5@wì%äAËÑ‹ ŸfaÍJÆÅkK>.bÉN±Ò_;;‹¼N5òu´²Œlj|VRö‡Õ±¸–é‘쿜˜¬³ødý™õBÍQÞ¬.åÆ&!xŠ~Œ²‚ßÊ›è|¦ü.ý=#Ä™eý%_T¾$ظ¢_~}Tø{åsŽŽ ýº\NüU±=8‘2q ðklN¥ ¾|PðBð)ðvðëm”gUóñF×ý-Pvµƒ«›ø(é-à[à…àZЫùtBQ<èstlôOÄlÙÚ™eÓG–×NËÅž=Ƈ“”ö]þËõlf<þn†Ö°Þp/¶çƧ×ú#ŸÖëÀ™.ÌÓº9f®S þx1§/ ŸPc¤¯@úÀά½€fxk¾ðör¾°³¤|¹ÎCØÍÙZ˜Ü .çàÇÏOðíü¢¸Øôé~)øúYýZŒ"~ËúÓ&ª/PÚšuû¿èødå¾dètx÷æ÷ZMq?¿¹ò¿7Óï‘*¢íî_?/ÎhÂ~@æ«Í…?m¦×LÏ }i3ßi²,ŧ†è~|ôÅk®W³Vjm$åÿ[ù2x>8°\3õ_»c¦îÑù’¾ô¹þä´1d€‘¥÷õ=áå(þ}§*½Øð^Ê·Û¯%]ÑD눌ÕCíÄè¼Qö š¤ûËr+M¤á·£àë_Uô¥!ÎŦzq 1‘ûêhÒ×6å«”z)êV|XóFýà( àœ 4ÙR„Ä@‘yø Äõ˶áD–5Fʳ‡Ïò jesÆÇZÒyµ~<ؘ_¼bŸÞöoß—‚@ÄâJòn;œÞìØx"èÖ¼V/ïŸølÝ-•㌱üWÏeËcçÙšv¢ßîvíÉPRô{ŠÈH¬è…àÛ„ã.¢I‰}”a‡ÀíÑ8“-m ´eÑL£Ã¢ÛYQôx›OeJá·*ÆH¿UôÙTk“O9ž—©<üÖ›=¯r¸"£)èÞÏ׌®Q{Åÿ/Áß´³@?LQ™(§Ã«×œ×]ø­ÆÖõûúÓã3A|z¹ù²'ž íšFRŽüÍ® ÇëÁ»@Õ<Æ™—ßlnxé£ÀcšËÝ$ú²qŒâÓú<Ïw±ÂVæÆæ†_“:ê·U•ÿªÅ1Õu½äP¬ˆiãË@§â©p*Š6¸ §®šûxùº])p[̸o4|d‡2*³<ªè[¶}« ÂßNôõÅã„ç©£Éiðâ'׋‰Ä²ò×úQjûpÄr1ø.Ð~'?õ¡ÍåãI¯íF1¬îÊl´ôiS'ÓOŒk¯«Sá ™ã3/ÝŠ¾5lŸ6¥ñXÑÜæ˜»Õ|Í}ÃÌß ]¦ú2Qv3£{ØöDôËÚ®¯ÝÊ´c^sý4g^>]9áWÈ ­_ãÆÙ¦ÿ@ýIÑŸNóجqjÛA…x*úÑàF-v›ëCðm^lv¼6Ü×FZ‹ýKƒýßNô-cu´¯ðÛAv,¿ûØêÓ(üBâtqYŒ÷!+Nëõj!1çs4²¸85¬è;Ú„¥Ee\¼–á÷0¯©n¼‹rF¹áÁ:®¸å}ÅÛ«ÖO*¯ÞnŠÛÅ1ÖœEå8v¹;ÀÀ±­jë–u®|5i-Œ6M–lŸì²ØUr~[°=­ÕW)ù¶}©?œ°´ýøÿ6p8؉~!†z þ £Ø°“¶€ ( ÑüG#Åq[Œð­ÜUÁWìrTVí¬Xþè˜XÎõÛd¹,Ü®/!ú_g Á‹˜˜.‚ñµeÜ’Û~¡y"#ù`àd cŸÚûÁ)e®ñÓDÍl_-}jÈYøj£ùHë/Ë¥EùLýµ¥7å¦>ýÛ¾yž‹Hí„}gžçõJ×dךÛYƒÆ)>ý¬?Á§³3â è?ü.†  þ¤è#p øÍѾ –㛉hÌ,•ëÊÔá{]T?>VÅ€€eXÎ(«ÂЇ)‚¯ß¬ n#½›ï[Á«"Åh^‘qÂý8ð p<Ð\¶që§ùäEcÔþµ¦ýü´Aœkjy·`¹¦ñ±ÄÆèÍàÁi`OPµGUšy»MË›ù’¾£™ï4QTÆ)>úÒÏúSå3:Y¶’òÔŸýao?ýóÇ3l&Bä[5„4Ä^Á6x·e´ÌÑa qŽJ5µJºŸz?J‚?•Û <ÞuM±[9…8uKïKïä"ì¥džlø)V;,öÅ®á,ëÊ×>÷åtC>‰×ÿT3>7ŸÇN]=¯¥C[öŠekßào€‚â¤úŸì—[/óIDAT'¬EUÐí|hvâ<ÆšÚ­ÝÌëÇ,>ƒª?»ÁÕYÝÝåq¨?)ú]ÆbSKQ$5ËŠ¥Í†M©"oZühü"eóÀÌri‘†è‡ð[ÞvÁ7‹8l*ïÚâj¾µÿLV8¼y @ Ê.Gò7‰JÄ4>­u9Ö×%ŸõÄç†>ÇÇÚ#üƒ›yï#+øsY¯£ÎˆÏ†IÑoGëò\¥ööéñvý´Ç¨?)úƒZMÎïè·ònc©ÍŽ£f_´ þ0ÄžbÕ ] :[{óíàßõ£fúþ™¿œ'íGÑö¼_RvÞ®núè>úþº¬éw]mz|®ìs|¬qǃKÀ‚#ÀÓÀ0ìª1ŒOÖŸY¯œýYé¯ ?)þЏyS`Å4ÄÞu²e¾šš´…TÓ²ì|°iˆ†©¦k±ocMýÿ·–7âpuq7þ݈»Ô߉YKx[ï/;“7 ÿªqŒü¬§Y 3Çç~|¹•R=¢%óÖÊ3úx¾ùœÊÆcŸ¬?³F߆>m3c TÈÆ¨8bEßéý*â!9Ÿ >çˆuýLãܦqÞ(ƒiµl1½¯+þcû¨™e–ÿˆA5qq^Ù5Ÿ¦–W&ŠËXñ Më,øú1{|.ƒøèÃcŸ¬?^Ãm-E¿--›ÇJŲi §ØœæŸ UîW~¶Ï,ËÕ,_”•Å‘{/®è¤„ð…*ޏËúu¾4A:ºf·í ø¹¾øvéÓ¦oWècjé ã8{|ÖŸï¯üQ5Ëþ]â°¡|Á•o…°þEçl´ã“õgÖ«2EVz6Ï¥šÖìßF¢<ü²¡Uô«žëY¾¾ü-A2#i_-}rZ¿ñ|ø&ÿBX¢£[77çŽÏ¥u+÷üÊs.eߢüæ¹Ïóü%8\Žk2fdXU;›;>þç¨ÚëOŠþ¨^Yî‘e 9²µÑ ᳑UôcÖÄ·¾ÈßFž=»’"¯æ‘ŠÏ‘sÌ·gª£É”Ú‰þ¼â³–è|ƒøØ¥5óy‘3‰ÏÚ2>Ÿaé¿–k ~i³àÆRã‹€O! a%[›W|²þ̰ý©É ÉÀ@ˆUñ‹Q¾¢¯@*”7±ö3åK[~ËÒ¨˜e}™Åå—Ð~ÍRõùŒèÔÄH²v‚_¡yîøL¿3ßxJåÐg-ëLjϢ)ñ¹—µgƒ7‚ç‚kÁ?¿OðnžûÝ™´n6w|²þ´YŠ~[Zre20ÚôÉÆC‹ ø¶þhŠ7g°V ­£Lê…eóGHÞ@Y72ŽÜXøRY_ûã«fâáLE?FûµéSư¹ã³”ø|Ÿ~À!w€ºÆÇ² ,ëñ™˜1>Î]|< ¬»€KþsÀëÀr–ëbsÇ'ëÏ´Xù“–ÓVæŠd ,4ž~-Òû§K€ïó1] |¨Êײø²\±ØÅys;ò’Ñ%4Ø[[„Ô­DL–ñE¶Û‹s(Øåànà·ø®9SáˆÒŽ€€˜ò¯å·/:ŽÏvÄg=ñYC|¬a|–Ÿ¥Äç®ÎãC¬–—UàéÀ_t8 í8ŸtA¬ãødý™ŒSŠþ$™I†Ë —3mоª¢ï»ñ~¿µ­Ø ;Âun¾‡Íãì4ÄlÝ0;1Rp¤Ï#Ä …¢®¸;‘Âoêºû€#÷-Gý‡ç¨¥e|Ê_×›8Ù‰`Ž^{«Á)ÄñFÒ¡ZÆgz|掴d X8P…STÄcô¢ÎªrÒØ} ; n·Ñuÿ°a¾i;ÑWÔwE¿:²ŸzÖëË1a¹®–ñ©DqwúÿƒÑõ¥Á«ÀÏÉÿ˜t5ø ûx Ë2>2#ý ËÝ“~1@C©HÇh_iþñǨ+¶™w½‚/í+øq²C™V¶‘ TEßΈÓö6øŽöþÝ»,~÷‹{ûèC}ï/f|æ÷¦Kxòºé;_« íIfŸùŧ5)ú­Œär20Dh¸b´®€+ä6 BWìMÞp›ûµŽô‡1ÊçcKk'ú yLñ‡ð›â>~|S¡|ÉRãtõýŸñé,6ðµG8ý¿ x®'Ó¸‘´ï–ñéœÒýÎ9Ë#’¾1@£¥X EßFRAw$¯¸‡ÈWó!øî/ì4 Sðù¸ÒþéÇí Gñ1šWä…˱Ύû ùž£Ö–ñé>åjkŸ¶´Ì{%üÙi})Xžœþ? ñÿ!iÏ–ñéŒÂýÎøÊ½“0Ðl¸ÿ˜îWØcä_Mû€bïþ a!ü¥ˆSE?¦î«"bïv÷¥­¯ïû”¯­e|ÚÒÒñJx|Ž^׫Oÿß@Úµu?³Žõ§ZwZRô»¾ÌòÀd  ÐhÅh=„<¦H£±ŠNAìg!Ìk‘6–ó?¦ä# Ñ7UØgÂÈ ¾4f|d¡¿§1ýÿÎüS°t5ý?C|¢Î˜Æ,™õÇeÓè0G½‰”M³¨7‘F}±~T…ßõ.W·÷¥Ãœ¢?°Øæ‰“ÎhÓpÙ…ÈG#eëýóÃh¬ü¬ªÙhEÃU :Ë6PæÛÁ!¾ëGÖ2>ƒ ¼¶›þ_Íõr^'ŸX‰OµÎØiöö˜iˆ½y÷Ѫu©±f°ÿ£ÎDý1úÒ*ú!ünï‹àëZо,¤%5b Òx… GÃT]6¯µ¦µƒý_m¸ü¤jX¤åv[¬ÁixgÏø Žk¸éÿU|Šb½œÌåsé¬V‰Kˆ~½©Åî<çUÀuZµN5Ö öÔ?%òÑY‘¾·Æ|×—ûö£¥èÃfZ2PGš˜E« {4R³9öŸmŸN·Í&Ú±-1Ï]®ëG#ÕiA‡µÆg°LÃïÓø„UÀ§ÿVƒ/qMM{ú¿"øŠyˆ~Lï…俬ä˯‹‹[Ê»ÕÄ|õV|ÓeEqiq[ùÇP²ê³0Ñ)ˆÔ·®HÑ<(X8*b³p…hùäq÷Wç\ÌøÌIQG;À§Óÿ/«À3A<ý_Nÿ·¾¢/÷ñ—ï+v(Žå—·æ,êiJømà|p*_o(ÞÍÛ->Ë’#þvâouëZøSôa5-H’d þ ð¤”Gc@LÿŸJþ&#{×/å?RœÂ~vFÁü5Ä#ÁÒâ8äþëäüêkLû÷ôb«}MK’d -èÄô¿Oÿ_ Ÿœ<ø^ñ)þï FÉ®¤°ogº]ñr×_rUÿrº¿ÛÑ~Š>l¦%É@2 Œ¿÷ï}Mµïþÿ#p08ƒ50p@ñ“Oé³z„ì :.'óÚrÜï3 "^x¥èoDô½Çß±IXZ2 $É@20R Tîå+‚_NŒÖòèë>Åá#*ø8@Ùóxßþä¼9á- o_u[÷»z`7EöÒ’d HFŽEO ÷Šâ.$r'ž÷÷žþèš2ÿj„YñFrÎd¸FBôKág¹cKÑ< H’d`!¨ŒòÕ°xZßÑðrFÈÏâ×¶‚ÀoQоc@Äh_ߺåsÜOXú´d H’Í•?DßQ°8°ØŽu;%Á‡•¥ÐïKNÁ ¾(}ïfŠßÓ’d H’Qb Fºj˜SÞ ¡¢x ï™ï}‹Ó¼\Û<Ý¥¤'€?«×i®ý¶ýËý9mLïÛ± á—ƒàaÞŸ,aiÉ@2 $ÉÀ¨1 ~…6Fú[ò ž½úøÆ½]›”lOz-x'8h§ƒ¿-sEq©è·í/ËʇùìÐèctpô½cÁ·x)ú²– $É@20J Ä(7„¿ñ ßÒbïb÷>ºátÍqöË\QœHúÁfÞ€£ýCÀ=ÍuýLôe>5f1ô1D_ÿ»~O– $É@2 ŒÍûØ ^ FûKxˆo^¸Û?«Þ(ðEÀ/~[>:Ï ï³õ[Ã@¿mNø`éQc&cÓý|KŸŽ>5E¿#ºrçd H’š0£]Å_\Â+mVðð[ÿÌéý³ZN÷o,º¹î!Íôy-ûôkÑÎÄDy»¢:Ê××èðØò%{ó~¿¦%É@2 $£Â@ŒpCøBô#úËÊQ÷ =qÄÿáæœIzû?LÑ×§MÓú!ørЕ¥èwE[” $É@2°À LÿÅL¾â:ý(ðI~mðš27˜{ùÍS÷“YTþàNµƒ~w%ü9½äfš $É@20* „àUp“ükK¡„nžôŸI}A®Oïk;6’üWôõiÓC{úk ê·ùyOï§èÃVZ2 $ÉÀH2Ð*~÷÷ˆ'ŽòœÒÚ_ä”5¾àÜE«Ÿ±Üñ'§èwLY $É@2P¾©¶Ÿ¢½¦Øôôß¼³þNplóÔ~wß;ìƒ4Xw]q}??Âi‚´d H’d`ÔpJ{ê´öÚâêâŠòKuƒó塜Z Zðõàr|Y?)úS}mõÝýça)úó )wI’d ¨% ¡¿+€Ë‹‹ËåZ¶ãB]RúuuÓG××ð×e­µ3ÐX;ÃÿýˆÉÕÉ@2 $µe UèÂàêânDñÆÚ–{þ»Ž]ï/6ðÿþE禚Îÿœì™¢ß]¹s2 $É@ PôbÔ«ØWóç•€³‡"èÃDqgпªú¢ßñ¤èwLY $É@2°€ (xZ_Uð7püë|nbÀwö%Ô¿¤÷ü[_|›œ£}âžÒÿNÞÆÇ9r¤/ iÉ@2 $#Å@~Œ€CM¯/ñÍrô?RNMö«eÙÖ¿¬á_ttä¡#Ë‘~GtåÎÉ@2 $ É@sd£]ÅO!TG±Ž_¾;‘¿Üá=»’"¯f@þ@ñ9rŽùý‰ŸþªèGLJÍó·ýùs•{&É@2 ÔƒÇæô¾$¤%É@2 Œ1Å]é+”ØX\ÀOÕ|¡x#òk”Юd’ãizaÙ¾Þ@Y7ò¾¿ÅE,ùÞ5@ô«:Ú÷¨èø¿ù“|óß;÷L’d H˜N–"øzß*»%XüQ]~[àûòÄv`?^¦ó"&ÉwäÇx–0Žžþ&?vZ0[„x¯Dì—·ò‹}çPŽËÁÝà.pg35/°#`' ¦ü':}/_à {iÉ@2 $£Ã€B‡îWGúŽö ;"vtìKs—oa_Š˜^Kê[ò…ëÕ>; q‹{˜i[~¿–·¡¸§,·b®¨+îÂwîÇh?DÞ[“Óû >Ç–Ž›¦%É@2 $£Æ€‚¯ˆÇè?D]b…SÄïÓ+øî6 áÁ7mˆ~㙄¸-a‡E±÷§vª#ûvÓûìÒ¹åH¿sÎòˆd H’…g „SñŒ§÷q…SAWÄCðÝ®àWgÜ×}†9Ú·<ªè[6Gú–ÏѽÂßNôc”ﱞ§cKÑ< H’d`¡hNñ+|!žNï‡ØÇ¨Ým®Á÷þLïWGú±?›níD?nMT…߀¨ÞÃÑ/º™Ú׳}YHK’d EP…PS¸«âí¶á;=^|Gù": d‡jÕÎJÜž°cb9cԯػÜnjß㻲ý®h˃’d Hšæh_¡Ñ~Uô]Óþ iŒðÕ½ªà·vØ÷Ó'ÝNчˆ´d H’ÍŒôo‚'ù±‡È· jiˆ}Œò{[‹2Zf¡ ‡¸GG¥šöEðu4Gú²– $É@20Ê (¢å(˜´*¨Š¥"¯ðÇèÞ´*ø1;)›f–M‹4D?„ßò¶C雜òèþåËyz /M’d ¨Íö(Ü!葆Ø;ª{ø1Âý‡íDtLüÜRÐIc4ïr+œÓïYðý°}YHK’d  ZÄ?D=Ä>–MµÖ´±v°ÿc”_M£`€yô¾û{ø­®¤è·2’ËÉ@2 $#Ï@Süõ£*ì!ú³ùû϶O§ÛBàÛÛBøÝ§\×O±NÑ&2M’d {*Úø:qŸÉ¹ý™˜ÉõÉ@2 $ÉÀ˜13Œ™[éN2 $É@2 ´2¢ßÊH.'É@2 $cÊ@Šþ˜6ÝJ’d HZHÑoe$—“d H’1e EL›n%É@2 $­ ¤è·2’ËÉ@2 $ÉÀ˜2¢?¦M·’d H’VRô[Éåd H’d`LHÑÓÀ¦[É@2 $É@+)ú­Œär2 $É@20¦ ¤èi`Ó­d H’d •ÿ> §z=Ê ¬IEND®B`‚parslet-2.0.0/website/build/images/favicon1.ico000066400000000000000000000013171362225661100214010ustar00rootroot00000000000000‰PNG  IHDRóÿa pHYs  šœIDAT8’ÏKUAÇ¿gfî¯wßóiEý"‚"¡–”«þ€6AmZB´síÂZDíxáÊ+ú±ÊÚ„™¹(Q ,õê{ú~ÜwßÜéÌCAʨÜ3óýœs/cð¿‹x±¾ °{”¹ lü7cAÈvFùb@HqŸýl.[oË‘`)Ûš½»%Ì‚€­Ï9ŒÉ &Á5 |v^Äøö~Çì»Ùj¼À»b[B¸×=æe²Iô)úÈ­W0äžB'µAǯ3%+ò„Îù„m{²i4œè¥B­‹änÜóºé""1]ŸÕo8¾äžD^d¡Aœ@HòîsÄgs¾Q ®üv½ü¤þ]Ÿ¥°> °PðÔ$’Ç;B7nMñ‹µ¹&ã´0T«.ÅÝÜý„bXÛB\É?ÙÀÂ>Û:Lж»Æ]¬½jˆâË䎅9-øj½E[  Zؾ5'®0¹¼7”ˆgŒ\,ÔÞ¦õtÐÂv‰æ|´Õ-h?·k÷Æ<Œ·æÂñ’ÈŸÃÕÅúœîåêkMÚJ›Um%L TŸrß80NÉApÎEZ&ŠFb]×—žÜ€í®ì¥% ÎÇõwœ¤Ê Ádv8hï SEűD”F“†m†­OA§ÐªUÀï £W ª_5ZN»héðá$•ž'b¾°z»Qmôs‚?þ{Úÿ0×PíœÊá_ºBHvy.ù2­Ôç—®éDßú½òF¬dŽ`b«Ô ¡V$Õ>–_¬L'+W¹è³ ñV»Š§R™òÜÉÉŒY‰§Ó÷å/ñ0\Þ Ú|Fa>s£\Œg=ÇŸšÝ,ø—ÿ 73õ y ŸIEND®B`‚parslet-2.0.0/website/build/images/favicon2.ico000066400000000000000000000011051362225661100213750ustar00rootroot00000000000000‰PNG  IHDRóÿa pHYs  šœ÷IDAT8¥’ÏkAÇ¿ofv³“l~X,K ‚ZHµÐ¢P½‰ xЛ'OŠWÁƒ'=xЫýŠ–"Ô«("UAEôЛT¬(±Ù65&6ÝÝÙñ-Ò 5¥Ugٙݙïç½ï{ Ykñ?Cý LD’õENºÄßZë~ÚŠïr÷ã¬êÇ¢Çr{£wÅCúŠy©&º`€ô˜¯‡Ê2ì+›/z¢~7þÐx^æò­Xk"Ãg“8ˆ¬d•UŸ„õóZ4gL;˜nŸcöcÊ*Ë€–ƒ8áŽàˆpXØeö?¬¾¼!a{æDø†hi*¾`—íÃNbYÂ)5DwäëRZЂë+xÒ:eB€ü²Ñ,­Ô¦š—¾Íµo­ÁéÊJnRÅz6AâG”Ž{pz,? »1…ió,¸ßº­DO…Hg¶œíqEiwn>ª&„ qÍPT³óßg£¬x6,•®Ê´ð‹ùо™-¸7Þž©.¬mö^çôÅ4Á¿¼ÄÑ·3l–e£ó®Wy#q·ýÎEêv¸•½-ðß(Âö­*IEND®B`‚parslet-2.0.0/website/build/images/favicon3.ico000066400000000000000000000011771362225661100214070ustar00rootroot00000000000000‰PNG  IHDRóÿa pHYs  šœ1IDAT8’=hA†ßofwövïÂq&˜˜\¡DQ1`!b!B""b#ZXGKÁ"i-,¬ÄB,¬ÒX(vv"#‚……"j …hD&!‰žÑÜÞÍŒïœÁ ˆávþvæy¿¿ï=þ§‰ˆ!×/í ,«œ‹öâ¬êÇŽM²ŠôE[Óê×WߟJ$'ÍA\`PôÁÿS@:e4Æ%JÒœóÏô6v#"hECTL‘¿…@7 ª‚ñaŒê* ¬$h‘—l}üþl-¸æ(cì†ÇsZ‚¦P°˜®Ã4iäƒøïFXT×Íá-tÓò2M„|«–Ø9J×´ïè*´¼C°ÚR)bÜÁùuË­B)º«¢¬=ò·ð)ñYJÄ*&D¤_A]äüÄ ã²ªÂ©ð4‚$¡æGZ\"øpܧM…ò±Òn£¾=²"LÌ©xPn«^oõ.DÁ»EBs‚¸ Q¨jŸì#\§È¼GçPBíT/ß«×>ß©]‰`aâCÞHœ¯ÃÙY +”NÇ0ô­•}æ‚îJç€Vv&ÂÂý¯×.°‚S¡ Þ¯qlÒòPÞŸªâP,n•Ì<s»Âhf›y>Ó˜^}‘ߪ¯4'óï¯2JX(&¢kO&Åíf1ŸqV&/ó Øe'vÉ}²9ÞðÚ4A³ÑBKžµÎz[êMnJÍ]{?öåÆ6.m¶Ú÷é¼2Ò1^e»=ˆOf:;qwu»p¸ÿËèBè+~IEND®B`‚parslet-2.0.0/website/build/images/parsley_logo.png000066400000000000000000000450771362225661100224170ustar00rootroot00000000000000‰PNG  IHDRwÈœI6½ IDATxœíw|Õúÿ?gfK6 BB½) 5ˆ`AE@Š(‚ \½ÊE¹ˆ‚‚‚W¾*HA¤‹bhùIµÐ{ RBK ¤²»3ç÷ÇdgÏÎì&›¶ ›ó~å•×ìÙ™3ggv>û<ÏyÎ9„R Ž_Ào¥P.2!Dy©nprÁPÒ àp®2Np})$\e8œÜ ”r})$\e8÷°& ×—ÂÀU†ÃqƒjÂp})¹lÙ²|ËU†ãÏðü—Âsýúõ×_}РAIII$IZ¿~½,ËÞ×À=&Ž?Ãõ¥¬[·n̘1ñññlajjj¾.,WŽßÂ%Æn·Ÿ?^ÅZµjÆ|›’’òá‡.X°Àf³iÞÊoU\e8þ›Ë[Ö ”ž;wîðáÃ)))ñññüñÇùóçFcÏž=¿üòK³Ùìe=gÏž2dÈŽ;ܾ+Š"·e8e¶G©,™™y÷îÝøøøK—.ìš5kN:Uír¶Ùl¿ÿþû²eËbccõýÜÚƒ> ÔHМ ›,]¿xSÝ=<<<_« ç>Ãÿ$†Rú믿N:ÕM\ Ò’Ð(J  ¶/&ç?HĹ]NógäȑժUpþüù7þðÃûöíc£Â9­§ÚÀvIŽÇDÓ‘™˜©” †ÈÈÈ|}@®2œû ÿ“˜íÛ·ÿßÿýßæÍ›µñÝà!  J)dÀîðbÔ?uÚä<ɺ“Ó T»víÎ;¯_¿~Ù²eÛ·oWF9j Z‚¿ ç>¢´õ(uíÚ•²ö‡µù:*99ùwÞ8p ‹Äˆ@C`0ШN‰¡Ž?V_—@Κêù”QÀSÀÐaÀ£@( x?ÀàÐâªhpœš€\ír *‹ÅÒ¹sç|}^p[†Ã)çÎÛvô· uÃ~^÷óä)“CBB¼9êÔ©SC† Ù½{7[Hê­Ea$ÆE‚kðE ”°qÈ w]&6Î94´Ð ¨”( VÆRþq™:•Êäœ ãvbuäÊ´iÓ¦Y³f^^"®2œû€R5F)5-uÍÚ5ãÞ7ªµŽº²ùêO?ýôꫯæyàòåËÇŒsõêUµ„”ž ´9…‰*Y¶9–…š«¢F„)ct¨p(B-3¨ü5@¨ù!Ðf@Ààèó†S• 8¬¥*%.CGɱM.iŸSöúõë—_w ÑLïιñ×[©|®üÎ6PL¬ýií'?9~ì8"'Фn½‹³®Õ©U{ïÞ½‹ÅÓQ—.]?~üòåËIÐè„»/p5(ÔYFp•Å ¹\ @%  È‘àpQB0F‡¸0¹VHÇ 0/7doÈé]ªQ£Æþýûóú·e8÷¥ÁŠ9rìÈôéÓ׬\#ÉjÏ8e¿PùÁÈ㇎¯^½zàÀú£Sn-ønÁÜó.;Ç  ¡ )DÀîêÁÅ”Ðê qµqc›Ôj1V53Fßó-2þqlS׳Ë@.Ûv§8°nËø~y+Kƒ¯töÜÙiÓ¦ýøÃ©©©0m&€H€Ãe£ýg[ýúõÿú믰°0õ¨ÌÌÌu?ÿôÙŒÏN<鬋Ñ@' \º4ö‹º¡öû°£Q ÖÆ˜}Ø“ ŒŸ%2VŒ9…Ž‘s>šy‰!;.G®¢¢¢öìÙS³fÍ\@nËp8±Z­+V­3fÌÍë7pÐg€JÈæ@‚½¢ •pöìÙqûüÿ>·-ñçãW¯\½zõê“'O²µ‘Hà BSÆ„QŸp6W]€®ƒI`„‰0ûk:¡TµR* ÆUWC†2†ŒÃYÁ¶Ó™+<ô­¡“p[ÆŸð¿[Y²†ÌæÍ›§N›º{÷îœ8hh " LJG½0¸lìhÛ¹MHhè¡m‡o'1c…‚Aª ­Oäê±ÈŽjÆQb»™5‚ã(âêX)ŒÐ¨& 0º }rJ˜jÈ(\2L_‰ÖC9îRš5þüóϪUªìJr[†Sz))}IHH7nÜòåËÓ\6m3Çj¿cÏ–}ÎZ@ª‚6jƒ†"ÍéK†ëÃÏ&ÂPW{Lø–ÕÊüg²fœ•èÍ0ÖʃãÙÕŠ‘{YGœS|¾7ò½K ¸-ãOøß­ô½-c³ÙÖ®];aÂç DH ¡1Ô%( ÇIO¯Hn @jEQÁñ;.ëlê* ¬‚°±U\Ø|¸†rÁÔ¬«v]«½×"`tç()£1SÃjOÈù¨­Z·Ü±sg%iƒsÛ2œÒ‹%æüùó#FŒØ¸q£³È tmLÝÄM4}@2PPBÇøiuÁ! Êš\õ®^Vt4ÑÖÆ«å‡¸À¡)Ó#ÓlâPLEe1ýB¬ 9Eqܸq…‘p•áp¶oß>xðàþùÇY ò ¡5©s0´¾ˆupTÕ°1f t~Ì<Þ”y×mäE/1„8Vbàh¤&¿Ž /«8¬©{J!æ ÛoΠïK¯ôíÞ­G¾®¤žR‘éÄá”,ß|óM÷îÝ]$¦nC'‚;3‡0 û–ê[y’!}°†•0£»èˆ‹±V„„~&KŽ—w|ü—_~ 0äƒÇe8%Œ§5g‹;î{îܹìÝ뜎 háÍwV a:q ó€4{êÝv[c‰èUIã…éƒ5úÊUcGd̃îAg;•ì ’@%"¬‚Õ!1A–O'}Z$®2œ‡5X|fŽ9räå—_>s挳È< <À 2óèÂUqôQ}À%gJ#1l×’àè„r{”FbØ~.0¦–Z­ˆa»ÏU† ± D$Æ­$û°3èûöð·Û?ÜE÷˜ü‡û÷V²f‹æS‡Eóמ¿^êû’ËR!åN@&k:CFy€eW`UFptoiLvCÝ™óE“J£ñ­d]Ôu73`vµ§X{Ç®øJDñ*‘¦JrZÎnѦùoÿo[ùòù»¬žá¶ §äa&B¥T]n©Èϵs×Î>}úܺyËYT xš™ Mz.u·­s4!ÞÕK€Û—šÚ]·—ÈX1F‡C\…‰õ•l ”,JVB•Keêÿ¦¡Ä€« §¢MqÔ¼sçÎz¿À2"5AŸB/IAã¹Ýf4š|·úâ֨ѿŞ‹5Oro K6fÆ,R3hÔ? °ADj„¸‘XO8}¥×‡¿öô£Ü]¼‚ÃU†SQ…ÆSl¸ìرã…^¸sçŽó,õ@,Œ÷Žtþ ˜CÀ¼$îžšhê/ :Ñ™Ôæ©RW‰18º“Ød_ö@‡ÄI€â)Ø69%¦a놣GÊëBæ®2œ’G/%EnÎè%Ae†óÀÕ3‚«Üä"1¹˜'`ä€à”ÌyUô±Oh5Ыö[³Ú¤~%cìD ‚tW"+óxAaSæ|Z­| ﯪ—p•á”<žz²‹ÊÙ¹sgïÞ½]$¦ð``’bÜ2l8FŸÁJÝiô…;YÉÅÕR×} Û™:âÐ"Ó]-:äF#1޼»‰±Á(‹6Ñ.¬'ÒeçòñëÝ[v0[¸ÊpJ˜\t¤H²òþøã_|Ñeqèhà‡»Áš š ‹ºMsM’×èˆZR°2¤QªÛÓí ÖÔ"WËèPµDÐé‘C_`%‚,Ú vò‘·;%¦mç6ïü{„¨Í.¸ÊpJŒÜM•"qšöíÛ÷â‹/&&&:‹¢öî̸3dÔçYã±ûèc1 nÃ1j¡àAqÀHŒêF±±ÂÔ¯ô%±!¾Pv² vrЕT xGV«øÅ—_T ¬åá*®2œ£¸;v¬OŸ>ׯ_w5uX1²î¡Õ‡cÔœ7èDD/pÕÍïÖWÒD|Á„ŠYãÈ­™£ü79B0j ì ›jRLN,DdH$`5EZN}A³Ïj[¿]î³0p•á” ÞèKa4èܹs}úô¹té’³¨ÐÝE=ôá^ÙÃâÒ8WóD#(nC¼¹ÄYàåÑTHí3tÀä:“Í qdÄäHŒ"*S"leržyµ÷S½=|È¢çþú÷×­,V+æÚµk½zõÚ·™³.ð`v·AºÛÐ#`¶5q?ŪŒš&§W}äEt§Y`$Fy×ÈX1lÀˆ5Ž$‡ÊX•d<¡Ø,t®ÕªmË›6Vª•ç%- Ü–áø)))¯¿þ:+1¤èŒÄ¨î ÑI ë%ip붨DzÑuµPÐ¥w 4±èêQ¶ Ìšmz‡ CF¬€M‘™PúÈ B¥œÚ#"Ã.XXÜ®2?Ãf³½÷Þ{¿üò‹ZB"AŸ‰ÑçŰZãvÖ}ÌÅS¹àj.¬èM$·«zÄÑ*# éLU1Ù~k§Ä°KÈ&â*"¥æ4Îl6Í?7ºió|^à‚K‡sÿñå—_.Z´ÈùºèÓ@yu@]K(æð‘a{s4¹vlÿ‘¦/I­êר\R¢ÂÁÀAˆ“A.í¬šên2ã(I”2`• BØéœó3¿>äõÞϽèî=<.ã?Ü_·²8â2ëׯïÓ§Ov¶#›Õtw7ÒšuIdf[í¬qŽ\}·q6£Ù†ã¥ÆÓÉ%¾£6L‰ònÖÂXG°‘QÁÝHÊ„cd€B Í–aö‚| êèºn÷H» ÖW ÷òÂî1q|MMbÙ±cGÿþýcžª2+Fçâ(ÁƒÄ胾úr}j/Ü9A¬‚Àu7U¼Ô†±;›Xü ±±½+#¾u~4O#ç ,'²œói+W©¼páBŸI ¸Êp|Oq¤É;v¬_¿~iiŽ$Ð ¨ãX¤ ®Á0Ûš¤½!ã)X«WOŠ£¾%è6Üb .k€c,Dþcš±D•{ŽÄ² Û$;İ”Ø2sÌ‹Å2þüF åqA‹®2ŸR†Ì7 tíÚµœ×èÔsH ë(Á± f¸~|€ [Ñnõ‹¾ \ÝY *FÓ=À¸6Œ¸ºoà ð ÁLèPJft“ý vçâ³D&„ ²M‚ Bˆi¥˜}Ã9êúƒÇvïÖ=ïkZ¤p•áø”"7d¬VëðáÃ:ä,j4e%V\(cÅx’Í8l½÷äv6+(šÚ”¡ŒW€mÀE <ðP™w‚2ãÒ€m9²"ìÎ9R·õ£Ñ âZ¨7X±ÍQJø6Øüé¢N‚@- ˜ó k@O»ì"Tƒ<œ¢®k¿5»¬µÈ€ ‹T’I &ZK¬rwNjú)gÄ·Oß>Ó¦MsןÂU¦Ì‘ËkÅ„—†Ì7¾_ú½úÒnwûŒbûöí£ØyöCA;Æ›PO¥Š‹º¨«§@ ‹S¨Ì¿›ìÙ šâþ3’0Ðÿ ©ÀO [ó{çð0ä@$cÅ€ù ’sT„I$™P‰Rj›*§}s/yošZMÇŽçÍW"JŠ^e8ð÷ßFQeJ©~›Rj·Û­V«ò’b4M&“Ùl6›ÍAAAaaa*T/_¾¼Ùl.òÖ–ÔçÜjµ:tèØ±cýû÷·X,¾9už*C)?~ü•+WÔ’àà`ƒAûýŒ4hPzzzÎkðæˆø² ‘}¬Wïøä‹¡ºÔÙ¤ìÀMà(ÈaÐÛn\¤œ3Tý/ÐHöüzÕuçPààqÀØuƒ­$çêºD&fÙH¨`“³aBK•ôï²wÞUkjܤñ·‹¿ óxÅ}Hѫ̥K—bccãããÏŸ?Ÿ’âAÒAhÔ¨QÅŠ)¥éééiiiééé·oß¾{÷®,Ëf³9888""¢uëÖmÚ´‰ŽŽnÓ¦ W/Qשּׁ¬C‡mݺuÆ 'Nœ0½{÷V®y±6ÀKCæðáÃË—/gKÁÅEIII¥¸Ædgdd¤¤¤ìܹsÉ’%qqq¹ì9vìØ>ú( @yI)µZ­6›-33óìÙ³Û¶m‹e3;Fc‹-^{íµ^½zåÒQq»ÂtffæÁƒýõ× 6œ>}ZLyæÌ™°°0ߨLžB3hР%K–°%O?ýôÆM&“òÒjµöïßÿ‡~pîÑxR=Ί‘= ðŽq;ÞZpnFÙ!H.¿g„Áæ-#Ш²ôoí¾¤A?~üc=æéÝ*Uªä^C@@ÀàÁƒ·oßÞ·o_¶üâÅ‹C‡íׯŸF}8!§OŸ^¾|ù‰'ôSÚX²d‰FbØ%{–”eµY:ôüóÏ»HL8èÓ‰¡Ðú¹HŒvL8öT¦¼œ6 Aë€ýÀ Sè2``vµqlî$¦ 0TwDa¨k/µÓK‘I5•£ÁAÄœ%gÁ€ªæÈô5Y¬ÄX‚>Ÿ;½´I |`Ë(LŸ>ýý÷ß×—‡„„8p ^½z^Ö3vìØ©S§j ëׯ¿fÍš‡z¨°­¼ÏÑ{L7oÞœ9sæ¼yó\–Uà+[Æw)55µuëÖñññšòàÐà­Ð ùì±øô{Ì3 ô"É5Œæ)…;í€c[Ý`"Unì@2pä"h‚‡ÎJH%І m mDs’t„Ž÷paE&ÀÓ„FÓœÁÐ%õÈNõ$1B  f L"fÈ™v#4„§}ïÖçm  ˜1çCÿõv®Í-|¤2ëׯïÙ³§¾¼R¥J{÷î­YÓÛ0•$IƒfÖš4i—§Yäßèo¥òx?~|äÈ‘¿þú+û–Ï<¦<Ý¥?þøã±Ç“$ €àÙÒÐJÔªPQsIàNb4Ž’º!:´Æ$@þõd­8!Öìõd4#¨ AsÆ©2a–±L‹$ ´!H;Bpì¯N^¥Wä˜0`±À$ñžœ"î|“’¸Ó™Pøåì/þý¯7r¿Ô%…òe¢¢¢L&“Õª½uf³Y ïyƒ(гfÍJHHøí·ßØò'N <ø‡~ )‚æú Š‚4mÚtÞ¼yÍš5sNòTš8räHŽÄ¤¨2©Š;HUÐ'Aé³SÉmÄW³ÞˆÞ]R#/€wó §A¯6ÏýÐ0ÔP‹HeZ¶HÁ¨vx0¯1ÀY ‰"¨„P šã ÌXÖË)$F0YH€ mr–`+ØÊ%~|g¿³÷6$4äë9³þÕïUï®w à#•±X,z•1 ¢(櫪ÐÐÐ/¿üò‘GIMMeËãââ¦M›6iҤ¶Õ Ë—š^Ž*P·iyàadИÁÊ$ ´ hSGrŠÞQRÿr ‡ ›c IDATýblÀ3 gL÷âBˆUyP  ©ô µ…ËfV¤V»ÉÕÆ¨§Sb+uZ9ž•©³·H5aܺK Äƒf3ŒF˜D̦V{ ˹öIi§î©- Ÿ¿`Þ Ïï²m…Äw*ã6ÃÅ`0 ‡¨I“&¯¾úê¬Y³4åsçÎ0`@ýúõ ØJÿ¥4O<îòÛ#fÐçë Y„ f‘Ð KΉã¸l¿µ‚Þ„Qô% 8rô:Ü^b„¡Ž€F°7§ö!RNýpHƒÈDp=£^øT?Ž8ì  ÈÕK2RÑ c 1›¨É(Db°SLrà?–« neü㜕ªjµª‹-z¦S)ê´v‹TÆ`0èó8ˆ¢˜_[F¡ÿþ .ÌÊÊb ïܹóý÷ßss¦4àýT2.»©Ø „*•hΤٚ¥àØÐ2p‹QÞº œŽinŒ"ÂPG@4±·–mUåœ%œ$]jŸàaú+Õß±»„o]ŒÙu¸î)ç˜0&b2Áh$B»`§mÛèÍ5·liÎt¦M›~»øÛV-[ys‘K©Œ n¿s‚ ,Û¥E‹5r™‡°yóæ±cǤ•œ¢Ãû©dì“H¥?þ#wW¡4¢K^e NéÇ&1*£zLÄÕ(`µƒ5d؈¯*7Fà°`fŸ#ÄØJ¥¶ú‚D0Ö’T#½1¢î 7²4襁a¢0‚ÑDM"1ˆ ¢h0ÄÌK™7VÞ¹wÄÅ„©U«Ö´iÓúôéãùZ–4½ %¬2…™ÖÓ°lç´œ’ ¿SˆËì³ÈfÁ°YöÐ2š` ˜¯"1 ÀzÀ‘VB 0>"Ø»QkM)ç»»oO]àê‰46 q}ÿ‘ºÁ:S”©`†!€˜É@D‘¢Q¤éôæŽ;ɱ)öt6PŒ^½zM›6íÁÌër–0n{3K^e ,4ž¦•.D‹8yËýR¾aù½§’ÌÄe¨#…ºÆ5X5Ñ<Æj¿ª2à çX04$RoX’]œ/OÙÀ”ùföìzMQÕ:}qòª¹0&b4ƒH " £P¤ÈHZw7ó‚ˬVAAAï¿ÿþ¸qãŠcÙߢE#1ÊïMÉÇePsÆ9³´++V,X…wîÜùûï¿333«T©R£F¤±%$$¤¤¤4jäfЬ¬¬k×®%$$„……Õ­[×›y¤RSS¯\¹’”””‘‘a±X‚ƒƒ+T¨P©R%‹ÅâËñèÊ’e9--íöíÛׯ_ÏÌÌ -_¾|TTTHHHÁn¥ËGC†_è%čޒ8Äå¸B¤2ЃØÛQX\ãÇ`¶=_Ø^-µ\Ö•P×Càú–«÷ä̅уƒI$TÈŠ·ÞÙ|7eÿ=Mà©}ûöŸ~úéã?ž÷u,i< p!„”¼-SàcõLL&S­ZµòUObbâöíÛþùçíÛ·'''K’иqã×_}À€ùš.Ëf³íÝ»wÍš5«V­êÞ½û¢E‹ØwÓÓÓ—/_>wîÜ¿ÿþ;++Ëh46hÐàã?v; ÀÝ»w7mÚ´nݺ£GFFFfee%%%]»v-333**ªqãÆíÛ·â‰'¢££•ß|}v/QnSBB–-[~üñÇ?ÿü€ ƒÁn·ggg‡„„´mÛ¶]»vݺus«­¹¡÷ƒàÁõÐìFt}ÌFà2ÈoPº­„hȯUI·lN0›VÞ­I¢žTïµ¹m¤&ÊK@ ‚¢F3Ì" Fb"‘ÌsÙw·¦§î¹Gí.ç 1bÄûï¿_"IÛù%÷/^É«LÁ„&==ýèÑ£úòFÕ­[×ËJ:ôí·ßþôÓOׯ_×T¾wïÞ½{÷®_¿~Þ¼yÞ Â¼|ùò† V®\¹wï^e&•qáÂ…×^{mÇŽj‰Ýn?|øpŸ>}~üñÇnݺ±;SJ¿ûî»Ï>û,>>þᇞ={vÛ¶m- ¥4;;ûÔ©S«V­Z´hÑ–-[¶lÙb4xà®]»Ž9²R¥¢_Ö‹’””4cÆŒE‹%&&èÝ»÷«¯¾Ú¸qãÐÐÐäää={ö,[¶,66666ö“O>éÒ¥Ë|мysoëtYy’öÕ2GÙ[À&Ðl xr/ǼP‚;®&´ó¹¸÷˜àº¿º§ÆÚr ßÍ0Àh"&“`d‘ì3Ù)ÛÒÒeR›Ë#Jéի׸q㼿†¥œûUevíÚõÏ?ÿèË;vìèMJÞŽ;fΜ¹iÓ&«Õàv$'€ 6‚°jÕ*O¹ »wï^µjÕ† nßvÉÇ`sããã»wﮟ܀ÕjýüóÏ;wî¬îŸ’’òöÛo/[¶Œ2cÆŒÿþ÷¿lU&“)&&&&&fàÀo¼ñƾ}ûl6ÛéÓ§OŸ>ݱcÇ®]»¡9£Üš-[¶Œ3FÑô*UªÌ™3‡5¾ÂÃÃxà~ýú-X°à½÷Þ»wïÞÚµk·nÝúᇾ÷Þ{ùvèˆã•KAÿæÙŽR:ÈFРð ÐÒ1Æla冺Ê¢ÌþlÛÔ 6Z¤ Ó8UFÑÑ£‰˜MFÑHŒô.Òe¤îº—y6‹ºDx &&fìØ±žÌÛû”’ËŒ¥K—ê'ß ý÷¿ÿû{÷î4iÒîÝ»;uê4þü‡z(,,,11qïÞ½+V¬Ø³gfÿØØØ¸¸8·wýŸþy饗öîÝëöDêȉÄÄÄþýû»•µžÔÔÔ *HKK4hкuëŒ=úÝwßõtT³fÍbccûôé³{÷n¥ÄS ª`(³páÂaÆ)óxÖ¬Yó§Ÿ~jÑ¢t2!äÍ7ß´ÛíÿýïeYNII=ztRRÒ”)SòABeWñä©0»š’*6`3è-  ð:Pß±› «‡l.3E9ÕÁSÜWÝ}ÑK (1‚©h$F“`4 !S´ý-¥ÊLÛ›a¿Í.yCÓ¦Mß}÷ݾ}ûúfÊ÷"$϶ûr.Ë;w®_¿^_>xðà x:êìÙ³C† iß¾ý;w¶nݺfÍš¶hÑ¢víÚmÚ´6lØöíÛÝNµuð û™ªT©òñÇ?õÔSnßU 1cÆìß¿¿}ûö/½ô’Ûæ………©¶Ò´iÓ‰©Y³fž‹uEEE-Y²¤víÚÊË"LR$fîܹo½õ–"1*Tøþûï[´hA)Õ«”’!C†<ñÄjáôéÓ?ÿüsoNæZ³ˆúØ«¸]f@vÿå9$Fïã(­–õK®åºéé´òÁŽ€ns¬‘ŠAÄh0[ÄS¦I8Gî­ÍJœ’|ã³;É›ÓôÓ¼yóùóçÿþûï¼ï$Æî?•IKK?~¼sudÑÑÑ. ³3¤¤¤L™2å‘G™?~=~ùå—Ö­[ëw ˜2eJïÞÚAôžºÌM&S—.]âââ–,YR¹reÍ»ŠÊÄÆÆ®X±âÛo¿Ý¾}ûÊ•+ÿüóÏ!C†hö|ñÅ>|ø‹/¾P Ÿ~úioæN¯S§ÎŒ3{¡hÓ7nÜ8|øpe6OBÈ'Ÿ|Ò¡C‡\~µ(¥¢(öë×u'MšäI£Ú™§NŸ,˾Ôt')ãÀaÀô»JŒ*’«¾Ø™ ¶\ÒˆÜ PƒA°¦›ÑxËL÷¬e¶”Éi·>K¹{/ë•\®žÁ`xâ‰'¾ÿþûßÿý7Þp»¾]éÇí¯Ž†ûÏc;v¬ê#¨T­ZuáÂ…n³:ôÖ[o)®PhhèôéÓs¹‚ ôë×oíÚµl¡fä·þW_}5++K#ƒÁjµþïÿ›;wîÀ•°°°¹sç>ôÐCŸ}öÙÍ›7FãË/¿<|øpåÝ•+Wfdä æs;~Â-Ï=÷Ü /¼°fÍš¤¤$/ÉBHBBˆ#Ô ƒŸ|òÉ7Þðj¶V­Z™ÍfõŠ¥§§üñÇëׯÏ5@ÃX¢nJ'OQ;%4£D|wžÚ80Q3舫Ti¢È2 ³‘[€PÈ„º¯Àn¨UA$"¤Ã˜*⪈¨õ»íR¶œéñÙ«UéÙgŸÐ@‡ 6'ÁýÅý¤2’$Mœ8qöìÙšò5j¬X±¢U+7Cà?Þ½{÷k×®)/CBBòŒ ëg¨ðfvîêÕ«kJ‚‚‚~üñÇfÍš©£òŸÿüçÅ_ûìï¿]Ö˪U«Ö¨Q£ì鹺téÒñãÇÙ’‚)HÁòk7nÜøî»ïV­ZÕËý“““ÙÞtMËóäÝwß}å•WîÝ»W•Q¾1 ,`[Ò§OŪÏóû”’’2}út½é·oß>Y–=]FÂ>u‚«õFV4“à€{ ¿†¯µP .ÎŽ " „Åf‘j¼*΂ „Ø(2 ½I½ šNa•dA‚d…|OÒ._ƒëկ׾Cû¶Ä<Ò¶}ÕªUBɯW]„(’—cÖJ>_ÆSjcffæéÓ§7lذzõêÓ§O«åF£±^½z|å•WrÏ—«P¡Bhh(»­Q£FV.˜Êdddä+ëÁb±°.úÖ­[W­ZõÒK/yyxóæÍ?øàƒÁPHwéòåËìú„ƒNž]8å†l½ ¼Ò*P*   ¡DÙy%dB7Àº/7§¦ÀFDDD7nܸqt³èfÑͪW«®õý|I J\eRRR6mÚ$ËrfffvvvVVVjjêåË—¯^½zäÈÕkˆˆˆh×®]»víZµjÕ¬Y3o†DFDD|øá‡Ã‡Wlø:uêüïÿ+ÀO}Áâs­ZµŠŠŠò~ÿŠ+–/_>99gù‹ìììaÆ%''<ØËQ @Aí5•¿þúKm³Ùl4Ô/–‚" Ê—LE‹ÅR£F:uêFý7Ïb±è=JƒÀ| ÕØªÀ¬åÊ~ uÕ¤tÐ ¤vTÕܘœtAÒþ0(!©—L¨]F`…Y0&c’‘^!4ÀîˆËÈ4--M“%‚ÑhTæµZªT©òàƒV­R52*²~½ú6 ˆˆ(ýc I¾`%¬2 Ó§O ˲,Ë&“Éd2Æ   gžy¦zõêUªT©\¹råÊ•kÖ¬Y®\¹üÖÿæ›o6nÜø¯¿þ ïÒ¥‹¾¿YÞr)˜-“ߣ‚‚‚bbbØpLRRÒСC—.]úüóÏwëÖ­V­Z¹'SR_”ÇCÓ1sæÌbMâÐ6[]zM ¬°9»ªß „Ô&*ÊΜ] 2(…ÁQõ¬Ó2¼EòTª\-" "ÜXÑ,YŒ0 ”"Bì6ûéÓ§oÞ¼yïÞ=‚ DDDY,£ÑZ—åQoV¾æ÷ð‘ʨ¿~4h°iÓ&õ·ZpP„§nß¾}ûöí½ßÿÆšŸõ5¾üòË«W¯ÖÎ;7uêÔŸþYÓì³Ù:wîüÌ3ÏlÙ²EÿVFFÆñãÇ?¾lÙ2ƒÁݶmÛgŸ}¶U«VE%7V«•í’`2™ô•{ß¹à nTFÐÅbØ|_ ˜€(˜DcÅh%Fїʦðg«t~þ®úXysù"ip™%¿á=?²,{ú1, Kݸqc̘1:tHOO?~¼æ]ŸÙ2&“iΜ9M›6Í}7»Ý~óæÍ¸¸¸‰'¶oß¾K—.ÊPïÂ?öéééš™ÓÓÓÕ…ŠVb˜ŒÌoŒ¤óŒ‹!ŽL¼H˜ÌNG‰§ÄH%Y’$ésõ‘ †­ï¼iÞSßu©Ó“KLaPãq(Э/y•)Y233gΜ³eË–yóæ­^½ºAƒš¦úr¦¨Úµk¯[·.::ÚËý­Vk\\ܳÏ>û駟ʲ\ȇ?==]ãØ¦¤¤°=Óúïa(ØI#*2yCª¤»fHòS AbÉš0’,A¦OTˆùºõŒ­=vüïÑ™­¢bDÁÿ“kK9>ò˜”à®oÎå=›6m7nÜ•+WFŽùÖ[o)Á<}º‡/U@ݺuãââ,X0wî\ÿ≌ŒŒ>ú(99ùóÏ?¡À‚¢1ܲ²²nݺ¥Œg%¦mu´'¤;0˜e¤Á2¡0”cý9H²D@Ú—oùï†CºÔéY1°€ó%rXT …˜¤>³e$IÒÞ%K||üK/½ôÜsÏÕ¯_ÿ?þ3fL.ý>V‘‘‘ãÆÛ³gÏܹs{÷îéÍ þâ‹/æÎ[˜ói²Z²²²”áŽÅ$1ê׫o4:2ÖnJ> È¬«@ œ¨Æ‹â(IT’$©mh³…1³î¶å_M^çSxTÿ¨HdÙ2Jú–Û·|ìIeggÏ™3gòäÉ¢(®^½úùçŸÏóß«ŒBõêÕ‡ 2dÈ[·n:tèĉ‡:xðà¥K—4Yü*&LxöÙgkÕªU°«ªÄ•5c28ÀŽ´.òŽ­°°0‹Å’3ø;4(ï˜$\ÍV\§˜ 9† % C¢ÕÌ߬ÿæ ÆoT Ñ%ãö›S€@¯[JØ–ñ±'uðàÁN:½ûjÕÚµk—7F=Ù¹sç‘#G®X±âÈ‘#;wîüøãÝNoœ””´xñâŸÈl6ë×âøý÷ß³³³‹ÃŠQˆŠŠR&jH­ö+•ƒÉbpJ • Ó>UºÿðÔOãÚ~Ê%¦H`s/Qt·Ûw*ãVMr±qŠœ tîÜy×®]111ëׯ¯_¿¾—–”-ã‹Å3a„mÛ¶uïÞ]¿Ã¶mÛ¬Vk¾”RAyäMùáÇwíÚ…â‘ÁÁÁÎ Ée@¢À¦É°å§ÄHR c¥9­¾ø¦Ó²¶UybK@ ‚qK ÷1I’äiìØ±o¾ùfRRRxxø‚ ¼™0\Å7*sûöíÜ'²ÑP»ví+VÄÄÄhÊÏž=›ššZàf´iÓFóy%IZ²dI+ô†> n“Ë€Ýá+)挨“™P T’¤®•:®ë´þ?Íß 1Ý—3?•6ØD˜âø-ñʸµY|ã13fêÔ©Êv¿~ýr™…À-¾Q™E‹}÷Ýwù:$88xâÄ‰šœÆ´´4%/¾`4mÚTï‹­[·Ž]}!¿\»vmÓ¦M¹ìЦ]u%zHrô1 ä.11ЧÉ*ø¸Éû+žYÛ¢r›·‡£RÈD/ñ‘ÊØl6ýdà”q½Åzêo¿ýV*¥ ÷J ™™™K—.ÍïÕˆ‰‰ÑŒ2EQ?——PJË•+÷ꫯêÛ6zôhu¿ü²dÉ· N¨´kùpµÚŽ)2²€ƒ€Ü~ù˜O­ m™ÿ³•ÿ5paÛÙÚM-ïõ=¬‹T¬'òʸ}~¬V«[õ)*•Y Ô’<së¯xŸ®|a6›÷ìÙsòäÉ|\£F ¶¤B… Þϳç–þýû—/¯Í”Ý¿ÿˆ# ð“púôé+VtíÚ5—}*”ÿ×+Œ´~ùØ šª$0Éh–ÑêÕy¬QÁñ†â Á¸ÅG*“žžî6þbµZ½™ï²Àüþûï.\`KNœ8‘û!úõF4k‚ ȲüÍ7ßäë(ý0ÔjÕªxµòÍ«S§ŽÞœ°`Á‚áÇçëWáêÕ«½{÷nÛ¶­Kê;†½5¬FM‡\fG@uAªîz <‘·°x8Rñ‘ÊܹsÇmyqÛ2—/_Ö”¬Zµ*--ÍÓþ©©©úàÈ‘#GôqÙ;wêGož%K–>|Øûým6›fRñN:™ÍæÄÔÙ¯ÝØ±c6l¨ßgöìÙ={öôr¿?þøã™gž¹råʨQ£òܹR¥JÏ÷îåö-£Åøèc–/_þÞ{W§âx‰Ï¼$©Œ§YlSRR ªÌýde‡ž8q¢[Ëÿܹso¿ývëÖ­5qcÇŽiLŒk×®}õÕWÅqŸÒÒÒ>úè#ï“‹/*«Ê*{?½‹úµSþWªT髯¾r¦ä2lÞ¼¹cÇŽcÆŒ9tèÛä@I’Nž<9lذÎ;Ÿ:ujâĉÞ$ üúë¯q›Õ–† Ó°'wíß¹rŪäääü±tކ»/ð±—Äâ£Ü_O~JvvöÙ³gó½~»×T¯^]?§ÿŒ3.^¼8tèÐÆƬ¬¬3gÎÄÅÅ-]ºt̘1O<ñÄ×_ÍZX²,1âÀ½zõ ?}úôœ9s6lX‹RؼyóÚµkûöíëÍÎÛ·ogÃF#FŒÐöÌÄ(<ýôÓŸ}öÙ¨Q£ôµ%%%M›6í‹/¾hÙ²eóæÍ###+W®”’’rñâÅÇïÙ³Gùñèß¿ÿСCs?»,ËS§N2eŠþ÷¦WŸž]yö»™K\÷cbbâàÁƒ{öìYàØvYÆIyŸ¾XILLt›¨ªÐ¥K%k¦˜N­ Žª‚¤\úÑ£G˲|çÎjÕªyj­’l6›÷íÛÇžhãÆš=_xá…|5uÚ´iê±ÕªU;räHž‡$''³ݹsç{÷îQJeW”=5‹UEFFÞ¹s‡Rª¤e«{jøúë¯ üT÷èÑ#%%%÷‘‘ñÚk¯yªÁlÊ™>µcÇŽk×®ÍÎÎÎ×%åPÇ—½Ë¾§ØU&99¹OŸ>¹ÇŽk³ÙŠ©l7v.¨hSJß~ûíÜwþè£4gÑ,à¹çžËW;'MšÄþàƒj„LCvv6£íÛ·ïíÛ·©;‰¡”^»vM3ŸiDDDrr2¥4‰QX¹re.?žxçwÒÒÒrÿÈwïÞíÖ­›æÀèÖÑêɰ°°^½zýüóÏV«5_“CK‡¾(—ÊȲœ˜˜øý÷ß»]+VOçηoßž™™Yä-¹wï^¿~ýr9uóæÍ×®]ËríÚµ\šÝ­[·ÔÔTvÿ„„„.]ºhv _»v­Ò¹æ èÑ£ÛÑöå—_*‡~çÎ;+»Õ®]û믿V~çÕûÊî|÷îÝÑ£GkLeƒÁ „9¼áòåËÇ÷²ƒ¼eË–šëé–¤¤¤G}”=000ðË/¿ÌÈȘ4yR‡&MštåÊ/[ÈÑ | JƒÄȲ\Øuõ\¸paæÌ™<|øp~#» 6lÓ¦MµjÕ}ôÑN:U“233¿úê«yóæ]¿~]]i(  N:C† 8p ~÷÷ô îIDATÚ‡7nÌš5ë›o¾a;¶+V¬øïÿûý÷ßWÖÀ½téÒòåË;öÛo¿yZ=¶V­Z-[¶¬X±b¥J•š7oÞ£GÜã“'OîØ±c×®]{öìILLÌÌ̬\¹r§NêÖ­[µjU«ÕúÏ?ÿ8pàÀ’$=òÈ#={öìÙ³§2`B½•Êò&ëÖ­KLL¼råÊÑ£G=ÍSóøãW­Z5"""<<¼oß¾nÏS9zôh\\܆ Ž=ªOM(_¾|»víz÷îÝ»wï<§ÝNHHxá…Ø9†«T©2{öìçž{Ny){^¿‰“;´èfl(*Š^eΟ? Àd2å÷‹¢tl‹¢Ø AƒÜó¸ À;wN:uõêÕ´´´ÐÐÐ:uê4iÒ$÷Ô’K—.;vìÒ¥K6›­jÕª?ü0»Rí… Ö¯_o·ÛF£§à…ÍfS‚ C5zõêåå5IMM½téÒåË—/]º””””™™i³ÙL&S```XXXÆ ëÕ«WµjUökÄÞÊ#GŽlß¾Ýn·B”æé¿p²,ggg+ϳ(ŠÝºuÓÈÖ#Ëò™3g®\¹rûöíÄÄD»Ý®ˆTãÆóLŠQ8~üø+¯¼ÂvDGGóÍ7-[¶ôæp Ô·²¥ÅA©Ñ…¢WNIÁÞÊRõ%cÙ½{÷€.]º¤–<ûì³óæÍË%âž 2d’ç‚eêó\;ïá*ã?ä÷Vúþë¸yóæAƒ±NèàÁƒgÍšå¯k0ú†Ò¬/ Üõ-£øþùÃ? 0€•˜!C†,\¸KLQc½(ÅÖ+¸Êp|Ãwß}÷Úk¯±M† 6gΜ|Eî((7½î}Qà*S¶(‘ïåüùó‡Ê{ï½÷fΜ™¯fPP;µS·+Ô–1T‰)ýú¢ÀU¦láû1,3gÎ|çwØacÇŽýüóÏó[1£PÆ¿±”Ê%0©0”í{VÆð½»1cÆŒQ£F±#Ú?úè£)S¦ø¸þ¥T†LÁ,SuŸÀž•!|ü8eÊ”?þ˜w:iÒ¤?üÐg ð(¥1)÷]Ç=WN±ðÉ'ŸLœ8Qv̰E™:uêèÑ£K¶U÷¬á)C¦ 2¨HîËI¼¸Ê”|,¤”Ž?~Ê”)ês"ÂçŸ>bĜݟ  Ž?”ˆD¼Oó¹Ê”|ã.QJÇŽËÎba0¾øâ‹<‡¹sôHT’‰D(!î_‰W™2‚o Y–G=cÆ µÄl6Ïœ9ó7Þ(îSû„D¦¸ß%\eÊ>0d$I9rä—_~©–Ì™3gРAÅz^?F€(‚ ÷¹Ä€« §H$éÝwß9s¦Z8oÞ¼”`«üá~—p•áI’FŒ1kÖ,µ$((háÂ…/¿ür ¶Ê?ˆ?d´ùÃgà” z‰ ùöÛo¹Äu˜’ÀU¦¬P_YJéÈ‘#Y‰)W®ÜâÅ‹óœé™“;÷×0¥<á§à|ðÁl¸·|ùò‹/VgÕä ?“p•)Síw÷ÓO?:uªú2,,ì»ï¾ëÞ½{QÕ_–ñ'‰Ÿ+ÏŸðþVê¿ÄìÌäÞÔ0mÚ´±cǪGU¨Paùòåê² œBâgO%Wÿ!÷[™ËÌ24Ÿ«šj$&""bõêÕ;vÌg{9¹áO&WÿÁ›[©:Mš©È½—˜¯¿þúwÞQWòŽŠŠZ·n]Û¶móß^NnøÓƒÉUÆ(Ø­ÌWà»ï¾ûÏþ“™™©¼Œˆˆˆm×®]ÎËÉ¿y6¹Êø^ÞÊüúG*±±± P'Ö [¹rå3Ï<“ßz8ÞàO&Wÿ!Ï[Y`}ðÇ<÷ÜsêšJ^L¯^½ PÇüéÁä*ã?xº•jÜ·Àý£gÏžíÖ­Ûùóç•—AAA‹/~ñÅ V'Oüì©äù2þO!d_¿~}À€ªÄƹsçr‰)&üL_¸Êø!jGRá“»dY3fÌþýû•—‚ |òÉ'|¤u1á—®2~I¾–RËÉ“'/]ºT}ùÁð¹{‹vl$!ÄÏFð¸ŒÿPä+º­^½º_¿~jjÌ;ï¼3}út£ÑXTõsô7Îφ2q•ñŠVeΞ=ûôÓO_¹rEy9hРyóæ™L¦"©œ“'þ$4\eü‡"T™7ntíÚõðáÃÊËîÝ»/[¶,44´ð5s¼ÇožM>¿ G‹Íf6l˜*1O=õ—Naà*ãWɯßÔ©S×®]«l׬Ysþüù\b8…{LþƒêÉÆiŠ‹‹ëÑ£‡Õj²yóæöíÛ]9ùÀožMÞ“íW( š¨¡÷¢sëÖ­ÿþ÷¿ŠÄ˜4i—˜’Ÿ¢¿Ücò74ú’¯¯éäÉ“ããã•í×^{mذaEÜ8Žwø“Ä€{Lþ„r+ ì4ýòË/={ö´Ùl:vìøóÏ?‡„„}+9¹ÂÞÄ’nK‘ÁUÆe¹À ²²²:vìø×_¨]»öÖ­[ëÖ­[ôMäxÆŸD—ñ 3°`Ù²e{öì`±XæÍ›Ç%ÆÇø™‹¤Û2\¼xñᇾyó&!dêÔ©|¤’ïñïÇÛ2eI’FŽyóæM9rdI·¨tAA%j' DÛ&~lÈ€Û2œÕ«W¿ôÒKjÖ¬ùçŸV©R¥¤[TŠ ”J°KT‰(AqILqT[zà*S¦IIIyøá‡OŸ> `åÊ•ŠÜp(¥ (&eq9‹¿ÃóeÊ4ëׯW$æå—_æÓßi „ø@bÊ‚Êp[¦ì’––öè£9r¤aÆ¿ýö[åÊ•KºEeŽ2òôq[¦ì²zõê#GŽ„„„|ûí·\b|O‘p•)³X­Öyóæ=z4_’S¬p©ŒÛ«W¯ÆïÙ³'((¨¤›Sæ(SÏ·eÊ"²,Ïž=›RúÎ;ïp‰ñ=e$è«Âm™²È™3g¢£££¢¢Ž=Z®\¹’nNÙ¿¸…Û2e‘Ý»wggg÷èуKŒ)òu&î ¸Ê”E’’’!ݺu+醔9Êš¾(p•)sPJ÷ïßo2™bbbJº-e‘2(4|´dY¤mÛ¶•+WæqߤL…fxô·ŒR¦¾å¥²æS¥ì|Å9%W§(S*ÏU†Ãñ)e0FÁU†ÃñePbÀU†Ãñ ”RY–Kº%ïcâpŠ2þ”q•áp8Å ÷˜8NñÂU†Ãá/\e8NñÂU†Ãá/\e8NñÂU†Ãá/\e8NñÂU†Ãá/\e8NñÂU†Ãá/ÿRû àh”"nIEND®B`‚parslet-2.0.0/website/build/index.html000066400000000000000000000065751362225661100177420ustar00rootroot00000000000000 parslet - About

About


  require 'parslet'
  include Parslet

  # Constructs a parser using a Parser Expression Grammar 
  parser =  str('"') >> 
            (
              str('\\').ignore >> any |
              str('"').absent? >> any
            ).repeat.as(:string) >> 
            str('"')

  result = parser.parse %Q("this is a valid \\"string\\"")
  result # => {:string=>"this is a valid \"string\""@1}

A small Ruby library for constructing parsers in the PEG (Parsing Expression Grammar) fashion.

Parslet makes developing complex parsers easy. It does so by

  • providing the best error reporting possible
  • not generating reams of code for you to debug

Parslet takes the long way around to make your job easier. It allows for incremental language construction. Often, you start out small, implementing the atoms of your language first; parslet takes pride in making this possible.

Eager to try this out? Get started!

parslet-2.0.0/website/build/install.html000066400000000000000000000047641362225661100202770ustar00rootroot00000000000000 parslet - Install

Install

Parslet is at version 2.0.0.

Rubygems

  gem install parslet

Bundler

  gem 'parslet', '~> 2.0'

or if you want to track the edge:

  gem 'parslet', :git => 'git://github.com/kschiess/parslet.git'
parslet-2.0.0/website/build/javascripts/000077500000000000000000000000001362225661100202615ustar00rootroot00000000000000parslet-2.0.0/website/build/javascripts/sh_main.min.js000066400000000000000000000122711362225661100230220ustar00rootroot00000000000000/* Copyright (C) 2007, 2008 gnombat@users.sourceforge.net */ /* License: http://shjs.sourceforge.net/doc/gplv3.html */ if(!this.sh_languages){this.sh_languages={}}var sh_requests={};function sh_isEmailAddress(a){if(/^mailto:/.test(a)){return false}return a.indexOf("@")!==-1}function sh_setHref(b,c,d){var a=d.substring(b[c-2].pos,b[c-1].pos);if(a.length>=2&&a.charAt(0)==="<"&&a.charAt(a.length-1)===">"){a=a.substr(1,a.length-2)}if(sh_isEmailAddress(a)){a="mailto:"+a}b[c-2].node.href=a}function sh_konquerorExec(b){var a=[""];a.index=b.length;a.input=b;return a}function sh_highlightString(B,o){if(/Konqueror/.test(navigator.userAgent)){if(!o.konquered){for(var F=0;FI){x(g.substring(I,E.index),null)}var e=O[u];var J=e[1];var b;if(J instanceof Array){for(var L=0;L0){var e=b.split(" ");for(var c=0;c0){a.push(e[c])}}}return a}function sh_addClass(c,a){var d=sh_getClasses(c);for(var b=0;b element with class="'+h+'", but no such language exists'}}break}}}};parslet-2.0.0/website/build/javascripts/sh_ruby.min.js000066400000000000000000000022671362225661100230630ustar00rootroot00000000000000if(!this.sh_languages){this.sh_languages={}}sh_languages.ruby=[[[/\b(?:require)\b/g,"sh_preproc",-1],[/\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g,"sh_number",-1],[/"/g,"sh_string",1],[/'/g,"sh_string",2],[/|\|/g,"sh_symbol",-1],[/(#)(\{)/g,["sh_symbol","sh_cbracket"],-1],[/#/g,"sh_comment",5],[/\{|\}/g,"sh_cbracket",-1]],[[/$/g,null,-2],[/\\(?:\\|")/g,null,-1],[/"/g,"sh_string",-2]],[[/$/g,null,-2],[/\\(?:\\|')/g,null,-1],[/'/g,"sh_string",-2]],[[/$/g,null,-2],[/>/g,"sh_string",-2]],[[/^(?:\=end)/g,"sh_comment",-2]],[[/$/g,null,-2]]];parslet-2.0.0/website/build/javascripts/toc.js000066400000000000000000000050251362225661100214060ustar00rootroot00000000000000/*! * toc - jQuery Table of Contents Plugin * v0.3.2 * http://projects.jga.me/toc/ * copyright Greg Allen 2014 * MIT License */ !function(a){a.fn.smoothScroller=function(b){b=a.extend({},a.fn.smoothScroller.defaults,b);var c=a(this);return a(b.scrollEl).animate({scrollTop:c.offset().top-a(b.scrollEl).offset().top-b.offset},b.speed,b.ease,function(){var a=c.attr("id");a.length&&(history.pushState?history.pushState(null,null,"#"+a):document.location.hash=a),c.trigger("smoothScrollerComplete")}),this},a.fn.smoothScroller.defaults={speed:400,ease:"swing",scrollEl:"body,html",offset:0},a("body").on("click","[data-smoothscroller]",function(b){b.preventDefault();var c=a(this).attr("href");0===c.indexOf("#")&&a(c).smoothScroller()})}(jQuery),function(a){var b={};a.fn.toc=function(b){var c,d=this,e=a.extend({},jQuery.fn.toc.defaults,b),f=a(e.container),g=a(e.selectors,f),h=[],i=e.activeClass,j=function(b,c){if(e.smoothScrolling&&"function"==typeof e.smoothScrolling){b.preventDefault();var f=a(b.target).attr("href");e.smoothScrolling(f,e,c)}a("li",d).removeClass(i),a(b.target).parent().addClass(i)},k=function(){c&&clearTimeout(c),c=setTimeout(function(){for(var b,c=a(window).scrollTop(),f=Number.MAX_VALUE,g=0,j=0,k=h.length;k>j;j++){var l=Math.abs(h[j]-c);f>l&&(g=j,f=l)}a("li",d).removeClass(i),b=a("li:eq("+g+")",d).addClass(i),e.onHighlight(b)},50)};return e.highlightOnScroll&&(a(window).bind("scroll",k),k()),this.each(function(){var b=a(this),c=a(e.listType);g.each(function(d,f){var g=a(f);h.push(g.offset().top-e.highlightOffset);var i=e.anchorName(d,f,e.prefix);if(f.id!==i){a("").attr("id",i).insertBefore(g)}var l=a("").text(e.headerText(d,f,g)).attr("href","#"+i).bind("click",function(c){a(window).unbind("scroll",k),j(c,function(){a(window).bind("scroll",k)}),b.trigger("selected",a(this).attr("href"))}),m=a("
  • ").addClass(e.itemClass(d,f,g,e.prefix)).append(l);c.append(m)}),b.html(c)})},jQuery.fn.toc.defaults={container:"body",listType:"
      ",selectors:"h1,h2,h3",smoothScrolling:function(b,c,d){a(b).smoothScroller({offset:c.scrollToOffset}).on("smoothScrollerComplete",function(){d()})},scrollToOffset:0,prefix:"toc",activeClass:"toc-active",onHighlight:function(){},highlightOnScroll:!0,highlightOffset:100,anchorName:function(c,d,e){if(d.id.length)return d.id;var f=a(d).text().replace(/[^a-z0-9]/gi," ").replace(/\s+/g,"-").toLowerCase();if(b[f]){for(var g=2;b[f+g];)g++;f=f+"-"+g}return b[f]=!0,e+"-"+f},headerText:function(a,b,c){return c.text()},itemClass:function(a,b,c,d){return d+"-"+c[0].tagName.toLowerCase()}}}(jQuery);parslet-2.0.0/website/build/overview.html000066400000000000000000000141131362225661100204640ustar00rootroot00000000000000 parslet - Overview

      Overview

      Parslet is a library with a clear philosophy: It makes parser writing easy and testable. On top of that, it provides understandable error messages (“General Protection Fault”) to you, the language writer. In extension, you will hopefully manage to provide good error messages to your users. Together, we can create a better world!

      Traditional texts on the subject will have you write a compiler or interpreter for a language in several stages:

      • Parsing or Lexing/Parsing
      • Abstract Syntax Tree construction
      • Optimization and checking of the tree
      • Generation of code / Execution

      This library will be good for the first two stages only. After that, you’ll be on your own.

      The parsing step has literally been implemented by hundreds (thousands) of clever people; No lack of alternatives. Even in Ruby, you’ll have the choice among a handful of libraries. There’s a distinction to make on this level as well:

      LRk parsers and related fields Parsers in this class use a lexical analyzer (a lexer ) to transform the input text into tokens (tokenizing ). They then check if your stream of tokens has a corresponding tree that conforms to the grammar. Most of these parsers allow grammars that are ambiguous and will provide a mechanism for resolving the arising ambiguities. These are the earliest parser generators and the most widely used (yacc, bison, …) — chances are, you’ll have more than one of these libraries installed on your system.

      PEG or packrat parsers Parsers in this class are based on a slightly more modern algorithm, which translates what one does when writing a parser by hand in top-down fashion. This is what we programmers do all over, methods calling methods – and its also (grossly) what these parsers do to recognize input. Left recursion is impossible to express in these grammars. No lexers are required – lexical tokenizing and parsing are one single step. Ruby has several implementations of this algorithm in library form: Treetop, Citrus, rsec and of course Parslet.

      All of these generators are different in small ways; yet most implement common patterns and provide almost identical APIs. We believe this is an error: choice is good, but there should be visible attributes distinguishing the choices.

      Parslet is not like the others, in fact it is radically different on some key elements.

      Writing a language

      Whether you write a language for a configuration file or a new computer language (the holy grail), the steps are always the same:

      1. Create a grammar: What should be legal syntax?
      2. Annotate the grammar: What is important data?
      3. Create a transformation: How do I want to work with that data?

      The creation of grammars and the various concepts that are associated are treated in Parslet::Parser.

      Transformation of the resulting intermediary tree is treated in Parslet::Transform

      parslet-2.0.0/website/build/parser.html000066400000000000000000000350061362225661100201160ustar00rootroot00000000000000 parslet - Parser construction

      Parser construction

      A parser is nothing more than a class that derives from Parslet::Parser. The simplest parser that one could write would look like this:

      
        class SimpleParser < Parslet::Parser
          rule(:a_rule) { str('simple_parser') }
          root(:a_rule)
        end
      

      The language recognized by this parser is simply the string “simple_parser”. Parser rules do look a lot like methods and are defined by

      
        rule(name) { definition_block }
      

      Behind the scenes, this really defines a method that returns whatever you return from it.

      Every parser has a root. This designates where parsing should start. It is like an entry point to your parser. With a root defined like this:

      
        root(:my_root)
      

      you create a #parse method in your parser that will start parsing by calling the #my_root method. You’ll also have a #root (instance) method that is an alias of the root method. The following things are really one and the same:

      
        SimpleParser.new.parse(string)
        SimpleParser.new.root.parse(string)
        SimpleParser.new.a_rule.parse(string)
      

      Knowing these things gives you a lot of flexibility; I’ll explain why at the end of the chapter. For now, just let me point out that because all of this is Ruby, your favorite editor will syntax highlight parser code just fine.

      Atoms: The inside of a parser

      Matching strings of characters

      A parser is constructed from parser atoms (or parslets, hence the name). The atoms are what appear inside your rules (and maybe elsewhere). We’ve already encountered an atom, the string atom:

      
        str('simple_parser')
      

      This returns a Parslet::Atoms::Str instance. These parser atoms all derive from Parslet::Atoms::Base and have essentially just one method you can call: #parse. So this works:

      
        str('foobar').parse('foobar') # => "foobar"@0
      

      The atoms are small parsers that can recognize languages and throw errors, just like real Parslet::Parser subclasses.

      Matching character ranges

      The second parser atom you will have to know about allows you to match character ranges:

      
        match('[0-9a-f]')
      

      The above atom would match the numbers zero through nine and the letters ‘a’ to ‘f’ – yeah, you guessed right – hexadecimal numbers for example. The inside of such a match parslet is essentially a regular expression that matches a single character of input. Because we’ll be using ranges so much with #match and because typing (‘[]’) is tiresome, here’s another way to write the above #match atom:

      
        match['0-9a-f']
      

      Character matches are instances of Parslet::Atoms::Re. Here are some more examples of character ranges:

      
        match['[:alnum:]']      # letters and numbers
        match['\n']             # newlines
        match('\w')             # word characters
        match('.')              # any character
      

      The wild wild #any

      The last example above corresponds to the regular expression /./ that matches any one character. There is a special atom for that:

      
        any 
      

      Composition of Atoms

      These basic atoms can be composed to form complex grammars. The following few sections will tell you about the various ways atoms can be composed.

      Simple Sequences

      Match ‘foo’ and then ‘bar’:

      
        str('foo') >> str('bar')    # same as str('foobar')
      

      Sequences correspond to instances of the class Parslet::Atoms::Sequence.

      Repetition and its Special Cases

      To model atoms that can be repeated, you should use #repeat:

      
        str('foo').repeat
      

      This will allow foo to repeat any number of times, including zero. If you look at the signature for #repeat in Parslet::Atoms::Base, you’ll see that it has really two arguments: min and max. So the following code all makes sense:

      
        str('foo').repeat(1)      # match 'foo' at least once
        str('foo').repeat(1,3)    # at least once and at most 3 times
        str('foo').repeat(0, nil) # the default: same as str('foo').repeat
      

      Repetition has a special case that is used frequently: Matching something once or not at all can be achieved by repeat(0,1), but also through the prettier:

      
        str('foo').maybe          # same as str('foo').repeat(0,1)
      

      These all map to Parslet::Atoms::Repetition. Please note this little twist to #maybe:

      
        str('foo').maybe.as(:f).parse('')         # => {:f=>nil}
        str('foo').repeat(0,1).as(:f).parse('')   # => {:f=>[]}
      

      The ‘nil’-value of #maybe is nil. This is catering to the intuition that foo.maybe either gives me foo or nothing at all, not an empty array. But have it your way!

      Alternation

      The most important composition method for grammars is alternation. Without it, your grammars would only vary in the amount of things matched, but not in content. Here’s how this looks:

      
        str('foo') | str('bar')   # matches 'foo' OR 'bar'
      

      This reads naturally as “‘foo’ or ‘bar’”.

      Operator precedence

      The operators we have chosen for parslet atom combination have the operator precedence that you would expect. No parenthesis are needed to express alternation of sequences:

      
        str('s') >> str('equence') | 
          str('se') >> str('quence')
      

      And more

      Parslet atoms are not as pretty as Treetop atoms. There you go, we said it. However, there seems to be a different kind of aesthetic about them; they are pure Ruby and integrate well with the rest of your environment. Have a look at this:

      
        # Also consumes the space after important things like ';' or ':'. Call this
        # giving the character you want to match as argument: 
        #
        #   arg >> (spaced(',') >> arg).repeat
        #
        def spaced(character)
          str(character) >> match['\s']
        end
      

      or even this:

      
        # Turns any atom into an expression that matches a left parenthesis, the 
        # atom and then a right parenthesis.
        #
        #   bracketed(sum)
        #
        def bracketed(atom)
          spaced('(') >> atom >> spaced(')')
        end
      

      You might say that because parslet is just plain old Ruby objects itself (PORO ™), it allows for very tight code. Module inclusion, class inheritance, … all your tools should work well with parslet.

      Tree construction

      By default, parslet will just echo back to you the strings you feed into it. Parslet will not generate a parser for you and neither will it generate your abstract syntax tree for you. The method #as(name) allows you to specify exactly how you want your tree to look like:

      
        str('foo').parse('foo')             # => "foo"@0
        str('foo').as(:bar).parse('foo')    # => {:bar=>"foo"@0}
      

      So you think: #as(name) allows me to create a hash, big deal. That’s not all. You’ll notice that annotating everything that you want to keep in your grammar with #as(name) autocreates a sensible tree composed of hashes and arrays and strings. It’s really somewhat magic: Parslet has a set of clever rules that merge the annotated output from your atoms into a tree. Here are some more examples, with the atom on the left and the resulting tree (assuming a successful parse) on the right:

      
        # Normal strings just map to strings
        str('a').repeat                         "aaa"@0                                 
      
        # Arrays capture repetition of non-strings
        str('a').repeat.as(:b)                  {:b=>"aaa"@0}                           
        str('a').as(:b).repeat                  [{:b=>"a"@0}, {:b=>"a"@1}, {:b=>"a"@2}] 
      
        # Subtrees get merged - unlabeled strings discarded
        str('a').as(:a) >> str('b').as(:b)      {:a=>"a"@0, :b=>"b"@1}                  
        str('a') >> str('b').as(:b) >> str('c') {:b=>"b"@1}                             
      
        # #maybe will return nil, not the empty array
        str('a').maybe.as(:a)                   {:a=>"a"@0}                             
        str('a').maybe.as(:a)                   {:a=>nil}
      

      Capturing input

      Advanced reading material – feel free to skip this.

      Sometimes a parser needs to match against something that was already matched against. Think about Ruby heredocs for example:

      
        str = <<-HERE
          This is part of the heredoc.
        HERE
      

      The key to matching this kind of document is to capture part of the input first and then construct the rest of the parser based on the captured part. This is what it looks like in its simplest form:

      
        match['ab'].capture(:capt) >>               # create the capture
          dynamic { |s,c| str(c.captures[:capt]) }  # and match using the capture
      

      This parser matches either ‘aa’ or ‘bb’, but not mixed forms ‘ab’ or ‘ba’. The last sample introduced two new concepts for this kind of complex parser: the #capture(name) method and the dynamic { ... } code block.

      Appending #capture(name) to any parser will capture that parsers result in the captures hash in the parse context. If and only if the parser match['ab'] succeeds, it stores either ‘a’ or ‘b’ in context.captures[:capt].

      The only way to get at that hash during the parse process is in a dynamic { ... } code block. (for reasons that are out of the scope of this document) In such a block, you can:

      
        dynamic { |source, context|
          # construct parsers by using randomness
          rand < 0.5 ? str('a') : str('b')
          
          # Or by using context information 
          str( context.captures[:capt] )
          
          # Or by .. doing other kind of work (consumes 100 chars and then 'a')
          source.consume(100)
          str('a')
        }
      

      Scopes

      What if you want to parse heredocs contained within heredocs? It’s turtles all the way down, after all. To be able to remember what string was used to construct the outer heredoc, you would use the #scope { ... } block that was introduced in parslet 1.5. Like opening a Ruby block, it allows you to capture results (assign values to variables) to the same names you’ve already used in outer scope – without destroying the outer scopes values for these captures!.

      Here’s an example for this:

      
        str('a').capture(:a) >> scope { str('b').capture(:a) } >> 
          dynamic { |s,c| str(c.captures[:a]) }
      

      This parses ‘aba’ – if you understand that, you understand scopes and captures. Congrats.

      And more

      Now you know exactly how to create parsers using Parslet. Your parsers will output intricate structures made of endless arrays, complex hashes and a few string leftovers. But your programming skills fail you when you try to put all this data to use. Selecting keys upon keys in hash after hash, you feel like a cockroach that has just read Kafka’s works. This is no fun. This is not what you signed up for.

      Time to introduce you to Parslet::Transform and its workings.

      parslet-2.0.0/website/build/projects.html000066400000000000000000000130061362225661100204470ustar00rootroot00000000000000 parslet - Projects

      Projects

      Have you got a project that uses parslet? Please write us about it.

      edn-ruby

      edn-ruby is a Ruby library to read and write edn- format/edn (extensible data notation), a subset of Clojure used for transferring data between applications, much like JSON, YAML, or XML.

      Examples

      In here, you can find a parser for a lisp like language and much more.

      Net::HTTP::Server

      A really small and elegant HTTP server written in Ruby. Think Webrick. Using parslet. (Postmodern)

      regexador

      An external DSL for Ruby that tries to make regular expressions readable and maintainable. (Hal Fulton)

      self-ml

      A self-ml implementation using parslet. (Ricardo Mendes)

      thnad

      Thnad is a tiny programming language with so few features that it is not useful for anything at all — except showing how to write a compiler in half an hour.

      Unitwise

      A units of measure library for Ruby. Part of it’s magic comes from supporting the “Unified Code for Units of Measure”, which has a formal grammar, described here. The grammar is sufficiently complicated that it can’t be solved with regular expressions (mostly because the grammar is recursive), and was therefore better suited for a PEG. (Josh Lewis)

      Werd.rb

      A variant of Chris Pound’s word generator written in Ruby, with some improvements. (Robert Kosek)

      versionub

      A semantic version parser. (meh)

      shortcode

      A ruby gem for parsing Wordpress style shortcodes using parslet. (Jamie Dyer)

      Simple Java Parser

      A simple Java parser that can parse the overall structure of a Java file.

      crystal

      A ruby virtual machine in 100% ruby, that tries to parse most of ruby syntax using parslet.

      parslet-2.0.0/website/build/stylesheets/000077500000000000000000000000001362225661100203045ustar00rootroot00000000000000parslet-2.0.0/website/build/stylesheets/sh_whitengrey.css000066400000000000000000000045071362225661100237030ustar00rootroot00000000000000pre.sh_sourceCode { background-color: #ffffff; color: #696969; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_keyword { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_type { color: #696969; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_string { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_regexp { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_specialchar { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_comment { color: #1326a2; font-weight: normal; font-style: italic; } pre.sh_sourceCode .sh_number { color: #bb00ff; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_preproc { color: #470000; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_function { color: #000000; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_url { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_date { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_time { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_file { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_ip { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_name { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_variable { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_oldfile { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_newfile { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_difflines { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_selector { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_property { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_value { color: #008800; font-weight: normal; font-style: normal; } parslet-2.0.0/website/build/stylesheets/site.css000066400000000000000000000034471362225661100217720ustar00rootroot00000000000000body { font-size: 17px; font-family: Helvetica; background-color: white; } a { color: black; } .main_menu { font-size: 1.2em; width: 50em; margin-bottom: 1cm; } .main_menu ul { display: inline-block; list-style-type: none; font-size: 1.33em; margin-left: -8em; } .main_menu ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } * html .main_menu ul { height: 1px; } .main_menu li { float: left; font-variant: small-caps; padding-left: 1em; } .main_menu a, .main_menu:visited { text-decoration: none; color: #7c7c7c; } .content { font-size: 1em; width: 50em; line-height: 1.4em; margin-left: 1cm; } .content p { margin: 1em; } .content img { margin-left: 3em; } .copyright { font-variant: small-caps; font-size: 0.6em; color: #00ad00; margin-left: 22em; margin-top: 3cm; } .copyright a { text-decoration: none; color: #00ad00; } body.code code { font-size: 0.9em; font-family: "Monaco", monospace; } body.code code .sh_keyword { color: #761A47; } body.code code .sh_comment { color: #686868; } body.code code .sh_string { color: #42aa7a; background-color: #edf7f7; } body.code code .sh_constant { color: #551e03; } body.code code .sh_method { color: #4679a9; } body.code code .sh_symbol { color: #551e03; } body.code code .sh_number { color: #42aa7a; } body.code pre { font-size: 1em; font-family: "Monaco", monospace; background-color: #f8f8f8; color: #4679a9; line-height: 1.1em; margin-left: 1em; padding: -1em 0 -1em 0; } body.code pre.sh_sourceCode { border-radius: 0.5em; background-color: #f8f8f8; padding-bottom: 1.4em; } #toc li { list-style-type: lower-alpha; } parslet-2.0.0/website/build/transform.html000066400000000000000000000316501362225661100206360ustar00rootroot00000000000000 parslet - Transformation

      Transformation

      Parslet parsers output deep nested hashes. Those are nice for printing, but hard to work with. The structure of the nested hashes is determined by the grammar and can thus vary largely. Testing for the presence of individual keys would produce code that is hard to read and maintain.

      This is why parslet also comes with a hash transformation engine. To construct such a transform, you have to derive from Parslet::Transform:

      
        class MyTransform < Parslet::Transform
          rule('a') { 'b' }
        end
        MyTransform.new.apply('a') # => "b"
      

      This is a transformation that replaces all ’a’s with ’b’s. A transformation rule has two parts: A pattern (here: 'a') and an action block ({ 'b' }).

      The engine will go through the input and traverse the tree in depth-first post-order fashion. This means that for a given tree node, it will first visit the children and only then look at the node itself. While traversing, all rules are tested in the order in which they are defined. If a rule matches, the corresponding tree is replaced by whatever the action block returns.

      Here’s another way of saying the same thing, perhaps more in line with what you need as a user of Parslet: Parslet::Transform is what allows you to transform the PORO-trees magically into a real abstract syntax tree. The rule definitions are the futuristic nano-machines that act on tree leaves first, eating them away and replacing them with contraptions of your own design. Here’s how that might look like in Ruby:

      
        tree = {:left => {:int => '1'}, 
                :op   => '+', 
                :right => {:int => '2'}}
              
        class Trans < Parslet::Transform
          rule(:int => simple(:x)) { Integer(x) }
        end
        Trans.new.apply(tree)     # => {:left=>1, :op=>"+", :right=>2}
      

      You can start thinking about the leaves first, transforming those :int => '1' into real Ruby integers. This incremental (test driven!) approach will prevent your intermediary tree from turning into grey goo from too many nano-machines. Rules should in general be simple and transform a small part of the tree into a more useful variant. Turns out that if we were looking for an interpreter, one more rule will give us evaluation:

      
        tree = {:left => {:int => '1'}, 
                :op   => '+', 
                :right => {:int => '2'}}
      
        class Trans < Parslet::Transform
          rule(:int => simple(:x)) { Integer(x) }
          rule(:op => '+', :left => simple(:l), :right => simple(:r)) { l + r }
        end
        Trans.new.apply(tree)     # => 3
      

      Cool, isn’t it? To recap: parslet intentionally spits out deep nested hashes, because it also gives you the tool to work with those. Turning the intermediary trees into something useful is really easy.

      Working with Captures

      What is this simple(symbol) business all about, you might ask. Glad you do.

      Simple captures

      Transform allows you to specify patterns that have wildcards in them. The wildcards match part of the tree, but at the same time capture it for working on it in your action block. The wildcard

      
        simple(:x)
      

      will match any object BUT hashes or arrays. While this is obviously useful for capturing strings, you can also capture other ‘simple’ (as opposed to composed) objects of your own creation. simple(:x) would thus match all of these objects:

      
        "a string"
        123
        Foo.new(:some, :class, :instance)
      

      If you think about what you’ll be doing to your intermediary trees, replacing leaves with more useful objects, simple really makes good sense, since it will stop you from matching entire subtrees.

      Matching Repetitions and Sequences

      Some patterns (like repetitions and sequences) produce arrays of objects as result. You can use simple(...) to replace all parts of these arrays with your own objects, but you cannot replace the array as a whole. This is the purpose of sequence(symbol):

      
        sequence(:x)
      

      will match all of these:

      
        ['a', 'b', 'c']
        ['a', 'a', 'a']
        [Foo.new, Bar.new]
      

      but not

      
        [{:a => :b}]
        [['a', 'b']]
      

      Like its smaller brother, sequence is very picky about what it consumes and what not. All for the same reasons.

      Matching entire subtrees

      So you don’t want to listen and really want that big gun with the foot aiming addon. You’ll be needing subtree(symbol). It always matches. Nuff said.

      Matching context

      A match always binds in a context. The context consists of all bindings that were previously made. If you reuse the same symbol for two consecutive matches within the same pattern, the engine will assume that you want these two matched objects to be equal (under ==). This allows to specify constraints on your matches that would need code to express otherwise:

      
        # The following code is an excerpt from example/simple_xml.rb in the distro
        t.rule(
          open: {name: simple(:tag)}, 
          close: {name: simple(:tag)}, 
          inner: simple(:t)
        ) { 'verified' }
      

      This replaces matching open and close tags with the word ‘verified’, consuming them from the tree and allowing the same rule to match higher up. A valid XML tree will leave only the word ‘verified’ behind, while the parser will stop at the problem nodes in invalid trees.

      Transformation rules

      In this chapter, we’ll look more closely at transformation rules and the different ways they can be laid out in your code.

      Usage Patterns

      The way the transformation engine is constructed, there is not one, but three ways to use it. Since at least one of those is inconvenient for you, the user, I am going to show only the remaining two, Variant 1 that produces an instance of the transform for direct use:

      
        # Variant 1
        transform = Parslet::Transform.new do
          rule(...) { ... } 
          rule(...) { ... } 
          rule(...) { ... } 
        end
        transform.apply(tree)
      

      and Variant 2 that allows constructing the transformation as a class:

      
        # Variant 2
        class MyTransform < Parslet::Transform
          rule(...) { ... } 
          rule(...) { ... } 
          rule(...) { ... } 
        end
        MyTransform.new.apply(tree)
      

      I guess both have their sweet spot.

      Action blocks: Two flavors

      As you might have noticed by now, parslet provides choice as well as nice parsers. To recap: Rules have a left side called pattern and a right side called action block:

      
        rule(PATTERN) {ACTION_BLOCK}
      

      There are two ways of writing action blocks, and the difference might be fundamental to know to you one day. If written like this:

      
        rule(:foo => simple(:x)) { puts x }
      

      the block will be able to access x as a local variable. This is very convenient and shortens the action code, often to the point of being very expressive.

      But there is a big downside to this way of writing things: The action block must be executed in the context of some magic instance that has x as a local method (aka accessor). You can only have one self at any one time; variable access to the binding of the block isn’t possible inside this kind of action blocks:

      
        y = 12
        rule(:foo => simple(:x)) { Integer(x) + y }
      

      This will (depending on the context) throw a NameError or a NoMethodError.

      But this can be fixed by using the other, less elegant style for action blocks:

      
        y = 12
        rule(:foo => simple(:x)) { |dictionary| Integer(dictionary[:x]) + y }
      

      In this second flavor, the block gets executed in the context of definition, whatever that was. This means that it can capture and access local variables just fine. Access to the bindings (called dictionary here) is more clumsy, but hey, you can’t have your cake and eat it too, I guess. Even though that is a pity.

      A word on patterns

      Given the PORO hash

      
        { 
          :dog => 'terrier', 
          :cat => 'suit' }
      

      one might assume that the following rule matches :dog and replaces it by 'foo':

      
        rule(:dog => 'terrier') { 'foo' }
      

      This is frankly impossible. How would 'foo' live besides :cat => 'suit' inside the hash? It cannot. This is why hashes are either matched completely, cats n’ all, or not at all.

      Transformations are there for one thing: Getting out of the hash/array/slice mess parslet creates (on purpose) into the realm of your own beautifully crafted AST classes. Such AST nodes will generally correspond 1:1 to hashes inside your intermediary tree.

      If transformations get you into a mess, remember this simple truth: They have been designed for the above purpose. Abusing them is fun (and almost all the examples in the project do so) but the mess you get when you do is all yours.

      If you are really desperate, try to look at the example in Get Started or at the parser in the sample project wt. Imitating them would be a good first step. And if all else fails, we’re there for you, see the ‘Contact’ section in Contribute.

      Summary

      This concludes this (three part) introduction to parslet and leaves you with a good knowledge of most tricky parts. If you are missing some detail, maybe you can find it in the texts referenced here? There is also an entire page on the tricks useful in practice here: Tricks.

      If not, please tell us about it. We’ll include it in this documentation in no time.

      parslet-2.0.0/website/build/tricks.html000066400000000000000000000221011362225661100201110ustar00rootroot00000000000000 parslet - Tricks for common situations

      Tricks for common situations

      Here’s a topic overview:

      Matching EOF (End Of File)

      Ahh Sir, you’ll be needin what us parsers call epsilon:

      
        rule(:eof) { any.absent? }
      

      Of course, most of us don’t use this at all, since any parser has EOF as implicit last input.

      Matching Strings Case Insensitive

      Parslet is fully hackable: You can use code to create parsers easily. Here’s how I would match a string in case insensitive manner:

      
        def stri(str)
          key_chars = str.split(//)
          key_chars.
            collect! { |char| match["#{char.upcase}#{char.downcase}"] }.
            reduce(:>>)
        end
      
        # Constructs a parser using a Parser Expression Grammar 
        stri('keyword').parse "kEyWoRd"     # => "kEyWoRd"@0
      

      Testing

      Parslet helps you to create parsers that are in turn created out of many small parsers. It is really turtles all the way down. Imagine you have a complex parser:

      
        class ComplexParser < Parslet::Parser
          root :lots_of_stuff
        
          rule(:lots_of_stuff) { ... }
        
          # and many lines later: 
          rule(:simple_rule) { str('a') }
        end
      

      Also imagine that the parser (as a whole) fails to consume the ‘a’ that simple_rule is talking about.

      This kind of problem can very often be fixed by bisecting it into two possible problems. Either:

      1. the lots_of_stuff rule somehow doesn’t place simple_rule in the right context or
      2. the simple_rule simply (hah!) fails to match its input.

      I find it very useful in this situation to eliminate 2. from our options:

      
        require 'rspec'
        require 'parslet/rig/rspec'
        
        class ComplexParser < Parslet::Parser
          rule(:simple_rule) { str('a') }
        end
      
        RSpec.describe ComplexParser  do
          let(:parser) { ComplexParser.new }
          context "simple_rule" do
            it "should consume 'a'" do
              expect(parser.simple_rule).to parse('a')
            end 
          end
        end
        
        RSpec::Core::Runner.run(['--format', 'documentation'])
      

      Output is:

      Example::ComplexParser simple_rule should consume ‘a’

      Finished in 0.00094 seconds (files took 0.29367 seconds to load) 1 example, 0 failures

      Parslet parsers have one method per rule. These methods return valid parsers for a subset of your grammar.

      Error reports

      If your grammar fails and you’re aching to know why, here’s a bit of exception handling code that will help you out:

      
        parser = str('foo')
        begin
          parser.parse('bar')
        rescue Parslet::ParseFailed => error
          puts error.parse_failure_cause.ascii_tree
        end
      

      This should print something akin to:

       
      Expected "foo", but got "bar" at line 1 char 1.
      

      These error reports are probably the fastest way to know exactly where you went wrong (or where your input is wrong, which is aequivalent).

      And since this is such a common idiom, we provide you with a shortcut: to get the above, just:

      
      require 'parslet/convenience'
      parser.parse_with_debug(input)
      

      Reporter engines

      Note that there is currently not one, but two error reporting engines! The default engine will report errors in a structure that looks exactly like the grammar structure:

      
        class P < Parslet::Parser
          root(:body)
          rule(:body) { elements }
          rule(:elements) { (call | element).repeat(2) }
          rule(:element) { str('bar') }
          rule(:call) { str('baz') >> str('()') }
        end
        
        begin
          P.new.parse('barbaz')
        rescue Parslet::ParseFailed => error
          puts error.parse_failure_cause.ascii_tree
        end
      

      Outputs:

       
      Expected at least 2 of CALL / ELEMENT at line 1 char 1.
      `- Expected one of [CALL, ELEMENT] at line 1 char 4.
         |- Failed to match sequence ('baz' '()') at line 1 char 7.
         |  `- Premature end of input at line 1 char 7.
         `- Expected "bar", but got "baz" at line 1 char 4.
      

      Let’s switch out the ‘grammar structure’ engine (called ‘Tree’) with the ‘deepest error position’ engine:

      
        class P < Parslet::Parser
          root(:body)
          rule(:body) { elements }
          rule(:elements) { (call | element).repeat(2) }
          rule(:element) { str('bar') }
          rule(:call) { str('baz') >> str('()') }
        end
        
        begin
          P.new.parse('barbaz', reporter: Parslet::ErrorReporter::Deepest.new)
        rescue Parslet::ParseFailed => error
          puts error.parse_failure_cause.ascii_tree
        end
      

      Outputs:

       
      Expected at least 2 of CALL / ELEMENT at line 1 char 1.
      `- Expected one of [CALL, ELEMENT] at line 1 char 4.
         |- Failed to match sequence ('baz' '()') at line 1 char 7.
         |  `- Premature end of input at line 1 char 7.
         `- Premature end of input at line 1 char 7.
      

      The 'Deepest' position engine will store errors that are the farthest into the input. In some examples, this produces more readable output for the end user.

      Line numbers from parser output

      A traditional parser would parse and then perform several checking phases, like for example verifying all type constraints are respected in the input. During this checking phase, you will most likely want to report screens full of type errors back to the user (‘cause that’s what types are for, right?). Now where did that ‘int’ come from?

      Parslet gives you slices (Parslet::Slice) of input as part of your tree. These are essentially strings with line numbers. Here’s how to print that error message:

      
        # assume that type == "int"@0 - a piece from your parser output
        line, col = type.line_and_column
        puts "Sorry. Can't have #{type} at #{line}:#{col}!"
      

      Precedence climber

      You might want to implement a parser for simple arithmetic infix expressions such as `1 + 2`. The quickest way to do this with parslet is to use the infix expression parser atom:

      
        infix_expression(
          match('[0-9]').repeat,
          [str('*'), 2],
          [str('+'), 1]) # matches both "1+2*3" and "1*2+3"
      

      Please also see the example and the inline documentation for this feature.

      parslet-2.0.0/website/config.rb000066400000000000000000000006021362225661100164210ustar00rootroot00000000000000require 'slim' require 'tilt' require 'RedCloth' class NBRedClothTemplate < Tilt::RedClothTemplate def prepare super @engine.hard_breaks = false end end Tilt.register NBRedClothTemplate, 'textile' Tilt.prefer NBRedClothTemplate Slim::Engine.set_default_options :pretty => true set :textile, :layout_engine => :slim configure :build do set :http_prefix, "/parslet/" endparslet-2.0.0/website/example_runner000077500000000000000000000002621362225661100176030ustar00rootroot00000000000000#!/usr/bin/env ruby require 'bundler/setup' $:.unshift File.expand_path(File.dirname(__FILE__) + "/lib") require 'prelude' require 'example_runner' ExampleRunner.new.run(ARGV)parslet-2.0.0/website/lib/000077500000000000000000000000001362225661100153775ustar00rootroot00000000000000parslet-2.0.0/website/lib/document.rb000066400000000000000000000040521362225661100175430ustar00rootroot00000000000000class Document def initialize(name) @name = name @state = :outside @line_count = 1 end def process @target = Tempfile.new('erdoc') File.open(@name, 'r') do |original| while line = original.gets consume(line.chomp) end end @target.close(false) FileUtils.mv(@target.path, @name) end def consume(line) @line_count += 1 a = lambda { |*args| Case::Array[*args] } any = Case::Any case [@state, line] when a[:outside, /
      /]
              @target.puts(line)
              @state = :inside
              extract_title(line)        
            when a[:inside, %r(
      )] @state = :outside run_example when a[:outside, /
      /]
              @target.puts(line)
              @state = :inside_old_output
            when a[:inside_old_output, %r(
      )] @target.puts(@last_output) if @last_output @state = :outside when a[:inside, any] @example << line else # do nothing end # Write lines outside a code block directly to target. @target.puts(line) if @state == :outside end def run_example String.highlighter = Text::ANSIHighlighter.new print " Running #@example... " if @example.skip? puts "Skipped, no inspection points." return end # Prevent Ruby from buffering the script for too long. After a fork, # Ruby buffers become a problem. @target.flush unless @example.run puts "error".red @example.output[:err].lines.each { |line| print " " + line.magenta } else puts 'ok.'.green # Stores last output for an eventual
       that might 
            # come somewhere.
            @last_output = @example.output[:out]
          end
          
          @example.check_expectations
      
          @target.puts @example.produce_modified_code
          @example = nil
        end
        def extract_title(line)
          if md=line.match(/title="(.*)"/)
            @example = Example.new(md[1], @name, @line_count)
          end
        end
      endparslet-2.0.0/website/lib/example.rb000066400000000000000000000046751362225661100173730ustar00rootroot00000000000000require 'tempfile'
      require 'cod'
      
      require 'site'
      require 'fail_site'
      
      class Example
        def initialize(title, file, line)
          @title = title
          @file, @line = file, line
          
          @lines = []
          
          @sites = {}
          @site_by_line = {}
        end
        
        def to_s
          "'#@title'"
        end
        
        def <<(line)
          @lines << line
        end
        
        attr_reader :output
        
        def skip?
          !@lines.grep(/# =>/)
        end
        
        def run
          # Create a tempfile per output
          tempfiles = [:err, :out].inject({}) { |h, name| 
            h[name] = Tempfile.new(name.to_s); h }
          
          # Where code results are communicated.  
          $instrumentation = Cod.pipe
          
          code = produce_example_code
          pid = fork do
            redirect_streams(tempfiles)
            # puts example_code
            eval(code, nil, @file, @line-2) 
          end
          Process.wait(pid)
      
          # Read these tempfiles.
          @output = tempfiles.inject({}) { |h, (name, io)| 
            io.rewind
            h[name] = io.read; 
            io.close 
            h }
                  
          loop do
            begin
              site_id, probe_value = $instrumentation.get
            rescue Exception => ex
              break if ex.message.match(/All pipe ends/)
            end
            fail "No such site #{site_id}." unless @sites.has_key?(site_id)
      
            @sites[site_id].store probe_value
          end
          
          $instrumentation.close; $instrumentation = nil
      
          return $?.success?
        end
        
        def redirect_streams(io_hash)
          {
            out: $stdout, 
            err: $stderr
          }.each do |name, io|
            io.reopen(io_hash[name])
          end
        end
        
        def produce_example_code
          root = File.expand_path(File.dirname(__FILE__))
      
          '' <<
            "$:.unshift #{root.inspect}\n" <<
            "load 'prelude.rb'\n" <<
            instrument(@lines).join("\n") <<
            "\nload 'postscriptum.rb'\n"
        end
        def instrument(code)
          code.map { |line| 
            md = line.match(/(?
      .*)# (?=>|raises) (?.*)/) 
            next line unless md
            
            if md[:type] == 'raises'
              site = FailSite.new(line, md[:pre], md[:expectation].strip)
            else
              site = Site.new(line, md[:pre], md[:expectation].strip) 
            end
      
            add_site site
            site.to_instrumented_line }
        end
        def add_site(site)
          @sites[site.id] = site
          @site_by_line[site.original_line] = site
        end
        
        def produce_modified_code
          @lines.map { |line| 
            site = @site_by_line[line]
            next line unless site 
            
            site.format_documentation_line }
        end
        
        def check_expectations
          @sites.each do |_, site|
            site.check
          end
        end
      endparslet-2.0.0/website/lib/example_runner.rb000066400000000000000000000004531362225661100207520ustar00rootroot00000000000000
      require 'case'
      require 'text/highlight'
      
      require 'document'
      require 'example'
      
      class ExampleRunner
        def run(args)
          Dir[File.join(args.last, '*.textile')].each do |name|
            puts name.white
            Document.new(name).process
          end
        end
      end
      
      if $0 == __FILE__
        ExampleRunner.new.run(ARGV)
      endparslet-2.0.0/website/lib/fail_site.rb000066400000000000000000000013321362225661100176620ustar00rootroot00000000000000require 'site'
      
      class FailSite < Site
        def format_documentation_line
          value_str = format_values
          "#@code# raises #{value_str}"
        end
        def format_values
          return 'NOT REACHED!' if @values.empty?
          
          v = @values.last
          s = v.inspect
          
          s = s[1..-1]
          
          max_len = 60
          s.size > max_len ? s[0,max_len] + '...' : s
        end
      
        def check
          return true if !@expectation || @expectation.match(/^\s*$/)
      
          str = format_values
          if str != @expectation
            puts "      #{@code.strip} # raises #{str.red}"
            puts "      #{' '*@code.strip.size} # expected: #@expectation"
          else
            puts "      #{@code.strip} # raises #{str.green}"
          end
        end
        def store(msg)
          store_if(:raised, msg)
        end
      endparslet-2.0.0/website/lib/postscriptum.rb000066400000000000000000000000171362225661100204760ustar00rootroot00000000000000Process.waitallparslet-2.0.0/website/lib/prelude.rb000066400000000000000000000000411362225661100173570ustar00rootroot00000000000000require 'parslet'
      include Parsletparslet-2.0.0/website/lib/site.rb000066400000000000000000000023671362225661100167000ustar00rootroot00000000000000class Site
        attr_reader :original_line
        
        def initialize(original_line, code, expectation)
          @original_line = original_line
          @code = code
          @expectation = expectation
          @values = []
        end
        def id
          object_id
        end
        
        def to_instrumented_line
          "begin "+
            "(#@code).tap { |o| $instrumentation.put [#{id}, [:ok, o]] }; "+
          "rescue Exception => exception; "+
            "$instrumentation.put [#{id}, [:raised, exception]];"+
            "raise;"+
          "end"
        end
        
        def format_documentation_line
          value_str = format_values
          "#@code# => #{value_str}"
        end
        def format_values
          return 'NOT REACHED!' if @values.empty?
          
          v = @values.size == 1 ? @values.first : @values
          s = v.inspect
          
          max_len = 60 - 3
          s.size > max_len ? s[0,max_len] + '...' : s
        end
        def check
          return true if !@expectation || @expectation.match(/^\s*$/)
      
          str = format_values
          if str != @expectation
            puts "      #{@code.strip} # => #{str.red}"
            puts "      #{' '*@code.strip.size} # expected: #@expectation"
          else
            puts "      #{@code.strip} # => #{str.green}"
          end
        end
        def store(msg)
          store_if(:ok, msg)
        end
        
      private
        def store_if(cond, msg)
          code, value = msg 
          @values << value if code == cond
        end
      end
      parslet-2.0.0/website/publish-gh-pages.sh000077500000000000000000000003321362225661100203250ustar00rootroot00000000000000#!/bin/bash
      doc_sha=$(git ls-tree -d HEAD build | awk '{print $3}')
      git cat-file -p $doc_sha
      new_commit=$(echo "Auto-update docs." | git commit-tree $doc_sha -p gh-pages)
      git update-ref refs/heads/gh-pages $new_commit
      parslet-2.0.0/website/source/000077500000000000000000000000001362225661100161315ustar00rootroot00000000000000parslet-2.0.0/website/source/contribute.html.textile000066400000000000000000000052431362225661100226560ustar00rootroot00000000000000---
      title: Contribute
      ---
      
      Find parslet to be really useful? Or just found a bug that is really ruining
      the day for you? Please contribute! Find the code on 
      "github":http://github.com/kschiess/parslet.
      
      h2. Contact
      
      Join us on IRC in #parslet on irc.freenode.net.
      
      Discussion and patches (or the odd cry for 'Help! How can I parse X?') should
      go to our mailing list at
      "ruby.parslet@librelist.com":mailto:ruby.parslet@librelist.com. Just write a
      short message to that address and "librelist":http://librelist.com will
      subscribe you. NNTP/web interface can be had through
      "gmane.org":http://dir.gmane.org/gmane.comp.lang.ruby.parslet:
      gmane.comp.lang.ruby.parslet.
      
      h2. Bugs 
      
      Log in to github and open a bug ticket
      "here":https://github.com/kschiess/parslet/issues. Please be sure to include
      the version of parslet and Ruby; maybe you can even provide some code that
      exhibits the bug?
      
      And of course if you provide a properly tested patch, you'll be our hero and 
      get a place in the space below for lifetime. 
      
      h2. Thanks for all the fish -- Contributions
      
      * *Chris Wendt* ("chrismwendt":https://github.com/chrismwendt) for the 'ignore'
        atom and other improvements!
      
      * *Raphaël Simon* ("raphael":https://github.com/raphael) for a nice contextual 
        error reporter!
      
      * *Dan Freeman* ("dfreeman":https://github.com/dfreeman) for allowing rules 
        to be inherited!
      
      * *Rory O’Kane* ("roryokane":https://github.com/roryokane) for a careful code 
        review!
      
      * *Zach Moazeni* ("zmoazeni":https://github.com/zmoazeni) for his work on
        Unicode performance.
      
      * *rogerbraun* ("rogerbraun":https://github.com/rogerbraun) for being my
        unicode tester. 
      
      * *meh* ("meh":http://meh.paranoid.pk/) for taking a real close look. 
      
      * *John Mettraux* ("jmettraux":http://jmettraux.wordpress.com/) for the really
        nice JSON example and for pushing parslet beyond its limits.
      
      * *Josep M. Bach* ("txus":http://www.txustice.me/) for minding the small 
        things that make a big difference.
      
      * *Matthew Draper* ("matthewd":http://matthewd.net/) for bothering with my 
        broken CSS.
      
      * *Hal Brodigan* ("postmodern":http://postmodern.github.com/) for solving our
        email parsing needs!
      
      * *R. Konstantin Haase* ("rhk":http://rkh.im/) for rspec matchers that help
        stamp out, eliminate and abolish redundancy.
      
      * *Florian Hanke* ("floere":http://floere.github.com) has given a lot of very
        inspiring input for parslet. His questions have been key to rounding off the
        corners and making the library as aesthetic as it is. And just look at the 
        logo. 
        
      * *Kaspar Schiess* ("absurd.li":http://www.absurd.li) for being brave enough
        to actually add another parser library to a field that's already bursting
        at the seams.
      parslet-2.0.0/website/source/documentation.html.textile000066400000000000000000000054541362225661100233550ustar00rootroot00000000000000---
      title: Documentation
      ---
      
      "*Getting Started*":get-started.html
      
      Are you brand new to parslet? Well then let's introduce you guys. This is what
      you should read and try out first. 
      
      There's also a "video version":https://www.youtube.com/watch?v=_F-eh66zw90!
      
      "*Examples*":https://github.com/kschiess/parslet/tree/master/example/
      
      Parslet comes with a lot of examples that explain how to use various aspects. 
      Take a look at those. 
      
      "*In depth*":overview.html
      
      This is the real technical documentation, showing you how to use all aspects
      of parslet. Especially: 
      
      * "Overview":overview.html explains parslet's goals and gives you a bigger
        picture.
      * Using "Parslet::Parser":parser.html to *write parsers*.
      * Using "Parslet::Transform":transform.html to *transmogrify your intermediary
        trees*.
      * "Tricks":tricks.html for common situations.
      
      *Projects*
      
      There is a "Projects page":projects.html with everything from JSON through Java
      to Ruby parsed with parslet.
      
      *Presentations*
      
      * "Parslet, An Introduction":https://docs.google.com/present/view?id=0AfXgUAUtzyc7ZGZrcG1mNXNfMzIwZ3JjY2c3NW0
        introduces parslet in a few poignant slides. (Bo Jeanes and David Pick)
      
      *Videos*
      
      * "Writing DSL's with Parslet":http://www.confreaks.com/videos/2730-wickedgoodruby-writing-dsl-s-with-parslet
        Talk given by Jason Garber at the "Wicked Good Ruby Conference 2013.
      
      *Blogs*
      
      * "Parslet Intro":http://florianhanke.com/blog/2011/02/01/parslet-intro.html
        explains quite a few things on how parsers work and on parser
        metaprogramming. Besides, Florian Hanke also explains how to create an ERB
        parser in just a few lines!
        
      * "Parslet and
        JSON":http://jmettraux.wordpress.com/2011/05/11/parslet-and-json/ shows how
        to construct a JSON parser in a few lines.
        "John":http://jmettraux.wordpress.com/about/ does a great job of explaining
        how parslet ties back in with railroad diagrams.
      
      * "Parsing TOML in Ruby with Parslet":http://zerowidth.com/2013/02/24/parsing-toml-in-ruby-with-parslet.html
        shows how to parse TOML ("Tom's Obvious Minimal Language":https://github.com/mojombo/toml)
        using parslet. I sense a theme here. Code is on github - this is the first
        article in a series, linked from the article.
      
      * "Write You a Parser for Fun and Win":http://viget.com/extend/write-you-a-parser-for-fun-and-win
        a succinct writeup on parsing text formats by David Eisinger.
      
      * "Build a query parser":http://www.recursion.org/query-parser/
        a step-by-step tutorial showing how to take user input and generate Elasticsearch queries by Luke Francl.
       
      "*YARD Class Documentation*":http://rubydoc.info/gems/parslet/frames
      
      The "YARD documentation":http://rubydoc.info/gems/parslet/frames will help you
      with the nitty gritty. This documentation is real important too. It will be
      constantly improved! (Thanks linode.com and DockYard for sponsoring this tool.)
      parslet-2.0.0/website/source/get-started.html.textile000066400000000000000000000276251362225661100227330ustar00rootroot00000000000000---
      title: Get Started
      ---
      
      Let's develop a small language that allows for simple computation together. 
      Here's a valid input file for that language: 
      
      
       
        puts(1 + 2)
        puts(4 + 2)
      
      To install the parslet library, please do a
       
        gem install parslet
      
      Now let's write the first part of our parser. For now, we'll just recognize simple numbers like '1' or '42'.
       
        require 'parslet' 
      
        class Mini < Parslet::Parser
          rule(:integer) { match('[0-9]').repeat(1) }
          root(:integer)
        end
      
        Mini.new.parse("132432")  # => "132432"@0
      
      Running this example will print "132432@0". Congratulations! You just have written your first parser. Running it on the input 'puts(1)' will not work yet. Let's see what happens in case of a failure:
       
        Mini.new.parse("puts(1)") # raises Parslet::ParseFailed
      
      Here's the error message provided by that exception: "Expected at least 1 of [0-9] at line 1 char 1." parslet tries to find a number there, but can't find one. There are just two lines to the definition of this parser, let's go through them:
       
        rule(:integer) { match('[0-9]').repeat(1) }
      
      rule lets you create a new parser rule. Inside the block of that :integer rule, you find match('[0-9]').repeat(1). This says: "match a character that is in the range 0-9, then match any number of those, but at least match one."
       
        root(:integer)
      
      That second line just says: Start parsing at the rule called :integer. h2. Addition Let's go for simple addition. We'll have to allow for spaces in our input, since those help make code readable.
       
        rule(:space)  { match('\s').repeat(1) }
        rule(:space?) { space.maybe }
      
      Two things are new here: (and both in the second line) * you can use ('call') other rules in your rules * .maybe, the same as .repeat(0,1)[1], indicating that the thing before it is maybe present once in the input. Essentially, you can think about parslet rules as instructing Ruby to "parse this" and "parse that". Calling other rules can be looked at in the same way; you tell Ruby to go off, parse that subrule and then come back with the results. This helps when thinking about rule recursion. For example, a self-recursive rule like this one will of course create an endless loop:
       
        rule(:infinity) {
          infinity >> str(';')
        }
      
      Even though infinity seems to be delimited by ';', in reality, infinity is very long, especially towards the end. There is no way of knowing for the parser when to stop processing infinity and start reading semicolons. Ergo, we need to make sure we talk about concrete items that consume input first, and then do recursion. This way we ensure that our grammar terminates, since in a way, it is like a normal program. Here's the full parser:
        
        class Mini < Parslet::Parser
          rule(:integer)    { match('[0-9]').repeat(1) >> space? }
        
          rule(:space)      { match('\s').repeat(1) }
          rule(:space?)     { space.maybe }
        
          rule(:operator)   { match('[+]') >> space? }
        
          rule(:sum)        { integer >> operator >> expression }
          rule(:expression) { sum | integer }
      
          root :expression
        end
      
        def parse(str)
          mini = Mini.new
        
          mini.parse(str)
        rescue Parslet::ParseFailed => failure
          puts failure.parse_failure_cause.ascii_tree
        end
      
        parse "1 + 2 + 3"  # => "1 + 2 + 3"@0
        parse "a + 2"      # fails, see below
      
      As you can see, the parser got decorated with the space? idiom. Every atom of our language consumes the space right after it. This is a useful convention that makes top level rules (the important ones) look cleaner. Note also the addition of :operator, :sum and :expression. The runner code has been extended a bit, so as to throw nice explanations of what went wrong when a parse failure is encountered. Running the code on 'a + 2' for example outputs:
        
      Expected one of [SUM, INTEGER] at line 1 char 1.
      |- Failed to match sequence (INTEGER OPERATOR EXPRESSION) at line 1 char 1.
      |  `- Failed to match sequence ([0-9]{1, } SPACE?) at line 1 char 1.
      |     `- Expected at least 1 of [0-9] at line 1 char 1.
      |        `- Failed to match [0-9] at line 1 char 1.
      `- Failed to match sequence ([0-9]{1, } SPACE?) at line 1 char 1.
         `- Expected at least 1 of [0-9] at line 1 char 1.
            `- Failed to match [0-9] at line 1 char 1.
      
      This is what parslet calls an #error_tree. Not only the output of your parser, but also its grammar is constructed like a tree. When things go wrong, every branch of the tree has its own reasons for not accepting a given input. The #parse_failure_cause method returns those reasons. Our grammar has essentially two branches, SUM and INTEGER. Can you see why all rules expect a number as the first character? h2. Tree output (and what to do about it) But if we leave the negative examples for a second; what happens if the parse succeeds? It turns out, not much:
      
        parse "1 + 2 + 3"  # => "1 + 2 + 3"@0
      
      The only notable difference between input and output is that the output has an extra '@0' appended to it. This is related to line number tracking and will be explained later on (or you can skip ahead and look up Parslet::Slice). The code we now have parses the input successfully, but doesn't do much else. Parslet hasn't got its own opinion on what to do with your input. By default, it will just play it back to you. But parslet provides also a method of structuring its output:
       
        # Without structure: just strings.
        str('ooo').parse('ooo')                           # => "ooo"@0
        str('o').repeat.parse('ooo')                      # => "ooo"@0
      
        # Added structure: .as(...)
        str('ooo').as(:ex1).parse('ooo')                  # => {:ex1=>"ooo"@0}
        
        long = str('o').as(:ex2a).repeat.as(:ex2b).parse('ooo')  
        long # => {:ex2b=>[{:ex2a=>"o"@0}, {:ex2a=>"o"@1}, {:ex2a=>"o"@2}]}
      
      You get to name things the way you want! This is also free. Seriously: parslet requires you to add all the structure to its output. Annotate important parts of your grammar with .as(:symbol) and get back a tree-like structure composed of hashes (sequence), arrays (repetition) and strings (like we had initially). Once you start naming things, you'll notice that what you don't name, disappears. Parslet assumes that _what you don't name is unimportant_.
       
      parser =  str('a').as(:a) >> str(' ').maybe >> 
                str('+').as(:o) >> str(' ').maybe >> 
                str('b').as(:b)
      parser.parse('a + b') # => {:a=>"a"@0, :o=>"+"@2, :b=>"b"@4}
      
      Think of this like using a highlighter on your input: What is there not to like about neon yellow? h2. Making the parser complete Let's look at the complete parser definition that also allows for function calls:
       
      class MiniP < Parslet::Parser
        # Single character rules
        rule(:lparen)     { str('(') >> space? }
        rule(:rparen)     { str(')') >> space? }
        rule(:comma)      { str(',') >> space? }
      
        rule(:space)      { match('\s').repeat(1) }
        rule(:space?)     { space.maybe }
      
        # Things
        rule(:integer)    { match('[0-9]').repeat(1).as(:int) >> space? }
        rule(:identifier) { match['a-z'].repeat(1) }
        rule(:operator)   { match('[+]') >> space? }
        
        # Grammar parts
        rule(:sum)        { integer.as(:left) >> operator.as(:op) >> expression.as(:right) }
        rule(:arglist)    { expression >> (comma >> expression).repeat }
        rule(:funcall)    { identifier.as(:funcall) >> lparen >> arglist.as(:arglist) >> rparen }
        
        rule(:expression) { funcall | sum | integer }
        root :expression
      end
      
      require 'pp'
      pp MiniP.new.parse("puts(1 + 2 + 3, 45)")
      
      That's really all there is to it -- our language is a really simple language. When fed with a string like 'puts(1 + 2 + 3, 45), our parser outputs the following:
       
      {:funcall=>"puts"@0,
       :arglist=>
        [{:left=>{:int=>"1"@5},
          :op=>"+ "@7,
          :right=>{:left=>{:int=>"2"@9}, :op=>"+ "@11, :right=>{:int=>"3"@13}}},
         {:int=>"45"@16}]}
      
      Parslet calls this the _intermediary tree_. There are three types of nodes in this tree: * *Hashes*: a node that has named subtrees * *Arrays*: a node storing a collection of sub-nodes * *Strings* are the leaves, containing the _accepted source_ The format of this tree is easy to work with and to read. Here's what the above tree would look like as a graphic: !images/ast.png! h2. Where to go from here: An Interpreter As nice as the format above is for printing and looking at - it may be difficult at times to get the information out of it again. Let's look at how to transform the tree:
       
      class SimpleTransform < Parslet::Transform
        rule(funcall: 'puts', arglist: sequence(:args)) {
          "puts(#{args.inspect})"
        }
        # ... other rules
      end
      
      tree = {funcall: 'puts', arglist: [1,2,3]}
      SimpleTransform.new.apply(tree) # => "puts([1, 2, 3])"
      
      Transformation is an entire topic by itself; this will be covered in detail "later on":transform.html. To whet your appetite, let me just give you a few teasers: * Transformations match portions of your tree at any depth, replacing them with whatever you decide. * In addition to sequence(sym), there is also simple(sym) and subtree(sym). Those match simple strings and entire subtrees respectively. Caution with the latter. Here's how you would write a somewhat classical interpreter for our little language by using a transformation. Note that from this point on, there is not one way to go about this, but thousands; you are really free (and on your own):
       
      class MiniP < Parslet::Parser
        # Single character rules
        rule(:lparen)     { str('(') >> space? }
        rule(:rparen)     { str(')') >> space? }
        rule(:comma)      { str(',') >> space? }
      
        rule(:space)      { match('\s').repeat(1) }
        rule(:space?)     { space.maybe }
      
        # Things
        rule(:integer)    { match('[0-9]').repeat(1).as(:int) >> space? }
        rule(:identifier) { match['a-z'].repeat(1) }
        rule(:operator)   { match('[+]') >> space? }
        
        # Grammar parts
        rule(:sum)        { 
          integer.as(:left) >> operator.as(:op) >> expression.as(:right) }
        rule(:arglist)    { expression >> (comma >> expression).repeat }
        rule(:funcall)    { 
          identifier.as(:funcall) >> lparen >> arglist.as(:arglist) >> rparen }
        
        rule(:expression) { funcall | sum | integer }
        root :expression
      end
      
      IntLit = Struct.new(:int) do
        def eval; int.to_i; end
      end
      Addition = Struct.new(:left, :right) do
        def eval; left.eval + right.eval; end
      end
      FunCall = Struct.new(:name, :args) do
        def eval; p args.map { |s| s.eval }; end
      end
      
      class MiniT < Parslet::Transform
        rule(:int => simple(:int))        { IntLit.new(int) }
        rule(
          :left => simple(:left), 
          :right => simple(:right), 
          :op => '+')                     { Addition.new(left, right) }
        rule(
          :funcall => 'puts', 
          :arglist => subtree(:arglist))  { FunCall.new('puts', arglist) }
      end
      
      parser = MiniP.new
      transf = MiniT.new
      
      ast = transf.apply(
        parser.parse(
          'puts(1,2,3, 4+5)'))
              
      ast.eval # => [1, 2, 3, 9]
      
      That's a bunch of code for printing [1, 2, 3, 9]. Welcome to the fantastic world of compiler and interpreter writing! [1] As far as parsing goes. There is a subtle difference between #repeat(0,1) and #maybe. Can you figure it out? parslet-2.0.0/website/source/images/000077500000000000000000000000001362225661100173765ustar00rootroot00000000000000parslet-2.0.0/website/source/images/ast.png000066400000000000000000000656741362225661100207150ustar00rootroot00000000000000‰PNG  IHDRý»oºGMWiCCPICC ProfilexÕYgXK³î™ 첤%眳d$ç$QX2’³ &@”  9"ˆ‚ŠQPDE”(wÐã9ß½ßýþÝ?wžgzÞ©ª®ª™ªéîê€cž3ag¢Ï¿ÓÅ•?€P€$ÀQ½"Ãôll,Á<¾h‹ùLfK×ûߌÞ>‘^@6(ÛÓ;Ò+Å×€õ½Â"¢À,£ôþب0#=(fŽ@DñØöû¶°ç/ŒE~É8Ø€e€@K¥Fø@Féü1^~¨²!8¦ï€(;Q¬íåOõ€£•‘ÝÂ](÷ü=~ÿ‚©TÏ¿uR©~ãßÏ‚öD D†Qã~Ýü_6ÁAÑèûúu0¡-mHÐŽ­Ø°¢ç¬7Õнr£çϰ _1Ce NŸG{”¶…¥Cá"¡ð”ðްFÃ@#B£AcMãMG“ISIÓJó„fšfÈH#jˆ{‰Gˆ…Ä:â=âq‘D" ’ÔI¶¤ÒaR!é ©‡4IZ¡e¢•¤5 u£¦=I{¶ö%í"™L%ë’]ÉQä“är'yœ¼LG¡“¥3£ó¦;DWB×D÷”î= ½½ýúxú|úú'ôó 4 ¢  T†ƒ % 7ž3,1R­ƒ3/2>`œeÂ3‰21y3%3U0u2MQ0!ŠÅ‹’D©¤Ü£L3ã˜Å˜Í˜÷2§3_fîc^`abQbqbÙÇRÂr›e‚Ã*ÊjÆÄšÉzu˜u•‡M͇-•­Ží)Ûv.v]vö4özö!öU~#Ž@ŽSͯ8NIN[ÎXÎ3œ÷8繘¹4¹¼¸Ò¸®qpÃÜ’ÜvÜû¹+¸{¹—xxyLxÂxŠx:yæyYyuy÷òæò¶ñÎñQø´ùørùîð½çgá×ãâ/äïâ_à0ˆ(èXtL¬|%DRòÊêZæ¶N®¡Qñ)éù!*&ê,zL´YtVŒ]ÌL,^¬VlLœ,®#.^.>(“P“”8-Ñ/ K*KúK–H>‘‚¥T¤¤NK Hc¥Õ¥C¤Ë¥ŸËÐÊèÉÄÈÔÊLʲÊZÊ&Ê6Ë~’–s•;%×-÷S^Y>H¾R~TIÁ\!Q¡Uá«¢¤¢—b‰âà6ò6ãm‡¶µlû¢$¥ä£tFé…2EÙJù˜r‡ò†ŠªJ„JÊœª°ª‡j©ês5f5µ µu¬º¾ú!õ[ê+*Q×4>kÊhj^ÔœÝ.¶Ýg{åö)-A-ªV™Ö„6¿¶‡ö9í ªN¹Î]!]oÝ*Ýwzz{õ.é}Ò—×ÐoÔÿa apÀ Ýchb˜fØgÄdähTl4n,hìg\k¼`¢l²ß¤ÝkjazÊô¹™—YÙ‚¹ªùó. Z {‹b‹7–’––­V°•¹UŽÕØ‘!;š­µ™uŽõ+1›p››¶8[ÛÛ;»»n{н»ýEûïú™£ŽâŽÑŽNôNnN5N?œ ³'vÊí<°ó‘ §K€K‹+ÞÕɵÊui—Ñ®¼]ÓnÊn)nûÅvïÛý`çž =·ÝéÝ©î Xg‹ëTkj9uÉÓ̳ÔsÁËÀ«À냷®w®÷œ–O¶Ï;_-ßlßY?-¿¿9ÿ|ÿùƒ€â€/{M÷žÝû#Ð:ðBàfsP}0!Ø#øFSH`HW(oè¾Ð0©°”°‰pð¼ð…‹ˆªH(rwdK3º8ì>=£S³ëÛ°q_ȾÞ8ɸԸwñÆñç÷#û½öw$$I˜< w ì tÐó`Ç!¡Cɇ¦›®>B<xäq¢|bvâ·$ç¤ÖdžäÃÉSGMŽÖ¦Ð¥D¤•¾¦'ªOZúÕû[¶´=Õyz÷™á³ûƒfƒ†v ;¿xîö|â…÷‹Ù—A/¿ŒÄŒ¬ÃŽ¥½bx•?Î=^þZâuý„ÊÄíIÃÉÞ7öoF§¼¦>¼|»>¨VkÑöâŒlb[q‡ðÖv"#‰žV˜¬MçC_ÀÐÏÄNÙËÜÆ*À–ʾÂÊõž'”w‘?A(T,".zSÜBb\*XzUöWÓ³GÀRÅÊG©uŸ-d§læÐì„wöÜÙâJÞåâVº{ÌÓÉšíùØ›à£ï{Äï~ó^ßÀÆ !r¡a§ÂïGlD)GÇTÆŽÇ ÆGísÀçà§Ã¾Gº“’mŽf¦<9N—ªŸæ™î—áxbûI©LÉ,•S†Ù;rlsmòlò ¼ c‹N×”t—NŸgYω•)–«T(UÊž—¬’¼ P­YczqgmÈ¥ôËõuýõóW6¯‘دó5 5 7 ·ˆÝnU¼©|Kî6_ÒöîÎ@ûý»÷:tövõÞ{p¿«»­§͵æGW{˧õ…>qî7P~*öŒ};8?Ô?|ýùéi/÷xŽŒqŽÍ½º1žõ:dÂfrÛæ7_§ž¼­›>9öÎzVpvrÎonþ}àû÷ æ->â?v~Jül°€]èþ’úÕ|‘°ØòMçÛÕ%®¥¸¥ßí¾¿üáûcqùð ÝJɪÄjËšåÚÌzú†ÊÆÌÏ¢MËÍÍ_ñ§‚èú0œ¯ A(ê€áS"& Dîa£q ¸U|?¡ž¦˜Iʦ­$ߦ›` 02%RºYØYƒÙpÈpsãyây?òû L y¿uë—°’l—V’)“#É*tncU¢*ŸQRé+h8hÆl/ÔjÕ~¥ é‰ëÛÄž5ê6þhJ1S7wµ8`YfÕ¹cƆl«jçk_èðÐqÝYvçn—t×»fvSöh»xäSïy.yKø¸ùžð»á?µ—.P=È'8+äfèL8c„vdxT]ôb¬æ¾qÍñï¸X<|¨áðt"c’J²Ãѽ) ÇN?›z9­5½+£÷ÄÐÉ×™óYëÙL9Ò¹–yAùÇ J kŠ®7–4–^;}åÌÕ³çn•u–?®«üREº ‚fƒåE—Z¯KA—#êb뮹züZVCáõŠÆ«M̓-oo|i]»…Ü&µ1Þamç¸ËÝÁ×)Ü%yoÛ}n˧»î|dßkñX¿Oó‰R¿ì€ØS¾g,ƒÄÁ¡Ã#Ï»^Ô½,IM zå:núZyBp’aróÍç©Ñ·Óe3±ïÌg¹g?Ïu¼Ïûà?¿ý#ÓǹOíŸ B¾|eý:±xú›Í·¥êï?àW—ÝWÈ+7WÖX×Ú×=7ÀFÚOÊÏÌMÒæÑ_ñ‡ÑŠ­ÌÑʵÞ™z+­1½>£æDùÉ’ÌܬŒSÉÙ 9¹^yfù‚ù‹]…åE¹Å9%E¥çN_8S¶ ÍžŽò]盪Ê/œªN­9y±¸¶öÒÍËêFêß_Y¹Fh`½.Ü(פ֬ÕbxöÕ÷fâ­s·ï´ßY¿ËÙ¡ÚéØy/ë~m÷Ýž“×z;ô|òp÷TïYüàõ¡ùçÂ/Ü^æŒ<¯ÔÆ÷¿~4)ù&÷-4236k6×øAp>ûíçê/ñ‹9K3ËÔÕ¹¨­øÿÞ[Úšp*Òà<€ º§”€èZgÑ{2êž4pQ€ª—þž?¸ÐêÒ­$óЪ±|BóG2CkÀè<Ô½…X6„}àcp5üžÇÐc1N˜xÌL'f¡GTÝÈQä2ò ¡u˜36 {;Ž#ã´p¡¸2Üžˆ×ÅÇáðŸÒ„ B=áÍvšš§Db±‹ÄN &uÒrÒFÐ>$ ““Çé´èÎÒÃô~ô} * çi÷1Î0Ù3µQÄ(Y”ÌTæ^ –K¬¬El¶Lv2{™#‹“™³˜‹«’[Œ»†Gšç ¯ïC>w¾eþliû‚TÁŸB¥ÂÛ…_‰$ˆ²‹Ö‹éŠõˆÛˆKP%%Ó¥ä¤F¥Ód4d>Èž–³—§È¿QèQìÞ֧ԣܠR¤š¨¡¨ é»Ý]ËAÛDGMWBCŸ¨¿i°b¸j ›0™J›Ù˜ÇZœ¶l³ݱlÃb+ogl¿ÓÁÏ1Î)Û¹qç¤+e—…[êîÇîlTêEÏEoŸ<ßuÿ„½4A›!¾¡Âå"J¢è£Scqû’â~î?pw°ü°M"’ÔpÔõ8^“æ–Á{âCæSGr òðùc…O‹§K×ÎlœûR>U9V5Qýþâ·KkõÈUÞ†SÍÜ­#·Ïµ«vôÞÛÕ=÷0î1ý“º§¹ÏéÇr&bߪÌB†>W,j~O]iXþküàZ`78JÁM0VÐ]ut *‚Z¡Qhæ…ua/8®Ã_ÐÚ]ãIÁ\Æ bÖd‹”#‘¬(Ö}v Ç‚3ÃÀ5àÞã…ð{ð%øaá<á3&MÍQxœ8IÒ$¾ÑÚÑ^%3’#ÉÃtšteôDú(ú k†;ŒòŒ•LL™,%ò•9€y’eË0«=ë36¶çìnìShù3‘‹žë4·4÷M žW¼á|4|•üºüãyï ù“„DÜD±¢—Åìľ‰çK¨KLJžÒ‘ú&}IÆ[–[ö¹\޼½‡Â´b˶l¥0e[%U.5¬Ú’ú¬Æ¸æàöGZwµu.èèÕ11 6Š2N4)4½n6l¾jÉc¥³ÃÃ:Éæ¼í=»IûUG&'9gû]ê]§ÝøvïÙsÎ}š*íéuÓë»Ã/Ïd¯@`PÐíæÐа¾…È¢hlLtìLœK|o‚öúC¼‡O%’’rŽŠ¦´wOéå'ŒOÎgd›æläÝ(8XdU"\º~fàÜùòý•NUªÕ<qµ —Gë{®¶5t5Nµð¶zݪnûrW§óä½±‰‡q½}OÄ2‡^ö½z=õf~z}Ž2¯øÙ÷ëø÷ñµ=¿â/ìÑè_èÎd퇪 §Ð&, »ÂÇáxEÌ10W0Ó'b‹¤"ÈVƒmÂþÀiàáºñLxwüBØMh¥á¡I¦Y R‰ƒ$ R­í5²¹’Ž®”ž›þ,ƒC£:c“ ÓGJ3óuk–yÖ 6Y¶gì bCœÇ¸´¹Ö¸Ûx’xù0|·ø£d>^ –^é=%æ-®!Á 1-Ù"•.í!£&Ë,»$7.?¬ðZñë¶U¥å•;ªj©êá»4M·«j‰k³ëÀ:Óºzgõ“ b ÷2n01Ù+[øY–Z=³¦³±´Í°ës`ttp*pup ØÕ¸Ùãè~‘ {îöºí#æ{Ñß*`3° ˜>$:t(\%¢(òg´oÌÀ>¸+ûÅjêš>r<‰+ùt Û±ÌT𴔠܉ã™ä¬¼l¾œËyÚùÃ…1Å\%·N;žy{.¤l¹"éü<ñEø«ÃbÔ·Ø¥¨ïž?,—eWèW>®>Z«^OÙðú©¿É¿ÿHßmŠ[³€hõÑíÇñÍÍEQtÿ2€S››kå››h‘þiúý¿bK‡î¹—Fn¡Sô7Êÿ8þ Ã8vY¹× pHYs  šœ IDATxí½ ¼eUy¦¿©‰*ŠIPDdpÅ £b4qB‘PЭ±ýÿÛ)qˆQHg°£iM4Q¡Q‰#j@pj±‘€D‘yŽ È\ÔtéçÙç|·ö=÷ÜáŒwŸSßwï]kg}ï·×z×Z{Ÿ}¶xðÁ‹´d H’d ¿‹éa2 $É@2 È@Š~^É@2 $ÉÀfÂ@Šþfèt3H’d HÑÏk H’d ØLHÑßLn&É@2 $)úy $É@2 $› )ú›I ÓÍd H’d E?¯d H’d`3a E3 tº™ $É@2 ¤èç5 $É@2 l& ¤èo&N7“d H’ý¼’d H’Í„%›‰Ÿéf2 $c` ¬yòHöY]ž¸üe5~`-a­KÇå°-ò—P¦É@20,š"o°Õ|,›ÃZEÝåXùÉåì #$õúŒýzÅ#K“ $5f "ö!즋iÜ.5u9¬šuƒJCÐãüU¡7?ÑD¬w¹@üËÔ|Úx3¢?ÞñMï’d O T_Q(øæ—=ÀRpp¦à/”èW…=#å á¯æË}säoÈÆÛòžþxÇ7½K’>0PüySÛOÓ£ŠåÅ{ÁÊbË·«“-bz«b e»¬¸­8ž¢]64¡ðOŠ?~:ê·6¦ äHL›n%É@h|E>)ãú÷;Çï(¶.öëÏçõý,Jømà|pj±–qþ»é|–%…}3­Š¿ºŸÂ1ãh)úãÕô)HúÆ¢Sù1²w*_Á£ç§0µ¿eß>n°'ºƒÓY–þ8äþëäÖ~;å¨ÍÏ{ü1Ž–¢?ŽQMŸ’d / 4Gùо£{ï×+øâñà{ŧø¿3%»’¾½x¹¹kÀ *þ!ü9Ú‡˜q³xØdÜüJ’d è>„¢ïH_áß’þ‡‹?f¢|Ô_FöÇâÇŠâCäVŸAÐ/ý‹Ya>|ÈǦ ‹ýa1Ÿ“ $#Å@˽üM‚_ûó<þ>Åá“Oè”_eagæâAü(ŠÇoN8{¡!üºŸÂ!ãf)úãÑô'HúÅ@Œòm'Cô½ÿ¦âFÆŽGÕ”ùW#üËŠ7’s´ïšíC¸[Šþ¸G8ýK’ލŒòm#ãi}GÃË!?«xf¹®ãóÖê€CèÈlQZúÔ˜âѾþæ(¿VÁê_aRôûÇež)HÆ‹ÛÇ}GÁâÀb;Öí4Ž>V–Óúû’Sð…‚/JßsŠ&ÆÌRôÇ, éN2 ô…éÚF:µ¯*Šôq”ÿ-ÎøBp-ó9ú?ÿ'VžÐDeU_²>PO1½oÇ&„ßmÁÙ´q` E¢˜>$ÉÀ °} lŒô·ä<{õñ{»6‹½=éðKðÏÀ/ÒÝÂî##úm{ã˲òa>;4ú}OÁï7ß58ŸNK’d ˜Ê@ŒrCøò--ö.vŸºcOKñÂ^ÇÙ7·Îp¶CXö ÛzY­/Ëði]9‹¡!úúŸÂß ·5=Ö ¦%É@2 4hÞǶm Äh ñmà wûgž9lO2ß+*éVä«,÷+» 'z°ô¨1“±é~¾%‹ŽO¿>-ÏSìÕ¥%É@2 Lg F»Š¿"¸„—Ô®àá·þ™ÓûgUNç§´ëT<¯²O?³v&&ÊÛÕQ¾¥ˆ} ßÜšoçë'ï x.›– $É@2°‰á†ð…è/Fô—ñ‹uãcú¢O›¦õCðå m HÑà¦KÉ@2Ц‹ÿbî~⺾·‹“ÜÏ1‹ÊÜ©vpÂïþ.(­û!)úuP–/H‚¼ª.b’m¡PŽ‹é‹>mzhOÕ…ªßÁ«ÓFýQ`–?HÉ@«øÝWÜ;È«œÛGëm÷”àÜE«Ÿ±<èäù‡Ì@>È7dÂëúqÍ'–-^]{õåƒDù@Q]¯ ±,×ôº°oÐ_SìÆ zg»qêêÃ}ƒû¤ÆûÖ×ò#òÜõb E¿^ñZiš"šiÀ2Tó±l: k}JØåX÷ W—³0Œl¶ŸQ½Ö$¬-®.®àÍû·ó>1—óŒÂúIÑz~µ.ÇúLG˜ý^7E¯ˆ}»©OìšÆíž¸§á¶aYkC o¤D”Ëøc¾@üËÔ|Z2ÐG¼Î&¯7ò——Ë}üˆ<Õ%e=ººé£Ñ×ð×eMÒÆ„ý1 ä|ܨ¾¢ˆ¯èøµ=€w¯hn'™6êwÝ ­ÚÀ”·E#dº±¹y;$ø¦?ü«Ç³*-èŠÖë(®·«‹»¹o䜻tuÞútE¹¿ØÀÿ@øu®šÖ§ÌY’žHÑï™ÂÑ8AEðCäM¿éQ¼žã½`%¯Y\ÜR¾¬£>Ž-b¤±o&_Q\VÜVOÁ.6VÂNÀdG?Kåg]Z2Ð ŠžBbXÍ_\œWT¼¦¬;½|ÆÂ{õf‚:ÕR‡š>‡è/lóÓûÎ@Š~ß)­ß [_‘Á_ʸþ}ÅűÅ;xØ~õ+{Y¢™m¸ Ù?¿xZqjñ%ªwÓølÓõ¤Žö~ÍQ ƒ‹üß žÂWüí¸ ~gq:WáË©G΢ù%½/àßúâÛäZ;ÐÑá)ýÏÙ³Q ðÌevJ4müPu¾MÕ–þ«=ǯzÕWð¥x)øʽ¦ø{Êþ2–ü™}ñ–DÌZè§G¤%½0‚ogÒkìpð÷à£Àëëúâ›å,Ù´¯–e¿’ß ì8· ttä!mŒHÑ£`¶s¥2ÊÑ·ûRÕÿ‰flY)í®ãº(T£Ùý¹=Á  ?SD¿é7«Ó’ÎhŽl»ƒÀ‡ÁÁ‹Á—Á«Á‰t6DâÀb;ÖíDnÔí18°’ñËZJ,ÊéJb¯hlÑpÛneÃ)›«q (ÄV§€Ïƒ#¹,.$˼v}ÑëJØn†¨“-g”ÜÇkOă¥Q÷XUÚ0„?®uÓ†è7:7vVÑ+ðн?µ£ðWGú­¢Ïæ´qc EÜ"ºÉŸh`lx¢²1: ±ášÛNhîòçsï:ëW²õNð$0Û'‹í~)êã`70óé„ïOd×_6;6Ñ@ÛËC4„dÓ6ûCðõ0mú~ž„p*žÎ Äu¥pZ¯âÚò:s»‚ﶘðjw÷ÕÌÚ,s *ú–-D_¡WøÛ‰¾¾xœÈz ãf)úãÑ©þØØØðˆÆHK^Á³×<߸g³Ðû$'ññ¨oÌq²]›Û·Ÿc¿êæ½ñå|æ[7ÙÐF'åêÞ™sú]pñh° (Ä'iÓ÷¬›Óœ!r¦ˆC<ý¸®BÀÝæú|ïõ[×¼£þ‘Šàû9še®–;:,ÕѾÂoEذüú¢Ÿ/¸‚Œq´ýqŒjÃ'¥e(úÆÚ‡÷ö.voì0çÿCØãìæ^ÿNúð4ð¯`pð-~Þ ýx9xzeùÈ{·]Á×þ ü7p38œü‰Ò‚ßî«ÙlÎ×ôe>5D_…þ†ï¦icÌ@Ó÷s1£x*„š×Rõzr[ŒðT¯Ü|¯A²Cµªè[þè˜XÎõ+ö. ·ëKˆ¾Ç§!)úcTÁ< ÐúÿÛðþ½ùÙVìvXs×;H¿ÛÄî¤ ù;ÁÇæ²Í‚XÞ®\Óøw#‰ÍÍ›šëŽ#ýpßÝ€¥ìÔì|R¤x?œ:1;&埭‚~Û*Ÿæëä´uÛWšeŠ´nE,EbÐb »àøÑ`P¨N]Mßsܼ ŸâWCäõUTÅTm½£þÍësú¼S”Ñ2G¹CÜMcd©¾¸Ÿ!ôر²¬?›ÂYmž7­@.I©3ŸÒÆ&œ?…p1ÍÔ2~º¦{{róÐý»8…ÍŠÏQ¾ Þ´g€8o¹¢ƒú¢OÓ¿®â8/çšõ%|ˆx¶.ǹb},2m„?3îÇ>}¸èåéû~q¡O¥(êg® Á4UDmW£Þ‘å#N“ÜûÁ˜eŠòUEßu.W·/ýyËú3s&úIṳ́qKˆE4@‹ýuL¿//GÖÝħðîšå`ï¶3¥Ùdž¼×¯Ð{õœØ8IÛ©ù8Ò¢²¡Ýäc£‘ ß;=ã‚î_©7Q~S;l¦ú¨™ºVÍǺA¥!*qþªšW4&ÅJéFL8öÎu Øô=çž—©†”'ö ŸLÍø˜w‡à'båºÈ“¸ÅçGM#&!ð‘ºÞ|¯nb4po:ü€¬?sÖwÑOÒç&}H{D ePô×–ßÌí¶Gsà[€SûÚcIùßûó޼ýÚ]Õâ[g³ò à­`wð6ðT èw;û èëÓ&!Ô×ÅðÛ4C²õ´Jݱüż]¦=€÷¯®ÓÂÇÆÒàÿWy4/JÑh¦!"!(–/¦ÇÕÎêñlšjp° kŽ«€#Ñ“À@§ï9·¦oÆAŸlKqº ¸MkMkûßòÄçV?ÉuULÙÏëo®øTOV·|ÖŸùE¤¯¢Ÿ¤Ïô!ïU­äæïãÛ¹ïº ŽÐCðÿ yŸÞ×^NUÁWž4Gö>¬çq_GSíðÌ2×ù¿{ÊCìV´úËŸsލÔySë§éQHý{‘“•ÌÑ,æíoñ=‡(i›ÜÑ[ÎΖÅe¼áxö¸ÄT²€ÉŽ~Nû*ëê0}߯±M«æŒÏrⳜø¬ >·Ô,>¾†w+¾–·‚7ÞVœ€WÆGSôf'ÍÎÙ´ø”[kþo Ⳇø\F|:®?†Æž]§Ç´ÝNÒ—P)–R)–R)î®Y¥°ÑZF£µ Òï™tœ·3ÜÒÚ2ÙûJbáèCÉUCû¼þ®àIøx¿P÷Ôò·éY1o‹÷ÎàYRŠlª«v/ ŽsD«yŒÛœæw”®¬UŸðg±#³±šG×—]ŠÛYòÄÝÀÒø)ÞlØ@ÈlÔji-uGfCð—ÂÏûŠm‰Øk‰¡ãÈ:šµAÖ/g3ë2Q¼›ÿŸeIá¯>(â_Ö!ün¾_Íþg°Ñ™›ÚجñYJ|v >ï >Ñ®MÉ›1>·gÔNmÆgÍÜñi]û$ãÓYˆú2ÒŸ•ôETŠ­©¿Çÿ;+ÜÐö~Q⽈ãU| ýÂâKe£µ¾¬6¾6ZŽ'{Äø[ÛÞ0eSàm~|¦Þ1ôþàñÀÀ5öML+Á!¿dça² É‚ŸÐÎfZï¾­Ÿf‡¡µÓÐîœs­»_Ö×7wk팵.Ïu¶…ÚîõeG-_¶|§Â+ÀqÅŸÀ^Ìš,T gû\Kÿp(8ñþñÌ÷,¢K7Q|5n -[£¸N&_÷é{˫͟Ä矉ϖkùßÒ?¼JI$>K‰Ïú¶ñÑ…¸]c~,ãÓA”ú2Ò§Û`‰˜Žl4Z‹h´–ñÚ–cøß—îEžu»«Å'‚ETæF£¥@ÆhÅFª­,ôÈΕ}€¯°›ŠÁ/À€ËÁ•ààˆ×¦ùÉÅØâ;µÃÙ–k9Rذ+Mu±×1rùmñAŠs1¸ü8Ò7’Žô5®whIZ;#ŽÕ˘Zw„µï•ßpèþf §X»‘Ïü×Ûúâä®i–ÀükÁ“Àéà$Br!i­mÎø|Šâ×u03³¶o'>ë&ãcÛPm碫e©º•ñ©²1¿|ÏRÜ$½Úpmj´&xÛ«(HÏŸ2?gú²—ßa?|‘ÎÊD)ž6Zú§Y Jè÷0„¤Éï|n«¸ïɺ«“ª—€“šù«H5Ywtïxzk° °2_$N7’ÛÌ×vcGQ'»ŽÂÜ_ŽíÔ8:Ñ¿ÉUòdkkÕºcÌþ-ùû0?Ö:ÁÓv¦G˼®^„ß,>„´\ÁÒïƒSÀË1c5 Ö>>+ˆÏ1ÄgçŒ-DZÄçdⳆqâÞÄ(?–료O‡²‘éÕÚ“¾”Jñ *Åö#X)YB¥8ŸJ±~x•Ÿ\µ+ô>ïî¹w§Lÿü‚N‡½óiÆy +­8İš¿¸8¯8ˆ‡ïê4fŸæÇœ+ÎC8&x£! ŠHøhZûF«Ù¡³þ‡M‚ï5°³8`ÝÁ‘ÒžŽOg–ײp¾\Q¯OJÇ™ÏêÊæŒÏá#ŸÃ‰Ïê2>ƒg«õ§l;2>]]6ý9h€ñéi1g¥Øo„+ÅʆX¶Rl œÁ°aò¦û6؇m~>¾nçDNQÿ)x 8üÿà‘ˆûnàÅàÝà4p1h+øì¯Yiµ¾C+ö&ò¾ÎëD9¡Wî6‚ÿœ´ÿþ­/¾]úÔ¸?¬¢l´š)TÕsjŸòE‡Ùë)DWÚ›xƇøFל¯8Œ:´¤¼›ìC•Ö!×Fý)ëËu¶öñÙ’ø/z3ªf4^M|–o$çl`ƧN±`|zmVÚWŠ%TЧQ%Fy)3OÁƒQ)6ïa)E5o£¢Jvº!äN¯ï ±W§ç·gÙ‘{ÀgÐ/E›n%í—EÙ,«eÞPÁõä¯gúu÷â%#Ú1ûjé“Óú7ƒÖg.Âgý–‡Ú׆u'êOŒôíX.§ÄÏb 6ʵ§Á÷þüwʧ@¬qý󦵵9ãóÌ1ˆÏ!ÄçôŒOm/ÂŧkÑŸµR4Z{ŒA¥x,•âÇsW ¸°Þ T…Ýü£Á¯@ˆû?6ó× rôé¹)SŒv£“bC«8ŠuÜË;‘¿PâEµý*mkW²vuÙYù9ÇüÎz„ð+&!úÑñaU-ÍŽ£ðúqÜ(ä‹­‹xØrô푸°œ±äú²ã{KvjŒ•þ–¾{².ð9½XûølGÙwêå´59ö1”c%1Y›ñ©ID¦c@ññ¢îÅÚWŠT Dz£n;à€“­Ñº©PÈï§ÐhýŒô^à½ö#£7ŸNö¥mhÐG‚¿ß×ÅhЂ§èÇ(«!ø ¡¼‰µŸ)ÞÌVŸw³¬ï`”¿¸8ܯ¢_~}¬Š>‹µ4¯Í:dç[!ôú:°Ø³Ì“Û³¬OÄ“˜>¶c£¯"f;ÈÖÎfŽÏ}ˆ]ŸtðýóµnŽ™ëÜ>=RŸ™xúŒÓµÍ~IúÇÀ›°«-¿vBåBÿ >]ôqkæJ±S*E¯¼y‡Ü&t›O´3~^Q¼Œ³¼ <Üü:œOÌüpjÞ'’ëf ~ëH_ôªÆ àhkn`¼¼x=ÂótÖú}Þˆ,ÙZ˜ø(ãÁitU6rcbcqK÷y×ýRðcêØ£¢ãC¶–¦à‡*†K‰Ç~|«Âéðñ0}¹„çb6”µQ£ƒ£ïu»ÒZ9ŸŸ-‰Ï^}ŒWl§ÖÍ13}ÆÞør>ñY—ñiKѮ͵ۓÞÞÚ\ö (íÄYà>0@|z}]œ^)–P)ÑÇJÑ-‘ŸåÀg‚'w{‚æq;âËÕx´±øÖ(47CBS×·½…àÅt·M…cÅQ¡t䵌5ç‚;x(îEêŽx¶„nB½c_#º²”[)ß9”ûr` }±Ð¿ªè+üu59Q‡¬‹>³w_¦Žõü;à›@{1xpýçÁîà‹ÀFÌíkà'àeÀníA ·vâú²{Šk­ÔF„ùDÏÉàùàÀXYIÛSÙÜUV_–áSCô3>­$F÷ÛÖòÛÍAúTðQp&¸Îý¶ÄÇ wk[p ˜Þh=¼ÛSrÜ]àçÀG’Žò¾4ï”K°AÐ.ç–¹F£æö_5—Ï#ÝìÙ\î&±KÃå×Z9³ßMkgÞBàÖƒM¼¨ ˆ¾ó ޼Œß¥¼ÏýZÒM´ŽÈX]úk: ³Ã¢5Ê>dÜSvVwcpo!üvbÖ;6UÑ—†8›êaÄ%®¹úÓ–™›2 ½š×¾‚¯¨ýÿ vŠ»°# à¿XGÜ÷[àRàH¦¶Uy’­ùï5%ô3|–‡ÚÕŸ9ã£7½š1–ãê¹¼²¿ öíD¿Ý1ìÚ“mÃÑ–¥Èø´#Ò«5ì`2»'4WÄmÑ•,{Ö\ßÏdñéJôg­N¨+'ÝšM¶ãiíànð%ð°+°gõ/à- ÿUä•÷³Û h{€Ë\÷ÿ‹‘j´ZœUôdEAônˆË¦Î «Êip÷Q8…ôڈƙliÃh C¤M¢ßr;+о¯Ø;Uüm*]ïöVÑgU­M>å8xv–eE_æÉBÄz{¸ì´c×-.`«€¢òzð.Ð/s¤´±ôÈëIÄ5~ÛœÔõûúÓã³–läûalžä.R¯bã£ý8ê·F·ªÅ1Õu½ä«‰ŒÏŒîÊ–³š[Í m58ß fŒZãTnèÿÄÇJØ‹µ¯ ¡ìå¼Eñ_9ÜFè§`°Ðþ(ú¿ç‚W€Gíà'À^²ÝcÀ6 SþF·R„p*žv§Bø•]ãç>6/nWðݦ×1*‹³ªÜßtfyUÑ·l!ú6‘ ;Ñמ§Ž&§Ákˆ_C 'à>¦{-ùç9Aˆºçªžw‡æÉeË˺¦õ~šŸ9QÊä7f;-º?ÛæúlÛH=±!î§9»òéÊ ¿B^h!8¥þÿ×—“üj‡¬Î™¹þèK¿ãSeývìk»ƒÿUæ÷oñéVôg&Ýß«ëe¤ôEÉ®cÅ^±’ÔËRS¾4›Ì°‡“±3`s¯Ù õjv`lˆ§Rä ÖæÔ¶ƒ( â)k!öQ~·¹>_mú€ûã±?«níDß2®Uá·ƒ"\gùÝ'D¿¶?ŠDÃäT„ð/"ç×)—÷ÜpÆY÷o úÇݺ°jݰcàU`)¢îÄ~½¦FÆßiÌ%ÜAz'plk§ÍØSŸ‹©M:#^ÿvYdÏ™¾mÁöÔ†¯Ròm{Lp²Is@ó6p88íÿBm Ù™‘YTÖ›M×`㚌ks`݇G7•}1õç>âÖë`¯]álaBð#ÿÒv;õyÝâÓëe5t­µ^Uôƒ÷u<,š†"å)Ì^ØÃ€—@¿Ì&ÉJ±±½ˆ~ûJa£e“Ü/Û91¸xø1Ðvÿ^lêÏ-ä ±Ôý}Ñ'ÿojªùhêÖh•>7Gû–14âæv×)”!¨v¡¼&„¢ßê'«†br0²Ëe52}î§Oº]ËxP¶V‹ë§‘nÁ8e ?³Ó«=›|¾‰G‘> XZtš·&ïlÀ?‚ÓÀž Ÿf£ÕÕOõsS]êç§ â\­å¾›J½Ç§]I½zÛ‚é IDATµê@¦±fpÿ}2&ã3?~Ÿ ;32¤ªÃ lñéEôÃÍé•b]*…gÕvŽ@>ãBÓ^×LŸOz"pêRÛ<¹Ì5~¶æ{äõ0Ö57u”4zÇ´úËn!vFÿüq6ð½ØôJ±‘*q•bç.OûPŽ{K˱‡²üT`!CæÝWIÖ\³ÁÛôêáo8dž‘­Á†©"ªx†˜FêºLSe@Öd9˜Ž8Gʦ™åÒ"µLQ>¯€€ëÌW·;ÀןQ±ˆåµ£µ'ì¯eš×ŒÞŸŒqj¿j ~«ù©1Âw–̼u§_¢¯/›êOÄ”(­u9Ö×%­Æ§Q¦µÜ,¹¢|Hïñ©ƒ——ŸM²Öx´.סÄÕ2d|ªlÌ3ß«$N'}•âÖTŠÖ¬ê`Uì«ë{õÎsýšJÑøÊ‘müüÜ ´ºWŠF)-(VyB:â¦`j6ýæõð©*ø‘góÀ->?ÊhªGç$DÞ4Ö™ŽšàïA™Ÿ”Ú§»ûOp÷ßõg8fd—?ýà,A¿ìªòzºšÓ…O¦"âì'Uó.×Éâú3—óUáð¥Nåì®,—d|º#nHG >ýÅé•âÆ1ª7••âRBìSÇ€¿¿??4Ѹû™"o³o ½"G0W·i­icí`ÿ[žøÜê'¹®Š)ûÑ©©å÷½)ÖÃ(·wÓxSáü”ÈŸƒ¿×ÇâåÁÕ”ßÓöІm͇Øýè·Ù‰x œ¹œBiG͘µ‚Uµ3ËXµ(ÿÕtù'ÊïÔïRÝ<‚ùë(óýŸÚFn@ñéEôg®k©¿…J¼e»ƒÂ;9Ùx ƒý·GùO'€iØo$uÀŽ€éOzÞKZ S)ˆpT¯Ð›Ó£¸Ÿû^ä~%ߺXŒ—ËYWÛ‘XŽ|lÉ»âî,ù¶£¥y *$š²Ï.,èWõø|礹‡¸›î~,÷ÇÁ±ÀëI‘w窶ÞUן‹yT<·Œ ‹#j—à˃Åe”^ŸD£fÂÏb-ÍòEy«eoÄç<âóšÏyÄd"ãSË«ÏB (>½ŠþÌ•â**ÅSG¼R\նѲÒÿ ø%§5Àû¯òðxà“6ø¯v쫵vîgÝPrT_‘Á_Šì¿¹9¶x-{9ί£=HgåNÄñR&žÏæñÅ»¹óýYŠª>ƒ ÆEšðC«';~!ðŽä÷N–_þ ¼üÊ{¤¥5ãaÝ á«Ö£ xôu¾:÷Tž¸_<Ô'¹›åëKbT¾‹Ê(mÃ’ã~c$BPKÿ«Ü°­±jŸuÄçtâórâ3ªwö}@ù Äa}ù²óês2£Ÿíh‘ï$>ŸöU©[ÑŸ½Rl Rü˜JñD*E·ŸÐ¾¼Ã[k5ø •bã¼*…Å[b5(hØ$!:¯%ë¯&µ#ŸÑèÙy¤)Š1º·\6UKù{8®ø–cÖÕ,½³F‡‚Ït¸/|™(Î8:4¦aŠh\£±®ç”Ø=š“(ì!òvðn ¼ø¸˜xÚœËBP¼v,o4¼¦×—¸€ï®üN7GÌÎŧ-Êož{½yKì—à+àk |Hœ8?lîø|“ø¼dDãóÕòš»¢nvÑâú36£۳Ã-_çëù퉌D´Z/’B„~Aî‚פ-ötŸÃíf< „À›z~§èø¿?"Nw‘vdS}S¢Âçµb£kã+|ÁÕ‰|¯þ̾,ê˯îqÒ¡™Ý 3Ëzÿ9rÆçã`_ð"ð§à p2ø¿@NkeóŠÏâs"ñÙ?÷¨Uñç.Œ­ÍêÉø8æ·“Â_ý¨OsŸsˆ{Tâc\^œyý2¸lÃì^˜hc6ŒmVϽŠFѼrá}É­@Ü—´±ôæ^Œò?À÷—öá[ûœnˆv+ŸuzùyÅ-ïî ¿Ñ|¸¬V˜õphÃÝ•Á£î¢#àèÑÞª·âùSG~^GÆùCì—p bo¼–óÿ šàøž…ÛGÏ~€P~“‡)(ޤðÆBØx‰²ášo\àHNž!ò$/ï | ÎçȨ/ÖŒ‹±7&>CaýÙl¬CažìÙxr µ¨÷ßàC1kÇ_ÒÛX|ŽÿòæÓ=ÖÓ»À ðð`gMñ_ ·7‘ÖÆ:ŠÏg)¶3 Ç™ ÄgcÛøÁûƒëR­~:œ¸ìB™Ž«€õü3À¤û@ûú“ñšMÖ‹è+³7Z‹h´&¸Äþ {nµéCkór?™ROÌØh ¼R4EèJbÀ΀éã€S¤ QÌ \BciÅlkœÇF5âd¬7Ý™/2M¾|do¿8.ùs:]ëÊ)½_°Tm¨bÔ2?SzµMNöaÿwSoÃÈí DþÚugŽóÌj”#:cѳ†l ¶!üÛSÞKŒ^ÎóüKÊR*.FµN&Kv‡/ç 'ëèŽm,¾ß\ë¥ÆÔºSí0?‘å£ÀàG`5ø2¼+8 jóŽÏ2Ⳍø¼žø<"?Ô1>·Q®‚ÓˆÏZâ³a^ñ)ëQkâ,C5ba›õ‡`°-ü<8üÌ^2>P4ÕzýÅœnvÒ7­ƒ©»³·}±ºU eÁæèjpa³Ñz°^•‚ ßÑàÀ‹>fö$ÿ Ú¸”JjeõvBK¾çØŠˆýCñ|î=‡±ä(Û·ˆÕ·Ëì=¸¡˜Üœ N—;ØÄÞÔ)û[Aˆ»©ß´ªÐkü;Ó˜}Ù4ÚáßžmÛýøÿ"&`wd>c âõªA‹(Ñrø^ ¯w!û~—½ñ-Göн©ˆɵU¯S» ¶!/«€Òùp19ŸtA¬ãø<”ø¬#>÷Ÿf¨“Ÿ•Ägñ¹½óø‡)ça¹F ᳎¯^ «ÁgmÆgSýé4>½ˆ~gÖJ*Å*ź6Z~%l¥[B¥¸o¤*…=`GKÕŽÀ,_ œ °'|ð.žÂßM.¥½ƒî×ÃX3ÊöŸþ#ˆÉúâyä§}ÀàI@·£#ø2¥’ÜÁº7.;eо¢g9çJàˆ_±v“…ëÜ.Œ¥ÇÙiðÚ0…&D@Á¶ceËŽ–bnÇËXؾ(ö¦®s v pßEzŽIƒ“Xpôoc¯o«Á)ÄìFÒ¡ZÆgøSûp^¾÷º: œJüoi ~Ƨ»øØpteö. Ý ßÊoE¶B[±mÈlœ¬¸—Rݯ%­g£å‹üm³µS­h¤lÄô'F&^ˆúªßÒ ‹Ã5>ÚrÙ¥…]Á{*x6x;x øpVà*deÑÈ >Ž˹ÎÖÿ¹gãçÀ©â/w‚«2F|þ\æõuGшºÇÇ>^ƒÂºåvë–û‡ Cø-‹fu¿*ú^“Š»¢o²þؘzòQÈN5bu3k>(¸–&]~NÞNìjð•æuOv(ÜÛ¶möñãÄV]h¾?’8; 7—e|æb¨e{×¢_9O’^!c¡³TÙó¨Hvl¤ˆ‡G½âé¼á½*¬ ý€sÛ\ ÞÞ ìË·3%äZ°ˆñ+ÙYmOF¸?+g0ÞÏ~?Ž*å@‘Q B¯Ñ:šåÒk¡p†°(œ2eW$Ý®?n‹µqtŸ`Ëü Í2ª¢oÙwË'ÿ ;ÑÁ÷ØYãµ|û\Àµü?Hcúÿc,‘eþû!é -ã3@v‰ã!œþðJ`{õIðbâjÝe|æÃRË>½Š~’ÞBhÍc$h%R¯D.öGtJ®9çóG`û9>öÙþ7 dlŽÝK_.aiCq»*‚1BÑœë ¶Æ­úÕ=ÅPÑr‡€+Ž®Áß’¼l¶Žôc6 ܬ÷Q÷£Cb½ÎªÂo@¸Îò»Oˆþ¼ßœØÓ9öt„b'Ò£À§ÉËÁjp2ûÜHÚWËøô“˜Ùå?¬vdOûÂõ-¤YƧ»øXiº¶$½;Ò»&¼³Âo¬—"‹{Ïû{߿ሯƒKÁ£À‹Á>Àõç›ßσ¿7/í`à]ó?p¡i6õׂ7 ¾¾´#ÀSÀ?»€ýx PÞæ2˰Ÿ|L©!„úYS ç*m»íЧìh³ÆRCXc„¯ V_Etâ˜a¥UÑ·üÑ1±œ ¼B/\n×÷s”Ï>m6çf6ÄôÿAäW§ÿBºøô¿~YƧG&‰M/Ó÷s}zÆg.†Z¶÷$úÍs%é-¤.ô"•,Ä^AÑW |ò{›òÉŠùò¯ÙI1~7¸|(ð6ßçíHð[àÝ ØG‚OíðFRþ·¹÷ÏwPðßîîÿPð,p%x>X æcv ¹y„!4mQVÕËšgË©*ˆæÃ\'s2®êŸu6:6Æ6|¬Çêšu>`™–3Êjyú ܯ|ý&ß“q ïù^ÈåîM£—‚U ¦ÿObûYîÉ8Gù¢'N’ñéIâr‡^ ΟLß³ûì–ñ™Ÿv[{ý$½­µY§( ¢!úHþòy–ï ì· PXmÊ[íxVl ~žšÕýï2·éŸ%»³™±D{Åß2í´ý@uÿrå ÿ<®ñÓÇU1ôÌá·} ¿¥Ò³ÈÌP‚žWS4/Àò†È· ji«ßžËÐÅ ¢Œ–9Êânj™«i_¿Z^øs&á_<>Šô(Pþ÷éÿXוe|æOüÛb VãèjúžãæeŸyÑ4¹SÏ¢ï™:$Ý9P§FËFÉ‹40´F‹Ïì·Ékpâ×à|‚iðùоc½–¢9îÔ. uزÈÌÀzq’V{x.0šÌÏgjß}õEŸ6~õSŸõ”LUÓiâzøôýôOm¿&ãÓž—vkû&ú-¤[ɵ¨ô6Zöú£a®6TæÅ°--?·l”H£r¹匆;ˆ§Á¯é"þ|ÁÜ"¾#»½ï4±ëÖj»³âdp °3ña0›]ÀÆ‹ÁÛÀ^ÀûüšŒk>(è$í|Ìr..=ªŠ}5þ‡XÍç¬uÙ§³†Ð[W÷Îj\ܦµ¦µƒý/—ñ¹ÕOr]SöC$ä6 m“Wü¿ Šà•uø$y¹\ |úÿÒNM_½Öôs³ˆœí‚¯GƒUÀ6ý$0Ðé{Îß­mvñ™Q}}?Lá7áÂló¦6åŠi»†˜Õ“‡ûË,›VM˲7×Yf—C†t-öeõH™¼nÂ|zÍ<~éå”ýÿlúú R›KGåG6×QÍéúƒ˜®w?'Y—‚V³ñTðMðÎÊÆ?#ïþûwà¯À|¦ø½ÑøNø&«þ–›ëÿ¯YgôA†dÖÔúizs3ï+y"c1ÜÎ÷ ‡Á|ÍëVÌõ¬à¦ÍmÅ |¢Ý:Í:cÝ׬K>»íD¹rØÿ¨ÆÕéÿ§ñù«€Óÿ?#] ¾Ä>ÓžþŸ3>K‰Ï2ⳜøÜ^³øøÆÑåÄgKâsgçñÁ÷:Lßš™mÎødý™$Ïž÷äB¿3Í@xZ3­Ú07Ö´ÿû·ßÚÝÚÙm¦Õü‚6Pݹ ɇ”PÅÁ±÷ÖÀ ô‡ ¬ïá…ÈÏ™|莕³Ú½lUv¬öÑ}ks›Ñ;À€v5ðiþ¹0‹ý–mžÛ’UÍÙ„ÖϨn¯æÿ g—1æø%9q ø9°D6Þ€ZýRå™b- –Œ)ïã7+åaÇ­§<71å ¼`¹ œNeÖe‚ï{¬)>Ë’#A£iªø ¯"š6<|@'ÿv/_Vg‚/‚“(ây¤Ö§h·"6›â³„ølK|^K|öpïšññáYŸ£9»ŸµsÇ·[§ïWs†òÝ÷¤µ±Yã“õgZœ*úÓ>­¹¢¤™6/Èú:5B½¿Ž•M3E ´>ÁtúQÜOwj³?v#§qJÿ¹ÀO°Ét6àÉ`Ðv*Ï(\TÎ(î»6aWäಠüå<»1µ²f¼ŒYŒì‚ÿ :[)N!VFrÌ®–±_Z‡ÜÜ:Ââï¯*þµ3bñH u8‡ÕàTp˜Ÿ¥ÄçÏÙÏÚ6 æ³:ǃEÄg¢m|Âÿ£ÙËxÚ¾ûžõµ°¬?…aAD¿³"æÞ2Ð"úŽÑéo”ä'“;†ª¿SÄJì/Êþ`X£ž0r¹§¼±àSJŽó »7(öû‚}ÀoÀ´Š/²ixF¬E*øŽ•…F<|¯øÿw£dWRØ·3òŽn¼áÁYUüËÝ;Ú„&¦ÿ_Cù½¾> Ζ¿Ÿw‘³«9Jf'ýcÄgýd|,½±z-x8œD|.$­µeýé<<)úsVû#¨1jTØCôíoŠ´œÂCt[•²X{of)à²í#<£°¾øäóÛÁ]À®Ç}ÀéýµÀéýxÖd7–í´ÂŽB»Î€×³f¬bºX±ß,'jg0Ö<€·Ëѳ3Æ“yõóšrÜo„Â/Í„¤–£}Ê6iÍøX‡^þ Î Jp›ìnŒf|~@ ¾I|(®ÀŸßþHÕgÀW€±¥ødý!`ó5§«ÒÆ“ïäÙ¨ŠrdUÉ_Ìïƒ˜Ž·²Œ®]‚_–Ó÷¥ˆàHøkªÿbÒ~®iâß&7¡qß…$:Žvœ¤Þ—õž«Úø…ËœÊéÞžŒsWï[é+úûóôË>¼Ñp4EVçÚZ]Îù<Ž%9«^ƒÆÁ‡útL‰ëkc•øXö¯³Á£Áqe|áø<øœYÆççøóBppFÆëÝën”⢟õ‡ÀÍe)ús14šÛ£!5-XR+³×†ò^ëwx†þYT|«É(šw‰¿‹oŠo“ÛÐDˆø\ú?aaŸ9‡8L ÿŽ,DgÀÔŸGÑ(;Íåëæóyì«ÅÔ¾lˆ¾÷ïßÄ“ÞÓ]³ëòj®¯Ïodlÿ–"6qês\Ÿuõ³]|îâæËNÅïQn=½¶#>ßf¾oCy;̈£QOÖŸ9jÓ(_¶s¸¶ÙoŽ5*r£éõ%.(vç…86¾£gçÒ8mQÜ@Áov¿ª¸Ø€ÉC׆€ÿšƒÅ÷ª'Aôw`9:'ÿ¼æòl‹N@¤v®æ\–­4ö‰Q¾üÇHÅéý唸YLö,Œ^‚,ž^~OÄÛLÕø˜ŸäÂ]ëfsÆgÿ1ˆÏþÄç;cŸ¬?3V©ý©Ý Ž4i´;E/zî6´Š£XÇîùÑ›ðÀÝ"Æ-£e7RÜ3ËÎÊçÈyÏÞûÄ!üUÑŽ›ûkPìCƒÿ·‰É“ûÏNT;oh.?’mW¯v~Åò5@wì%äAËÑ‹ ŸfaÍJÆÅkK>.bÉN±Ò_;;‹¼N5òu´²Œlj|VRö‡Õ±¸–é‘쿜˜¬³ødý™õBÍQÞ¬.åÆ&!xŠ~Œ²‚ßÊ›è|¦ü.ý=#Ä™eý%_T¾$ظ¢_~}Tø{åsŽŽ ýº\NüU±=8‘2q ðklN¥ ¾|PðBð)ðvðëm”gUóñF×ý-Pvµƒ«›ø(é-à[à…àZЫùtBQ<èstlôOÄlÙÚ™eÓG–×NËÅž=Ƈ“”ö]þËõlf<þn†Ö°Þp/¶çƧ×ú#ŸÖëÀ™.ÌÓº9f®S þx1§/ ŸPc¤¯@úÀά½€fxk¾ðör¾°³¤|¹ÎCØÍÙZ˜Ü .çàÇÏOðíü¢¸Øôé~)øúYýZŒ"~ËúÓ&ª/PÚšuû¿èødå¾dètx÷æ÷ZMq?¿¹ò¿7Óï‘*¢íî_?/ÎhÂ~@æ«Í…?m¦×LÏ }i3ßi²,ŧ†è~|ôÅk®W³Vjm$åÿ[ù2x>8°\3õ_»c¦îÑù’¾ô¹þä´1d€‘¥÷õ=áå(þ}§*½Øð^Ê·Û¯%]ÑD눌ÕCíÄè¼Qö š¤ûËr+M¤á·£àë_Uô¥!ÎŦzq 1‘ûêhÒ×6å«”z)êV|XóFýà( àœ 4ÙR„Ä@‘yø Äõ˶áD–5Fʳ‡Ïò jesÆÇZÒyµ~<ؘ_¼bŸÞöoß—‚@ÄâJòn;œÞìØx"èÖ¼V/ïŸølÝ-•㌱üWÏeËcçÙšv¢ßîvíÉPRô{ŠÈH¬è…àÛ„ã.¢I‰}”a‡ÀíÑ8“-m ´eÑL£Ã¢ÛYQôx›OeJá·*ÆH¿UôÙTk“O9ž—©<üÖ›=¯r¸"£)èÞÏ׌®Q{Åÿ/Áß´³@?LQ™(§Ã«×œ×]ø­ÆÖõûúÓã3A|z¹ù²'ž íšFRŽüÍ® ÇëÁ»@Õ<Æ™—ßlnxé£ÀcšËÝ$ú²qŒâÓú<Ïw±ÂVæÆæ†_“:ê·U•ÿªÅ1Õu½äP¬ˆiãË@§â©p*Š6¸ §®šûxùº])p[̸o4|d‡2*³<ªè[¶}« ÂßNôõÅã„ç©£Éiðâ'׋‰Ä²ò×úQjûpÄr1ø.Ð~'?õ¡ÍåãI¯íF1¬îÊl´ôiS'ÓOŒk¯«Sá ™ã3/ÝŠ¾5lŸ6¥ñXÑÜæ˜»Õ|Í}ÃÌß ]¦ú2Qv3£{ØöDôËÚ®¯ÝÊ´c^sý4g^>]9áWÈ ­_ãÆÙ¦ÿ@ýIÑŸNóجqjÛA…x*úÑàF-v›ëCðm^lv¼6Ü×FZ‹ýKƒýßNô-cu´¯ðÛAv,¿ûØêÓ(üBâtqYŒ÷!+Nëõj!1çs4²¸85¬è;Ú„¥Ee\¼–á÷0¯©n¼‹rF¹áÁ:®¸å}ÅÛ«ÖO*¯ÞnŠÛÅ1ÖœEå8v¹;ÀÀ±­jë–u®|5i-Œ6M–lŸì²ØUr~[°=­ÕW)ù¶}©?œ°´ýøÿ6p8؉~!†z þ £Ø°“¶€ ( ÑüG#Åq[Œð­ÜUÁWìrTVí¬Xþè˜XÎõÛd¹,Ü®/!ú_g Á‹˜˜.‚ñµeÜ’Û~¡y"#ù`àd cŸÚûÁ)e®ñÓDÍl_-}jÈYøj£ùHë/Ë¥EùLýµ¥7å¦>ýÛ¾yž‹Hí„}gžçõJ×dךÛYƒÆ)>ý¬?Á§³3â è?ü.†  þ¤è#p øÍѾ –㛉hÌ,•ëÊÔá{]T?>VÅ€€eXÎ(«ÂЇ)‚¯ß¬ n#½›ï[Á«"Åh^‘qÂý8ð p<Ð\¶që§ùäEcÔþµ¦ýü´Aœkjy·`¹¦ñ±ÄÆèÍàÁi`OPµGUšy»MË›ù’¾£™ï4QTÆ)>úÒÏúSå3:Y¶’òÔŸýao?ýóÇ3l&Bä[5„4Ä^Á6x·e´ÌÑa qŽJ5µJºŸz?J‚?•Û <ÞuM±[9…8uKïKïä"ì¥džlø)V;,öÅ®á,ëÊ×>÷åtC>‰×ÿT3>7ŸÇN]=¯¥C[öŠekßào€‚â¤úŸì—[/óIDAT'¬EUÐí|hvâ<ÆšÚ­ÝÌëÇ,>ƒª?»ÁÕYÝÝåq¨?)ú]ÆbSKQ$5ËŠ¥Í†M©"oZühü"eóÀÌri‘†è‡ð[ÞvÁ7‹8l*ïÚâj¾µÿLV8¼y @ Ê.Gò7‰JÄ4>­u9Ö×%ŸõÄç†>ÇÇÚ#üƒ›yï#+øsY¯£ÎˆÏ†IÑoGëò\¥ööéñvý´Ç¨?)úƒZMÎïè·ònc©ÍŽ£f_´ þ0ÄžbÕ ] :[{óíàßõ£fúþ™¿œ'íGÑö¼_RvÞ®núè>úþº¬éw]mz|®ìs|¬qǃKÀ‚#ÀÓÀ0ìª1ŒOÖŸY¯œýYé¯ ?)þЏyS`Å4ÄÞu²e¾šš´…TÓ²ì|°iˆ†©¦k±ocMýÿ·–7âpuq7þ݈»Ô߉YKx[ï/;“7 ÿªqŒü¬§Y 3Çç~|¹•R=¢%óÖÊ3úx¾ùœÊÆcŸ¬?³F߆>m3c TÈÆ¨8bEßéý*â!9Ÿ >çˆuýLãܦqÞ(ƒiµl1½¯+þcû¨™e–ÿˆA5qq^Ù5Ÿ¦–W&ŠËXñ Më,øú1{|.ƒøèÃcŸ¬?^Ãm-E¿--›ÇJŲi §ØœæŸ UîW~¶Ï,ËÕ,_”•Å‘{/®è¤„ð…*ޏËúu¾4A:ºf·í ø¹¾øvéÓ¦oWècjé ã8{|ÖŸï¯üQ5Ëþ]â°¡|Á•o…°þEçl´ã“õgÖ«2EVz6Ï¥šÖìßF¢<ü²¡Uô«žëY¾¾ü-A2#i_-}rZ¿ñ|ø&ÿBX¢£[77çŽÏ¥u+÷üÊs.eߢüæ¹Ïóü%8\Žk2fdXU;›;>þç¨ÚëOŠþ¨^Yî‘e 9²µÑ ᳑UôcÖÄ·¾ÈßFž=»’"¯æ‘ŠÏ‘sÌ·gª£É”Ú‰þ¼â³–è|ƒøØ¥5óy‘3‰ÏÚ2>Ÿaé¿–k ~i³àÆRã‹€O! a%[›W|²þ̰ý©É ÉÀ@ˆUñ‹Q¾¢¯@*”7±ö3åK[~ËÒ¨˜e}™Åå—Ð~ÍRõùŒèÔÄH²v‚_¡yîøL¿3ßxJåÐg-ëLjϢ)ñ¹—µgƒ7‚ç‚kÁ?¿OðnžûÝ™´n6w|²þ´YŠ~[Zre20ÚôÉÆC‹ ø¶þhŠ7g°V ­£Lê…eóGHÞ@Y72ŽÜXøRY_ûã«fâáLE?FûµéSư¹ã³”ø|Ÿ~À!w€ºÆÇ² ,ëñ™˜1>Î]|< ¬»€KþsÀëÀr–ëbsÇ'ëÏ´Xù“–ÓVæŠd ,4ž~-Òû§K€ïó1] |¨Êײø²\±ØÅys;ò’Ñ%4Ø[[„Ô­DL–ñE¶Û‹s(Øåànà·ø®9SáˆÒŽ€€˜ò¯å·/:ŽÏvÄg=ñYC|¬a|–Ÿ¥Äç®ÎãC¬–—UàéÀ_t8 í8ŸtA¬ãødý™ŒSŠþ$™I†Ë —3mоª¢ï»ñ~¿µ­Ø ;Âun¾‡Íãì4ÄlÝ0;1Rp¤Ï#Ä …¢®¸;‘Âoêºû€#÷-Gý‡ç¨¥e|Ê_×›8Ù‰`Ž^{«Á)ÄñFÒ¡ZÆgz|掴d X8P…STÄcô¢ÎªrÒØ} ; n·Ñuÿ°a¾i;ÑWÔwE¿:²ŸzÖëË1a¹®–ñ©DqwúÿƒÑõ¥Á«ÀÏÉÿ˜t5ø ûx Ë2>2#ý ËÝ“~1@C©HÇh_iþñǨ+¶™w½‚/í+øq²C™V¶‘ TEßΈÓö6øŽöþÝ»,~÷‹{ûèC}ï/f|æ÷¦Kxòºé;_« íIfŸùŧ5)ú­Œär20Dh¸b´®€+ä6 BWìMÞp›ûµŽô‡1ÊçcKk'ú yLñ‡ð›â>~|S¡|ÉRãtõýŸñé,6ðµG8ý¿ x®'Ó¸‘´ï–ñéœÒýÎ9Ë#’¾1@£¥X EßFRAw$¯¸‡ÈWó!øî/ì4 Sðù¸ÒþéÇí Gñ1šWä…˱Ύû ùž£Ö–ñé>åjkŸ¶´Ì{%üÙi})Xžœþ? ñÿ!iÏ–ñéŒÂýÎøÊ½“0Ðl¸ÿ˜îWØcä_Mû€bïþ a!ü¥ˆSE?¦î«"bïv÷¥­¯ïû”¯­e|ÚÒÒñJx|Ž^׫Oÿß@Úµu?³Žõ§ZwZRô»¾ÌòÀd  ÐhÅh=„<¦H£±ŠNAìg!Ìk‘6–ó?¦ä# Ñ7UØgÂÈ ¾4f|d¡¿§1ýÿÎüS°t5ý?C|¢Î˜Æ,™õÇeÓè0G½‰”M³¨7‘F}±~T…ßõ.W·÷¥Ãœ¢?°Øæ‰“ÎhÓpÙ…ÈG#eëýóÃh¬ü¬ªÙhEÃU :Ë6PæÛÁ!¾ëGÖ2>ƒ ¼¶›þ_Íõr^'ŸX‰OµÎØiöö˜iˆ½y÷Ѫu©±f°ÿ£ÎDý1úÒ*ú!ünï‹àëZо,¤%5b Òx… GÃT]6¯µ¦µƒý_m¸ü¤jX¤åv[¬ÁixgÏø Žk¸éÿU|Šb½œÌåsé¬V‰Kˆ~½©Åî<çUÀuZµN5Ö öÔ?%òÑY‘¾·Æ|×—ûö£¥èÃfZ2PGš˜E« {4R³9öŸmŸN·Í&Ú±-1Ï]®ëG#ÕiA‡µÆg°LÃïÓø„UÀ§ÿVƒ/qMM{ú¿"øŠyˆ~Lï…俬ä˯‹‹[Ê»ÕÄ|õV|ÓeEqiq[ùÇP²ê³0Ñ)ˆÔ·®HÑ<(X8*b³p…hùäq÷Wç\ÌøÌIQG;À§Óÿ/«À3A<ý_Nÿ·¾¢/÷ñ—ï+v(Žå—·æ,êiJømà|p*_o(ÞÍÛ->Ë’#þvâouëZøSôa5-H’d þ ð¤”Gc@LÿŸJþ&#{×/å?RœÂ~vFÁü5Ä#ÁÒâ8äþëäüêkLû÷ôb«}MK’d -èÄô¿Oÿ_ Ÿœ<ø^ñ)þï FÉ®¤°ogº]ñr×_rUÿrº¿ÛÑ~Š>l¦%É@2 Œ¿÷ï}Mµïþÿ#p08ƒ50p@ñ“Oé³z„ì :.'óÚrÜï3 "^x¥èoDô½Çß±IXZ2 $É@20R Tîå+‚_NŒÖòèë>Åá#*ø8@Ùóxßþä¼9á- o_u[÷»z`7EöÒ’d HFŽEO ÷Šâ.$r'ž÷÷žþèš2ÿj„YñFrÎd¸FBôKág¹cKÑ< H’d`!¨ŒòÕ°xZßÑðrFÈÏâ×¶‚ÀoQоc@Äh_ߺåsÜOXú´d H’Í•?DßQ°8°ØŽu;%Á‡•¥ÐïKNÁ ¾(}ïfŠßÓ’d H’Qb Fºj˜SÞ ¡¢x ï™ï}‹Ó¼\Û<Ý¥¤'€?«×i®ý¶ýËý9mLïÛ± á—ƒàaÞŸ,aiÉ@2 $ÉÀ¨1 ~…6Fú[ò ž½úøÆ½]›”lOz-x'8h§ƒ¿-sEq©è·í/ËʇùìÐèctpô½cÁ·x)ú²– $É@20J Ä(7„¿ñ ßÒbïb÷>ºátÍqöË\QœHúÁfÞ€£ýCÀ=ÍuýLôe>5f1ô1D_ÿ»~O– $É@2 ŒÍûØ ^ FûKxˆo^¸Û?«Þ(ðEÀ/~[>:Ï ï³õ[Ã@¿mNø`éQc&cÓý|KŸŽ>5E¿#ºrçd H’š0£]Å_\Â+mVðð[ÿÌéý³ZN÷o,º¹î!Íôy-ûôkÑÎÄDy»¢:Ê××èðØò%{ó~¿¦%É@2 $£Â@ŒpCøBô#úËÊQ÷ =qÄÿáæœIzû?LÑ×§MÓú!ørЕ¥èwE[” $É@2°À LÿÅL¾â:ý(ðI~mðš27˜{ùÍS÷“YTþàNµƒ~w%ü9½äfš $É@20* „àUp“ükK¡„nžôŸI}A®Oïk;6’üWôõiÓC{úk ê·ùyOï§èÃVZ2 $ÉÀH2Ð*~÷÷ˆ'ŽòœÒÚ_ä”5¾àÜE«Ÿ±Üñ'§èwLY $É@2P¾©¶Ÿ¢½¦Øôôß¼³þNplóÔ~wß;ìƒ4Xw]q}??Âi‚´d H’d`ÔpJ{ê´öÚâêâŠòKuƒó塜Z Zðõàr|Y?)úS}mõÝýça)úó )wI’d ¨% ¡¿+€Ë‹‹ËåZ¶ãB]RúuuÓG××ð×e­µ3ÐX;ÃÿýˆÉÕÉ@2 $µe UèÂàêânDñÆÚ–{þ»Ž]ï/6ðÿþE禚Îÿœì™¢ß]¹s2 $É@ PôbÔ«ØWóç•€³‡"èÃDqgпªú¢ßñ¤èwLY $É@2°€ (xZ_Uð7püë|nbÀwö%Ô¿¤÷ü[_|›œ£}âžÒÿNÞÆÇ9r¤/ iÉ@2 $#Å@~Œ€CM¯/ñÍrô?RNMö«eÙÖ¿¬á_ttä¡#Ë‘~GtåÎÉ@2 $ É@sd£]ÅO!TG±Ž_¾;‘¿Üá=»’"¯f@þ@ñ9rŽùý‰ŸþªèGLJÍó·ýùs•{&É@2 ÔƒÇæô¾$¤%É@2 Œ1Å]é+”ØX\ÀOÕ|¡x#òk”Юd’ãizaÙ¾Þ@Y7ò¾¿ÅE,ùÞ5@ô«:Ú÷¨èø¿ù“|óß;÷L’d H˜N–"øzß*»%XüQ]~[àûòÄv`?^¦ó"&ÉwäÇx–0Žžþ&?vZ0[„x¯Dì—·ò‹}çPŽËÁÝà.pg35/°#`' ¦ü':}/_à {iÉ@2 $£Ã€B‡îWGúŽö ;"vtìKs—oa_Š˜^Kê[ò…ëÕ>; q‹{˜i[~¿–·¡¸§,·b®¨+îÂwîÇh?DÞ[“Óû >Ç–Ž›¦%É@2 $£Æ€‚¯ˆÇè?D]b…SÄïÓ+øî6 áÁ7mˆ~㙄¸-a‡E±÷§vª#ûvÓûìÒ¹åH¿sÎòˆd H’…g „SñŒ§÷q…SAWÄCðÝ®àWgÜ×}†9Ú·<ªè[6Gú–ÏѽÂßNôc”ﱞ§cKÑ< H’d`¡hNñ+|!žNï‡ØÇ¨Ým®Á÷þLïWGú±?›níD?nMT…߀¨ÞÃÑ/º™Ú׳}YHK’d EP…PS¸«âí¶á;=^|Gù": d‡jÕÎJÜž°cb9cԯػÜnjß㻲ý®h˃’d Hšæh_¡Ñ~Uô]Óþ iŒðÕ½ªà·vØ÷Ó'ÝNчˆ´d H’ÍŒôo‚'ù±‡È· jiˆ}Œò{[‹2Zf¡ ‡¸GG¥šöEðu4Gú²– $É@20Ê (¢å(˜´*¨Š¥"¯ðÇèÞ´*ø1;)›f–M‹4D?„ßò¶C雜òèþåËyz /M’d ¨Íö(Ü!葆Ø;ª{ø1Âý‡íDtLüÜRÐIc4ïr+œÓïYðý°}YHK’d  ZÄ?D=Ä>–MµÖ´±v°ÿc”_M£`€yô¾û{ø­®¤è·2’ËÉ@2 $#Ï@Süõ£*ì!ú³ùû϶O§ÛBàÛÛBøÝ§\×O±NÑ&2M’d {*Úø:qŸÉ¹ý™˜ÉõÉ@2 $ÉÀ˜13Œ™[éN2 $É@2 ´2¢ßÊH.'É@2 $cÊ@Šþ˜6ÝJ’d HZHÑoe$—“d H’1e EL›n%É@2 $­ ¤è·2’ËÉ@2 $ÉÀ˜2¢?¦M·’d H’VRô[Éåd H’d`LHÑÓÀ¦[É@2 $É@+)ú­Œär2 $É@20¦ ¤èi`Ó­d H’d •ÿ> §z=Ê ¬IEND®B`‚parslet-2.0.0/website/source/images/favicon1.ico000066400000000000000000000013171362225661100216020ustar00rootroot00000000000000‰PNG  IHDRóÿa pHYs  šœIDAT8’ÏKUAÇ¿gfî¯wßóiEý"‚"¡–”«þ€6AmZB´síÂZDíxáÊ+ú±ÊÚ„™¹(Q ,õê{ú~ÜwßÜéÌCAʨÜ3óýœs/cð¿‹x±¾ °{”¹ lü7cAÈvFùb@HqŸýl.[oË‘`)Ûš½»%Ì‚€­Ï9ŒÉ &Á5 |v^Äøö~Çì»Ùj¼À»b[B¸×=æe²Iô)úÈ­W0äžB'µAǯ3%+ò„Îù„m{²i4œè¥B­‹änÜóºé""1]ŸÕo8¾äžD^d¡Aœ@HòîsÄgs¾Q ®üv½ü¤þ]Ÿ¥°> °PðÔ$’Ç;B7nMñ‹µ¹&ã´0T«.ÅÝÜý„bXÛB\É?ÙÀÂ>Û:Lж»Æ]¬½jˆâË䎅9-øj½E[  Zؾ5'®0¹¼7”ˆgŒ\,ÔÞ¦õtÐÂv‰æ|´Õ-h?·k÷Æ<Œ·æÂñ’ÈŸÃÕÅúœîåêkMÚJ›Um%L TŸrß80NÉApÎEZ&ŠFb]×—žÜ€í®ì¥% ÎÇõwœ¤Ê Ádv8hï SEűD”F“†m†­OA§ÐªUÀï £W ª_5ZN»héðá$•ž'b¾°z»Qmôs‚?þ{Úÿ0×PíœÊá_ºBHvy.ù2­Ôç—®éDßú½òF¬dŽ`b«Ô ¡V$Õ>–_¬L'+W¹è³ ñV»Š§R™òÜÉÉŒY‰§Ó÷å/ñ0\Þ Ú|Fa>s£\Œg=ÇŸšÝ,ø—ÿ 73õ y ŸIEND®B`‚parslet-2.0.0/website/source/images/favicon2.ico000066400000000000000000000011051362225661100215760ustar00rootroot00000000000000‰PNG  IHDRóÿa pHYs  šœ÷IDAT8¥’ÏkAÇ¿ofv³“l~X,K ‚ZHµÐ¢P½‰ xЛ'OŠWÁƒ'=xЫýŠ–"Ô«("UAEôЛT¬(±Ù65&6ÝÝÙñ-Ò 5¥Ugٙݙïç½ï{ Ykñ?Cý LD’õENºÄßZë~ÚŠïr÷ã¬êÇ¢Çr{£wÅCúŠy©&º`€ô˜¯‡Ê2ì+›/z¢~7þÐx^æò­Xk"Ãg“8ˆ¬d•UŸ„õóZ4gL;˜nŸcöcÊ*Ë€–ƒ8áŽàˆpXØeö?¬¾¼!a{æDø†hi*¾`—íÃNbYÂ)5DwäëRZЂë+xÒ:eB€ü²Ñ,­Ô¦š—¾Íµo­ÁéÊJnRÅz6AâG”Ž{pz,? »1…ió,¸ßº­DO…Hg¶œíqEiwn>ª&„ qÍPT³óßg£¬x6,•®Ê´ð‹ùо™-¸7Þž©.¬mö^çôÅ4Á¿¼ÄÑ·3l–e£ó®Wy#q·ýÎEêv¸•½-ðß(Âö­*IEND®B`‚parslet-2.0.0/website/source/images/favicon3.ico000066400000000000000000000011771362225661100216100ustar00rootroot00000000000000‰PNG  IHDRóÿa pHYs  šœ1IDAT8’=hA†ßofwövïÂq&˜˜\¡DQ1`!b!B""b#ZXGKÁ"i-,¬ÄB,¬ÒX(vv"#‚……"j …hD&!‰žÑÜÞÍŒïœÁ ˆávþvæy¿¿ï=þ§‰ˆ!×/í ,«œ‹öâ¬êÇŽM²ŠôE[Óê×WߟJ$'ÍA\`PôÁÿS@:e4Æ%JÒœóÏô6v#"hECTL‘¿…@7 ª‚ñaŒê* ¬$h‘—l}üþl-¸æ(cì†ÇsZ‚¦P°˜®Ã4iäƒøïFXT×Íá-tÓò2M„|«–Ø9J×´ïè*´¼C°ÚR)bÜÁùuË­B)º«¢¬=ò·ð)ñYJÄ*&D¤_A]äüÄ ã²ªÂ©ð4‚$¡æGZ\"øpܧM…ò±Òn£¾=²"LÌ©xPn«^oõ.DÁ»EBs‚¸ Q¨jŸì#\§È¼GçPBíT/ß«×>ß©]‰`aâCÞHœ¯ÃÙY +”NÇ0ô­•}æ‚îJç€Vv&ÂÂý¯×.°‚S¡ Þ¯qlÒòPÞŸªâP,n•Ì<s»Âhf›y>Ó˜^}‘ߪ¯4'óï¯2JX(&¢kO&Åíf1ŸqV&/ó Øe'vÉ}²9ÞðÚ4A³ÑBKžµÎz[êMnJÍ]{?öåÆ6.m¶Ú÷é¼2Ò1^e»=ˆOf:;qwu»p¸ÿËèBè+~IEND®B`‚parslet-2.0.0/website/source/images/parsley_logo.png000066400000000000000000000450771362225661100226200ustar00rootroot00000000000000‰PNG  IHDRwÈœI6½ IDATxœíw|Õúÿ?gfK6 BB½) 5ˆ`AE@Š(‚ \½ÊE¹ˆ‚‚‚W¾*HA¤‹bhùIµÐ{ RBK ¤²»3ç÷ÇdgÏÎì&›¶ ›ó~å•×ìÙ™3ggv>û<ÏyÎ9„R Ž_Ào¥P.2!Dy©nprÁPÒ àp®2Np})$\e8œÜ ”r})$\e8÷°& ×—ÂÀU†ÃqƒjÂp})¹lÙ²|ËU†ãÏðü—Âsýúõ×_}РAIII$IZ¿~½,ËÞ×À=&Ž?Ãõ¥¬[·n̘1ñññlajjj¾.,WŽßÂ%Æn·Ÿ?^ÅZµjÆ|›’’òá‡.X°Àf³iÞÊoU\e8þ›Ë[Ö ”ž;wîðáÃ)))ñññüñÇùóçFcÏž=¿üòK³Ùìe=gÏž2dÈŽ;ܾ+Š"·e8e¶G©,™™y÷îÝøøøK—.ìš5kN:Uír¶Ùl¿ÿþû²eËbccõýÜÚƒ> ÔHМ ›,]¿xSÝ=<<<_« ç>Ãÿ$†Rú믿N:ÕM\ Ò’Ð(J  ¶/&ç?HĹ]NógäȑժUpþüù7þðÃûöíc£Â9­§ÚÀvIŽÇDÓ‘™˜©” †ÈÈÈ|}@®2œû ÿ“˜íÛ·ÿßÿýßæÍ›µñÝà!  J)dÀîðbÔ?uÚä<ɺ“Ó T»víÎ;¯_¿~Ù²eÛ·oWF9j Z‚¿ ç>¢´õ(uíÚ•²ö‡µù:*99ùwÞ8p ‹Äˆ@C`0ШN‰¡Ž?V_—@Κêù”QÀSÀÐaÀ£@( x?ÀàÐâªhpœš€\ír *‹ÅÒ¹sç|}^p[†Ã)çÎÛvô· uÃ~^÷óä)“CBB¼9êÔ©SC† Ù½{7[Hê­Ea$ÆE‚kðE ”°qÈ w]&6Î94´Ð ¨”( VÆRþq™:•Êäœ ãvbuäÊ´iÓ¦Y³f^^"®2œû€R5F)5-uÍÚ5ãÞ7ªµŽº²ùêO?ýôꫯæyàòåËÇŒsõêUµ„”ž ´9…‰*Y¶9–…š«¢F„)ct¨p(B-3¨ü5@¨ù!Ðf@Ààèó†S• 8¬¥*%.CGɱM.iŸSöúõë—_w ÑLïιñ×[©|®üÎ6PL¬ýií'?9~ì8"'Фn½‹³®Õ©U{ïÞ½‹ÅÓQ—.]?~üòåËIÐè„»/p5(ÔYFp•Å ¹\ @%  È‘àpQB0F‡¸0¹VHÇ 0/7doÈé]ªQ£Æþýûóú·e8÷¥ÁŠ9rìÈôéÓ׬\#ÉjÏ8e¿PùÁÈ㇎¯^½zàÀú£Sn-ønÁÜó.;Ç  ¡ )DÀîêÁÅ”Ðê qµqc›Ôj1V53Fßó-2þqlS׳Ë@.Ûv§8°nËø~y+Kƒ¯töÜÙiÓ¦ýøÃ©©©0m&€H€Ãe£ýg[ýúõÿú믰°0õ¨ÌÌÌu?ÿôÙŒÏN<鬋Ñ@' \º4ö‹º¡öû°£Q ÖÆ˜}Ø“ ŒŸ%2VŒ9…Ž‘s>šy‰!;.G®¢¢¢öìÙS³fÍ\@nËp8±Z­+V­3fÌÍë7pÐg€JÈæ@‚½¢ •pöìÙqûüÿ>·-ñçãW¯\½zõê“'O²µ‘Hà BSÆ„QŸp6W]€®ƒI`„‰0ûk:¡TµR* ÆUWC†2†ŒÃYÁ¶Ó™+<ô­¡“p[ÆŸð¿[Y²†ÌæÍ›§N›º{÷îœ8hh " LJG½0¸lìhÛ¹MHhè¡m‡o'1c…‚Aª ­Oäê±ÈŽjÆQb»™5‚ã(âêX)ŒÐ¨& 0º }rJ˜jÈ(\2L_‰ÖC9îRš5þüóϪUªìJr[†Sz))}IHH7nÜòåËÓ\6m3Çj¿cÏ–}ÎZ@ª‚6jƒ†"ÍéK†ëÃÏ&ÂPW{Lø–ÕÊüg²fœ•èÍ0ÖʃãÙÕŠ‘{YGœS|¾7ò½K ¸-ãOøß­ô½-c³ÙÖ®];aÂç DH ¡1Ô%( ÇIO¯Hn @jEQÁñ;.ëlê* ¬‚°±U\Ø|¸†rÁÔ¬«v]«½×"`tç()£1SÃjOÈù¨­Z·Ü±sg%iƒsÛ2œÒ‹%æüùó#FŒØ¸q£³È tmLÝÄM4}@2PPBÇøiuÁ! Êš\õ®^Vt4ÑÖÆ«å‡¸À¡)Ó#ÓlâPLEe1ýB¬ 9Eqܸq…‘p•áp¶oß>xðàþùÇY ò ¡5©s0´¾ˆupTÕ°1f t~Ì<Þ”y×mäE/1„8Vbàh¤&¿Ž /«8¬©{J!æ ÛoΠïK¯ôíÞ­G¾®¤žR‘éÄá”,ß|óM÷îÝ]$¦nC'‚;3‡0 û–ê[y’!}°†•0£»èˆ‹±V„„~&KŽ—w|ü—_~ 0äƒÇe8%Œ§5g‹;î{îܹìÝ뜎 háÍwV a:q ó€4{êÝv[c‰èUIã…éƒ5úÊUcGd̃îAg;•ì ’@%"¬‚Õ!1A–O'}Z$®2œ‡5X|fŽ9räå—_>s挳È< <À 2óèÂUqôQ}À%gJ#1l×’àè„r{”FbØ~.0¦–Z­ˆa»ÏU† ± D$Æ­$û°3èûöð·Û?ÜE÷˜ü‡û÷V²f‹æS‡Eóמ¿^êû’ËR!åN@&k:CFy€eW`UFptoiLvCÝ™óE“J£ñ­d]Ôu73`vµ§X{Ç®øJDñ*‘¦JrZÎnѦùoÿo[ùòù»¬žá¶ §äa&B¥T]n©Èϵs×Î>}úܺyËYT xš™ Mz.u·­s4!ÞÕK€Û—šÚ]·—ÈX1F‡C\…‰õ•l ”,JVB•Keêÿ¦¡Ä€« §¢MqÔ¼sçÎz¿À2"5AŸB/IAã¹Ýf4š|·úâ֨ѿŞ‹5Oro K6fÆ,R3hÔ? °ADj„¸‘XO8}¥×‡¿öô£Ü]¼‚ÃU†SQ…ÆSl¸ìرã…^¸sçŽó,õ@,Œ÷Žtþ ˜CÀ¼$îžšhê/ :Ñ™Ôæ©RW‰18º“Ød_ö@‡ÄI€â)Ø69%¦a놣GÊëBæ®2œ’G/%EnÎè%Ae†óÀÕ3‚«Üä"1¹˜'`ä€à”ÌyUô±Oh5Ыö[³Ú¤~%cìD ‚tW"+óxAaSæ|Z­| ﯪ—p•á”<žz²‹ÊÙ¹sgïÞ½]$¦ð``’bÜ2l8FŸÁJÝiô…;YÉÅÕR×} Û™:âÐ"Ó]-:äF#1޼»‰±Á(‹6Ñ.¬'ÒeçòñëÝ[v0[¸ÊpJ˜\t¤H²òþøã_|Ñeqèhà‡»Áš š ‹ºMsM’×èˆZR°2¤QªÛÓí ÖÔ"WËèPµDÐé‘C_`%‚,Ú vò‘·;%¦mç6ïü{„¨Í.¸ÊpJŒÜM•"qšöíÛ÷â‹/&&&:‹¢öî̸3dÔçYã±ûèc1 nÃ1j¡àAqÀHŒêF±±ÂÔ¯ô%±!¾Pv² vrЕT xGV«øÅ—_T ¬åá*®2œ£¸;v¬OŸ>ׯ_w5uX1²î¡Õ‡cÔœ7èDD/pÕÍïÖWÒD|Á„ŠYãÈ­™£ü79B0j ì ›jRLN,DdH$`5EZN}A³Ïj[¿]î³0p•á” ÞèKa4èܹs}úô¹té’³¨ÐÝE=ôá^ÙÃâÒ8WóD#(nC¼¹ÄYàåÑTHí3tÀä:“Í qdÄäHŒ"*S"leržyµ÷S½=|È¢çþú÷×­,V+æÚµk½zõÚ·™³.ð`v·AºÛÐ#`¶5q?ŪŒš&§W}äEt§Y`$Fy×ÈX1lÀˆ5Ž$‡ÊX•d<¡Ø,t®ÕªmË›6Vª•ç%- Ü–áø)))¯¿þ:+1¤èŒÄ¨î ÑI ë%ip붨DzÑuµPÐ¥w 4±èêQ¶ Ìšmz‡ CF¬€M‘™PúÈ B¥œÚ#"Ã.XXÜ®2?Ãf³½÷Þ{¿üò‹ZB"AŸ‰ÑçŰZãvÖ}ÌÅS¹àj.¬èM$·«zÄÑ*# éLU1Ù~k§Ä°KÈ&â*"¥æ4Îl6Í?7ºió|^à‚K‡sÿñå—_.Z´ÈùºèÓ@yu@]K(æð‘a{s4¹vlÿ‘¦/I­êר\R¢ÂÁÀAˆ“A.í¬šên2ã(I”2`• BØéœó3¿>äõÞϽèî=<.ã?Ü_·²8â2ëׯïÓ§Ov¶#›Õtw7ÒšuIdf[í¬qŽ\}·q6£Ù†ã¥ÆÓÉ%¾£6L‰ònÖÂXG°‘QÁÝHÊ„cd€B Í–aö‚| êèºn÷H» ÖW ÷òÂî1q|MMbÙ±cGÿþýcžª2+Fçâ(ÁƒÄ胾úr}j/Ü9A¬‚Àu7U¼Ô†±;›Xü ±±½+#¾u~4O#ç ,'²œói+W©¼páBŸI ¸Êp|Oq¤É;v¬_¿~iiŽ$Ð ¨ãX¤ ®Á0Ûš¤½!ã)X«WOŠ£¾%è6Üb .k€c,Dþcš±D•{ŽÄ² Û$;İ”Ø2sÌ‹Å2þüF åqA‹®2ŸR†Ì7 tíÚµœ×èÔsH ë(Á± f¸~|€ [Ñnõ‹¾ \ÝY *FÓ=À¸6Œ¸ºoà ð ÁLèPJft“ý vçâ³D&„ ²M‚ Bˆi¥˜}Ã9êúƒÇvïÖ=ïkZ¤p•áø”"7d¬VëðáÃ:ä,j4e%V\(cÅx’Í8l½÷äv6+(šÚ”¡ŒW€mÀE <ðP™w‚2ãÒ€m9²"ìÎ9R·õ£Ñ âZ¨7X±ÍQJø6Øüé¢N‚@- ˜ó k@O»ì"Tƒ<œ¢®k¿5»¬µÈ€ ‹T’I &ZK¬rwNjú)gÄ·Oß>Ó¦MsןÂU¦Ì‘ËkÅ„—†Ì7¾_ú½úÒnwûŒbûöí£ØyöCA;Æ›PO¥Š‹º¨«§@ ‹S¨Ì¿›ìÙ šâþ3’0Ðÿ ©ÀO [ó{çð0ä@$cÅ€ù ’sT„I$™P‰Rj›*§}s/yošZMÇŽçÍW"JŠ^e8ð÷ßFQeJ©~›Rj·Û­V«ò’b4M&“Ùl6›ÍAAAaaa*T/_¾¼Ùl.òÖ–ÔçÜjµ:tèØ±cýû÷·X,¾9už*C)?~ü•+WÔ’àà`ƒAûýŒ4hPzzzÎkðæˆø² ‘}¬Wïøä‹¡ºÔÙ¤ìÀMà(ÈaÐÛn\¤œ3Tý/ÐHöüzÕuçPààqÀØuƒ­$çêºD&fÙH¨`“³aBK•ôï²wÞUkjܤñ·‹¿ óxÅ}Hѫ̥K—bccãããÏŸ?Ÿ’âAÒAhÔ¨QÅŠ)¥éééiiiééé·oß¾{÷®,Ëf³9888""¢uëÖmÚ´‰ŽŽnÓ¦ W/Qשּׁ¬C‡mݺuÆ 'Nœ0½{÷V®y±6ÀKCæðáÃË—/gKÁÅEIII¥¸Ædgdd¤¤¤ìܹsÉ’%qqq¹ì9vìØ>ú( @yI)µZ­6›-33óìÙ³Û¶m‹e3;Fc‹-^{íµ^½zåÒQq»ÂtffæÁƒýõ× 6œ>}ZLyæÌ™°°0ߨLžB3hР%K–°%O?ýôÆM&“òÒjµöïßÿ‡~pîÑxR=Ί‘= ðŽq;ÞZpnFÙ!H.¿g„Áæ-#Ш²ôoí¾¤A?~üc=æéÝ*Uªä^C@@ÀàÁƒ·oßÞ·o_¶üâÅ‹C‡íׯŸF}8!§OŸ^¾|ù‰'ôSÚX²d‰FbØ%{–”eµY:ôüóÏ»HL8èÓ‰¡Ðú¹HŒvL8öT¦¼œ6 Aë€ýÀ Sè2``vµqlî$¦ 0TwDa¨k/µÓK‘I5•£ÁAÄœ%gÁ€ªæÈô5Y¬ÄX‚>Ÿ;½´I |`Ë(LŸ>ýý÷ß×—‡„„8p ^½z^Ö3vìØ©S§j ëׯ¿fÍš‡z¨°­¼ÏÑ{L7oÞœ9sæ¼yó\–Uà+[Æw)55µuëÖñññšòàÐà­Ð ùì±øô{Ì3 ô"É5Œæ)…;í€c[Ý`"Unì@2pä"h‚‡ÎJH%І m mDs’t„Ž÷paE&ÀÓ„FÓœÁÐ%õÈNõ$1B  f L"fÈ™v#4„§}ïÖçm  ˜1çCÿõv®Í-|¤2ëׯïÙ³§¾¼R¥J{÷î­YÓÛ0•$IƒfÖš4i—§Yäßèo¥òx?~|äÈ‘¿þú+û–Ï<¦<Ý¥?þøã±Ç“$ €àÙÒÐJÔªPQsIàNb4Ž’º!:´Æ$@þõd­8!Öìõd4#¨ AsÆ©2a–±L‹$ ´!H;Bpì¯N^¥Wä˜0`±À$ñžœ"î|“’¸Ó™Pøåì/þý¯7r¿Ô%…òe¢¢¢L&“Õª½uf³Y ïyƒ(гfÍJHHøí·ßØò'N <ø‡~ )‚æú Š‚4mÚtÞ¼yÍš5sNòTš8räHŽÄ¤¨2©Š;HUÐ'Aé³SÉmÄW³ÞˆÞ]R#/€wó §A¯6ÏýÐ0ÔP‹HeZ¶HÁ¨vx0¯1ÀY ‰"¨„P šã ÌXÖË)$F0YH€ mr–`+ØÊ%~|g¿³÷6$4äë9³þÕïUï®w à#•±X,z•1 ¢(櫪ÐÐÐ/¿üò‘GIMMeËãââ¦M›6iҤ¶Õ Ë—š^Ž*P·iyàadИÁÊ$ ´ hSGrŠÞQRÿr ‡ ›c IDATýblÀ3 gL÷âBˆUyP  ©ô µ…ËfV¤V»ÉÕÆ¨§Sb+uZ9ž•©³·H5aܺK Äƒf3ŒF˜D̦V{ ˹öIi§î©- Ÿ¿`Þ Ïï²m…Äw*ã6ÃÅ`0 ‡¨I“&¯¾úê¬Y³4åsçÎ0`@ýúõ ØJÿ¥4O<îòÛ#fÐçë Y„ f‘Ð KΉã¸l¿µ‚Þ„Qô% 8rô:Ü^b„¡Ž€F°7§ö!RNýpHƒÈDp=£^øT?Ž8ì  ÈÕK2RÑ c 1›¨É(Db°SLrà?–« neü㜕ªjµª‹-z¦S)ê´v‹TÆ`0èó8ˆ¢˜_[F¡ÿþ .ÌÊÊb ïܹóý÷ßss¦4àýT2.»©Ø „*•hΤٚ¥àØÐ2p‹QÞº œŽinŒ"ÂPG@4±·–mUåœ%œ$]jŸàaú+Õß±»„o]ŒÙu¸î)ç˜0&b2Áh$B»`§mÛèÍ5·liÎt¦M›~»øÛV-[ys‘K©Œ n¿s‚ ,Û¥E‹5r™‡°yóæ±cǤ•œ¢Ãû©dì“H¥?þ#wW¡4¢K^e NéÇ&1*£zLÄÕ(`µƒ5d؈¯*7Fà°`fŸ#ÄØJ¥¶ú‚D0Ö’T#½1¢î 7²4襁a¢0‚ÑDM"1ˆ ¢h0ÄÌK™7VÞ¹wÄÅ„©U«Ö´iÓúôéãùZ–4½ %¬2…™ÖÓ°lç´œ’ ¿SˆËì³ÈfÁ°YöÐ2š` ˜¯"1 ÀzÀ‘VB 0>"Ø»QkM)ç»»oO]àê‰46 q}ÿ‘ºÁ:S”©`†!€˜É@D‘¢Q¤éôæŽ;ɱ)öt6PŒ^½zM›6íÁÌër–0n{3K^e ,4ž¦•.D‹8yËýR¾aù½§’ÌÄe¨#…ºÆ5X5Ñ<Æj¿ª2à çX04$RoX’]œ/OÙÀ”ùföìzMQÕ:}qòª¹0&b4ƒH " £P¤ÈHZw7ó‚ˬVAAAï¿ÿþ¸qãŠcÙߢE#1ÊïMÉÇePsÆ9³´++V,X…wîÜùûï¿333«T©R£F¤±%$$¤¤¤4jäfЬ¬¬k×®%$$„……Õ­[×›y¤RSS¯\¹’”””‘‘a±X‚ƒƒ+T¨P©R%‹ÅâËñèÊ’e9--íöíÛׯ_ÏÌÌ -_¾|TTTHHHÁn¥ËGC†_è%čޒ8Äå¸B¤2ЃØÛQX\ãÇ`¶=_Ø^-µ\Ö•P×Càú–«÷ä̅уƒI$TÈŠ·ÞÙ|7eÿ=Mà©}ûöŸ~úéã?ž÷u,i< p!„”¼-SàcõLL&S­ZµòUObbâöíÛþùçíÛ·'''K’иqã×_}À€ùš.Ëf³íÝ»wÍš5«V­êÞ½û¢E‹ØwÓÓÓ—/_>wîÜ¿ÿþ;++Ëh46hÐàã?v; ÀÝ»w7mÚ´nݺ£GFFFfee%%%]»v-333**ªqãÆíÛ·â‰'¢££•ß|}v/QnSBB–-[~üñÇ?ÿü€ ƒÁn·ggg‡„„´mÛ¶]»vݺus«­¹¡÷ƒàÁõÐìFt}ÌFà2ÈoPº­„hȯUI·lN0›VÞ­I¢žTïµ¹m¤&ÊK@ ‚¢F3Ì" Fb"‘ÌsÙw·¦§î¹Gí.ç 1bÄûï¿_"IÛù%÷/^É«LÁ„&==ýèÑ£úòFÕ­[×ËJ:ôí·ßþôÓOׯ_×T¾wïÞ½{÷®_¿~Þ¼yÞ Â¼|ùò† V®\¹wï^e&•qáÂ…×^{mÇŽj‰Ýn?|øpŸ>}~üñÇnݺ±;SJ¿ûî»Ï>û,>>þᇞ={vÛ¶m- ¥4;;ûÔ©S«V­Z´hÑ–-[¶lÙb4xà®]»Ž9²R¥¢_Ö‹’””4cÆŒE‹%&&èÝ»÷«¯¾Ú¸qãÐÐÐäää={ö,[¶,66666ö“O>éÒ¥Ë|мysoëtYy’öÕ2GÙ[À&Ðl xr/ǼP‚;®&´ó¹¸÷˜àº¿º§ÆÚr ßÍ0Àh"&“`d‘ì3Ù)ÛÒÒeR›Ë#Jéի׸q㼿†¥œûUevíÚõÏ?ÿèË;vìèMJÞŽ;fΜ¹iÓ&«Õàv$'€ 6‚°jÕ*O¹ »wï^µjÕ† nßvÉÇ`sããã»wﮟ܀ÕjýüóÏ;wî¬îŸ’’òöÛo/[¶Œ2cÆŒÿþ÷¿lU&“)&&&&&fàÀo¼ñƾ}ûl6ÛéÓ§OŸ>ݱcÇ®]»¡9£Üš-[¶Œ3FÑô*UªÌ™3‡5¾ÂÃÃxà~ýú-X°à½÷Þ»wïÞÚµk·nÝúᇾ÷Þ{ùvèˆã•KAÿæÙŽR:ÈFРð ÐÒ1Æla冺Ê¢ÌþlÛÔ 6Z¤ Ó8UFÑÑ£‰˜MFÑHŒô.Òe¤îº—y6‹ºDx &&fìØ±žÌÛû”’ËŒ¥K—ê'ß ý÷¿ÿû{÷î4iÒîÝ»;uê4þü‡z(,,,11qïÞ½+V¬Ø³gfÿØØØ¸¸8·wýŸþy饗öîÝëöDêȉÄÄÄþýû»•µžÔÔÔ *HKK4hкuëŒ=úÝwßõtT³fÍbccûôé³{÷n¥ÄS ª`(³páÂaÆ)óxÖ¬Yó§Ÿ~jÑ¢t2!äÍ7ß´ÛíÿýïeYNII=ztRRÒ”)SòABeWñä©0»š’*6`3è-  ð:Pß±› «‡l.3E9ÕÁSÜWÝ}ÑK (1‚©h$F“`4 !S´ý-¥ÊLÛ›a¿Í.yCÓ¦Mß}÷ݾ}ûúfÊ÷"$϶ûr.Ë;w®_¿^_>xðà x:êìÙ³C† iß¾ý;w¶nݺfÍš¶hÑ¢víÚmÚ´6lØöíÛÝNµuð û™ªT©òñÇ?õÔSnßU 1cÆìß¿¿}ûö/½ô’Ûæ………©¶Ò´iÓ‰©Y³fž‹uEEE-Y²¤víÚÊË"LR$fîܹo½õ–"1*Tøþûï[´hA)Õ«”’!C†<ñÄjáôéÓ?ÿüsoNæZ³ˆúØ«¸]f@vÿå9$Fïã(­–õK®åºéé´òÁŽ€ns¬‘ŠAÄh0[ÄS¦I8Gî­ÍJœ’|ã³;É›ÓôÓ¼yóùóçÿþûï¼ï$Æî?•IKK?~¼sudÑÑÑ. ³3¤¤¤L™2å‘G™?~=~ùå—Ö­[ëw ˜2eJïÞÚAôžºÌM&S—.]âââ–,YR¹reÍ»ŠÊÄÆÆ®X±âÛo¿Ý¾}ûÊ•+ÿüóÏ!C†hö|ñÅ>|ø‹/¾P Ÿ~úioæN¯S§ÎŒ3{¡hÓ7nÜ8|øpe6OBÈ'Ÿ|Ò¡C‡\~µ(¥¢(öë×u'MšäI£Ú™§NŸ,˾Ôt')ãÀaÀô»JŒ*’«¾Ø™ ¶\ÒˆÜ PƒA°¦›ÑxËL÷¬e¶”Éi·>K¹{/ë•\®žÁ`xâ‰'¾ÿþûßÿý7Þp»¾]éÇí¯Ž†ûÏc;v¬ê#¨T­ZuáÂ…n³:ôÖ[o)®PhhèôéÓs¹‚ ôë×oíÚµl¡fä·þW_}5++K#ƒÁjµþïÿ›;wîÀ•°°°¹sç>ôÐCŸ}öÙÍ›7FãË/¿<|øpåÝ•+Wfdä æs;~Â-Ï=÷Ü /¼°fÍš¤¤$/ÉBHBBˆ#Ô ƒŸ|òÉ7Þðj¶V­Z™ÍfõŠ¥§§üñÇëׯÏ5@ÃX¢nJ'OQ;%4£D|wžÚ80Q3舫Ti¢È2 ³‘[€PÈ„º¯Àn¨UA$"¤Ã˜*⪈¨õ»íR¶œéñÙ«UéÙgŸÐ@‡ 6'ÁýÅý¤2’$Mœ8qöìÙšò5j¬X±¢U+7Cà?Þ½{÷k×®)/CBBòŒ ëg¨ðfvîêÕ«kJ‚‚‚~üñÇfÍš©£òŸÿüçÅ_ûìï¿]Ö˪U«Ö¨Q£ì鹺téÒñãÇÙ’‚)HÁòk7nÜøî»ïV­ZÕËý“““ÙÞtMËóäÝwß}å•WîÝ»W•Q¾1 ,`[Ò§OŪÏóû”’’2}út½é·oß>Y–=]FÂ>u‚«õFV4“à€{ ¿†¯µP .ÎŽ " „Åf‘j¼*΂ „Ø(2 ½I½ šNa•dA‚d…|OÒ._ƒëկ׾Cû¶Ä<Ò¶}ÕªUBɯW]„(’—cÖJ>_ÆSjcffæéÓ§7lذzõêÓ§O«åF£±^½z|å•WrÏ—«P¡Bhh(»­Q£FV.˜Êdddä+ëÁb±°.úÖ­[W­ZõÒK/yyxóæÍ?øàƒÁPHwéòåËìú„ƒNž]8å†l½ ¼Ò*P*   ¡DÙy%dB7Àº/7§¦ÀFDDD7nܸqt³èfÑͪW«®õý|I J\eRRR6mÚ$ËrfffvvvVVVjjêåË—¯^½zäÈÕkˆˆˆh×®]»víZµjÕ¬Y3o†DFDD|øá‡Ã‡Wlø:uêüïÿ+ÀO}Áâs­ZµŠŠŠò~ÿŠ+–/_>99gù‹ìììaÆ%''<ØËQ @Aí5•¿þúKm³Ùl4Ô/–‚" Ê—LE‹ÅR£F:uêFý7Ïb±è=JƒÀ| ÕØªÀ¬åÊ~ uÕ¤tÐ ¤vTÕܘœtAÒþ0(!©—L¨]F`…Y0&c’‘^!4ÀîˆËÈ4--M“%‚ÑhTæµZªT©òàƒV­R52*²~½ú6 ˆˆ(ýc I¾`%¬2 Ó§O ˲,Ë&“Éd2Æ   gžy¦zõêUªT©\¹råÊ•kÖ¬Y®\¹üÖÿæ›o6nÜø¯¿þ ïÒ¥‹¾¿YÞr)˜-“ߣ‚‚‚bbbØpLRRÒСC—.]úüóÏwëÖ­V­Z¹'SR_”ÇCÓ1sæÌbMâÐ6[]zM ¬°9»ªß „Ô&*ÊΜ] 2(…ÁQõ¬Ó2¼EòTª\-" "ÜXÑ,YŒ0 ”"Bì6ûéÓ§oÞ¼yïÞ=‚ DDDY,£ÑZ—åQoV¾æ÷ð‘ʨ¿~4h°iÓ&õ·ZpP„§nß¾}ûöí½ßÿÆšŸõ5¾üòË«W¯ÖÎ;7uêÔŸþYÓì³Ù:wîüÌ3ÏlÙ²EÿVFFÆñãÇ?¾lÙ2ƒÁݶmÛgŸ}¶U«VE%7V«•í’`2™ô•{ß¹à nTFÐÅbØ|_ ˜€(˜DcÅh%Fїʦðg«t~þ®úXysù"ip™%¿á=?²,{ú1, Kݸqc̘1:tHOO?~¼æ]ŸÙ2&“iΜ9M›6Í}7»Ý~óæÍ¸¸¸‰'¶oß¾K—.ÊPïÂ?öéééš™ÓÓÓÕ…ŠVb˜ŒÌoŒ¤óŒ‹!ŽL¼H˜ÌNG‰§ÄH%Y’$ésõ‘ †­ï¼iÞSßu©Ó“KLaPãq(Э/y•)Y233gΜ³eË–yóæ­^½ºAƒš¦úr¦¨Úµk¯[·.::ÚËý­Vk\\ܳÏ>û駟ʲ\ȇ?==]ãØ¦¤¤°=Óúïa(ØI#*2yCª¤»fHòS AbÉš0’,A¦OTˆùºõŒ­=vüïÑ™­¢bDÁÿ“kK9>ò˜”à®oÎå=›6m7nÜ•+WFŽùÖ[o)Á<}º‡/U@ݺuãââ,X0wî\ÿ≌ŒŒ>ú(99ùóÏ?¡À‚¢1ܲ²²nݺ¥Œg%¦mu´'¤;0˜e¤Á2¡0”cý9H²D@Ú—oùï†CºÔéY1°€ó%rXT …˜¤>³e$IÒÞ%K||üK/½ôÜsÏÕ¯_ÿ?þ3fL.ý>V‘‘‘ãÆÛ³gÏܹs{÷îéÍ þâ‹/æÎ[˜ói²Z²²²”áŽÅ$1ê׫o4:2ÖnJ> È¬«@ œ¨Æ‹â(IT’$©mh³…1³î¶å_M^çSxTÿ¨HdÙ2Jú–Û·|ìIeggÏ™3gòäÉ¢(®^½úùçŸÏóß«ŒBõêÕ‡ 2dÈ[·n:tèĉ‡:xðà¥K—4Yü*&LxöÙgkÕªU°«ªÄ•5c28ÀŽ´.òŽ­°°0‹Å’3ø;4(ï˜$\ÍV\§˜ 9† % C¢ÕÌ߬ÿæ ÆoT Ñ%ãö›S€@¯[JØ–ñ±'uðàÁN:½ûjÕÚµk—7F=Ù¹sç‘#G®X±âÈ‘#;wîüøãÝNoœ””´xñâŸÈl6ë×âøý÷ß³³³‹ÃŠQˆŠŠR&jH­ö+•ƒÉbpJ • Ó>UºÿðÔOãÚ~Ê%¦H`s/Qt·Ûw*ãVMr±qŠœ tîÜy×®]111ëׯ¯_¿¾—–”-ã‹Å3a„mÛ¶uïÞ]¿Ã¶mÛ¬Vk¾”RAyäMùáÇwíÚ…â‘ÁÁÁÎ Ée@¢À¦É°å§ÄHR c¥9­¾ø¦Ó²¶UybK@ ‚qK ÷1I’äiìØ±o¾ùfRRRxxø‚ ¼™0\Å7*sûöíÜ'²ÑP»ví+VÄÄÄhÊÏž=›ššZàf´iÓFóy%IZ²dI+ô†> n“Ë€Ýá+)挨“™P T’¤®•:®ë´þ?Íß 1Ý—3?•6ØD˜âø-ñʸµY|ã13fêÔ©Êv¿~ýr™…À-¾Q™E‹}÷Ýwù:$88xâÄ‰šœÆ´´4%/¾`4mÚTï‹­[·Ž]}!¿\»vmÓ¦M¹ìЦ]u%zHrô1 ä.11ЧÉ*ø¸Éû+žYÛ¢r›·‡£RÈD/ñ‘ÊØl6ýdà”q½Åzêo¿ýV*¥ ÷J ™™™K—.ÍïÕˆ‰‰ÑŒ2EQ?——PJË•+÷ꫯêÛ6zôhu¿ü²dÉ· N¨´kùpµÚŽ)2²€ƒ€Ü~ù˜O­ m™ÿ³•ÿ5paÛÙÚM-ïõ=¬‹T¬'òʸ}~¬V«[õ)*•Y Ô’<së¯xŸ®|a6›÷ìÙsòäÉ|\£F ¶¤B… Þϳç–þýû—/¯Í”Ý¿ÿˆ# ð“púôé+VtíÚ5—}*”ÿ×+Œ´~ùØ šª$0Éh–ÑêÕy¬QÁñ†â Á¸ÅG*“žžî6þbµZ½™ï²Àüþûï.\`KNœ8‘û!úõF4k‚ ȲüÍ7ßäë(ý0ÔjÕªxµòÍ«S§ŽÞœ°`Á‚áÇçëWáêÕ«½{÷nÛ¶­Kê;†½5¬FM‡\fG@uAªîz <‘·°x8Rñ‘ÊܹsÇmyqÛ2—/_Ö”¬Zµ*--ÍÓþ©©©úàÈ‘#GôqÙ;wêGož%K–>|Øûým6›fRñN:™ÍæÄÔÙ¯ÝØ±c6l¨ßgöìÙ={öôr¿?þøã™gž¹råʨQ£òܹR¥JÏ÷îåö-£Åøèc–/_þÞ{W§âx‰Ï¼$©Œ§YlSRR ªÌýde‡ž8q¢[Ëÿܹso¿ývëÖ­5qcÇŽiLŒk×®}õÕWÅqŸÒÒÒ>úè#ï“‹/*«Ê*{?½‹úµSþWªT髯¾r¦ä2lÞ¼¹cÇŽcÆŒ9tèÛä@I’Nž<9lذÎ;Ÿ:ujâĉÞ$ üúë¯q›Õ–† Ó°'wíß¹rŪäääü±tކ»/ð±—Äâ£Ü_O~JvvöÙ³gó½~»×T¯^]?§ÿŒ3.^¼8tèÐÆƬ¬¬3gÎÄÅÅ-]ºt̘1O<ñÄ×_ÍZX²,1âÀ½zõ ?}úôœ9s6lX‹RؼyóÚµkûöíëÍÎÛ·ogÃF#FŒÐöÌÄ(<ýôÓŸ}öÙ¨Q£ôµ%%%M›6í‹/¾hÙ²eóæÍ###+W®”’’rñâÅÇïÙ³Gùñèß¿ÿСCs?»,ËS§N2eŠþ÷¦WŸž]yö»™K\÷cbbâàÁƒ{öìYàØvYÆIyŸ¾XILLt›¨ªÐ¥K%k¦˜N­ Žª‚¤\úÑ£G˲|çÎjÕªyj­’l6›÷íÛÇžhãÆš=_xá…|5uÚ´iê±ÕªU;räHž‡$''³ݹsç{÷îQJeW”=5‹UEFFÞ¹s‡Rª¤e«{jøúë¯ üT÷èÑ#%%%÷‘‘ñÚk¯yªÁlÊ™>µcÇŽk×®ÍÎÎÎ×%åPÇ—½Ë¾§ØU&99¹OŸ>¹ÇŽk³ÙŠ©l7v.¨hSJß~ûíÜwþè£4gÑ,à¹çžËW;'MšÄþàƒj„LCvv6£íÛ·ïíÛ·©;‰¡”^»vM3ŸiDDDrr2¥4‰QX¹re.?žxçwÒÒÒrÿÈwïÞíÖ­›æÀèÖÑêɰ°°^½zýüóÏV«5_“CK‡¾(—ÊȲœ˜˜øý÷ß»]+VOçηoßž™™Yä-¹wï^¿~ýr9uóæÍ×®]ËríÚµ\šÝ­[·ÔÔTvÿ„„„.]ºhv _»v­Ò¹æ èÑ£ÛÑöå—_*‡~çÎ;+»Õ®]û믿V~çÕûÊî|÷îÝÑ£GkLeƒÁ „9¼áòåËÇ÷²ƒ¼eË–šëé–¤¤¤G}”=000ðË/¿ÌÈȘ4yR‡&MštåÊ/[ÈÑ | JƒÄȲ\Øuõ\¸paæÌ™<|øp~#» 6lÓ¦MµjÕ}ôÑN:U“233¿úê«yóæ]¿~]]i(  N:C† 8p ~÷÷ô îIDATÚ‡7nÌš5ë›o¾a;¶+V¬øïÿûý÷ßWÖÀ½téÒòåË;öÛo¿yZ=¶V­Z-[¶¬X±b¥J•š7oÞ£GÜã“'OîØ±c×®]{öìILLÌÌ̬\¹r§NêÖ­[µjU«ÕúÏ?ÿ8pàÀ’$=òÈ#={öìÙ³§2`B½•Êò&ëÖ­KLL¼råÊÑ£G=ÍSóøãW­Z5"""<<¼oß¾nÏS9zôh\\܆ Ž=ªOM(_¾|»víz÷îÝ»wï<§ÝNHHxá…Ø9†«T©2{öìçž{Ny){^¿‰“;´èfl(*Š^eΟ? Àd2å÷‹¢tl‹¢Ø AƒÜó¸ À;wN:uõêÕ´´´ÐÐÐ:uê4iÒ$÷Ô’K—.;vìÒ¥K6›­jÕª?ü0»Rí… Ö¯_o·ÛF£§à…ÍfS‚ C5zõêåå5IMM½téÒåË—/]º””””™™i³ÙL&S```XXXÆ ëÕ«WµjUökÄÞÊ#GŽlß¾Ýn·B”æé¿p²,ggg+ϳ(ŠÝºuÓÈÖ#Ëò™3g®\¹rûöíÄÄD»Ý®ˆTãÆóLŠQ8~üø+¯¼ÂvDGGóÍ7-[¶ôæp Ô·²¥ÅA©Ñ…¢WNIÁÞÊRõ%cÙ½{÷€.]º¤–<ûì³óæÍË%âž 2d’ç‚eêó\;ïá*ã?ä÷Vúþë¸yóæAƒ±NèàÁƒgÍšå¯k0ú†Ò¬/ Üõ-£øþùÃ? 0€•˜!C†,\¸KLQc½(ÅÖ+¸Êp|Ãwß}÷Úk¯±M† 6gΜ|Eî((7½î}Qà*S¶(‘ïåüùó‡Ê{ï½÷fΜ™¯fPP;µS·+Ô–1T‰)ýú¢ÀU¦láû1,3gÎ|çwØacÇŽýüóÏó[1£PÆ¿±”Ê%0©0”í{VÆð½»1cÆŒQ£F±#Ú?úè£)S¦ø¸þ¥T†LÁ,SuŸÀž•!|ü8eÊ”?þ˜w:iÒ¤?üÐg ð(¥1)÷]Ç=WN±ðÉ'ŸLœ8Qv̰E™:uêèÑ£K¶U÷¬á)C¦ 2¨HîËI¼¸Ê”|,¤”Ž?~Ê”)ês"ÂçŸ>bĜݟ  Ž?”ˆD¼Oó¹Ê”|ã.QJÇŽËÎba0¾øâ‹<‡¹sôHT’‰D(!î_‰W™2‚o Y–G=cÆ µÄl6Ïœ9ó7Þ(îSû„D¦¸ß%\eÊ>0d$I9rä—_~©–Ì™3gРAÅz^?F€(‚ ÷¹Ä€« §H$éÝwß9s¦Z8oÞ¼”`«üá~—p•áI’FŒ1kÖ,µ$((háÂ…/¿ür ¶Ê?ˆ?d´ùÃgà” z‰ ùöÛo¹Äu˜’ÀU¦¬P_YJéÈ‘#Y‰)W®ÜâÅ‹óœé™“;÷×0¥<á§à|ðÁl¸·|ùò‹/VgÕä ?“p•)Síw÷ÓO?:uªú2,,ì»ï¾ëÞ½{QÕ_–ñ'‰Ÿ+ÏŸðþVê¿ÄìÌäÞÔ0mÚ´±cǪGU¨Paùòåê² œBâgO%Wÿ!÷[™ËÌ24Ÿ«šj$&""bõêÕ;vÌg{9¹áO&WÿÁ›[©:Mš©È½—˜¯¿þúwÞQWòŽŠŠZ·n]Û¶móß^NnøÓƒÉUÆ(Ø­ÌWà»ï¾ûÏþ“™™©¼Œˆˆˆm×®]ÎËÉ¿y6¹Êø^ÞÊüúG*±±± P'Ö [¹rå3Ï<“ßz8ÞàO&Wÿ!Ï[Y`}ðÇ<÷ÜsêšJ^L¯^½ PÇüéÁä*ã?xº•jÜ·Àý£gÏžíÖ­Ûùóç•—AAA‹/~ñÅ V'Oüì©äù2þO!d_¿~}À€ªÄƹsçr‰)&üL_¸Êø!jGRá“»dY3fÌþýû•—‚ |òÉ'|¤u1á—®2~I¾–RËÉ“'/]ºT}ùÁð¹{‹vl$!ÄÏFð¸ŒÿPä+º­^½º_¿~jjÌ;ï¼3}út£ÑXTõsô7Îφ2q•ñŠVeΞ=ûôÓO_¹rEy9hРyóæ™L¦"©œ“'þ$4\eü‡"T™7ntíÚõðáÃÊËîÝ»/[¶,44´ð5s¼ÇožM>¿ G‹Íf6l˜*1O=õ—Naà*ãWɯßÔ©S×®]«l׬Ysþüù\b8…{LþƒêÉÆiŠ‹‹ëÑ£‡Õj²yóæöíÛ]9ùÀožMÞ“íW( š¨¡÷¢sëÖ­ÿþ÷¿ŠÄ˜4i—˜’Ÿ¢¿Ücò74ú’¯¯éäÉ“ããã•í×^{mذaEÜ8Žwø“Ä€{Lþ„r+ ì4ýòË/={ö´Ùl:vìøóÏ?‡„„}+9¹ÂÞÄ’nK‘ÁUÆe¹À ²²²:vìø×_¨]»öÖ­[ëÖ­[ôMäxÆŸD—ñ 3°`Ù²e{öì`±XæÍ›Ç%ÆÇø™‹¤Û2\¼xñᇾyó&!dêÔ©|¤’ïñïÇÛ2eI’FŽyóæM9rdI·¨tAA%j' DÛ&~lÈ€Û2œÕ«W¿ôÒKjÖ¬ùçŸV©R¥¤[TŠ ”J°KT‰(AqILqT[zà*S¦IIIyøá‡OŸ> `åÊ•ŠÜp(¥ (&eq9‹¿ÃóeÊ4ëׯW$æå—_æÓßi „ø@bÊ‚Êp[¦ì’––öè£9r¤aÆ¿ýö[åÊ•KºEeŽ2òôq[¦ì²zõê#GŽ„„„|ûí·\b|O‘p•)³X­Öyóæ=z4_’S¬p©ŒÛ«W¯ÆïÙ³'((¨¤›Sæ(SÏ·eÊ"²,Ïž=›RúÎ;ïp‰ñ=e$è«Âm™²È™3g¢£££¢¢Ž=Z®\¹’nNÙ¿¸…Û2e‘Ý»wggg÷èуKŒ)òu&î ¸Ê”E’’’!ݺu+醔9Êš¾(p•)sPJ÷ïßo2™bbbJº-e‘2(4|´dY¤mÛ¶•+WæqߤL…fxô·ŒR¦¾å¥²æS¥ì|Å9%W§(S*ÏU†Ãñ)e0FÁU†ÃñePbÀU†Ãñ ”RY–Kº%ïcâpŠ2þ”q•áp8Å ÷˜8NñÂU†Ãá/\e8NñÂU†Ãá/\e8NñÂU†Ãá/\e8NñÂU†Ãá/\e8NñÂU†Ãá/ÿRû àh”"nIEND®B`‚parslet-2.0.0/website/source/index.html.textile000066400000000000000000000020631362225661100216040ustar00rootroot00000000000000--- layout: layout title: About ---
      
        require 'parslet'
        include Parslet
      
        # Constructs a parser using a Parser Expression Grammar 
        parser =  str('"') >> 
                  (
                    str('\\').ignore >> any |
                    str('"').absent? >> any
                  ).repeat.as(:string) >> 
                  str('"')
      
        result = parser.parse %Q("this is a valid \\"string\\"")
        result # => {:string=>"this is a valid \"string\""@1}
      
      A small Ruby library for constructing parsers in the "PEG":http://en.wikipedia.org/wiki/Parsing_expression_grammar (Parsing Expression Grammar) fashion. Parslet makes developing complex parsers easy. It does so by * providing the best *error reporting* possible * *not generating* reams of code for you to debug Parslet takes the long way around to make *your job* easier. It allows for incremental language construction. Often, you start out small, implementing the atoms of your language first; _parslet_ takes pride in making this possible. Eager to try this out? "Get started":get-started.html! parslet-2.0.0/website/source/install.html.textile000066400000000000000000000004161362225661100221430ustar00rootroot00000000000000--- title: Install --- Parslet is at version _2.0.0_. *Rubygems*
        gem install parslet
      
      *Bundler*
        gem 'parslet', '~> 2.0'
      
      or if you want to track the edge:
        gem 'parslet', :git => 'git://github.com/kschiess/parslet.git'
      
      parslet-2.0.0/website/source/javascripts/000077500000000000000000000000001362225661100204625ustar00rootroot00000000000000parslet-2.0.0/website/source/javascripts/sh_main.min.js000066400000000000000000000122711362225661100232230ustar00rootroot00000000000000/* Copyright (C) 2007, 2008 gnombat@users.sourceforge.net */ /* License: http://shjs.sourceforge.net/doc/gplv3.html */ if(!this.sh_languages){this.sh_languages={}}var sh_requests={};function sh_isEmailAddress(a){if(/^mailto:/.test(a)){return false}return a.indexOf("@")!==-1}function sh_setHref(b,c,d){var a=d.substring(b[c-2].pos,b[c-1].pos);if(a.length>=2&&a.charAt(0)==="<"&&a.charAt(a.length-1)===">"){a=a.substr(1,a.length-2)}if(sh_isEmailAddress(a)){a="mailto:"+a}b[c-2].node.href=a}function sh_konquerorExec(b){var a=[""];a.index=b.length;a.input=b;return a}function sh_highlightString(B,o){if(/Konqueror/.test(navigator.userAgent)){if(!o.konquered){for(var F=0;FI){x(g.substring(I,E.index),null)}var e=O[u];var J=e[1];var b;if(J instanceof Array){for(var L=0;L0){var e=b.split(" ");for(var c=0;c0){a.push(e[c])}}}return a}function sh_addClass(c,a){var d=sh_getClasses(c);for(var b=0;b element with class="'+h+'", but no such language exists'}}break}}}};parslet-2.0.0/website/source/javascripts/sh_ruby.min.js000066400000000000000000000022671362225661100232640ustar00rootroot00000000000000if(!this.sh_languages){this.sh_languages={}}sh_languages.ruby=[[[/\b(?:require)\b/g,"sh_preproc",-1],[/\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g,"sh_number",-1],[/"/g,"sh_string",1],[/'/g,"sh_string",2],[/|\|/g,"sh_symbol",-1],[/(#)(\{)/g,["sh_symbol","sh_cbracket"],-1],[/#/g,"sh_comment",5],[/\{|\}/g,"sh_cbracket",-1]],[[/$/g,null,-2],[/\\(?:\\|")/g,null,-1],[/"/g,"sh_string",-2]],[[/$/g,null,-2],[/\\(?:\\|')/g,null,-1],[/'/g,"sh_string",-2]],[[/$/g,null,-2],[/>/g,"sh_string",-2]],[[/^(?:\=end)/g,"sh_comment",-2]],[[/$/g,null,-2]]];parslet-2.0.0/website/source/javascripts/toc.js000066400000000000000000000050251362225661100216070ustar00rootroot00000000000000/*! * toc - jQuery Table of Contents Plugin * v0.3.2 * http://projects.jga.me/toc/ * copyright Greg Allen 2014 * MIT License */ !function(a){a.fn.smoothScroller=function(b){b=a.extend({},a.fn.smoothScroller.defaults,b);var c=a(this);return a(b.scrollEl).animate({scrollTop:c.offset().top-a(b.scrollEl).offset().top-b.offset},b.speed,b.ease,function(){var a=c.attr("id");a.length&&(history.pushState?history.pushState(null,null,"#"+a):document.location.hash=a),c.trigger("smoothScrollerComplete")}),this},a.fn.smoothScroller.defaults={speed:400,ease:"swing",scrollEl:"body,html",offset:0},a("body").on("click","[data-smoothscroller]",function(b){b.preventDefault();var c=a(this).attr("href");0===c.indexOf("#")&&a(c).smoothScroller()})}(jQuery),function(a){var b={};a.fn.toc=function(b){var c,d=this,e=a.extend({},jQuery.fn.toc.defaults,b),f=a(e.container),g=a(e.selectors,f),h=[],i=e.activeClass,j=function(b,c){if(e.smoothScrolling&&"function"==typeof e.smoothScrolling){b.preventDefault();var f=a(b.target).attr("href");e.smoothScrolling(f,e,c)}a("li",d).removeClass(i),a(b.target).parent().addClass(i)},k=function(){c&&clearTimeout(c),c=setTimeout(function(){for(var b,c=a(window).scrollTop(),f=Number.MAX_VALUE,g=0,j=0,k=h.length;k>j;j++){var l=Math.abs(h[j]-c);f>l&&(g=j,f=l)}a("li",d).removeClass(i),b=a("li:eq("+g+")",d).addClass(i),e.onHighlight(b)},50)};return e.highlightOnScroll&&(a(window).bind("scroll",k),k()),this.each(function(){var b=a(this),c=a(e.listType);g.each(function(d,f){var g=a(f);h.push(g.offset().top-e.highlightOffset);var i=e.anchorName(d,f,e.prefix);if(f.id!==i){a("").attr("id",i).insertBefore(g)}var l=a("").text(e.headerText(d,f,g)).attr("href","#"+i).bind("click",function(c){a(window).unbind("scroll",k),j(c,function(){a(window).bind("scroll",k)}),b.trigger("selected",a(this).attr("href"))}),m=a("
    • ").addClass(e.itemClass(d,f,g,e.prefix)).append(l);c.append(m)}),b.html(c)})},jQuery.fn.toc.defaults={container:"body",listType:"
        ",selectors:"h1,h2,h3",smoothScrolling:function(b,c,d){a(b).smoothScroller({offset:c.scrollToOffset}).on("smoothScrollerComplete",function(){d()})},scrollToOffset:0,prefix:"toc",activeClass:"toc-active",onHighlight:function(){},highlightOnScroll:!0,highlightOffset:100,anchorName:function(c,d,e){if(d.id.length)return d.id;var f=a(d).text().replace(/[^a-z0-9]/gi," ").replace(/\s+/g,"-").toLowerCase();if(b[f]){for(var g=2;b[f+g];)g++;f=f+"-"+g}return b[f]=!0,e+"-"+f},headerText:function(a,b,c){return c.text()},itemClass:function(a,b,c,d){return d+"-"+c[0].tagName.toLowerCase()}}}(jQuery);parslet-2.0.0/website/source/layout.slim000066400000000000000000000035051362225661100203370ustar00rootroot00000000000000doctype 5 html head meta(http-equiv="Content-type" content="text/html;charset=UTF-8") title | parslet - = current_page.data.title || 'parslet - beautiful parsers.' meta name='author' content="Kaspar Schiess (http://absurd.li)" link rel='shortcut icon' href='images/favicon3.ico' == stylesheet_link_tag 'site' == stylesheet_link_tag 'sh_whitengrey' == javascript_include_tag 'http://code.jquery.com/jquery-2.1.4.min.js' == javascript_include_tag 'toc.js' == javascript_include_tag 'sh_main.min.js' == javascript_include_tag 'sh_ruby.min.js' body.code onload="sh_highlightDocument(); $('#toc').toc({selectors: 'h2'});" #everything .main_menu == image_tag 'parsley_logo.png', alt: 'Parslet Logo' ul li== link_to('about', 'index.html') li== link_to('get started', 'get-started.html') li== link_to('install', 'install.html') li== link_to('docs', 'documentation.html') li== link_to('contribute', 'contribute.html') li== link_to('projects', 'projects.html') .content h1= current_page.data.title || 'parslet - beautiful parsers.' == yield .copyright textile: MIT License, 2010-2018, (c) Kaspar Schiess
        javascript: var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-16365074-2']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();parslet-2.0.0/website/source/overview.html.textile000066400000000000000000000061311362225661100223430ustar00rootroot00000000000000--- title: Overview --- Parslet is a library with a clear philosophy: It makes parser writing easy and testable. On top of that, it provides understandable error messages ("General Protection Fault") to you, the language writer. In extension, you will hopefully manage to provide good error messages to your users. Together, we can create a better world! Traditional texts on the subject will have you write a compiler or interpreter for a language in several stages: * Parsing or Lexing/Parsing * Abstract Syntax Tree construction * Optimization and checking of the tree * Generation of code / Execution This library will be good for the first two stages only. After that, you'll be on your own. The parsing step has literally been implemented by hundreds (thousands) of clever people; No lack of "alternatives":http://jeffreykegler.github.io/Ocean-of-Awareness-blog/individual/2016/08/timeline2.html. Even in Ruby, you'll have the choice among a handful of libraries. There's a distinction to make on this level as well: *LRk parsers and related fields* Parsers in this class use a lexical analyzer (a _lexer_ ) to transform the input text into tokens (_tokenizing_ ). They then check if your stream of tokens has a corresponding tree that conforms to the grammar. Most of these parsers allow grammars that are ambiguous and will provide a mechanism for resolving the arising ambiguities. These are the earliest parser generators and the most widely used (_yacc_, _bison_, ...) -- chances are, you'll have more than one of these libraries installed on your system. *PEG or packrat parsers* Parsers in this class are based on a slightly more modern algorithm, which translates what one does when writing a parser by hand in top-down fashion. This is what we programmers do all over, methods calling methods - and its also (grossly) what these parsers do to recognize input. Left recursion is impossible to express in these grammars. No lexers are required - lexical tokenizing and parsing are one single step. Ruby has several implementations of this algorithm in library form: "Treetop":http://github.com/nathansobo/treetop, "Citrus":http://github.com/mjijackson/citrus, "rsec":http://wiki.github.com/luikore/rsec/ and of course "Parslet":http://kschiess.github.com/parslet. All of these generators are different in small ways; yet most implement common patterns and provide almost identical APIs. We believe this is an error: choice is good, but there should be visible attributes distinguishing the choices. Parslet is not like the others, in fact it is radically different on some key elements. h2. Writing a language Whether you write a language for a configuration file or a new computer language (the holy grail), the steps are always the same: # Create a grammar: _What should be legal syntax?_ # Annotate the grammar: _What is important data?_ # Create a transformation: _How do I want to work with that data?_ The creation of grammars and the various concepts that are associated are treated in "Parslet::Parser":parser.html. Transformation of the resulting intermediary tree is treated in "Parslet::Transform":transform.html parslet-2.0.0/website/source/parser.html.textile000066400000000000000000000271031362225661100217730ustar00rootroot00000000000000--- title: Parser construction --- A parser is nothing more than a class that derives from Parslet::Parser. The simplest parser that one could write would look like this:
        
          class SimpleParser < Parslet::Parser
            rule(:a_rule) { str('simple_parser') }
            root(:a_rule)
          end
        
        The language recognized by this parser is simply the string "simple_parser". Parser rules do look a lot like methods and are defined by
        
          rule(name) { definition_block }
        
        Behind the scenes, this really defines a method that returns whatever you return from it. Every parser has a root. This designates where parsing should start. It is like an entry point to your parser. With a root defined like this:
        
          root(:my_root)
        
        you create a #parse method in your parser that will start parsing by calling the #my_root method. You'll also have a #root (instance) method that is an alias of the root method. The following things are really one and the same:
        
          SimpleParser.new.parse(string)
          SimpleParser.new.root.parse(string)
          SimpleParser.new.a_rule.parse(string)
        
        Knowing these things gives you a lot of flexibility; I'll explain why at the end of the chapter. For now, just let me point out that because all of this is Ruby, your favorite editor will syntax highlight parser code just fine. h2. Atoms: The inside of a parser h3. Matching strings of characters A parser is constructed from parser atoms (or parslets, hence the name). The atoms are what appear inside your rules (and maybe elsewhere). We've already encountered an atom, the string atom:
        
          str('simple_parser')
        
        This returns a Parslet::Atoms::Str instance. These parser atoms all derive from Parslet::Atoms::Base and have essentially just one method you can call: #parse. So this works:
        
          str('foobar').parse('foobar') # => "foobar"@0
        
        The atoms are small parsers that can recognize languages and throw errors, just like real Parslet::Parser subclasses. h3. Matching character ranges The second parser atom you will have to know about allows you to match character ranges:
        
          match('[0-9a-f]')
        
        The above atom would match the numbers zero through nine and the letters 'a' to 'f' - yeah, you guessed right - hexadecimal numbers for example. The inside of such a match parslet is essentially a regular expression that matches a single character of input. Because we'll be using ranges so much with #match and because typing ('[]') is tiresome, here's another way to write the above #match atom:
        
          match['0-9a-f']
        
        Character matches are instances of Parslet::Atoms::Re. Here are some more examples of character ranges:
        
          match['[:alnum:]']      # letters and numbers
          match['\n']             # newlines
          match('\w')             # word characters
          match('.')              # any character
        
        h3. The wild wild #any The last example above corresponds to the regular expression /./ that matches any one character. There is a special atom for that:
        
          any 
        
        h2. Composition of Atoms These basic atoms can be composed to form complex grammars. The following few sections will tell you about the various ways atoms can be composed. h3. Simple Sequences Match 'foo' and then 'bar':
        
          str('foo') >> str('bar')    # same as str('foobar')
        
        Sequences correspond to instances of the class Parslet::Atoms::Sequence. h3. Repetition and its Special Cases To model atoms that can be repeated, you should use #repeat:
        
          str('foo').repeat
        
        This will allow foo to repeat any number of times, including zero. If you look at the signature for #repeat in Parslet::Atoms::Base, you'll see that it has really two arguments: _min_ and _max_. So the following code all makes sense:
        
          str('foo').repeat(1)      # match 'foo' at least once
          str('foo').repeat(1,3)    # at least once and at most 3 times
          str('foo').repeat(0, nil) # the default: same as str('foo').repeat
        
        Repetition has a special case that is used frequently: Matching something once or not at all can be achieved by repeat(0,1), but also through the prettier:
        
          str('foo').maybe          # same as str('foo').repeat(0,1)
        
        These all map to Parslet::Atoms::Repetition. Please note this little twist to #maybe:
        
          str('foo').maybe.as(:f).parse('')         # => {:f=>nil}
          str('foo').repeat(0,1).as(:f).parse('')   # => {:f=>[]}
        
        The 'nil'-value of #maybe is nil. This is catering to the intuition that foo.maybe either gives me foo or nothing at all, not an empty array. But have it your way! h3. Alternation The most important composition method for grammars is alternation. Without it, your grammars would only vary in the amount of things matched, but not in content. Here's how this looks:
        
          str('foo') | str('bar')   # matches 'foo' OR 'bar'
        
        This reads naturally as "'foo' or 'bar'". h3. Operator precedence The operators we have chosen for parslet atom combination have the operator precedence that you would expect. No parenthesis are needed to express alternation of sequences:
        
          str('s') >> str('equence') | 
            str('se') >> str('quence')
        
        h3. And more Parslet atoms are not as pretty as Treetop atoms. There you go, we said it. However, there seems to be a different kind of aesthetic about them; they are pure Ruby and integrate well with the rest of your environment. Have a look at this:
        
          # Also consumes the space after important things like ';' or ':'. Call this
          # giving the character you want to match as argument: 
          #
          #   arg >> (spaced(',') >> arg).repeat
          #
          def spaced(character)
            str(character) >> match['\s']
          end
        
        or even this:
        
          # Turns any atom into an expression that matches a left parenthesis, the 
          # atom and then a right parenthesis.
          #
          #   bracketed(sum)
          #
          def bracketed(atom)
            spaced('(') >> atom >> spaced(')')
          end
        
        You might say that because parslet is just plain old Ruby objects itself (PORO (tm)), it allows for very tight code. Module inclusion, class inheritance, ... all your tools should work well with parslet. h2. Tree construction By default, parslet will just echo back to you the strings you feed into it. Parslet will not generate a parser for you and neither will it generate your abstract syntax tree for you. The method #as(name) allows you to specify exactly how you want your tree to look like:
        
          str('foo').parse('foo')             # => "foo"@0
          str('foo').as(:bar).parse('foo')    # => {:bar=>"foo"@0}
        
        So you think: #as(name) allows me to create a hash, big deal. That's not all. You'll notice that annotating everything that you want to keep in your grammar with #as(name) autocreates a sensible tree composed of hashes and arrays and strings. It's really somewhat magic: Parslet has a set of clever rules that merge the annotated output from your atoms into a tree. Here are some more examples, with the atom on the left and the resulting tree (assuming a successful parse) on the right:
        
          # Normal strings just map to strings
          str('a').repeat                         "aaa"@0                                 
        
          # Arrays capture repetition of non-strings
          str('a').repeat.as(:b)                  {:b=>"aaa"@0}                           
          str('a').as(:b).repeat                  [{:b=>"a"@0}, {:b=>"a"@1}, {:b=>"a"@2}] 
        
          # Subtrees get merged - unlabeled strings discarded
          str('a').as(:a) >> str('b').as(:b)      {:a=>"a"@0, :b=>"b"@1}                  
          str('a') >> str('b').as(:b) >> str('c') {:b=>"b"@1}                             
        
          # #maybe will return nil, not the empty array
          str('a').maybe.as(:a)                   {:a=>"a"@0}                             
          str('a').maybe.as(:a)                   {:a=>nil}
        
        h2. Capturing input _Advanced reading material - feel free to skip this._ Sometimes a parser needs to match against something that was already matched against. Think about Ruby heredocs for example:
        
          str = <<-HERE
            This is part of the heredoc.
          HERE
        
        The key to matching this kind of document is to capture part of the input first and then construct the rest of the parser based on the captured part. This is what it looks like in its simplest form:
        
          match['ab'].capture(:capt) >>               # create the capture
            dynamic { |s,c| str(c.captures[:capt]) }  # and match using the capture
        
        This parser matches either 'aa' or 'bb', but not mixed forms 'ab' or 'ba'. The last sample introduced two new concepts for this kind of complex parser: the #capture(name) method and the dynamic { ... } code block. Appending #capture(name) to any parser will capture that parsers result in the captures hash in the parse context. If and only if the parser match['ab'] succeeds, it stores either 'a' or 'b' in context.captures[:capt]. The only way to get at that hash during the parse process is in a dynamic { ... } code block. (for reasons that are out of the scope of this document) In such a block, you can:
        
          dynamic { |source, context|
            # construct parsers by using randomness
            rand < 0.5 ? str('a') : str('b')
            
            # Or by using context information 
            str( context.captures[:capt] )
            
            # Or by .. doing other kind of work (consumes 100 chars and then 'a')
            source.consume(100)
            str('a')
          }
        
        h3. Scopes What if you want to parse heredocs contained within heredocs? It's turtles all the way down, after all. To be able to remember what string was used to construct the outer heredoc, you would use the #scope { ... } block that was introduced in parslet 1.5. Like opening a Ruby block, it allows you to capture results (assign values to variables) to the same names you've already used in outer scope - _without destroying the outer scopes values for these captures!_. Here's an example for this:
        
          str('a').capture(:a) >> scope { str('b').capture(:a) } >> 
            dynamic { |s,c| str(c.captures[:a]) }
        
        This parses 'aba' - if you understand that, you understand scopes and captures. Congrats. h2. And more Now you know exactly how to create parsers using Parslet. Your parsers will output intricate structures made of endless arrays, complex hashes and a few string leftovers. But your programming skills fail you when you try to put all this data to use. Selecting keys upon keys in hash after hash, you feel like a cockroach that has just read Kafka's works. This is no fun. This is not what you signed up for. Time to introduce you to "Parslet::Transform":transform.html and its workings. parslet-2.0.0/website/source/projects.html.textile000066400000000000000000000044741362225661100223360ustar00rootroot00000000000000--- title: Projects --- Have you got a project that uses parslet? Please write "us":mailto:kaspar.schiess@absurd.li about it. "*edn-ruby*":https://github.com/relevance/edn-ruby edn-ruby is a Ruby library to read and write "edn":https://github.com/edn- format/edn (extensible data notation), a subset of Clojure used for transferring data between applications, much like JSON, YAML, or XML. "*Examples*":https://github.com/kschiess/parslet/tree/master/example/ In here, you can find a parser for a lisp like language and much more. "*Net::HTTP::Server*":https://github.com/postmodern/net-http-server A really small and elegant HTTP server written in Ruby. Think Webrick. Using parslet. (Postmodern) "*regexador*":https://github.com/Hal9000/regexador An external DSL for Ruby that tries to make regular expressions readable and maintainable. (Hal Fulton) "*self-ml*":https://github.com/self-ml/selfml A "self-ml":http://self-ml.github.io/ implementation using parslet. (Ricardo Mendes) "*thnad*":https://github.com/undees/thnad Thnad is a tiny programming language with so few features that it is not useful for anything at all -- except showing how to write a compiler in half an hour. "*Unitwise*":https://github.com/joshwlewis/unitwise A units of measure library for Ruby. Part of it's magic comes from supporting the "Unified Code for Units of Measure", which has a formal grammar, described "here":http://unitsofmeasure.org/ucum.html#section-Grammar-of-Units-and-Unit-Terms. The grammar is sufficiently complicated that it can't be solved with regular expressions (mostly because the grammar is recursive), and was therefore better suited for a PEG. (Josh Lewis) "*Werd.rb*":https://github.com/rk/werd A variant of Chris Pound's word generator written in Ruby, with some improvements. (Robert Kosek) "*versionub*":https://github.com/meh/versionub A semantic version parser. (meh) "*shortcode*":https://github.com/kernow/shortcode A ruby gem for parsing Wordpress style shortcodes using parslet. (Jamie Dyer) "*Simple Java Parser*":https://github.com/parttimenerd/parser-experiments/tree/master/SimpleJavaParser A simple Java parser that can parse the overall structure of a Java file. "*crystal*":https://github.com/ruby-in-ruby/crystal A ruby virtual machine in 100% ruby, that tries to parse most of ruby syntax using parslet. parslet-2.0.0/website/source/stylesheets/000077500000000000000000000000001362225661100205055ustar00rootroot00000000000000parslet-2.0.0/website/source/stylesheets/sh_whitengrey.css000066400000000000000000000045071362225661100241040ustar00rootroot00000000000000pre.sh_sourceCode { background-color: #ffffff; color: #696969; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_keyword { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_type { color: #696969; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_string { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_regexp { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_specialchar { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_comment { color: #1326a2; font-weight: normal; font-style: italic; } pre.sh_sourceCode .sh_number { color: #bb00ff; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_preproc { color: #470000; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_function { color: #000000; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_url { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_date { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_time { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_file { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_ip { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_name { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_variable { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_oldfile { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_newfile { color: #008800; font-weight: normal; font-style: normal; } pre.sh_sourceCode .sh_difflines { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_selector { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_property { color: #696969; font-weight: bold; font-style: normal; } pre.sh_sourceCode .sh_value { color: #008800; font-weight: normal; font-style: normal; } parslet-2.0.0/website/source/stylesheets/site.css.sass000066400000000000000000000032511362225661100231340ustar00rootroot00000000000000// main site styling =clearfix display: inline-block &:after content: "." display: block height: 0 clear: both visibility: hidden * html & height: 1px body font: size: 17px family: Helvetica background-color: white a color: black .main_menu font-size: 1.2em width: 50em margin: bottom: 1cm ul +clearfix list-style-type: none font-size: 1.33em margin-left: -8em li float: left font-variant: small-caps padding-left: 1em a, &:visited text-decoration: none color: #7c7c7c .content font-size: 1em width: 50em line-height: 1.4em margin: left: 1cm p margin: 1em img margin-left: 3em .copyright font-variant: small-caps font-size: 0.6em color: #00ad00 margin: left: 22em top: 3cm a text-decoration: none color: #00ad00 body.code code font: size: 0.9em family: "Monaco", monospace // _why's poignant guide theme .sh_keyword color: #761A47 .sh_comment color: #686868 .sh_string color: #42aa7a background-color: #edf7f7 .sh_constant color: #551e03 .sh_method color: #4679a9 .sh_symbol color: #551e03 .sh_number color: #42aa7a pre font: size: 1em family: "Monaco", monospace background-color: #f8f8f8 color: #4679a9 line-height: 1.1em margin: left: 1em padding: -1em 0 -1em 0 &.sh_sourceCode border-radius: 0.5em background-color: #f8f8f8 padding-bottom: 1.4em #toc li list-style-type: lower-alpha parslet-2.0.0/website/source/transform.html.textile000066400000000000000000000237721362225661100225220ustar00rootroot00000000000000--- title: Transformation --- Parslet parsers output deep nested hashes. Those are nice for printing, but hard to work with. The structure of the nested hashes is determined by the grammar and can thus vary largely. Testing for the presence of individual keys would produce code that is hard to read and maintain. This is why parslet also comes with a hash transformation engine. To construct such a transform, you have to derive from Parslet::Transform:
        
          class MyTransform < Parslet::Transform
            rule('a') { 'b' }
          end
          MyTransform.new.apply('a') # => "b"
        
        This is a transformation that replaces all 'a's with 'b's. A transformation rule has two parts: A *pattern* (here: 'a') and an *action block* ({ 'b' }). The engine will go through the input and traverse the tree in depth-first post-order fashion. This means that for a given tree node, it will first visit the children and only then look at the node itself. While traversing, all rules are tested in the order in which they are defined. If a rule matches, the corresponding tree is _replaced_ by whatever the action block returns. Here's another way of saying the same thing, perhaps more in line with what you need as a user of Parslet: Parslet::Transform is what allows you to transform the PORO-trees magically into a real abstract syntax tree. The rule definitions are the futuristic nano-machines that act on tree leaves first, eating them away and replacing them with contraptions of your own design. Here's how that might look like in Ruby:
        
          tree = {:left => {:int => '1'}, 
                  :op   => '+', 
                  :right => {:int => '2'}}
                
          class Trans < Parslet::Transform
            rule(:int => simple(:x)) { Integer(x) }
          end
          Trans.new.apply(tree)     # => {:left=>1, :op=>"+", :right=>2}
        
        You can start thinking about the leaves first, transforming those :int => '1' into real Ruby integers. This incremental (test driven!) approach will prevent your intermediary tree from turning into grey goo from too many nano-machines. Rules should in general be simple and transform a small part of the tree into a more useful variant. Turns out that if we were looking for an interpreter, one more rule will give us evaluation:
        
          tree = {:left => {:int => '1'}, 
                  :op   => '+', 
                  :right => {:int => '2'}}
        
          class Trans < Parslet::Transform
            rule(:int => simple(:x)) { Integer(x) }
            rule(:op => '+', :left => simple(:l), :right => simple(:r)) { l + r }
          end
          Trans.new.apply(tree)     # => 3
        
        Cool, isn't it? To recap: parslet intentionally spits out deep nested hashes, because it also gives you the tool to work with those. Turning the intermediary trees into something useful is really easy. h2. h2. Working with Captures What is this simple(symbol) business all about, you might ask. Glad you do. h3. Simple captures Transform allows you to specify patterns that have wildcards in them. The wildcards match part of the tree, but at the same time capture it for working on it in your action block. The wildcard
        
          simple(:x)
        
        will match any object BUT hashes or arrays. While this is obviously useful for capturing strings, you can also capture other 'simple' (as opposed to composed) objects of your own creation. simple(:x) would thus match all of these objects:
        
          "a string"
          123
          Foo.new(:some, :class, :instance)
        
        If you think about what you'll be doing to your intermediary trees, replacing leaves with more useful objects, simple really makes good sense, since it will stop you from matching entire subtrees. h3. Matching Repetitions and Sequences Some patterns (like repetitions and sequences) produce arrays of objects as result. You can use simple(...) to replace all parts of these arrays with your own objects, but you cannot replace the array as a whole. This is the purpose of sequence(symbol):
        
          sequence(:x)
        
        will match all of these:
        
          ['a', 'b', 'c']
          ['a', 'a', 'a']
          [Foo.new, Bar.new]
        
        but not
        
          [{:a => :b}]
          [['a', 'b']]
        
        Like its smaller brother, sequence is very picky about what it consumes and what not. All for the same reasons. h3. Matching entire subtrees So you don't want to listen and really want that big gun with the foot aiming addon. You'll be needing subtree(symbol). It always matches. Nuff said. h3. Matching context A match always binds in a context. The context consists of all bindings that were previously made. If you reuse the same symbol for two consecutive matches within the same pattern, the engine will assume that you want these two matched objects to be equal (under ==). This allows to specify constraints on your matches that would need code to express otherwise:
        
          # The following code is an excerpt from example/simple_xml.rb in the distro
          t.rule(
            open: {name: simple(:tag)}, 
            close: {name: simple(:tag)}, 
            inner: simple(:t)
          ) { 'verified' }
        
        This replaces _matching_ open and close tags with the word 'verified', consuming them from the tree and allowing the same rule to match higher up. A valid XML tree will leave only the word 'verified' behind, while the parser will stop at the problem nodes in invalid trees. h2. Transformation rules In this chapter, we'll look more closely at transformation rules and the different ways they can be laid out in your code. h3. Usage Patterns The way the transformation engine is constructed, there is not one, but three ways to use it. Since at least one of those is inconvenient for you, the user, I am going to show only the remaining two, Variant 1 that produces an instance of the transform for direct use:
        
          # Variant 1
          transform = Parslet::Transform.new do
            rule(...) { ... } 
            rule(...) { ... } 
            rule(...) { ... } 
          end
          transform.apply(tree)
        
        and Variant 2 that allows constructing the transformation as a class:
        
          # Variant 2
          class MyTransform < Parslet::Transform
            rule(...) { ... } 
            rule(...) { ... } 
            rule(...) { ... } 
          end
          MyTransform.new.apply(tree)
        
        I guess both have their sweet spot. h3. Action blocks: Two flavors As you might have noticed by now, parslet provides choice as well as nice parsers. To recap: Rules have a left side called _pattern_ and a right side called _action block_:
        
          rule(PATTERN) {ACTION_BLOCK}
        
        There are two ways of writing action blocks, and the difference might be fundamental to know to you one day. If written like this:
        
          rule(:foo => simple(:x)) { puts x }
        
        the block will be able to access x as a local variable. This is very convenient and shortens the action code, often to the point of being very expressive. But there is a _big downside_ to this way of writing things: The action block must be executed in the context of some magic instance that has x as a local method (aka accessor). You can only have one self at any one time; variable access to the binding of the block isn't possible inside this kind of action blocks:
        
          y = 12
          rule(:foo => simple(:x)) { Integer(x) + y }
        
        This will (depending on the context) throw a NameError or a NoMethodError. But this can be fixed by using the other, less elegant style for action blocks:
        
          y = 12
          rule(:foo => simple(:x)) { |dictionary| Integer(dictionary[:x]) + y }
        
        In this second flavor, the block gets executed in the context of definition, whatever that was. This means that it can capture and access local variables just fine. Access to the bindings (called dictionary here) is more clumsy, but hey, you can't have your cake and eat it too, I guess. Even though that is a pity. h2. A word on patterns Given the PORO hash
        
          { 
            :dog => 'terrier', 
            :cat => 'suit' }
        
        one might assume that the following rule matches :dog and replaces it by 'foo':
        
          rule(:dog => 'terrier') { 'foo' }
        
        This is frankly impossible. How would 'foo' live besides :cat => 'suit' inside the hash? It cannot. This is why hashes are either matched completely, cats n' all, or not at all. Transformations are there for one thing: Getting out of the hash/array/slice mess parslet creates (on purpose) into the realm of your own beautifully crafted AST classes. Such "AST":http://en.wikipedia.org/wiki/Abstract_syntax_tree nodes will generally correspond 1:1 to hashes inside your intermediary tree. If transformations get you into a mess, remember this simple truth: They have been designed for the above purpose. Abusing them is fun (and almost all the examples in the project do so) but the mess you get when you do is all yours. If you are really desperate, try to look at the example in "Get Started":get-started.html or at the parser in the sample project "wt":https://github.com/kschiess/wt. Imitating them would be a good first step. And if all else fails, we're there for you, see the 'Contact' section in "Contribute":contribute.html. h2. Summary This concludes this (three part) introduction to parslet and leaves you with a good knowledge of most tricky parts. If you are missing some detail, maybe you can find it in the texts referenced "here":documentation.html? There is also an entire page on the tricks useful in practice here: "Tricks":tricks.html. If not, please tell us about it. We'll include it in this documentation in no time. parslet-2.0.0/website/source/tricks.html.textile000066400000000000000000000147201362225661100217770ustar00rootroot00000000000000--- title: Tricks for common situations --- Here's a topic overview:
        h2. Matching EOF (End Of File) Ahh Sir, you'll be needin what us parsers call _epsilon_:
        
          rule(:eof) { any.absent? }
        
        Of course, most of us don't use this at all, since any parser has EOF as implicit last input. h2. Matching Strings Case Insensitive Parslet is fully hackable: You can use code to create parsers easily. Here's how I would match a string in case insensitive manner:
        
          def stri(str)
            key_chars = str.split(//)
            key_chars.
              collect! { |char| match["#{char.upcase}#{char.downcase}"] }.
              reduce(:>>)
          end
        
          # Constructs a parser using a Parser Expression Grammar 
          stri('keyword').parse "kEyWoRd"     # => "kEyWoRd"@0
        
        h2. Testing Parslet helps you to create parsers that are in turn created out of many small parsers. It is really turtles all the way down. Imagine you have a complex parser:
        
          class ComplexParser < Parslet::Parser
            root :lots_of_stuff
          
            rule(:lots_of_stuff) { ... }
          
            # and many lines later: 
            rule(:simple_rule) { str('a') }
          end
        
        Also imagine that the parser (as a whole) fails to consume the 'a' that simple_rule is talking about. This kind of problem can very often be fixed by bisecting it into two possible problems. Either: # the lots_of_stuff rule somehow doesn't place simple_rule in the right context or # the simple_rule simply (hah!) fails to match its input. I find it very useful in this situation to eliminate 2. from our options:
        
          require 'rspec'
          require 'parslet/rig/rspec'
          
          class ComplexParser < Parslet::Parser
            rule(:simple_rule) { str('a') }
          end
        
          RSpec.describe ComplexParser  do
            let(:parser) { ComplexParser.new }
            context "simple_rule" do
              it "should consume 'a'" do
                expect(parser.simple_rule).to parse('a')
              end 
            end
          end
          
          RSpec::Core::Runner.run(['--format', 'documentation'])
        
        Output is:
        
        Example::ComplexParser
          simple_rule
            should consume 'a'
        
        Finished in 0.00094 seconds (files took 0.29367 seconds to load)
        1 example, 0 failures
        
        Parslet parsers have one method per rule. These methods return valid parsers for a subset of your grammar. h2. Error reports If your grammar fails and you're aching to know why, here's a bit of exception handling code that will help you out:
        
          parser = str('foo')
          begin
            parser.parse('bar')
          rescue Parslet::ParseFailed => error
            puts error.parse_failure_cause.ascii_tree
          end
        
        This should print something akin to:
         
        Expected "foo", but got "bar" at line 1 char 1.
        
        These error reports are probably the fastest way to know exactly where you went wrong (or where your input is wrong, which is aequivalent). And since this is such a common idiom, we provide you with a shortcut: to get the above, just:
        
        require 'parslet/convenience'
        parser.parse_with_debug(input)
        
        h3. Reporter engines Note that there is currently not one, but two error reporting engines! The default engine will report errors in a structure that looks exactly like the grammar structure:
        
          class P < Parslet::Parser
            root(:body)
            rule(:body) { elements }
            rule(:elements) { (call | element).repeat(2) }
            rule(:element) { str('bar') }
            rule(:call) { str('baz') >> str('()') }
          end
          
          begin
            P.new.parse('barbaz')
          rescue Parslet::ParseFailed => error
            puts error.parse_failure_cause.ascii_tree
          end
        
        Outputs:
         
        Expected at least 2 of CALL / ELEMENT at line 1 char 1.
        `- Expected one of [CALL, ELEMENT] at line 1 char 4.
           |- Failed to match sequence ('baz' '()') at line 1 char 7.
           |  `- Premature end of input at line 1 char 7.
           `- Expected "bar", but got "baz" at line 1 char 4.
        
        Let's switch out the 'grammar structure' engine (called 'Tree') with the 'deepest error position' engine:
        
          class P < Parslet::Parser
            root(:body)
            rule(:body) { elements }
            rule(:elements) { (call | element).repeat(2) }
            rule(:element) { str('bar') }
            rule(:call) { str('baz') >> str('()') }
          end
          
          begin
            P.new.parse('barbaz', reporter: Parslet::ErrorReporter::Deepest.new)
          rescue Parslet::ParseFailed => error
            puts error.parse_failure_cause.ascii_tree
          end
        
        Outputs:
         
        Expected at least 2 of CALL / ELEMENT at line 1 char 1.
        `- Expected one of [CALL, ELEMENT] at line 1 char 4.
           |- Failed to match sequence ('baz' '()') at line 1 char 7.
           |  `- Premature end of input at line 1 char 7.
           `- Premature end of input at line 1 char 7.
        
        The 'Deepest' position engine will store errors that are the farthest into the input. In some examples, this produces more readable output for the end user. h2. Line numbers from parser output A traditional parser would parse and then perform several checking phases, like for example verifying all type constraints are respected in the input. During this checking phase, you will most likely want to report screens full of type errors back to the user ('cause that's what types are for, right?). Now where did that 'int' come from? Parslet gives you slices (Parslet::Slice) of input as part of your tree. These are essentially strings with line numbers. Here's how to print that error message:
        
          # assume that type == "int"@0 - a piece from your parser output
          line, col = type.line_and_column
          puts "Sorry. Can't have #{type} at #{line}:#{col}!"
        
        h2. Precedence climber You might want to implement a parser for simple arithmetic infix expressions such as `1 + 2`. The quickest way to do this with parslet is to use the infix expression parser atom:
        
          infix_expression(
            match('[0-9]').repeat,
            [str('*'), 2],
            [str('+'), 1]) # matches both "1+2*3" and "1*2+3"
        
        Please also see the "example":https://github.com/kschiess/parslet/blob/master/example/prec_calc.rb and the "inline documentation":https://github.com/kschiess/parslet/blob/master/lib/parslet.rb#L203-L229 for this feature.