indentation-parser 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,8 +1,11 @@
1
- [![Build Status](https://secure.travis-ci.org/ssmm/indentation-parser.png)](http://travis-ci.org/ssmm/indentation-parser)
1
+ _This is not complete yet! More to come in the next weeks, so stay tuned._
2
2
 
3
- # Indentation-Parser
3
+ # Indentation-Parser
4
4
 
5
- A parser for indented files.
5
+ [![Build Status](https://secure.travis-ci.org/ssmm/indentation-parser.png)](http://travis-ci.org/ssmm/indentation-parser)
6
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/ssmm/indentation-parser)
7
+
8
+ Parses source code that defines context by indentation.
6
9
 
7
10
  ## How
8
11
 
@@ -37,13 +40,12 @@ You write a parser like so:
37
40
 
38
41
  ```ruby
39
42
  parser = IndentationParser.new do |p|
40
- p.default do |parent, indentation, source|
43
+ p.default do |parent, source|
41
44
  node = OpenStruct.new
42
45
  parent.send("#{source}=", node)
43
46
  node
44
47
  end
45
-
46
- p.on /([^ ]+) : (.+)/ do |parent, indentation, source, captures|
48
+ p.on /([^ ]+) : (.+)/ do |parent, source, captures|
47
49
  node = captures[2]
48
50
  parent.send("#{captures[1]}=", node)
49
51
  node
@@ -51,7 +53,7 @@ parser = IndentationParser.new do |p|
51
53
  end
52
54
  ```
53
55
 
54
- Then you read your special syntax from a file, parse it and celebrate:
56
+ Then you read your special syntax from a file and parse it:
55
57
 
56
58
  ```ruby
57
59
  source = IO.read("path/to/file")
@@ -60,4 +62,40 @@ output = parser.read(source, OpenStruct.new).value
60
62
  puts output.this.is.an.example
61
63
  puts output.this.is.the.second.one
62
64
  puts output.this.serves.as.another.example
63
- ```
65
+ ```
66
+
67
+ # Details
68
+
69
+ ## Hooks
70
+
71
+ The following hooks are available:
72
+
73
+ ```ruby
74
+ p.on /regex/ do |parent, source, captures|
75
+ #...
76
+ end
77
+ ```
78
+
79
+ ```ruby
80
+ p.default do |parent, source|
81
+ #...
82
+ end
83
+ ```
84
+
85
+ ```ruby
86
+ p.on_leaf /regex/ do |parent, source, captures|
87
+ #...
88
+ end
89
+ ```
90
+
91
+ ```ruby
92
+ p.on_leaf do |parent, source|
93
+ #...
94
+ end
95
+ ```
96
+
97
+ ```ruby
98
+ p.as_a_child_of Type do |parent, source|
99
+ #...
100
+ end
101
+ ```
@@ -2,18 +2,19 @@
2
2
  require File.expand_path('../lib/indentation-parser/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
+ gem.name = "indentation-parser"
6
+ gem.version = Indentation::Parser::VERSION
7
+
5
8
  gem.authors = ["Samuel Müller"]
6
9
  gem.email = ["mueller.samu@gmail.com"]
7
- gem.description = %q{indentation-parser}
8
- gem.summary = %q{indentation-parser}
9
- gem.homepage = ""
10
+ gem.description = %q{Parses source code that defines context by indentation.}
11
+ gem.summary = gem.description
12
+ gem.homepage = "https://github.com/ssmm/indentation-parser"
10
13
 
11
14
  gem.files = `git ls-files`.split($\)
12
15
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "indentation-parser"
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
17
  gem.require_paths = ["lib"]
16
- gem.version = Indentation::Parser::VERSION
17
18
 
18
19
  gem.add_development_dependency "rspec", "~> 2.11"
19
20
  end
@@ -18,43 +18,60 @@ class IndentationParser
18
18
  end
19
19
 
20
20
  def read text, root_value
21
-
22
21
  root = IndentationParser::RootNode.new
23
-
24
22
  root.set_value root_value
25
-
26
23
  node_stack = [root]
27
24
 
28
25
  text.each_line do |line|
29
26
  line.chomp!
30
27
  next if line.length == 0 || line =~ /^\s*$/
28
+
31
29
  indentation, source = parse_line line
32
30
  new_node = IndentationParser::Node.new source, indentation
31
+ previous_node = node_stack.last
33
32
 
34
- lastone = node_stack.last
35
-
36
- if new_node.indentation() - 1 == lastone.indentation #the current node is indented to the previous node
37
- lastone.add new_node
38
- handle_node lastone unless lastone.is_a? RootNode
39
- node_stack.push new_node
40
- elsif new_node.indentation == lastone.indentation #the current node is on the same level as the previous node
41
- leaf = node_stack.pop
42
- handle_leaf leaf
43
- node_stack.last.add new_node
44
- node_stack.push new_node
45
- elsif new_node.indentation() - 1 > lastone.indentation #too large indentation -> raise an error
33
+ if new_node.indentation() - 1 == previous_node.indentation
34
+ #the current node is indented to the previous node
35
+ handle_by_one_indentation previous_node, new_node, node_stack
36
+
37
+ elsif new_node.indentation == previous_node.indentation
38
+ #the current node is on the same level as the previous node
39
+ handle_same_indentation new_node, node_stack
40
+
41
+ elsif new_node.indentation() - 1 > previous_node.indentation
42
+ #too large indentation -> raise an error
46
43
  raise "ou neei"
47
- else #indentation is less than previous node. Pop everything from stack until parent is found
48
- leaf = node_stack.pop
49
- handle_leaf leaf
50
- (lastone.indentation - new_node.indentation).times do
51
- node_stack.pop
52
- end
53
- node_stack.last.add new_node
54
- node_stack.push new_node
44
+
45
+ else
46
+ #indentation is less than previous node.
47
+ #pop everything from stack until parent is found
48
+ handle_less_indentation previous_node, new_node, node_stack
55
49
  end
56
50
  end
57
51
  handle_leaf node_stack.last
58
52
  root
59
53
  end
54
+
55
+ def handle_by_one_indentation previous_node, new_node, node_stack
56
+ previous_node.add new_node
57
+ handle_node previous_node unless previous_node.is_a? RootNode
58
+ node_stack.push new_node
59
+ end
60
+
61
+ def handle_same_indentation new_node, node_stack
62
+ leaf = node_stack.pop
63
+ handle_leaf leaf
64
+ node_stack.last.add new_node
65
+ node_stack.push new_node
66
+ end
67
+
68
+ def handle_less_indentation previous_node, new_node, node_stack
69
+ leaf = node_stack.pop
70
+ handle_leaf leaf
71
+ (previous_node.indentation - new_node.indentation).times do
72
+ node_stack.pop
73
+ end
74
+ node_stack.last.add new_node
75
+ node_stack.push new_node
76
+ end
60
77
  end
@@ -1,9 +1,13 @@
1
1
  class IndentationParser
2
2
 
3
+ def call_handler block, node
4
+ block.call(node.parent.value, node.source)
5
+ end
6
+
3
7
  def execute_regex_handler node, regex, block
4
8
  captures = regex.match node.source
5
9
  if captures
6
- node_value = block.call(node.parent.value, node.indentation, node.source, captures)
10
+ node_value = block.call(node.parent.value, node.source, captures)
7
11
  node.set_value node_value
8
12
  return true
9
13
  end
@@ -13,7 +17,7 @@ class IndentationParser
13
17
  def execute_child_of_handler node
14
18
  handler = @child_of_handlers[node.parent.value.class]
15
19
  if handler
16
- node_value = handler.call node.parent.value, node.indentation, node.source
20
+ node_value = call_handler handler, node
17
21
  node.set_value node_value
18
22
  return true
19
23
  end
@@ -36,7 +40,7 @@ class IndentationParser
36
40
 
37
41
  handled = try_to_handle @node_handlers, node
38
42
  if not handled and @default
39
- node_value = @default.call node.parent.value, node.indentation, node.source if @default
43
+ node_value = call_handler @default, node if @default
40
44
  node.set_value node_value
41
45
  end
42
46
  end
@@ -44,7 +48,7 @@ class IndentationParser
44
48
  def handle_leaf node
45
49
  handled = try_to_handle @leaf_handlers, node
46
50
  if not handled and @on_leaf
47
- node_value = @on_leaf.call node.parent.value, node.indentation, node.source
51
+ node_value = call_handler @on_leaf, node
48
52
  node.set_value node_value
49
53
  else
50
54
  handle_node node
@@ -1,5 +1,5 @@
1
1
  module Indentation
2
2
  module Parser
3
- VERSION = "0.0.1"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
@@ -4,13 +4,12 @@ require 'ostruct'
4
4
  describe IndentationParser do
5
5
  it "does what is written in the readme" do
6
6
  parser = IndentationParser.new do |p|
7
- p.default do |parent, indentation, source|
7
+ p.default do |parent, source|
8
8
  node = OpenStruct.new
9
9
  parent.send("#{source}=", node)
10
10
  node
11
11
  end
12
-
13
- p.on /([^ ]+) : (.+)/ do |parent, indentation, source, captures|
12
+ p.on /([^ ]+) : (.+)/ do |parent, source, captures|
14
13
  node = captures[2]
15
14
  parent.send("#{captures[1]}=", node)
16
15
  node
@@ -24,6 +23,70 @@ describe IndentationParser do
24
23
  output.this.is.an.example.should eq "First example"
25
24
  output.this.is.the.second.one.should eq "Second example"
26
25
  output.this.serves.as.another.example.should eq "Third example"
27
-
28
26
  end
29
- end
27
+ end
28
+
29
+
30
+ # ##### First block
31
+
32
+ # ```ruby
33
+ # p.default do |parent, indentation, source|
34
+ # node = OpenStruct.new
35
+ # parent.send("#{source}=", node)
36
+ # node
37
+ # end
38
+ # ```
39
+
40
+ # This defines what the parser does with a line of code when no other hook is defined. The
41
+ # parameters you get from the parser inside your block are:
42
+
43
+ # - The `parent` node. This is the object you have set for an already parsed line.
44
+ # - The `source`, basically the whole line the parser currently evaluates without indentation.
45
+
46
+ # *Now, step by step:*
47
+
48
+ # We define a new `OpenStruct` instance and store it in a variable called `node`.
49
+
50
+ # Since we know that all parent objects are going to be `OpenStruct`s too, we set an attribute on
51
+ # it. The name of the attribute is given by the `source` parameter.
52
+
53
+ # Last but not least, we return the node. **This is very important!** In
54
+ # order to be able to pass the `parent` parameter to the block, the parser maintains an internal
55
+ # node structure. Only if you pass the node as a return value, the parser can store it there!
56
+
57
+ # ##### Second block
58
+
59
+ # ```ruby
60
+ # p.on /([^ ]+) : (.+)/ do |parent, indentation, source, captures|
61
+ # node = captures[2]
62
+ # parent.send("#{captures[1]}=", node)
63
+ # node
64
+ # end
65
+ # ```
66
+
67
+ # The regular expression `/([^ ]+) : (.+)/` will match with a text that has the format
68
+ # `"text_without_spaces : Any text"`. Every time the parser comes along a line of code which
69
+ # matches this expression, it will execute the block you provide. There is an additional parameter
70
+ # you can use in this block:
71
+ # - The `captures`, the result of matching the
72
+ # [regular expression](http://www.ruby-doc.org/core-1.9.3/Regexp.html) to the source.
73
+
74
+ # *Step by step:*
75
+
76
+ # We assign the second capture, which, with the example source given, will be `"First example"` in
77
+ # the very first case, to a local variable called `node`.
78
+
79
+ # Again, at this point, we know that our parent is an `OpenStruct`. We define an attribute on it,
80
+ # with the name taken from our captures object. This is what happens for each of the three cases:
81
+
82
+ # ```ruby
83
+ # #parent.send("#{captures[1]}=", node) translates to
84
+ # parent.example = "First example"
85
+ # parent.one = "Second example"
86
+ # parent.example = "Third example"
87
+ # ```
88
+
89
+ # Actually, the last line of the second block is not required, since there will not be any child
90
+ # to this node, so there is no block that would need the parent object as a parameter. You can
91
+ # return it anyways, just for the sake of thoroughness.
92
+
data/spec/simple_spec.rb CHANGED
@@ -3,16 +3,16 @@ require 'indentation-parser'
3
3
  describe IndentationParser do
4
4
  it "parses indented files" do
5
5
  parser = IndentationParser.new do |p|
6
- p.default do |parent, indentation, source|
6
+ p.default do |parent, source|
7
7
  node = {}
8
8
  parent[source.to_sym] = node
9
9
  node
10
10
  end
11
11
 
12
- p.on_leaf do |parent, indentation, source|
13
- node = {}
14
- parent[source.to_sym] = node
15
- node
12
+ p.on_leaf do |parent, source|
13
+ parent[:leafs] = Array.new unless parent[:leafs]
14
+ parent[:leafs] << source
15
+ source
16
16
  end
17
17
  end
18
18
 
@@ -22,22 +22,21 @@ describe IndentationParser do
22
22
 
23
23
  expected_hash = {
24
24
  :this => {
25
- :structure => {},
26
25
  :is => {
27
26
  :crazy => {
28
- :and => {}
27
+ :leafs => ["and"]
29
28
  }
30
29
  },
31
- :weird => {}
30
+ :leafs => ["structure", "weird"]
32
31
  },
33
32
  :and => {
34
33
  :does => {
35
34
  :not => {
36
- :make => {}
35
+ :leafs => ["make"]
37
36
  },
38
- :any => {}
37
+ :leafs => ["any"]
39
38
  },
40
- :sense => {}
39
+ :leafs => ["sense"]
41
40
  }
42
41
  }
43
42
 
@@ -10,39 +10,39 @@ describe IndentationParser do
10
10
  it "parses a .universe file" do
11
11
  parser = IndentationParser.new do |p|
12
12
 
13
- p.on /planet = ([a-zA-Z]+)/ do |parent, indentation, source, captures|
13
+ p.on /planet = ([a-zA-Z]+)/ do |parent, source, captures|
14
14
  planet = Planet.new captures[1]
15
15
  planet.continents = []
16
16
  parent.planets << planet
17
17
  planet
18
18
  end
19
19
 
20
- p.on /continent = ([a-zA-Z]+)/ do |parent, indentation, source, captures|
20
+ p.on /continent = ([a-zA-Z]+)/ do |parent, source, captures|
21
21
  continent = Continent.new captures[1]
22
22
  continent.countries = []
23
23
  parent.continents << continent
24
24
  continent
25
25
  end
26
26
 
27
- p.on /country = ([a-zA-Z]+)/ do |parent, indentation, source, captures|
27
+ p.on /country = ([a-zA-Z]+)/ do |parent, source, captures|
28
28
  country = Country.new captures[1]
29
29
  parent.countries << country
30
30
  country
31
31
  end
32
32
 
33
- p.on /population = ([0-9]+)/ do |parent, indentation, source, captures|
33
+ p.on /population = ([0-9]+)/ do |parent, source, captures|
34
34
  parent.population = captures[1].to_i
35
35
  end
36
36
 
37
- p.on /currency = ([a-zA-Z]+)/ do |parent, indentation, source, captures|
37
+ p.on /currency = ([a-zA-Z]+)/ do |parent, source, captures|
38
38
  parent.currency = captures[1]
39
39
  end
40
40
 
41
- p.on /specialities/ do |parent, indentation, source, captures|
41
+ p.on /specialities/ do |parent, source, captures|
42
42
  parent.specialities = []
43
43
  end
44
44
 
45
- p.as_a_child_of Array do |parent, indentation, source|
45
+ p.as_a_child_of Array do |parent, source|
46
46
  parent << source
47
47
  end
48
48
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: indentation-parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-15 00:00:00.000000000 Z
12
+ date: 2012-07-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -27,7 +27,7 @@ dependencies:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
29
  version: '2.11'
30
- description: indentation-parser
30
+ description: Parses source code that defines context by indentation.
31
31
  email:
32
32
  - mueller.samu@gmail.com
33
33
  executables: []
@@ -52,7 +52,7 @@ files:
52
52
  - spec/readme_example_spec.rb
53
53
  - spec/simple_spec.rb
54
54
  - spec/universe_spec.rb
55
- homepage: ''
55
+ homepage: https://github.com/ssmm/indentation-parser
56
56
  licenses: []
57
57
  post_install_message:
58
58
  rdoc_options: []
@@ -75,7 +75,7 @@ rubyforge_project:
75
75
  rubygems_version: 1.8.24
76
76
  signing_key:
77
77
  specification_version: 3
78
- summary: indentation-parser
78
+ summary: Parses source code that defines context by indentation.
79
79
  test_files:
80
80
  - spec/material/an.example
81
81
  - spec/material/test.mylang