bel_parser 1.0.0.alpha.1

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.
Files changed (182) hide show
  1. checksums.yaml +7 -0
  2. data/.gemspec +28 -0
  3. data/CHANGELOG.md +10 -0
  4. data/LICENSE +191 -0
  5. data/README.md +9 -0
  6. data/VERSION +1 -0
  7. data/bin/bel2_termcheck +39 -0
  8. data/lib/bel_parser.rb +17 -0
  9. data/lib/bel_parser/ast_filter.rb +27 -0
  10. data/lib/bel_parser/ast_generator.rb +86 -0
  11. data/lib/bel_parser/ast_validator.rb +40 -0
  12. data/lib/bel_parser/expression/parser.rb +42 -0
  13. data/lib/bel_parser/expression/term_semantics.rb +36 -0
  14. data/lib/bel_parser/language.rb +7 -0
  15. data/lib/bel_parser/language/function.rb +59 -0
  16. data/lib/bel_parser/language/quoting.rb +236 -0
  17. data/lib/bel_parser/language/semantic_ast.rb +604 -0
  18. data/lib/bel_parser/language/semantics/analyzer.rb +59 -0
  19. data/lib/bel_parser/language/signature.rb +39 -0
  20. data/lib/bel_parser/language/specification.rb +49 -0
  21. data/lib/bel_parser/language/syntax/expression/incomplete_node.rb +14 -0
  22. data/lib/bel_parser/language/syntax/expression/invalid_term_function.rb +22 -0
  23. data/lib/bel_parser/language/version1.rb +50 -0
  24. data/lib/bel_parser/language/version1/functions/abundance.rb +85 -0
  25. data/lib/bel_parser/language/version1/functions/biological_process.rb +85 -0
  26. data/lib/bel_parser/language/version1/functions/catalytic_activity.rb +110 -0
  27. data/lib/bel_parser/language/version1/functions/cell_secretion.rb +80 -0
  28. data/lib/bel_parser/language/version1/functions/cell_surface_expression.rb +80 -0
  29. data/lib/bel_parser/language/version1/functions/chaperone_activity.rb +110 -0
  30. data/lib/bel_parser/language/version1/functions/complex_abundance.rb +115 -0
  31. data/lib/bel_parser/language/version1/functions/composite_abundance.rb +80 -0
  32. data/lib/bel_parser/language/version1/functions/degradation.rb +80 -0
  33. data/lib/bel_parser/language/version1/functions/fusion.rb +302 -0
  34. data/lib/bel_parser/language/version1/functions/gene_abundance.rb +125 -0
  35. data/lib/bel_parser/language/version1/functions/gtp_bound_activity.rb +110 -0
  36. data/lib/bel_parser/language/version1/functions/kinase_activity.rb +110 -0
  37. data/lib/bel_parser/language/version1/functions/list.rb +115 -0
  38. data/lib/bel_parser/language/version1/functions/micro_rna_abundance.rb +85 -0
  39. data/lib/bel_parser/language/version1/functions/molecular_activity.rb +80 -0
  40. data/lib/bel_parser/language/version1/functions/pathology.rb +85 -0
  41. data/lib/bel_parser/language/version1/functions/peptidase_activity.rb +110 -0
  42. data/lib/bel_parser/language/version1/functions/phosphatase_activity.rb +110 -0
  43. data/lib/bel_parser/language/version1/functions/products.rb +80 -0
  44. data/lib/bel_parser/language/version1/functions/protein_abundance.rb +245 -0
  45. data/lib/bel_parser/language/version1/functions/protein_modification.rb +167 -0
  46. data/lib/bel_parser/language/version1/functions/reactants.rb +80 -0
  47. data/lib/bel_parser/language/version1/functions/reaction.rb +85 -0
  48. data/lib/bel_parser/language/version1/functions/ribosylation_activity.rb +110 -0
  49. data/lib/bel_parser/language/version1/functions/rna_abundance.rb +125 -0
  50. data/lib/bel_parser/language/version1/functions/substitution.rb +96 -0
  51. data/lib/bel_parser/language/version1/functions/transcriptional_activity.rb +110 -0
  52. data/lib/bel_parser/language/version1/functions/translocation.rb +100 -0
  53. data/lib/bel_parser/language/version1/functions/transport_activity.rb +110 -0
  54. data/lib/bel_parser/language/version1/functions/truncation.rb +82 -0
  55. data/lib/bel_parser/language/version1/return_types/abundance.rb +20 -0
  56. data/lib/bel_parser/language/version1/return_types/any.rb +74 -0
  57. data/lib/bel_parser/language/version1/return_types/biological_process.rb +17 -0
  58. data/lib/bel_parser/language/version1/return_types/catalytic_activity.rb +20 -0
  59. data/lib/bel_parser/language/version1/return_types/chaperone_activity.rb +20 -0
  60. data/lib/bel_parser/language/version1/return_types/complex_abundance.rb +17 -0
  61. data/lib/bel_parser/language/version1/return_types/fusion.rb +17 -0
  62. data/lib/bel_parser/language/version1/return_types/gene_abundance.rb +17 -0
  63. data/lib/bel_parser/language/version1/return_types/gtp_bound_activity.rb +20 -0
  64. data/lib/bel_parser/language/version1/return_types/kinase_activity.rb +20 -0
  65. data/lib/bel_parser/language/version1/return_types/list.rb +17 -0
  66. data/lib/bel_parser/language/version1/return_types/micro_rna_abundance.rb +17 -0
  67. data/lib/bel_parser/language/version1/return_types/molecular_activity.rb +20 -0
  68. data/lib/bel_parser/language/version1/return_types/pathology.rb +17 -0
  69. data/lib/bel_parser/language/version1/return_types/peptidase_activity.rb +20 -0
  70. data/lib/bel_parser/language/version1/return_types/phosphatase_activity.rb +20 -0
  71. data/lib/bel_parser/language/version1/return_types/products.rb +17 -0
  72. data/lib/bel_parser/language/version1/return_types/protein_abundance.rb +17 -0
  73. data/lib/bel_parser/language/version1/return_types/protein_modification.rb +17 -0
  74. data/lib/bel_parser/language/version1/return_types/reactants.rb +17 -0
  75. data/lib/bel_parser/language/version1/return_types/ribosylation_activity.rb +20 -0
  76. data/lib/bel_parser/language/version1/return_types/rna_abundance.rb +17 -0
  77. data/lib/bel_parser/language/version1/return_types/substitution.rb +17 -0
  78. data/lib/bel_parser/language/version1/return_types/transcriptional_activity.rb +20 -0
  79. data/lib/bel_parser/language/version1/return_types/transport_activity.rb +20 -0
  80. data/lib/bel_parser/language/version1/return_types/truncation.rb +17 -0
  81. data/lib/bel_parser/language/version2.rb +50 -0
  82. data/lib/bel_parser/language/version2/functions/abundance.rb +165 -0
  83. data/lib/bel_parser/language/version2/functions/activity.rb +115 -0
  84. data/lib/bel_parser/language/version2/functions/biological_process.rb +85 -0
  85. data/lib/bel_parser/language/version2/functions/cell_secretion.rb +80 -0
  86. data/lib/bel_parser/language/version2/functions/cell_surface_expression.rb +80 -0
  87. data/lib/bel_parser/language/version2/functions/complex_abundance.rb +190 -0
  88. data/lib/bel_parser/language/version2/functions/composite_abundance.rb +80 -0
  89. data/lib/bel_parser/language/version2/functions/degradation.rb +80 -0
  90. data/lib/bel_parser/language/version2/functions/fragment.rb +119 -0
  91. data/lib/bel_parser/language/version2/functions/from_location.rb +85 -0
  92. data/lib/bel_parser/language/version2/functions/fusion.rb +227 -0
  93. data/lib/bel_parser/language/version2/functions/gene_abundance.rb +195 -0
  94. data/lib/bel_parser/language/version2/functions/list.rb +115 -0
  95. data/lib/bel_parser/language/version2/functions/location.rb +85 -0
  96. data/lib/bel_parser/language/version2/functions/micro_rna_abundance.rb +165 -0
  97. data/lib/bel_parser/language/version2/functions/molecular_activity.rb +83 -0
  98. data/lib/bel_parser/language/version2/functions/pathology.rb +85 -0
  99. data/lib/bel_parser/language/version2/functions/products.rb +80 -0
  100. data/lib/bel_parser/language/version2/functions/protein_abundance.rb +285 -0
  101. data/lib/bel_parser/language/version2/functions/protein_modification.rb +167 -0
  102. data/lib/bel_parser/language/version2/functions/reactants.rb +80 -0
  103. data/lib/bel_parser/language/version2/functions/reaction.rb +85 -0
  104. data/lib/bel_parser/language/version2/functions/rna_abundance.rb +195 -0
  105. data/lib/bel_parser/language/version2/functions/to_location.rb +85 -0
  106. data/lib/bel_parser/language/version2/functions/translocation.rb +90 -0
  107. data/lib/bel_parser/language/version2/functions/variant.rb +83 -0
  108. data/lib/bel_parser/language/version2/return_types/abundance.rb +20 -0
  109. data/lib/bel_parser/language/version2/return_types/activity.rb +20 -0
  110. data/lib/bel_parser/language/version2/return_types/any.rb +74 -0
  111. data/lib/bel_parser/language/version2/return_types/biological_process.rb +17 -0
  112. data/lib/bel_parser/language/version2/return_types/complex_abundance.rb +17 -0
  113. data/lib/bel_parser/language/version2/return_types/fragment.rb +20 -0
  114. data/lib/bel_parser/language/version2/return_types/from_location.rb +20 -0
  115. data/lib/bel_parser/language/version2/return_types/fusion.rb +17 -0
  116. data/lib/bel_parser/language/version2/return_types/gene_abundance.rb +17 -0
  117. data/lib/bel_parser/language/version2/return_types/list.rb +17 -0
  118. data/lib/bel_parser/language/version2/return_types/location.rb +20 -0
  119. data/lib/bel_parser/language/version2/return_types/micro_rna_abundance.rb +17 -0
  120. data/lib/bel_parser/language/version2/return_types/molecular_activity.rb +20 -0
  121. data/lib/bel_parser/language/version2/return_types/pathology.rb +17 -0
  122. data/lib/bel_parser/language/version2/return_types/products.rb +17 -0
  123. data/lib/bel_parser/language/version2/return_types/protein_abundance.rb +17 -0
  124. data/lib/bel_parser/language/version2/return_types/protein_modification.rb +17 -0
  125. data/lib/bel_parser/language/version2/return_types/reactants.rb +17 -0
  126. data/lib/bel_parser/language/version2/return_types/rna_abundance.rb +17 -0
  127. data/lib/bel_parser/language/version2/return_types/to_location.rb +20 -0
  128. data/lib/bel_parser/language/version2/return_types/variant.rb +20 -0
  129. data/lib/bel_parser/mixin/line_continuator.rb +15 -0
  130. data/lib/bel_parser/mixin/line_mapping.rb +14 -0
  131. data/lib/bel_parser/parser.rb +54 -0
  132. data/lib/bel_parser/parsers/ast/mapped_traversal.rb +36 -0
  133. data/lib/bel_parser/parsers/ast/node.rb +705 -0
  134. data/lib/bel_parser/parsers/ast/sexp.rb +8 -0
  135. data/lib/bel_parser/parsers/ast/traversal.rb +21 -0
  136. data/lib/bel_parser/parsers/bel_script.rb +4 -0
  137. data/lib/bel_parser/parsers/bel_script/define_annotation.rb +5476 -0
  138. data/lib/bel_parser/parsers/bel_script/define_annotation.rl +141 -0
  139. data/lib/bel_parser/parsers/bel_script/define_namespace.rb +1780 -0
  140. data/lib/bel_parser/parsers/bel_script/define_namespace.rl +121 -0
  141. data/lib/bel_parser/parsers/bel_script/set.rb +4556 -0
  142. data/lib/bel_parser/parsers/bel_script/set.rl +116 -0
  143. data/lib/bel_parser/parsers/bel_script/unset.rb +706 -0
  144. data/lib/bel_parser/parsers/bel_script/unset.rl +95 -0
  145. data/lib/bel_parser/parsers/common.rb +5 -0
  146. data/lib/bel_parser/parsers/common/blank_line.rb +211 -0
  147. data/lib/bel_parser/parsers/common/blank_line.rl +81 -0
  148. data/lib/bel_parser/parsers/common/comment_line.rb +245 -0
  149. data/lib/bel_parser/parsers/common/comment_line.rl +97 -0
  150. data/lib/bel_parser/parsers/common/common.rb +7 -0
  151. data/lib/bel_parser/parsers/common/common.rl +13 -0
  152. data/lib/bel_parser/parsers/common/identifier.rb +289 -0
  153. data/lib/bel_parser/parsers/common/identifier.rl +106 -0
  154. data/lib/bel_parser/parsers/common/list.rb +2142 -0
  155. data/lib/bel_parser/parsers/common/list.rl +144 -0
  156. data/lib/bel_parser/parsers/common/string.rb +271 -0
  157. data/lib/bel_parser/parsers/common/string.rl +107 -0
  158. data/lib/bel_parser/parsers/expression.rb +7 -0
  159. data/lib/bel_parser/parsers/expression/comment.rb +239 -0
  160. data/lib/bel_parser/parsers/expression/comment.rl +97 -0
  161. data/lib/bel_parser/parsers/expression/parameter.rb +1506 -0
  162. data/lib/bel_parser/parsers/expression/parameter.rl +97 -0
  163. data/lib/bel_parser/parsers/expression/relationship.rb +254 -0
  164. data/lib/bel_parser/parsers/expression/relationship.rl +98 -0
  165. data/lib/bel_parser/parsers/expression/statement_nested.rb +17802 -0
  166. data/lib/bel_parser/parsers/expression/statement_nested.rl +141 -0
  167. data/lib/bel_parser/parsers/expression/statement_observed_term.rb +7291 -0
  168. data/lib/bel_parser/parsers/expression/statement_observed_term.rl +92 -0
  169. data/lib/bel_parser/parsers/expression/statement_simple.rb +10475 -0
  170. data/lib/bel_parser/parsers/expression/statement_simple.rl +112 -0
  171. data/lib/bel_parser/parsers/expression/term.rb +3989 -0
  172. data/lib/bel_parser/parsers/expression/term.rl +157 -0
  173. data/lib/bel_parser/parsers/line_parser.rb +92 -0
  174. data/lib/bel_parser/parsers/mixin/buffer.rb +10 -0
  175. data/lib/bel_parser/parsers/nonblocking_io_wrapper.rb +50 -0
  176. data/lib/bel_parser/script/parser.rb +49 -0
  177. data/lib/bel_parser/vendor/ast.rb +17 -0
  178. data/lib/bel_parser/vendor/ast/node.rb +254 -0
  179. data/lib/bel_parser/vendor/ast/processor.rb +12 -0
  180. data/lib/bel_parser/vendor/ast/processor/mixin.rb +282 -0
  181. data/lib/bel_parser/vendor/ast/sexp.rb +30 -0
  182. metadata +226 -0
@@ -0,0 +1,157 @@
1
+ # begin: ragel
2
+ =begin
3
+ %%{
4
+ machine bel;
5
+
6
+ include 'parameter.rl';
7
+
8
+ action start_function {
9
+ @buffers[:function] = []
10
+ }
11
+
12
+ action append_function {
13
+ @buffers[:function] << fc
14
+ }
15
+
16
+ action finish_function {
17
+ @buffers[:function] = identifier(utf8_string(@buffers[:function]))
18
+ }
19
+
20
+ action term_init {
21
+ @buffers[:term_stack] = [ term() ]
22
+ }
23
+
24
+ action inner_term_init {
25
+ @buffers[:term_stack] << term()
26
+ }
27
+
28
+ action term_fx {
29
+ fx = @buffers[:function]
30
+ @buffers[:term_stack][-1] = @buffers[:term_stack][-1] << function(fx)
31
+ }
32
+
33
+ action term_argument {
34
+ @buffers[:term_stack][-1] = @buffers[:term_stack][-1] << argument(@buffers[:parameter])
35
+ @buffers[:parameter] = nil
36
+ }
37
+
38
+ action fxbt {
39
+ fpc -= @buffers[:function].length + 1
40
+ fcall inner_term;
41
+ }
42
+
43
+ action fxret {
44
+ inner_term = @buffers[:term_stack].pop
45
+ @buffers[:term_stack][-1] = @buffers[:term_stack][-1] << argument(inner_term)
46
+ fret;
47
+ }
48
+
49
+ action yield_term_ast {
50
+ yield @buffers[:term_stack][-1]
51
+ }
52
+
53
+ inner_term :=
54
+ IDENT >inner_term_init >start_function $append_function %finish_function
55
+ SP*
56
+ '(' @term_fx
57
+ (
58
+ BEL_PARAMETER %term_argument |
59
+ IDENT >start_function $append_function '(' @fxbt
60
+ )
61
+ (
62
+ SP* ',' SP*
63
+ (
64
+ BEL_PARAMETER %term_argument |
65
+ IDENT >start_function $append_function '(' @fxbt
66
+ )
67
+ )*
68
+ ')' @fxret;
69
+
70
+ outer_term =
71
+ IDENT >term_init >start_function $append_function %finish_function
72
+ SP*
73
+ '(' @term_fx
74
+ (
75
+ BEL_PARAMETER %term_argument |
76
+ IDENT >start_function $append_function '(' @fxbt
77
+ )
78
+ (
79
+ SP* ',' SP*
80
+ (
81
+ BEL_PARAMETER %term_argument |
82
+ IDENT >start_function $append_function '(' @fxbt
83
+ )
84
+ )*
85
+ ')';
86
+
87
+ term :=
88
+ outer_term %yield_term_ast NL;
89
+ }%%
90
+ =end
91
+ # end: ragel
92
+
93
+ require_relative '../ast/node'
94
+ require_relative '../mixin/buffer'
95
+ require_relative '../nonblocking_io_wrapper'
96
+
97
+ module BELParser
98
+ module Parsers
99
+ module Expression
100
+ module Term
101
+
102
+ class << self
103
+
104
+ MAX_LENGTH = 1024 * 128 # 128K
105
+
106
+ def parse(content)
107
+ return nil unless content
108
+
109
+ Parser.new(content).each do |obj|
110
+ yield obj
111
+ end
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ class Parser
118
+ include Enumerable
119
+ include BELParser::Parsers::Buffer
120
+ include BELParser::Parsers::AST::Sexp
121
+
122
+ def initialize(content)
123
+ @content = content
124
+ # begin: ragel
125
+ %% write data;
126
+ # end: ragel
127
+ end
128
+
129
+ def each
130
+ @buffers = {}
131
+ stack = []
132
+ data = @content.unpack('C*')
133
+ p = 0
134
+ pe = data.length
135
+ eof = data.length
136
+
137
+ # begin: ragel
138
+ %% write init;
139
+ %% write exec;
140
+ # end: ragel
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ if __FILE__ == $0
149
+ $stdin.each_line do |line|
150
+ BELParser::Parsers::Expression::Term.parse(line) { |obj|
151
+ puts obj.inspect
152
+ }
153
+ end
154
+ end
155
+
156
+ # vim: ft=ruby ts=2 sw=2:
157
+ # encoding: utf-8
@@ -0,0 +1,92 @@
1
+ require_relative 'common'
2
+ require_relative 'expression'
3
+ require_relative 'bel_script'
4
+
5
+ # Top-level LINE module.
6
+ module LINE
7
+ include BELParser::Parsers::Common
8
+ include BELParser::Parsers::Expression
9
+ include BELParser::Parsers::BELScript
10
+
11
+ BEL_PARSERS = [
12
+ BELParser::Parsers::Common::BlankLine,
13
+ BELParser::Parsers::Common::CommentLine,
14
+ BELParser::Parsers::Common::Identifier,
15
+ BELParser::Parsers::Common::String,
16
+ BELParser::Parsers::Common::List,
17
+ BELParser::Parsers::Expression::Parameter,
18
+ BELParser::Parsers::Expression::Term,
19
+ BELParser::Parsers::Expression::Relationship,
20
+ BELParser::Parsers::Expression::Comment,
21
+ BELParser::Parsers::Expression::StatementObservedTerm,
22
+ BELParser::Parsers::Expression::StatementSimple,
23
+ BELParser::Parsers::Expression::StatementNested,
24
+ BELParser::Parsers::BELScript::DefineAnnotation,
25
+ BELParser::Parsers::BELScript::DefineNamespace,
26
+ BELParser::Parsers::BELScript::Set,
27
+ BELParser::Parsers::BELScript::Unset
28
+ ].freeze
29
+
30
+ # rubocop:disable MethodLength, AbcSize
31
+ def self.parse(io)
32
+ # single line transform
33
+ line_enum = io
34
+ .each_line
35
+ .lazy
36
+ .map { |line| LINE.normalize_line_terminators(line) }
37
+
38
+ # multi-line transform
39
+ loop do
40
+ begin
41
+ line = line_enum.next
42
+
43
+ while line.end_with?("\\\n")
44
+ line.chomp!("\\\n")
45
+ line += line_enum.next
46
+ end
47
+
48
+ BEL_PARSERS.each do |parser|
49
+ # rubocop:disable BlockDelimiters
50
+ parser.parse(line) { |obj|
51
+ puts "parser: #{parser.inspect},"\
52
+ "line: #{line.strip},"\
53
+ "object: \n#{obj.inspect}"
54
+ }
55
+ end
56
+ rescue StopIteration
57
+ return
58
+ end
59
+ end
60
+ end
61
+
62
+ def self.normalize_line_terminators(string)
63
+ return nil unless string
64
+ string.strip + "\n"
65
+ end
66
+
67
+ def self.map_empty_line(string)
68
+ if LINE.match_empty_line(string)
69
+ nil
70
+ else
71
+ string
72
+ end
73
+ end
74
+
75
+ def self.map_comment_line(string)
76
+ if LINE.match_comment_line(string)
77
+ nil
78
+ else
79
+ string
80
+ end
81
+ end
82
+
83
+ def self.match_empty_line(string)
84
+ string =~ /^\s*$/
85
+ end
86
+
87
+ def self.match_comment_line(string)
88
+ string =~ /^\s*#/
89
+ end
90
+ end
91
+
92
+ LINE.parse($stdin) if FILE == $PROGRAM_NAME
@@ -0,0 +1,10 @@
1
+ module BELParser
2
+ module Parsers
3
+ # Buffer module.
4
+ module Buffer
5
+ def utf8_string(buffer)
6
+ buffer.pack('C*').force_encoding('utf-8')
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,50 @@
1
+ # Provides a platform-independent, non-blocking wrapper for reading an
2
+ # {http://ruby-doc.org/core-2.2.2/IO.html IO}-like object. This wrapper
3
+ # object must be enumerated using the {#each} method.
4
+ class NonblockingIOWrapper
5
+ # Initialize this wrapper around the +io+ object and read at most
6
+ # +read_length+ bytes in a non-blocking manner.
7
+ #
8
+ # @param [IO] io an IO-like object
9
+ # @param [Fixnum] read_length the buffer length to read
10
+ def initialize(io, read_length = (128 * 1024))
11
+ @io = io
12
+ @read_length = read_length
13
+ @read_method = nonblocking_read(@io)
14
+ end
15
+
16
+ # Yields each buffer read from the wrapped IO-like object to the provided
17
+ # block (e.g. +{ |block| ... }+). The read length is set on {#initialize}.
18
+ #
19
+ # @yield the buffers read from the IO-like object
20
+ # @yieldparam [String] buffer the read buffer as uninterpreted bytes
21
+ def each
22
+ while (buffer = @read_method.call(@read_length))
23
+ yield buffer
24
+ end
25
+ rescue IO::WaitReadable
26
+ IO.select([@io])
27
+ retry
28
+ # rubocop:disable HandleExceptions
29
+ rescue EOFError
30
+ # end of stream; parsing complete
31
+ end
32
+
33
+ private
34
+
35
+ # Returns the method, appropriate for your platform, to read IO in a
36
+ # non-blocking manner.
37
+ #
38
+ # @example Call directly.
39
+ # nonblocking_read(StringIO.new('hello')).call(4)
40
+ #
41
+ # @param [IO] io an IO-like object
42
+ # @return [Method] a non-blocking read method
43
+ def nonblocking_read(io)
44
+ if Gem.win_platform?
45
+ io.method(:sysread)
46
+ else
47
+ io.method(:read_nonblock)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,49 @@
1
+ require_relative '../ast_filter'
2
+ require_relative '../ast_generator'
3
+ require_relative '../parsers/common'
4
+ require_relative '../parsers/expression'
5
+ require_relative '../parsers/bel_script'
6
+
7
+ module BELParser
8
+ module Script
9
+ # Parser for BEL Script.
10
+ class Parser
11
+ include BELParser::Parsers::Common
12
+ include BELParser::Parsers::Expression
13
+ include BELParser::Parsers::BELScript
14
+
15
+ FILTER = BELParser::ASTFilter.new(
16
+ :statement_simple,
17
+ :observed_term,
18
+ :nested_statement,
19
+ :define_annotation,
20
+ :define_namespace,
21
+ :set,
22
+ :unset,
23
+ :blank_line,
24
+ :comment_line
25
+ )
26
+
27
+ def each(io)
28
+ if block_given?
29
+ filtered_ast = FILTER.each(BELParser::ASTGenerator.new.each(io))
30
+ filtered_ast.each do |results|
31
+ yield results
32
+ end
33
+ else
34
+ enum_for(:each, io)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ if __FILE__ == $PROGRAM_NAME
42
+ BELParser::Script::Parser.new.each($stdin) do |line_result|
43
+ line_number, line, ast_results = line_result
44
+ puts "#{line_number}: #{line}"
45
+ ast_results.each do |ast|
46
+ puts ast.to_s(1)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,17 @@
1
+ # {AST} is a library for manipulating abstract syntax trees.
2
+ #
3
+ # It embraces immutability; each AST node is inherently frozen at
4
+ # creation, and updating a child node requires recreating that node
5
+ # and its every parent, recursively.
6
+ # This is a design choice. It does create some pressure on
7
+ # garbage collector, but completely eliminates all concurrency
8
+ # and aliasing problems.
9
+ #
10
+ # See also {AST::Node}, {AST::Processor::Mixin} and {AST::Sexp} for
11
+ # additional recommendations and design patterns.
12
+ #
13
+ module AST
14
+ require_relative 'ast/node'
15
+ require_relative 'ast/processor'
16
+ require_relative 'ast/sexp'
17
+ end
@@ -0,0 +1,254 @@
1
+ module AST
2
+ # Node is an immutable class, instances of which represent abstract
3
+ # syntax tree nodes. It combines semantic information (i.e. anything
4
+ # that affects the algorithmic properties of a program) with
5
+ # meta-information (line numbers or compiler intermediates).
6
+ #
7
+ # Notes on inheritance
8
+ # ====================
9
+ #
10
+ # The distinction between semantics and metadata is important. Complete
11
+ # semantic information should be contained within just the {#type} and
12
+ # {#children} of a Node instance; in other words, if an AST was to be
13
+ # stripped of all meta-information, it should remain a valid AST which
14
+ # could be successfully processed to yield a result with the same
15
+ # algorithmic properties.
16
+ #
17
+ # Thus, Node should never be inherited in order to define methods which
18
+ # affect or return semantic information, such as getters for `class_name`,
19
+ # `superclass` and `body` in the case of a hypothetical `ClassNode`. The
20
+ # correct solution is to use a generic Node with a {#type} of `:class`
21
+ # and three children. See also {Processor} for tips on working with such
22
+ # ASTs.
23
+ #
24
+ # On the other hand, Node can and should be inherited to define
25
+ # application-specific metadata (see also {#initialize}) or customize the
26
+ # printing format. It is expected that an application would have one or two
27
+ # such classes and use them across the entire codebase.
28
+ #
29
+ # The rationale for this pattern is extensibility and maintainability.
30
+ # Unlike static ones, dynamic languages do not require the presence of a
31
+ # predefined, rigid structure, nor does it improve dispatch efficiency,
32
+ # and while such a structure can certainly be defined, it does not add
33
+ # any value but incurs a maintaining cost.
34
+ # For example, extending the AST even with a transformation-local
35
+ # temporary node type requires making globally visible changes to
36
+ # the codebase.
37
+ #
38
+ class Node
39
+ # Returns the type of this node.
40
+ # @return [Symbol]
41
+ attr_reader :type
42
+
43
+ # Returns the children of this node.
44
+ # The returned value is frozen.
45
+ # @return [Array]
46
+ attr_reader :children
47
+
48
+ # Returns the precomputed hash value for this node
49
+ # @return [Fixnum]
50
+ attr_reader :hash
51
+
52
+ # Constructs a new instance of Node.
53
+ #
54
+ # The arguments `type` and `children` are converted with `to_sym` and
55
+ # `to_a` respectively. Additionally, the result of converting `children`
56
+ # is frozen. While mutating the arguments is generally considered harmful,
57
+ # the most common case is to pass an array literal to the constructor. If
58
+ # your code does not expect the argument to be frozen, use `#dup`.
59
+ #
60
+ # The `properties` hash is passed to {#assign_properties}.
61
+ def initialize(type, children=[], properties={})
62
+ @type, @children = type.to_sym, children.to_a.freeze
63
+
64
+ assign_properties(properties)
65
+
66
+ @hash = [@type, @children, self.class].hash
67
+
68
+ freeze
69
+ end
70
+
71
+ # Test if other object is equal to
72
+ # @param [Object] other
73
+ # @return [Boolean]
74
+ def eql?(other)
75
+ self.class.eql?(other.class) &&
76
+ @type.eql?(other.type) &&
77
+ @children.eql?(other.children)
78
+ end
79
+
80
+ # By default, each entry in the `properties` hash is assigned to
81
+ # an instance variable in this instance of Node. A subclass should define
82
+ # attribute readers for such variables. The values passed in the hash
83
+ # are not frozen or whitelisted; such behavior can also be implemented
84
+ # by subclassing Node and overriding this method.
85
+ #
86
+ # @return [nil]
87
+ def assign_properties(properties)
88
+ properties.each do |name, value|
89
+ instance_variable_set :"@#{name}", value
90
+ end
91
+
92
+ nil
93
+ end
94
+ protected :assign_properties
95
+
96
+ alias :original_dup :dup
97
+ private :original_dup
98
+
99
+ # Nodes are already frozen, so there is no harm in returning the
100
+ # current node as opposed to initializing from scratch and freezing
101
+ # another one.
102
+ #
103
+ # @return self
104
+ def dup
105
+ self
106
+ end
107
+ alias :clone :dup
108
+
109
+ # Returns a new instance of Node where non-nil arguments replace the
110
+ # corresponding fields of `self`.
111
+ #
112
+ # For example, `Node.new(:foo, [ 1, 2 ]).updated(:bar)` would yield
113
+ # `(bar 1 2)`, and `Node.new(:foo, [ 1, 2 ]).updated(nil, [])` would
114
+ # yield `(foo)`.
115
+ #
116
+ # If the resulting node would be identical to `self`, does nothing.
117
+ #
118
+ # @param [Symbol, nil] type
119
+ # @param [Array, nil] children
120
+ # @param [Hash, nil] properties
121
+ # @return [AST::Node]
122
+ def updated(type=nil, children=nil, properties=nil)
123
+ new_type = type || @type
124
+ new_children = children || @children
125
+ new_properties = properties || {}
126
+
127
+ if @type == new_type &&
128
+ @children == new_children &&
129
+ properties.nil?
130
+ self
131
+ else
132
+ # Maybe change call?
133
+ original_dup.send :initialize, new_type, new_children, new_properties
134
+ end
135
+ end
136
+
137
+ # Compares `self` to `other`, possibly converting with `to_ast`. Only
138
+ # `type` and `children` are compared; metadata is deliberately ignored.
139
+ #
140
+ # @return [Boolean]
141
+ def ==(other)
142
+ if equal?(other)
143
+ true
144
+ elsif other.respond_to? :to_ast
145
+ other = other.to_ast
146
+ other.type == self.type &&
147
+ other.children == self.children
148
+ else
149
+ false
150
+ end
151
+ end
152
+
153
+ # Concatenates `array` with `children` and returns the resulting node.
154
+ #
155
+ # @return [AST::Node]
156
+ def concat(array)
157
+ updated(nil, @children + array.to_a)
158
+ end
159
+
160
+ alias + concat
161
+
162
+ # Appends `element` to `children` and returns the resulting node.
163
+ #
164
+ # @return [AST::Node]
165
+ def append(element)
166
+ updated(nil, @children + [element])
167
+ end
168
+
169
+ alias << append
170
+
171
+ # Returns {#children}. This is very useful in order to decompose nodes
172
+ # concisely. For example:
173
+ #
174
+ # node = s(:gasgn, :$foo, s(:integer, 1))
175
+ # s
176
+ # var_name, value = *node
177
+ # p var_name # => :$foo
178
+ # p value # => (integer 1)
179
+ #
180
+ # @return [Array]
181
+ def to_a
182
+ children
183
+ end
184
+
185
+ # Converts `self` to a pretty-printed s-expression.
186
+ #
187
+ # @param [Integer] indent Base indentation level.
188
+ # @return [String]
189
+ def to_sexp(indent=0)
190
+ indented = " " * indent
191
+ sexp = "#{indented}(#{fancy_type}"
192
+
193
+ first_node_child = children.index do |child|
194
+ child.is_a?(Node) || child.is_a?(Array)
195
+ end || children.count
196
+
197
+ children.each_with_index do |child, idx|
198
+ if child.is_a?(Node) && idx >= first_node_child
199
+ sexp << "\n#{child.to_sexp(indent + 1)}"
200
+ else
201
+ sexp << " #{child.inspect}"
202
+ end
203
+ end
204
+
205
+ sexp << ")"
206
+
207
+ sexp
208
+ end
209
+
210
+ alias to_s to_sexp
211
+
212
+ # Converts `self` to a s-expression ruby string.
213
+ # The code return will recreate the node, using the sexp module s()
214
+ #
215
+ # @param [Integer] indent Base indentation level.
216
+ # @return [String]
217
+ def inspect(indent=0)
218
+ indented = " " * indent
219
+ sexp = "#{indented}s(:#{@type}"
220
+
221
+ first_node_child = children.index do |child|
222
+ child.is_a?(Node) || child.is_a?(Array)
223
+ end || children.count
224
+
225
+ children.each_with_index do |child, idx|
226
+ if child.is_a?(Node) && idx >= first_node_child
227
+ sexp << ",\n#{child.inspect(indent + 1)}"
228
+ else
229
+ sexp << ", #{child.inspect}"
230
+ end
231
+ end
232
+
233
+ sexp << ")"
234
+
235
+ sexp
236
+ end
237
+
238
+ # @return [AST::Node] self
239
+ def to_ast
240
+ self
241
+ end
242
+
243
+ protected
244
+
245
+ # Returns `@type` with all underscores replaced by dashes. This allows
246
+ # to write symbol literals without quotes in Ruby sources and yet have
247
+ # nicely looking s-expressions.
248
+ #
249
+ # @return [String]
250
+ def fancy_type
251
+ @type.to_s.gsub('_', '-')
252
+ end
253
+ end
254
+ end