indentation-parser 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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