arst 0.0.1 → 0.0.3

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.
@@ -0,0 +1,29 @@
1
+ module ARST
2
+ module Generator
3
+
4
+ class Base
5
+
6
+ def self.generate(node, options={})
7
+ new(node).generate(options)
8
+ end
9
+
10
+ attr_reader :node
11
+
12
+ def initialize(node)
13
+ @node = node
14
+ end
15
+
16
+ def generate(options={})
17
+ parse_children(@node, options)
18
+ end
19
+
20
+ protected
21
+
22
+ def parse_children(node, options={})
23
+ raise NotImplementedError
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ require 'arst/generator/base'
2
+
3
+ module ARST
4
+ module Generator
5
+
6
+ class C < Base
7
+
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,164 @@
1
+ require 'arst/generator/base'
2
+ require 'arst/helpers'
3
+
4
+ module ARST
5
+ module Generator
6
+
7
+ # TODO: This class is so dirty >=) CLEANN EEETTT
8
+ class Ruby < Base
9
+
10
+ def initialize(node)
11
+ super(node)
12
+
13
+ @current_output = nil
14
+ end
15
+
16
+ protected
17
+
18
+ # =-=-= Hooks =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
19
+
20
+ def default_options
21
+ {
22
+ depth: 0,
23
+ split_files: true,
24
+ indent_size: 2,
25
+ indent_char: ' ',
26
+ newline_size: 1,
27
+ newline_character: "\n"
28
+ }
29
+ end
30
+
31
+ def indent(options)
32
+ (options[:indent_char] * options[:indent_size]) * options[:depth]
33
+ end
34
+
35
+ def newline(options)
36
+ newline_char = options[:newline_size] == 0 ? '; ' : options[:newline_character]
37
+
38
+ options[:newline_character] * options[:newline_size]
39
+ end
40
+
41
+ # =-=-= Parsers =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
42
+
43
+ def parse_children(node, options={})
44
+ options = default_options.merge( options )
45
+
46
+ if options[:split_files]
47
+ @current_output = []
48
+ parse_children_as_multiple_files(node, options)
49
+ else
50
+ @current_output = [ { filename: filename_for_single_file(node), body: '' } ]
51
+
52
+ parse_children_as_single_file(node, options)
53
+ end
54
+
55
+ result = @current_output
56
+ @current_output = nil
57
+
58
+ result
59
+ end
60
+
61
+ def parse_children_as_single_file(node, options={})
62
+ node.children.each do |node|
63
+ @current_output[0][:body] << indent(options) # TODO: DRY this up as a helper method
64
+ @current_output[0][:body] << code_from_node(node) # TODO: DRY this up as a helper method
65
+ @current_output[0][:body] << newline(options) # TODO: DRY this up as a helper method
66
+ parse_children_as_single_file( node, options.merge(depth: options[:depth]+1) ) unless node.children.empty?
67
+ @current_output[0][:body] << "#{ indent(options) }end#{ newline(options) }" if [:module, :class].include?(node.type)
68
+ end
69
+ end
70
+
71
+ def parse_children_as_multiple_files(node, options={})
72
+ options[:ancestors] = node.ancestors # The main (first) node's ancestor list
73
+
74
+ unless [:root, :include, :extend].include?(node.type) # TODO: Hold container (class, module) and non-container (include, extend) in helper method or constant
75
+ body_code = parse_node_for_multiple_files(node, options)
76
+ body_requirements = require_statements_from_node(node)
77
+ body = "#{ body_requirements }#{ body_code }"
78
+
79
+ @current_output << { filename: filename_for_split_files(node), body: body }
80
+ end
81
+ node.children.each { |child| parse_children_as_multiple_files(child, options) }
82
+ end
83
+
84
+ def parse_node_for_multiple_files(node, options={})
85
+ output = ''
86
+ options[:current_ancestor_index] ||= 0
87
+ ancestor = options[:ancestors][ options[:current_ancestor_index] ]
88
+
89
+ output << indent(options) # TODO: DRY this up as a helper method
90
+ output << code_from_node(ancestor) # TODO: DRY this up as a helper method
91
+ output << newline(options) # TODO: DRY this up as a helper method
92
+
93
+ if options[:current_ancestor_index] == options[:ancestors].count - 1 # The first node
94
+ child_options = options.merge(depth: options[:depth]+1)
95
+
96
+ ancestor.children.each do |child|
97
+ if [:include, :extend].include?(child.type) # TODO: Hold container (class, module) and non-container (include, extend) in helper method or constant
98
+ output << indent(child_options) # TODO: DRY this up as a helper method
99
+ output << code_from_node(child) # TODO: DRY this up as a helper method
100
+ output << newline(child_options) # TODO: DRY this up as a helper method
101
+ end
102
+ end
103
+ else
104
+ child_options = options.merge(depth: options[:depth]+1, current_ancestor_index: options[:current_ancestor_index]+1)
105
+ output << parse_node_for_multiple_files( ancestor, child_options )
106
+ end
107
+
108
+ output << "#{ indent(options) }end#{ newline(options) }" if [:module, :class].include?(ancestor.type)
109
+
110
+ output
111
+ end
112
+
113
+ # =-=-= Helpers =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
114
+
115
+ def filename_for_single_file(node)
116
+ node.children.collect(&:human_name).join('_and_') + '.rb'
117
+ end
118
+
119
+ def filename_for_split_files(node)
120
+ node.ancestors.collect(&:human_name).join('/') + '.rb'
121
+ end
122
+
123
+ def code_from_node(node)
124
+ case node.type
125
+ when :module
126
+ "module #{ node.name }"
127
+ when :class
128
+ code_class = "class #{ node.name }"
129
+ code_subclass = " < #{ node.superclass }" if node.superclass?
130
+
131
+ "#{code_class}#{code_subclass}"
132
+ when :extend
133
+ "extend #{ node.name }"
134
+ when :include
135
+ "include #{ node.name }"
136
+ else
137
+ ""
138
+ # TODO: Raise ARST::Error::InvalidNodeType
139
+ end
140
+ end
141
+
142
+ def require_statements_from_node(node)
143
+ # TODO: Determine scope of subclass or included/extended module
144
+ nodes_with_requirements = node.children.find_all do |child|
145
+ [:include, :extend].include?(child.type) || child.type == :class && child.superclass?
146
+ end
147
+
148
+ nodes_with_requirements.unshift(node) if node.type == :class && node.superclass?
149
+
150
+ nodes_with_requirements.collect! do |child|
151
+ name = child.type == :class ? child.superclass : child.name
152
+ ancestry = name.include?('::') ? name.split('::') : child.ancestors.collect(&:name)
153
+ path = ancestry.collect { |name| Helpers.underscore(name) }.join('/')
154
+
155
+ "require '#{path}'"
156
+ end
157
+
158
+ nodes_with_requirements.empty? ? nil : nodes_with_requirements.join("\n") << "\n\n"
159
+ end
160
+
161
+ end
162
+
163
+ end
164
+ end
@@ -0,0 +1,13 @@
1
+ module ARST
2
+
3
+ module Helpers
4
+
5
+ def underscore(string)
6
+ string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
7
+ end
8
+
9
+ extend self
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,25 @@
1
+ require 'arst/node/root'
2
+ require 'arst/node/module'
3
+ require 'arst/node/class'
4
+ require 'arst/node/include'
5
+ require 'arst/node/extend'
6
+
7
+ module ARST
8
+
9
+ module Node
10
+
11
+ # TODO: Rename to something more specific... from_raw_tree?
12
+ def self.from_options(options)
13
+ case options[:type]
14
+ when 'module' then Node::Module.new(options)
15
+ when 'class' then Node::Class.new(options)
16
+ when 'extend' then Node::Extend.new(options)
17
+ when 'include' then Node::Include.new(options)
18
+ else
19
+ # TODO: Raise ARST::Error::InvalidNodeType
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,39 @@
1
+ require 'arst/helpers'
2
+
3
+ module ARST
4
+ module Node
5
+
6
+ class Base
7
+
8
+ attr_reader :parent, :children
9
+
10
+ def initialize(options={})
11
+ # TODO: Validate keys
12
+ options = default_options.merge(options)
13
+
14
+ @parent, @children = options.values_at(:parent, :children)
15
+ @children.collect! { |child_options| Node.from_options( child_options.merge(parent: self) ) }
16
+ end
17
+
18
+ def ancestors
19
+ return [] if parent.nil?
20
+
21
+ ( parent.ancestors || [] ) + [self]
22
+ end
23
+
24
+ def type
25
+ @type ||= ARST::Helpers.underscore(self.class.to_s.split(/::/).last).to_sym
26
+ end
27
+
28
+ protected
29
+
30
+ def default_options
31
+ {
32
+ children: []
33
+ }
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,32 @@
1
+ require 'arst/node/base'
2
+ require 'arst/node/namable'
3
+
4
+ module ARST
5
+ module Node
6
+
7
+ class Class < Base
8
+
9
+ include Namable
10
+
11
+ attr_reader :superclass
12
+
13
+ def initialize(options={})
14
+ super(options)
15
+
16
+ # TODO: Validate keys
17
+ self.superclass = options[:superclass]
18
+ end
19
+
20
+ def superclass=(superclass)
21
+ # TODO: Sanitize `superclass` given
22
+ @superclass = superclass.to_s unless superclass.nil?
23
+ end
24
+
25
+ def superclass?
26
+ !superclass.nil?
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ require 'arst/node/base'
2
+ require 'arst/node/namable'
3
+
4
+ module ARST
5
+ module Node
6
+
7
+ class Extend < Base
8
+
9
+ include Namable
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'arst/node/base'
2
+ require 'arst/node/namable'
3
+
4
+ module ARST
5
+ module Node
6
+
7
+ class Include < Base
8
+
9
+ include Namable
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'arst/node/base'
2
+ require 'arst/node/namable'
3
+
4
+ module ARST
5
+ module Node
6
+
7
+ class Module < Base
8
+
9
+ include Namable
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ module ARST
2
+ module Node
3
+
4
+ module Namable
5
+
6
+ attr_reader :name
7
+
8
+ def initialize(options={})
9
+ super(options)
10
+
11
+ # TODO: Validate keys
12
+ self.name = options[:name]
13
+ end
14
+
15
+ def name=(name)
16
+ @name = name.to_s # TODO: Sanitize
17
+ end
18
+
19
+ def human_name
20
+ ARST::Helpers.underscore(name)
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ require 'arst/node/base'
2
+
3
+ module ARST
4
+ module Node
5
+
6
+ class Root < Base
7
+
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,64 @@
1
+ require 'parslet'
2
+ require 'arst/node'
3
+
4
+ module ARST
5
+
6
+ # The parser for ARST.
7
+ class Parser < Parslet::Parser
8
+
9
+ def self.parse(input, options={})
10
+ new.parse(input, options)
11
+ end
12
+
13
+ def parse(input, options={})
14
+ tree = super(input)
15
+
16
+ options[:raw] ? tree : Node::Root.new(tree)
17
+ end
18
+
19
+ def indent(depth)
20
+ str(' ' * depth)
21
+ end
22
+
23
+ rule(:space) { match('[\t\s]') }
24
+ rule(:spaces) { space.repeat(1) }
25
+ rule(:newline) { match('\n') }
26
+
27
+ rule :constant do
28
+ match('[A-Z]') >> ( match('[A-Za-z0-9]') | str('::') ).repeat(1) # TODO: Cannot end with '::'
29
+ end
30
+
31
+ rule :module_keyword do
32
+ str('module').as(:type) >> spaces >> constant.as(:name)
33
+ end
34
+
35
+ rule :class_keyword do
36
+ str('class').as(:type) >> spaces >> constant.as(:name) >>
37
+ ( spaces >> str('<') >> spaces >> constant.as(:superclass) ).maybe
38
+ end
39
+
40
+ rule :include_keyword do
41
+ str('include').as(:type) >> space >> constant.as(:name)
42
+ end
43
+
44
+ rule :extend_keyword do
45
+ str('extend').as(:type) >> space >> constant.as(:name)
46
+ end
47
+
48
+ def node(depth)
49
+ indent(depth) >>
50
+ (
51
+ ( class_keyword | module_keyword ) >> newline.maybe >>
52
+ dynamic { |soutce, context| node(depth+1).repeat(0) }.as(:children) |
53
+ ( include_keyword | extend_keyword ) >> newline.maybe |
54
+ newline
55
+ )
56
+ end
57
+
58
+ rule(:document) { node(0).repeat.as(:children) }
59
+
60
+ root :document
61
+
62
+ end
63
+
64
+ end