indentation-parser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .project
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - ruby-head
7
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in indentation-parser.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Samuel Müller
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ [![Build Status](https://secure.travis-ci.org/ssmm/indentation-parser.png)](http://travis-ci.org/ssmm/indentation-parser)
2
+
3
+ # Indentation-Parser
4
+
5
+ A parser for indented files.
6
+
7
+ ## How
8
+
9
+ Assume you have this structure:
10
+
11
+ this
12
+ is
13
+ an
14
+ example : First example
15
+ the
16
+ second
17
+ one : Second example
18
+ serves
19
+ as
20
+ another
21
+ example : Third example
22
+
23
+ You want to create an output which you can use like this:
24
+
25
+ ```ruby
26
+ puts output.this.is.an.example
27
+ # => "First example"
28
+
29
+ puts output.this.is.the.second.one
30
+ # => "Second example"
31
+
32
+ puts output.this.serves.as.another.example
33
+ # => "Third example"
34
+ ```
35
+
36
+ You write a parser like so:
37
+
38
+ ```ruby
39
+ parser = IndentationParser.new do |p|
40
+ p.default do |parent, indentation, source|
41
+ node = OpenStruct.new
42
+ parent.send("#{source}=", node)
43
+ node
44
+ end
45
+
46
+ p.on /([^ ]+) : (.+)/ do |parent, indentation, source, captures|
47
+ node = captures[2]
48
+ parent.send("#{captures[1]}=", node)
49
+ node
50
+ end
51
+ end
52
+ ```
53
+
54
+ Then you read your special syntax from a file, parse it and celebrate:
55
+
56
+ ```ruby
57
+ source = IO.read("path/to/file")
58
+ output = parser.read(source, OpenStruct.new).value
59
+
60
+ puts output.this.is.an.example
61
+ puts output.this.is.the.second.one
62
+ puts output.this.serves.as.another.example
63
+ ```
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/indentation-parser/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Samuel Müller"]
6
+ gem.email = ["mueller.samu@gmail.com"]
7
+ gem.description = %q{indentation-parser}
8
+ gem.summary = %q{indentation-parser}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ 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"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Indentation::Parser::VERSION
17
+
18
+ gem.add_development_dependency "rspec", "~> 2.11"
19
+ end
@@ -0,0 +1,60 @@
1
+ require "indentation-parser/version"
2
+ require "indentation-parser/node"
3
+ require "indentation-parser/hooks"
4
+ require "indentation-parser/handlers"
5
+
6
+ class IndentationParser
7
+
8
+ def initialize
9
+ @node_handlers = {}
10
+ @leaf_handlers = {}
11
+ @child_of_handlers = {}
12
+ yield self if block_given?
13
+ end
14
+
15
+ def parse_line line
16
+ idx = line.index(/[^ ]/)
17
+ return idx / 2, line[idx..line.length]
18
+ end
19
+
20
+ def read text, root_value
21
+
22
+ root = IndentationParser::RootNode.new
23
+
24
+ root.set_value root_value
25
+
26
+ node_stack = [root]
27
+
28
+ text.each_line do |line|
29
+ line.chomp!
30
+ next if line.length == 0 || line =~ /^\s*$/
31
+ indentation, source = parse_line line
32
+ new_node = IndentationParser::Node.new source, indentation
33
+
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
46
+ 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
55
+ end
56
+ end
57
+ handle_leaf node_stack.last
58
+ root
59
+ end
60
+ end
@@ -0,0 +1,54 @@
1
+ class IndentationParser
2
+
3
+ def execute_regex_handler node, regex, block
4
+ captures = regex.match node.source
5
+ if captures
6
+ node_value = block.call(node.parent.value, node.indentation, node.source, captures)
7
+ node.set_value node_value
8
+ return true
9
+ end
10
+ false
11
+ end
12
+
13
+ def execute_child_of_handler node
14
+ handler = @child_of_handlers[node.parent.value.class]
15
+ if handler
16
+ node_value = handler.call node.parent.value, node.indentation, node.source
17
+ node.set_value node_value
18
+ return true
19
+ end
20
+ false
21
+ end
22
+
23
+ def try_to_handle handlers, node
24
+ handled = false
25
+ handlers.each do |key, value|
26
+ result = execute_regex_handler node, key, value
27
+ handled = true if result
28
+ end
29
+ handled
30
+ end
31
+
32
+ def handle_node node
33
+ handled = execute_child_of_handler node
34
+
35
+ return handled if handled
36
+
37
+ handled = try_to_handle @node_handlers, node
38
+ if not handled and @default
39
+ node_value = @default.call node.parent.value, node.indentation, node.source if @default
40
+ node.set_value node_value
41
+ end
42
+ end
43
+
44
+ def handle_leaf node
45
+ handled = try_to_handle @leaf_handlers, node
46
+ if not handled and @on_leaf
47
+ node_value = @on_leaf.call node.parent.value, node.indentation, node.source
48
+ node.set_value node_value
49
+ else
50
+ handle_node node
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,23 @@
1
+ class IndentationParser
2
+ def on regex, &block
3
+ @node_handlers[regex] = block
4
+ end
5
+
6
+ def default &block
7
+ @default = block
8
+ end
9
+
10
+ alias :else :default
11
+
12
+ def on_leaf regex, &block
13
+ @leaf_handlers[regex] = block
14
+ end
15
+
16
+ def on_leaf &block
17
+ @on_leaf = block
18
+ end
19
+
20
+ def as_a_child_of parent_node_type, &block
21
+ @child_of_handlers[parent_node_type] = block
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ class IndentationParser
2
+ class Node
3
+ def initialize source, indentation
4
+ @source = source
5
+ @indentation = indentation
6
+ @subnodes = []
7
+ end
8
+ def add node
9
+ node.set_parent self
10
+ @subnodes << node
11
+ end
12
+ def parent
13
+ @parent
14
+ end
15
+ def set_parent parent
16
+ @parent = parent
17
+ end
18
+ def indentation
19
+ @indentation
20
+ end
21
+ def source
22
+ @source
23
+ end
24
+ def value
25
+ @value
26
+ end
27
+ def set_value value
28
+ @value = value
29
+ end
30
+ end
31
+
32
+ class RootNode < Node
33
+ def initialize
34
+ super "root", -1
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ module Indentation
2
+ module Parser
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ this
2
+ is
3
+ an
4
+ example : First example
5
+ the
6
+ second
7
+ one : Second example
8
+ serves
9
+ as
10
+ another
11
+ example : Third example
@@ -0,0 +1,13 @@
1
+ this
2
+ structure
3
+ is
4
+ crazy
5
+ and
6
+ weird
7
+ and
8
+ does
9
+ not
10
+ make
11
+ any
12
+ sense
13
+
@@ -0,0 +1,21 @@
1
+ planet = Earth
2
+ continent = Europe
3
+ country = Switzerland
4
+ population = 8
5
+ currency = CHF
6
+ specialities:
7
+ chocolate
8
+ cheese
9
+ country = England
10
+ population = 40
11
+ currency = GBP
12
+ specialities:
13
+ fish'n'chips
14
+ drum'n'bass
15
+ continent = America
16
+ country = USA
17
+ population = 100
18
+ currency = USD
19
+ specialities:
20
+ hamburgers
21
+ baseball
@@ -0,0 +1,29 @@
1
+ require 'indentation-parser'
2
+ require 'ostruct'
3
+
4
+ describe IndentationParser do
5
+ it "does what is written in the readme" do
6
+ parser = IndentationParser.new do |p|
7
+ p.default do |parent, indentation, source|
8
+ node = OpenStruct.new
9
+ parent.send("#{source}=", node)
10
+ node
11
+ end
12
+
13
+ p.on /([^ ]+) : (.+)/ do |parent, indentation, source, captures|
14
+ node = captures[2]
15
+ parent.send("#{captures[1]}=", node)
16
+ node
17
+ end
18
+ end
19
+
20
+ source = IO.read("spec/material/an.example")
21
+
22
+ output = parser.read(source, OpenStruct.new).value
23
+
24
+ output.this.is.an.example.should eq "First example"
25
+ output.this.is.the.second.one.should eq "Second example"
26
+ output.this.serves.as.another.example.should eq "Third example"
27
+
28
+ end
29
+ end
@@ -0,0 +1,46 @@
1
+ require 'indentation-parser'
2
+
3
+ describe IndentationParser do
4
+ it "parses indented files" do
5
+ parser = IndentationParser.new do |p|
6
+ p.default do |parent, indentation, source|
7
+ node = {}
8
+ parent[source.to_sym] = node
9
+ node
10
+ end
11
+
12
+ p.on_leaf do |parent, indentation, source|
13
+ node = {}
14
+ parent[source.to_sym] = node
15
+ node
16
+ end
17
+ end
18
+
19
+ source = IO.read("spec/material/test.mylang")
20
+
21
+ result = parser.read(source, {}).value
22
+
23
+ expected_hash = {
24
+ :this => {
25
+ :structure => {},
26
+ :is => {
27
+ :crazy => {
28
+ :and => {}
29
+ }
30
+ },
31
+ :weird => {}
32
+ },
33
+ :and => {
34
+ :does => {
35
+ :not => {
36
+ :make => {}
37
+ },
38
+ :any => {}
39
+ },
40
+ :sense => {}
41
+ }
42
+ }
43
+
44
+ result.should eq expected_hash
45
+ end
46
+ end
@@ -0,0 +1,68 @@
1
+ require 'indentation-parser'
2
+
3
+ Universe = Struct.new(:planets)
4
+ Planet = Struct.new(:name, :continents)
5
+ Continent = Struct.new(:name, :countries)
6
+ Country = Struct.new(:name, :population, :currency, :specialities)
7
+
8
+ describe IndentationParser do
9
+
10
+ it "parses a .universe file" do
11
+ parser = IndentationParser.new do |p|
12
+
13
+ p.on /planet = ([a-zA-Z]+)/ do |parent, indentation, source, captures|
14
+ planet = Planet.new captures[1]
15
+ planet.continents = []
16
+ parent.planets << planet
17
+ planet
18
+ end
19
+
20
+ p.on /continent = ([a-zA-Z]+)/ do |parent, indentation, source, captures|
21
+ continent = Continent.new captures[1]
22
+ continent.countries = []
23
+ parent.continents << continent
24
+ continent
25
+ end
26
+
27
+ p.on /country = ([a-zA-Z]+)/ do |parent, indentation, source, captures|
28
+ country = Country.new captures[1]
29
+ parent.countries << country
30
+ country
31
+ end
32
+
33
+ p.on /population = ([0-9]+)/ do |parent, indentation, source, captures|
34
+ parent.population = captures[1].to_i
35
+ end
36
+
37
+ p.on /currency = ([a-zA-Z]+)/ do |parent, indentation, source, captures|
38
+ parent.currency = captures[1]
39
+ end
40
+
41
+ p.on /specialities/ do |parent, indentation, source, captures|
42
+ parent.specialities = []
43
+ end
44
+
45
+ p.as_a_child_of Array do |parent, indentation, source|
46
+ parent << source
47
+ end
48
+
49
+ end
50
+
51
+ source = IO.read("spec/material/the.universe")
52
+ universe = Universe.new
53
+ universe.planets = []
54
+ result = parser.read(source, universe).value
55
+
56
+ countries = result.planets.first.continents.first.countries
57
+
58
+ switzerland = countries.shift
59
+ switzerland.name.should eq "Switzerland"
60
+ switzerland.population.should eq 8
61
+ switzerland.specialities.should eq ["chocolate", "cheese"]
62
+
63
+ england = countries.shift
64
+ england.name.should eq "England"
65
+ england.population.should eq 40
66
+ england.specialities.should eq ["fish'n'chips", "drum'n'bass"]
67
+ end
68
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: indentation-parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Samuel Müller
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.11'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.11'
30
+ description: indentation-parser
31
+ email:
32
+ - mueller.samu@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - .travis.yml
39
+ - Gemfile
40
+ - LICENSE
41
+ - README.md
42
+ - Rakefile
43
+ - indentation-parser.gemspec
44
+ - lib/indentation-parser.rb
45
+ - lib/indentation-parser/handlers.rb
46
+ - lib/indentation-parser/hooks.rb
47
+ - lib/indentation-parser/node.rb
48
+ - lib/indentation-parser/version.rb
49
+ - spec/material/an.example
50
+ - spec/material/test.mylang
51
+ - spec/material/the.universe
52
+ - spec/readme_example_spec.rb
53
+ - spec/simple_spec.rb
54
+ - spec/universe_spec.rb
55
+ homepage: ''
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.24
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: indentation-parser
79
+ test_files:
80
+ - spec/material/an.example
81
+ - spec/material/test.mylang
82
+ - spec/material/the.universe
83
+ - spec/readme_example_spec.rb
84
+ - spec/simple_spec.rb
85
+ - spec/universe_spec.rb