kpeg 0.8.5 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.autotest +10 -0
  2. data/.gemtest +0 -0
  3. data/Gemfile +11 -3
  4. data/History.txt +21 -0
  5. data/LICENSE +25 -0
  6. data/Manifest.txt +47 -0
  7. data/README.rdoc +222 -0
  8. data/Rakefile +23 -11
  9. data/bin/kpeg +4 -2
  10. data/examples/calculator/calculator.kpeg +17 -0
  11. data/examples/calculator/calculator.rb +7 -0
  12. data/examples/foreign_reference/literals.kpeg +5 -0
  13. data/examples/foreign_reference/matcher.kpeg +9 -0
  14. data/examples/foreign_reference/matcher.rb +5 -0
  15. data/examples/lua_string/driver.rb +21 -0
  16. data/examples/lua_string/lua_string.kpeg +14 -0
  17. data/examples/lua_string/lua_string.kpeg.rb +460 -0
  18. data/examples/phone_number/README.md +3 -0
  19. data/examples/phone_number/phone_number.kpeg +20 -0
  20. data/examples/phone_number/phone_number.rb +6 -0
  21. data/examples/upper/README.md +83 -0
  22. data/examples/upper/upper.kpeg +24 -0
  23. data/examples/upper/upper.rb +9 -0
  24. data/kpeg.gemspec +35 -17
  25. data/lib/hoe/kpeg.rb +94 -0
  26. data/lib/kpeg.rb +3 -0
  27. data/lib/kpeg/code_generator.rb +16 -3
  28. data/lib/kpeg/compiled_parser.rb +18 -28
  29. data/lib/kpeg/format_parser.kpeg +129 -0
  30. data/lib/kpeg/format_parser.rb +88 -49
  31. data/lib/kpeg/grammar.rb +10 -0
  32. data/lib/kpeg/string_escape.kpeg +20 -0
  33. data/test/inputs/comments.kpeg +5 -0
  34. data/test/test_file_parser_roundtrip.rb +3 -3
  35. data/test/test_gen_calc.rb +2 -2
  36. data/test/test_kpeg.rb +2 -2
  37. data/test/test_kpeg_code_generator.rb +65 -2
  38. data/test/test_kpeg_compiled_parser.rb +2 -2
  39. data/test/test_kpeg_format.rb +49 -4
  40. data/test/test_kpeg_grammar_renderer.rb +2 -2
  41. data/test/test_left_recursion.rb +2 -2
  42. data/{doc → vim}/syntax_kpeg/ftdetect/kpeg.vim +0 -0
  43. data/{doc → vim}/syntax_kpeg/syntax/kpeg.vim +0 -0
  44. metadata +89 -26
  45. data/README.md +0 -183
  46. data/lib/kpeg/version.rb +0 -3
@@ -0,0 +1,3 @@
1
+ # Phone Number Parser
2
+
3
+ A string is parsed to determine if it is a valid phone number. A phone number in the following format (888) 555-0000 will be available using the resolved_number method if the string successfully matches
@@ -0,0 +1,20 @@
1
+ %% name = PhoneNumber
2
+
3
+ %% {
4
+ attr_accessor :phone_number
5
+ }
6
+ digit = [0-9]
7
+ space = " "
8
+ dash = "-"
9
+ LP = "("
10
+ RP = ")"
11
+
12
+ country_code = < digit > { text }
13
+ area_code = < digit[3] > { text }
14
+ prefix = < digit[3] > { text }
15
+ suffix = < digit[4] > { text }
16
+
17
+ phone_number = LP? area_code:ac RP? space* prefix:p space* dash? space* suffix:s space* { "(#{ac}) #{p}-#{s}" }
18
+
19
+ root = phone_number:pn { @phone_number = pn }
20
+ | country_code:c space* phone_number:pn { @phone_number = "+#{c} #{pn}" }
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require "./phone_number.kpeg.rb"
3
+
4
+ parser = PhoneNumber.new("8888888888")
5
+ puts parser.parse
6
+ puts parser.phone_number
@@ -0,0 +1,83 @@
1
+ # Upper Parser
2
+
3
+ A parser that matches a string with alpha characters, spaces and a period and returns the string in upper case.
4
+
5
+ ## Grammar
6
+
7
+ Name of the class that will be used to do the parsing
8
+
9
+ %% name = Upper
10
+
11
+ A variable that I want to store the converted text for accessing later
12
+
13
+ %% {
14
+ attr_accessor :output
15
+ }
16
+
17
+ My literals
18
+
19
+ period = "."
20
+ space = " "
21
+
22
+ A rule that states that all characters that match the regex [A-Za-z] should be returned uppercase
23
+
24
+ alpha = < /[A-Za-z]/ > { text.upcase }
25
+
26
+ My rules that defines a word, it consists of three different cases, first that a word is an alpha followed by another word. If this matches return the alpha and the word that follows.
27
+
28
+
29
+ word = alpha:a word:w { "#{a}#{w}" }
30
+
31
+ This rule states that a word can be an alpha followed by a space. If this matches return an alpha followed by a space
32
+
33
+ | alpha:a space+ { "#{a} "}
34
+
35
+ This rule states that a word can consist of just an alpha. If this matches just return the alpha
36
+
37
+ | alpha:a { a }
38
+
39
+ My rules that defines a sentence. The first states that a sentence consists of a word followed by a sentence. If this matches return the word followed by the sentence.
40
+
41
+ sentence = < word:w sentence:s > { "#{w}#{s}" }
42
+
43
+ This rule states that a sentence can just be a word. If this matches just return the word.
44
+
45
+ | word:w { w }
46
+
47
+ My rules that define a document. The first rule states that a document can be a sentence followed by a period that may have space followed by another document. If this matches return the sentence followed by a period with a space followed by the document.
48
+
49
+ document = sentence:s period space* document:d { "#{s}. #{d}" }
50
+
51
+ This rule states that a document can be a sentence followed by a period. If this matches return the sentence followed by a period.
52
+
53
+ | sentence:s period { "#{s}." }
54
+
55
+ This rule states that a document can just be a sentence. If this matches just return the sentence.
56
+
57
+ | sentence:s { s }
58
+
59
+ The root node it the first rule evaluated, is it essentially the starting point for your grammar. If the string provided can successfully be matched by the grammar provided store the returned document in the @output variable.
60
+
61
+ root = document:d { @output = d }
62
+
63
+ ## Generate the parser
64
+
65
+ To generate the parser make sure you have kpeg installed and run the following command (you may have to remove upper.kpeg.rb if it was previously generated)
66
+
67
+ kpeg upper.kpeg
68
+
69
+ ## Run the parser
70
+
71
+ To run the parser run the following
72
+
73
+ ruby upper.rb
74
+
75
+ ## Accepted Strings
76
+
77
+ + a lower case string. Another lower case string.
78
+ + A LOWER CASE STRING. ANOTHER LOWER CASE STRING.
79
+ + a string with lots of spaces.
80
+
81
+ ## Not accepted strings (there are tons)
82
+
83
+ Anything that doesn't stick to spaces and periods, very brittle but it is a simple example
@@ -0,0 +1,24 @@
1
+ %% name = Upper
2
+
3
+
4
+ %% {
5
+ attr_accessor :output
6
+ }
7
+
8
+ period = "."
9
+ space = " "
10
+ alpha = < /[A-Za-z]/ > { text.upcase }
11
+
12
+ word = alpha:a word:w { "#{a}#{w}" }
13
+ | alpha:a space { "#{a} "}
14
+ | alpha:a { a }
15
+
16
+ sentence = word:w sentence:s { "#{w}#{s}" }
17
+ | word:w { w }
18
+
19
+ document = sentence:s period space* document:d { "#{s}. #{d}" }
20
+ | sentence:s period { "#{s}." }
21
+ | sentence:s { "#{s}" }
22
+
23
+ root = document:d { @output = d }
24
+
@@ -0,0 +1,9 @@
1
+ # Make sure you have the kpeg gem installed
2
+ require 'rubygems'
3
+ # To generate the upper.kpeg file run kpeg upper.kpeg
4
+ require "./upper.kpeg.rb" # Require the generated parser
5
+
6
+ parser = Upper.new("a lower case string. Another lower case string.")
7
+ if parser.parse
8
+ puts parser.output
9
+ end
data/kpeg.gemspec CHANGED
@@ -1,24 +1,42 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "kpeg/version"
4
2
 
5
3
  Gem::Specification.new do |s|
6
- s.name = "kpeg"
7
- s.version = KPeg::VERSION
8
- s.platform = Gem::Platform::RUBY
9
- s.authors = ["Evan Phoenix"]
10
- s.email = ["evan@fallingsnow.net"]
11
- s.homepage = "https://github.com/evanphx/kpeg"
12
- s.summary = %q{Peg-based Code Generator}
13
- s.description = %q{A tool for generating parsers using PEG}
4
+ s.name = "kpeg"
5
+ s.version = "0.8.5.20120306163408"
14
6
 
15
- rb = Dir["lib/**/*.rb"] << "bin/kpeg"
16
- docs = Dir["doc/**/*"]
17
-
18
- s.files = rb + docs + ["README.md", "Rakefile", "kpeg.gemspec", "Gemfile"]
19
- s.test_files = Dir["test/**/*.rb"]
20
- s.bindir = "bin"
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Eric Hodel"]
9
+ s.cert_chain = ["/Users/drbrain/.gem/gem-public_cert.pem"]
10
+ s.date = "2012-03-07"
11
+ s.description = "KPeg is a simple PEG library for Ruby. It provides an API as well as native\ngrammar to build the grammar.\n\nKPeg strives to provide a simple, powerful API without being too exotic.\n\nKPeg supports direct left recursion of rules via the\n{OMeta memoization}[http://www.vpri.org/pdf/tr2008003_experimenting.pdf] trick."
12
+ s.email = ["drbrain@segment7.net"]
21
13
  s.executables = ["kpeg"]
14
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc"]
15
+ s.files = [".autotest", "Gemfile", "Gemfile.lock", "History.txt", "LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "bin/kpeg", "examples/calculator/calculator.kpeg", "examples/calculator/calculator.rb", "examples/foreign_reference/literals.kpeg", "examples/foreign_reference/matcher.kpeg", "examples/foreign_reference/matcher.rb", "examples/lua_string/driver.rb", "examples/lua_string/lua_string.kpeg", "examples/lua_string/lua_string.kpeg.rb", "examples/phone_number/README.md", "examples/phone_number/phone_number.kpeg", "examples/phone_number/phone_number.rb", "examples/upper/README.md", "examples/upper/upper.kpeg", "examples/upper/upper.rb", "kpeg.gemspec", "lib/kpeg.rb", "lib/kpeg/code_generator.rb", "lib/kpeg/compiled_parser.rb", "lib/kpeg/format.kpeg", "lib/kpeg/format_parser.rb", "lib/kpeg/grammar.rb", "lib/kpeg/grammar_renderer.rb", "lib/kpeg/match.rb", "lib/kpeg/parser.rb", "lib/kpeg/position.rb", "lib/kpeg/string_escape.kpeg", "lib/kpeg/string_escape.rb", "lib/kpeg/version.rb", "test/inputs/comments.kpeg", "test/test_file_parser_roundtrip.rb", "test/test_gen_calc.rb", "test/test_kpeg.rb", "test/test_kpeg_code_generator.rb", "test/test_kpeg_compiled_parser.rb", "test/test_kpeg_format.rb", "test/test_kpeg_grammar_renderer.rb", "test/test_left_recursion.rb", "vim/syntax_kpeg/ftdetect/kpeg.vim", "vim/syntax_kpeg/syntax/kpeg.vim", ".gemtest"]
16
+ s.homepage = "https://github.com/evanphx/kpeg"
17
+ s.rdoc_options = ["--main", "README.rdoc"]
22
18
  s.require_paths = ["lib"]
23
- s.add_development_dependency "rake"
19
+ s.rubyforge_project = "kpeg"
20
+ s.rubygems_version = "1.8.12"
21
+ s.signing_key = "/Users/drbrain/.gem/gem-private_key.pem"
22
+ s.summary = "KPeg is a simple PEG library for Ruby"
23
+ s.test_files = ["test/test_file_parser_roundtrip.rb", "test/test_gen_calc.rb", "test/test_kpeg.rb", "test/test_kpeg_code_generator.rb", "test/test_kpeg_compiled_parser.rb", "test/test_kpeg_format.rb", "test/test_kpeg_grammar_renderer.rb", "test/test_left_recursion.rb"]
24
+
25
+ if s.respond_to? :specification_version then
26
+ s.specification_version = 3
27
+
28
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
29
+ s.add_development_dependency(%q<minitest>, ["~> 2.11"])
30
+ s.add_development_dependency(%q<rdoc>, ["~> 3.10"])
31
+ s.add_development_dependency(%q<hoe>, ["~> 2.15"])
32
+ else
33
+ s.add_dependency(%q<minitest>, ["~> 2.11"])
34
+ s.add_dependency(%q<rdoc>, ["~> 3.10"])
35
+ s.add_dependency(%q<hoe>, ["~> 2.15"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<minitest>, ["~> 2.11"])
39
+ s.add_dependency(%q<rdoc>, ["~> 3.10"])
40
+ s.add_dependency(%q<hoe>, ["~> 2.15"])
41
+ end
24
42
  end
data/lib/hoe/kpeg.rb ADDED
@@ -0,0 +1,94 @@
1
+ ##
2
+ # Kpeg plugin for hoe.
3
+ #
4
+ # === Tasks Provided:
5
+ #
6
+ # parser :: Generate parsers for all .kpeg files in your manifest
7
+ # .kpeg -> .rb rule :: Generate a parser using kpeg.
8
+ #
9
+ # NOTE: This plugin is derived from the Hoe::Racc and used under the MIT
10
+ # license:
11
+ #
12
+ # Copyright (c) Ryan Davis, seattle.rb
13
+ #
14
+ # Permission is hereby granted, free of charge, to any person obtaining
15
+ # a copy of this software and associated documentation files (the
16
+ # "Software"), to deal in the Software without restriction, including
17
+ # without limitation the rights to use, copy, modify, merge, publish,
18
+ # distribute, sublicense, and/or sell copies of the Software, and to
19
+ # permit persons to whom the Software is furnished to do so, subject to
20
+ # the following conditions:
21
+ #
22
+ # The above copyright notice and this permission notice shall be
23
+ # included in all copies or substantial portions of the Software.
24
+ #
25
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
28
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
29
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
30
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
31
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
+
33
+ module Hoe::Kpeg
34
+
35
+ ##
36
+ # Optional: Defines what tasks need to generate parsers first.
37
+ #
38
+ # Defaults to [:multi, :test, :check_manifest]
39
+ #
40
+ # If you have extra tasks that require your parser to be built, add their
41
+ # names here in your hoe spec. eg:
42
+ #
43
+ # kpeg_tasks << :debug
44
+
45
+ attr_accessor :kpeg_tasks
46
+
47
+ ##
48
+ # Optional: Defines what flags to use for kpeg. default: "-s -v"
49
+
50
+ attr_accessor :kpeg_flags
51
+
52
+ ##
53
+ # Initialize variables for kpeg plugin.
54
+
55
+ def initialize_kpeg
56
+ self.kpeg_tasks = [:multi, :test, :check_manifest]
57
+
58
+ # -v = verbose
59
+ # -s = parser does not require runtime
60
+ self.kpeg_flags ||= "-s -v"
61
+
62
+ dependency 'kpeg', '~> 0.9', :development
63
+ end
64
+
65
+ ##
66
+ # Define tasks for kpeg plugin
67
+
68
+ def define_kpeg_tasks
69
+ kpeg_files = self.spec.files.find_all { |f| f =~ /\.kpeg$/ }
70
+
71
+ parser_files = kpeg_files.map { |f| f.sub(/\.kpeg$/, ".rb") }
72
+
73
+ self.clean_globs += parser_files
74
+
75
+ rule ".rb" => ".kpeg" do |t|
76
+ kpeg = Gem.bin_path "kpeg", "kpeg"
77
+
78
+ begin
79
+ ruby "-rubygems #{kpeg} #{kpeg_flags} -o #{t.name} #{t.source}"
80
+ rescue
81
+ abort "need kpeg, please run rake check_extra_deps"
82
+ end
83
+ end
84
+
85
+ desc "build the parser" unless parser_files.empty?
86
+ task :parser
87
+
88
+ task :parser => parser_files
89
+
90
+ kpeg_tasks.each do |t|
91
+ task t => :parser
92
+ end
93
+ end
94
+ end
data/lib/kpeg.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  module KPeg
2
+
3
+ VERSION = "0.9.0"
4
+
2
5
  def self.grammar
3
6
  g = Grammar.new
4
7
  yield g
@@ -323,8 +323,16 @@ module KPeg
323
323
 
324
324
  def output
325
325
  return @output if @output
326
+
327
+ code = []
328
+
329
+ if header = @grammar.directives['header']
330
+ code << header.action.strip
331
+ code << "\n"
332
+ end
333
+
326
334
  if @standalone
327
- code = "class #{@name}\n"
335
+ code << "class #{@name}\n"
328
336
 
329
337
  unless cp = standalone_region(
330
338
  File.expand_path("../compiled_parser.rb", __FILE__))
@@ -341,7 +349,7 @@ module KPeg
341
349
  cp.gsub!(/include Position/, pp)
342
350
  code << cp << "\n"
343
351
  else
344
- code = "require 'kpeg/compiled_parser'\n\n"
352
+ code << "require 'kpeg/compiled_parser'\n\n"
345
353
  code << "class #{@name} < KPeg::CompiledParser\n"
346
354
  end
347
355
 
@@ -417,7 +425,12 @@ module KPeg
417
425
  end
418
426
 
419
427
  code << "end\n"
420
- @output = code
428
+
429
+ if footer = @grammar.directives['footer']
430
+ code << footer.action
431
+ end
432
+
433
+ @output = code.join
421
434
  end
422
435
 
423
436
  def make(str)
@@ -183,32 +183,24 @@ module KPeg
183
183
  end
184
184
  end
185
185
 
186
- class LeftRecursive
187
- def initialize(detected=false)
188
- @detected = detected
189
- end
190
-
191
- attr_accessor :detected
192
- end
193
-
194
186
  class MemoEntry
195
187
  def initialize(ans, pos)
196
188
  @ans = ans
197
189
  @pos = pos
198
- @uses = 1
199
190
  @result = nil
191
+ @set = false
192
+ @left_rec = false
200
193
  end
201
194
 
202
- attr_reader :ans, :pos, :uses, :result
203
-
204
- def inc!
205
- @uses += 1
206
- end
195
+ attr_reader :ans, :pos, :result, :set
196
+ attr_accessor :left_rec
207
197
 
208
198
  def move!(ans, pos, result)
209
199
  @ans = ans
210
200
  @pos = pos
211
201
  @result = result
202
+ @set = true
203
+ @left_rec = false
212
204
  end
213
205
  end
214
206
 
@@ -236,12 +228,10 @@ module KPeg
236
228
  def apply_with_args(rule, *args)
237
229
  memo_key = [rule, args]
238
230
  if m = @memoizations[memo_key][@pos]
239
- m.inc!
240
-
241
231
  prev = @pos
242
232
  @pos = m.pos
243
- if m.ans.kind_of? LeftRecursive
244
- m.ans.detected = true
233
+ if !m.set
234
+ m.left_rec = true
245
235
  return nil
246
236
  end
247
237
 
@@ -249,18 +239,19 @@ module KPeg
249
239
 
250
240
  return m.ans
251
241
  else
252
- lr = LeftRecursive.new(false)
253
- m = MemoEntry.new(lr, @pos)
242
+ m = MemoEntry.new(nil, @pos)
254
243
  @memoizations[memo_key][@pos] = m
255
244
  start_pos = @pos
256
245
 
257
246
  ans = __send__ rule, *args
258
247
 
248
+ lr = m.left_rec
249
+
259
250
  m.move! ans, @pos, @result
260
251
 
261
252
  # Don't bother trying to grow the left recursion
262
253
  # if it's failing straight away (thus there is no seed)
263
- if ans and lr.detected
254
+ if ans and lr
264
255
  return grow_lr(rule, args, start_pos, m)
265
256
  else
266
257
  return ans
@@ -272,12 +263,10 @@ module KPeg
272
263
 
273
264
  def apply(rule)
274
265
  if m = @memoizations[rule][@pos]
275
- m.inc!
276
-
277
266
  prev = @pos
278
267
  @pos = m.pos
279
- if m.ans.kind_of? LeftRecursive
280
- m.ans.detected = true
268
+ if !m.set
269
+ m.left_rec = true
281
270
  return nil
282
271
  end
283
272
 
@@ -285,18 +274,19 @@ module KPeg
285
274
 
286
275
  return m.ans
287
276
  else
288
- lr = LeftRecursive.new(false)
289
- m = MemoEntry.new(lr, @pos)
277
+ m = MemoEntry.new(nil, @pos)
290
278
  @memoizations[rule][@pos] = m
291
279
  start_pos = @pos
292
280
 
293
281
  ans = __send__ rule
294
282
 
283
+ lr = m.left_rec
284
+
295
285
  m.move! ans, @pos, @result
296
286
 
297
287
  # Don't bother trying to grow the left recursion
298
288
  # if it's failing straight away (thus there is no seed)
299
- if ans and lr.detected
289
+ if ans and lr
300
290
  return grow_lr(rule, nil, start_pos, m)
301
291
  else
302
292
  return ans