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,42 @@
1
+ require_relative '../ast_filter'
2
+ require_relative '../ast_generator'
3
+ require_relative '../parsers/common'
4
+ require_relative '../parsers/expression'
5
+ require_relative '../mixin/line_mapping'
6
+
7
+ module BELParser
8
+ module Expression
9
+ # Parser for BEL Expression.
10
+ class Parser
11
+ include BELParser::Parsers::Common
12
+ include BELParser::Parsers::Expression
13
+
14
+ FILTER = BELParser::ASTFilter.new(
15
+ :statement_simple,
16
+ :observed_term,
17
+ :nested_statement
18
+ )
19
+
20
+ def each(io)
21
+ if block_given?
22
+ filtered_ast = FILTER.each(BELParser::ASTGenerator.new.each(io))
23
+ filtered_ast.each do |results|
24
+ yield results
25
+ end
26
+ else
27
+ enum_for(:each, io)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ if __FILE__ == $PROGRAM_NAME
35
+ BELParser::Expression::Parser.new.each($stdin) do |line_result|
36
+ line_number, line, ast_results = line_result
37
+ puts "#{line_number}: #{line}"
38
+ ast_results.each do |ast|
39
+ puts ast.to_s(1)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
1
+ require_relative '../ast_filter'
2
+ require_relative '../ast_generator'
3
+ require_relative '../parsers/expression'
4
+ require_relative '../language/version2'
5
+ require_relative '../language/semantics/analyzer'
6
+
7
+ module BELParser
8
+ module Expression
9
+ # Parser for BEL Expression.
10
+ class TermSemanticsParser
11
+ include BELParser::Parsers::Common
12
+ include BELParser::Parsers::Expression
13
+
14
+ FILTER = BELParser::ASTFilter.new(:term)
15
+
16
+ def each(io)
17
+ if block_given?
18
+ v2 = BELParser::Language::Version2::Specification.new
19
+ filtered_ast = FILTER.each(BELParser::ASTGenerator.new.each(io))
20
+ filtered_ast.each do |results|
21
+ term = results.last.first
22
+ yield BELParser::Language::Semantics.check_term(term, v2)
23
+ end
24
+ else
25
+ enum_for(:each, io)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ if __FILE__ == $PROGRAM_NAME
33
+ BELParser::Expression::TermSemanticsParser.new.each($stdin) do |semantics|
34
+ puts semantics.join(", ")
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'language/function'
2
+ require_relative 'language/quoting'
3
+ require_relative 'language/semantic_ast'
4
+ require_relative 'language/signature'
5
+ require_relative 'language/specification'
6
+
7
+ # BEL language/version1 and language/version2 files are loaded when needed.
@@ -0,0 +1,59 @@
1
+ module BELParser
2
+ module Language
3
+ # Function allows you to describe the type of BEL Term.
4
+ #
5
+ # BEL Terms are composed of BEL Functions and entity definitions
6
+ # referenced using BEL Namespace identifiers. Each BEL Term represents
7
+ # either an abundance of a biological entity, the abundance of human AKT1
8
+ # for example, or a biological process such as cardiomyopathy.
9
+ module Function
10
+ def short
11
+ raise NotImplementedError, "#{__method__} is not implemented."
12
+ end
13
+
14
+ def long
15
+ raise NotImplementedError, "#{__method__} is not implemented."
16
+ end
17
+
18
+ def return_type
19
+ raise NotImplementedError, "#{__method__} is not implemented."
20
+ end
21
+
22
+ def description
23
+ raise NotImplementedError, "#{__method__} is not implemented."
24
+ end
25
+
26
+ def signatures
27
+ raise NotImplementedError, "#{__method__} is not implemented."
28
+ end
29
+
30
+ def ===(other)
31
+ return false if other.nil?
32
+ short == other || long == other
33
+ end
34
+
35
+ def to_sym(form = :short)
36
+ value = _form_value(form)
37
+ return nil unless value
38
+ value.to_s
39
+ end
40
+
41
+ def to_s(form = :short)
42
+ value = _form_value(form)
43
+ return nil unless value
44
+ value.to_s
45
+ end
46
+
47
+ private
48
+
49
+ def _form_value(form = :short)
50
+ case form
51
+ when :short
52
+ short
53
+ when :long
54
+ long
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,236 @@
1
+ module BELParser
2
+
3
+ # The Quoting module implements quoting rules consistent with BEL
4
+ # and BEL Script. Double quotes are used to group a string together
5
+ # which may contain whitespace or special characters.
6
+ #
7
+ # A value can either be an identifier or a string value. An
8
+ # identifier can only include the characters +[0-9A-Za-z_]+. A string
9
+ # value is necessary when at least one of +[^0-9A-Za-z_]+ exists in
10
+ # the value.
11
+ #
12
+ # Uses:
13
+ #
14
+ # BEL: The BEL parameters must be an identifier or string value.
15
+ #
16
+ # BEL Script: BEL parameters, document property values, and annotation
17
+ # values must be an identifier or string value.
18
+ module Quoting
19
+
20
+ # Declares BEL Script keywords that cause problems with the OpenBEL
21
+ # Framework parser.
22
+ Keywords = %w(SET DEFINE a g p r m)
23
+
24
+ # Regular expression that matches one of {Quoting::Keywords}.
25
+ KeywordMatcher = Regexp.compile(/^(#{Keywords.join('|')})$/)
26
+
27
+ # Regular expression that matches on any non-word character.
28
+ NonWordMatcher = Regexp.compile(/[^0-9a-zA-Z_]/)
29
+
30
+ # Regular expression that matches a value surrounded by unescaped
31
+ # double quotes.
32
+ StrictQuotedMatcher = Regexp.compile(/\A".*?(?<!\\)"\Z/m)
33
+
34
+ # Regular expression that matches a value surrounded by double quotes
35
+ # that may be escaped.
36
+ LenientQuotedMatcher = Regexp.compile(/\A".*?"\Z/m)
37
+
38
+ # Regular expression that matches double quotes that are not escaped.
39
+ QuoteNotEscapedMatcher = Regexp.compile(/(?<!\\)"/m)
40
+
41
+ # Returns +value+ surrounded by double quotes. This method is idempotent
42
+ # so +value+ will only be quoted once regardless of how may times the
43
+ # method is called on it.
44
+ #
45
+ # @example Quoting a BEL parameter.
46
+ # quote("apoptotic process")
47
+ # # => "\"apoptotic process\""
48
+ # @example Escaping quotes within a value.
49
+ # quote("vesicle fusion with \"Golgi apparatus\"")
50
+ # # => "\"vesicle fusion with \\\"Golgi apparatus\\\"\""
51
+ #
52
+ # @parameter [#to_s] value a value to be quoted
53
+ # @return [String] value surrounded by double quotes
54
+ def quote(value)
55
+ string = value.to_s
56
+ unquoted = unquote(string)
57
+ escaped = unquoted.gsub(QuoteNotEscapedMatcher, "\\\"")
58
+ %Q{"#{escaped}"}
59
+ end
60
+
61
+ # Returns +value+ with surrounded quotes removed.
62
+ #
63
+ # @example Unquoting a BEL parameter.
64
+ # unquote("\"apoptotic process\"")
65
+ # # => "apoptotic process"
66
+ # @example Escaped quotes are preserved.
67
+ # unquote("\"vesicle fusion with \"Golgi apparatus\"\"")
68
+ #
69
+ # @parameter [#to_s] value a value to be unquoted
70
+ # @return [String] value with surrounding double quotes removed
71
+ def unquote(value)
72
+ string = value.to_s
73
+ if string =~ StrictQuotedMatcher
74
+ string[1...-1]
75
+ else
76
+ string
77
+ end
78
+ end
79
+
80
+ # Returns +value+ with quoting applied only if necessary. A +value+
81
+ # consisting of only word character (e.g. [0-9A-Za-z_]) does not need
82
+ # quoting. A +value+ consisting of at least one non-word character
83
+ # (e.g. [^0-9A-Za-z_]) will requiring quoting.
84
+ #
85
+ # @example Quotes added when value includes spaces.
86
+ # quote_if_needed("apoptotic process")
87
+ # # => "\"apoptotic process\""
88
+ # @example Quotes added when value includes double quote.
89
+ # quote_if_needed("vesicle fusion with \"Golgi apparatus\"")
90
+ # # => "\"vesicle fusion with \\\"Golgi apparatus\\\"\""
91
+ # @example No quotes necessary for identifier.
92
+ # quote_if_needed("AKT1_HUMAN")
93
+ # # => "AKT1_HUMAN"
94
+ #
95
+ # @parameter [#to_s] value that may be quoted
96
+ # @return [String] original value or quoted value
97
+ def quote_if_needed(value)
98
+ if string_value?(value)
99
+ quote(value)
100
+ else
101
+ value.to_s
102
+ end
103
+ end
104
+
105
+ # Returns whether the +value+ is surrounded by double quotes.
106
+ #
107
+ # @example Returns +true+ when value is quoted.
108
+ # quoted?("\"vesicle fusion with \"Golgi apparatus\"")
109
+ # # => true
110
+ # @example Returns +false+ when value is not quoted.
111
+ # quoted?("apoptotic process")
112
+ # # => false
113
+ #
114
+ # @parameter [#to_s] value to test
115
+ # @return [Boolean] +true+ if +value+ is quoted, +false+ if
116
+ # +value+ is not quoted
117
+ def quoted?(value)
118
+ string = value.to_s
119
+ (string =~ LenientQuotedMatcher) != nil
120
+ end
121
+
122
+ # Returns whether the +value+ is not surrounded by double quotes.
123
+ #
124
+ # @example Returns +true+ when value is not quoted.
125
+ # unquoted?("apoptotic process")
126
+ # # => true
127
+ # @example Returns +false+ when value is quoted.
128
+ # unquoted?("\"vesicle fusion with \"Golgi apparatus\"")
129
+ # # => false
130
+ #
131
+ # @parameter [#to_s] value to test
132
+ # @return [Boolean] +true+ if +value+ is not quoted, +false+ if
133
+ # +value+ is quoted
134
+ def unquoted?(value)
135
+ !quoted?(value)
136
+ end
137
+
138
+ # Returns whether the +value+ represents an identifier. An
139
+ # identifier consists of only word characters (e.g. [0-9A-Za-z_]).
140
+ #
141
+ # @example Returns +true+ when representing an identifier.
142
+ # identifier_value?("AKT1_HUMAN")
143
+ # # => true
144
+ # @example Returns +false+ when not representing an identifier.
145
+ # identifier_value?("apoptotic process")
146
+ # # => false
147
+ #
148
+ # @parameter [#to_s] value to test
149
+ # @return [Boolean] +true+ if +value+ is an identifier,
150
+ # +false+ if +value+ is not an identifier
151
+ def identifier_value?(value)
152
+ string = value.to_s
153
+ [NonWordMatcher, KeywordMatcher].none? { |matcher|
154
+ matcher.match string
155
+ }
156
+ end
157
+
158
+ # Returns whether the +value+ represents a string value. A string
159
+ # value consists of at least one non-word character
160
+ # (e.g. [^0-9A-Za-z_]).
161
+ #
162
+ # @example Returns +true+ when representing a string value.
163
+ # string_value?("apoptotic process")
164
+ # # => true
165
+ # @example Returns +false+ when not representing a string value.
166
+ # string_value?("AKT1_HUMAN")
167
+ # # => false
168
+ #
169
+ # @parameter [#to_s] value to test
170
+ # @return [Boolean] +true+ if +value+ is a string value,
171
+ # +false+ if +value+ is not a string value
172
+ def string_value?(value)
173
+ string = value.to_s
174
+ [NonWordMatcher, KeywordMatcher].any? { |matcher|
175
+ matcher.match string
176
+ }
177
+ end
178
+
179
+ ## Deprecated, remove in [0.6.0].
180
+
181
+ # @deprecated Use {#quote_if_needed} instead. Will be removed in a
182
+ # future release.
183
+ def ensure_quotes identifier
184
+ warn <<-DOC.gsub(/^\s+/, '')
185
+ Deprecation Warning
186
+ -------------------
187
+ The BEL::Quoting::ensure_quotes method is deprecated and
188
+ will be removed in a future relase.
189
+ Call module method BEL::Quoting.quote_if_needed instead.
190
+ DOC
191
+ quote_if_needed(identifier)
192
+ end
193
+
194
+ # @deprecated Use {#unquote} instead. Will be removed in a
195
+ # future release.
196
+ def remove_quotes identifier
197
+ warn <<-DOC.gsub(/^\s+/, '')
198
+ Deprecation Warning
199
+ -------------------
200
+ The BEL::Quoting::remove_quotes method is deprecated and
201
+ will be removed in a future relase.
202
+ Call module method BEL::Quoting.unquote instead.
203
+ DOC
204
+ unquote(identifier)
205
+ end
206
+
207
+ # @deprecated Use {#quote} instead. Will be removed in a
208
+ # future release.
209
+ def always_quote identifier
210
+ warn <<-DOC.gsub(/^\s+/, '')
211
+ Deprecation Warning
212
+ -------------------
213
+ The BEL::Quoting::always_quote method is deprecated and
214
+ will be removed in a future relase.
215
+ Call module method BEL::Quoting.quote instead.
216
+ DOC
217
+ quote(identifier)
218
+ end
219
+
220
+ # @deprecated Use {#quoted?} or {#unquoted?} instead. Will be removed
221
+ # in a future release.
222
+ def quotes_required? identifier
223
+ warn <<-DOC.gsub(/^\s+/, '')
224
+ Deprecation Warning
225
+ -------------------
226
+ The BEL::Quoting::quotes_required? method is deprecated and
227
+ will be removed in a future relase.
228
+ You can use BEL::Quoting.quoted? and BEL::Quoting.unquoted?
229
+ going forward.
230
+ DOC
231
+ [NonWordMatcher, KeywordMatcher].any? { |m|
232
+ m.match identifier.to_s
233
+ }
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,604 @@
1
+ require_relative 'quoting'
2
+ require_relative '../parsers/ast/node'
3
+
4
+ module BELParser
5
+ module Language
6
+ # Semantics capture BEL version-independent semantics for terms and
7
+ # statements.
8
+ module Semantics
9
+ # rubocop:disable Metrics/MethodLength
10
+ # rubocop:disable Metrics/AbcSize
11
+ def self.match(input_ast, semantic_ast, spec, match_results = [])
12
+ res = semantic_ast.match(input_ast, spec)
13
+ match_results.concat(res)
14
+ if res.flatten.all?(&:success?) && !semantic_ast.terminal?
15
+ return match_results if semantic_ast.children.empty?
16
+
17
+ var_test = semantic_ast.children.any? { |x| x.is_a?(SemanticVariadicArguments) }
18
+ if var_test
19
+ test_pairs = input_ast.children.zip(semantic_ast.children).select do |pair|
20
+ !pair.include?(nil)
21
+ end
22
+
23
+ test_pairs.each do |(input_child, semantic_child)|
24
+ if semantic_child.is_a?(SemanticVariadicArguments)
25
+ input_children = input_ast.children
26
+ input_arguments = input_children[input_children.index(input_child)..-1]
27
+ argument_pattern = semantic_child.children.first
28
+ input_arguments.each do |argument_child|
29
+ res = semantic_child.match(argument_child, spec)
30
+ match_results << res
31
+ if res.all?(&:success?)
32
+ param_or_term = argument_child.children.first
33
+ match(param_or_term, argument_pattern, spec, match_results)
34
+ end
35
+ end
36
+ else
37
+ match(input_child, semantic_child, spec, match_results)
38
+ end
39
+ end
40
+ else
41
+ semantic_ast
42
+ .children
43
+ .zip(input_ast.children)
44
+ .each do |semantic_child, input_child|
45
+ match(input_child, semantic_child, spec, match_results)
46
+ end
47
+ end
48
+ end
49
+ match_results.flatten
50
+ end
51
+
52
+ # MatchResult holds the results of an input AST to semantic AST match.
53
+ class MatchResult
54
+ attr_reader :input_node, :semantic_node
55
+
56
+ def initialize(success, input_node, semantic_node)
57
+ @success = success
58
+ @input_node = input_node
59
+ @semantic_node = semantic_node
60
+ end
61
+
62
+ def success?
63
+ @success
64
+ end
65
+
66
+ def failure?
67
+ !@success
68
+ end
69
+
70
+ def to_s
71
+ msg
72
+ end
73
+ end
74
+
75
+ # Builder contains methods to build semantic AST nodes.
76
+ # A convenient {Builder.build} method allows you to use these
77
+ # methods within a block scope.
78
+ #
79
+ # see Builder.build
80
+ module Builder
81
+ def self.build(&block)
82
+ raise ArgumentError, 'expecting block' unless block_given?
83
+
84
+ builder = _builder_class.new
85
+ builder.instance_eval(&block)
86
+ builder.result
87
+ end
88
+
89
+ def self._builder_class
90
+ Class.new do
91
+ include Builder
92
+
93
+ attr_reader :result
94
+
95
+ def term(function, *arguments, **properties)
96
+ @result = super
97
+ end
98
+ end
99
+ end
100
+ private_class_method :_builder_class
101
+
102
+ def term(function, *arguments, **properties)
103
+ SemanticTerm.new([function, *arguments], **properties)
104
+ end
105
+
106
+ def function(identifier, **properties)
107
+ SemanticFunction.new([identifier], **properties)
108
+ end
109
+
110
+ def argument(param_or_term, **properties)
111
+ cls = param_or_term.class
112
+ if cls != SemanticParameter && cls != SemanticTerm
113
+ raise ArgumentError, 'expected SemanticParameter or SemanticTerm'
114
+ end
115
+
116
+ SemanticArgument.new([param_or_term], **properties)
117
+ end
118
+
119
+ def parameter(prefix, value, **properties)
120
+ SemanticParameter.new([prefix, value], **properties)
121
+ end
122
+
123
+ def identifier(*value_patterns, **properties)
124
+ SemanticIdentifier.new(value_patterns, **properties)
125
+ end
126
+
127
+ def prefix(has_namespace, **properties)
128
+ SemanticPrefix.new([has_namespace], **properties)
129
+ end
130
+
131
+ def value(value_type, **properties)
132
+ SemanticValue.new([value_type], **properties)
133
+ end
134
+
135
+ def value_type(*value_patterns, **properties)
136
+ SemanticValueType.new(value_patterns, **properties)
137
+ end
138
+
139
+ def any(**properties)
140
+ SemanticAny.new(**properties)
141
+ end
142
+
143
+ # rubocop:disable Style/PredicateName
144
+ def is_nil(**properties)
145
+ SemanticIsNil.new(**properties)
146
+ end
147
+
148
+ def has_namespace(**properties)
149
+ SemanticHasNamespace.new(**properties)
150
+ end
151
+
152
+ def has_encoding(**properties)
153
+ SemanticHasEncoding.new(**properties)
154
+ end
155
+
156
+ def function_of(*functions, **properties)
157
+ SemanticFunctionOf.new(functions, **properties)
158
+ end
159
+
160
+ def return_type_of(*return_types, **properties)
161
+ SemanticReturnTypeOf.new(return_types, **properties)
162
+ end
163
+
164
+ def namespace_of(*namespaces, **properties)
165
+ SemanticNamespaceOf.new(namespaces, **properties)
166
+ end
167
+
168
+ def encoding_of(*encoding_types, **properties)
169
+ SemanticEncodingOf.new(encoding_types, **properties)
170
+ end
171
+
172
+ def covalent_modification_of(*covalent_mod_types, **properties)
173
+ SemanticCovalentModificationOf.new(covalent_mod_types, **properties)
174
+ end
175
+
176
+ def amino_acid_of(*amino_acids, **properties)
177
+ SemanticAminoAcidOf.new(amino_acids, **properties)
178
+ end
179
+
180
+ def is_amino_acid_range_pattern(**properties)
181
+ SemanticIsAminoAcidRange.new(**properties)
182
+ end
183
+
184
+ def variadic_arguments(*params_or_terms, **properties)
185
+ SemanticVariadicArguments.new(params_or_terms, **properties)
186
+ end
187
+ end
188
+
189
+ # SemanticASTNode represents a node in the semantic tree structure.
190
+ class SemanticASTNode < AST::Node
191
+ def initialize(type, children = [], **properties)
192
+ super(type, children, properties)
193
+ end
194
+
195
+ def terminal?
196
+ false
197
+ end
198
+
199
+ protected
200
+
201
+ def updated(_ = nil, children = nil, properties = nil)
202
+ new_children = children || @children
203
+ new_properties = properties || {}
204
+
205
+ if @children == new_children && properties.nil?
206
+ self
207
+ else
208
+ # Maybe change call?
209
+ original_dup.send :initialize, new_children, new_properties
210
+ end
211
+ end
212
+
213
+ def success(node)
214
+ [MatchResult.new(true, node, self)]
215
+ end
216
+
217
+ def failure(node)
218
+ [MatchResult.new(false, node, self)]
219
+ end
220
+ end
221
+
222
+ # AST node for Term is a semantic AST.
223
+ class SemanticTerm < SemanticASTNode
224
+ def initialize(children = [], **properties)
225
+ super(:term, children, properties)
226
+ end
227
+
228
+ def match(parse_node, _)
229
+ type == parse_node.type ? success(parse_node) : failure(parse_node)
230
+ end
231
+ end
232
+
233
+ # AST node for Statement is a semantic AST.
234
+ class SemanticStatement < SemanticASTNode
235
+ def initialize(children = [], **properties)
236
+ super(:statement, children, properties)
237
+ end
238
+
239
+ def match(parse_node, _)
240
+ type == parse_node.type ? success(parse_node) : failure(parse_node)
241
+ end
242
+ end
243
+
244
+ # AST node for Parameter is a semantic AST.
245
+ class SemanticParameter < SemanticASTNode
246
+ def initialize(children = [], **properties)
247
+ super(:parameter, children, properties)
248
+ end
249
+
250
+ def match(parse_node, _)
251
+ type == parse_node.type ? success(parse_node) : failure(parse_node)
252
+ end
253
+ end
254
+
255
+ # AST node for Function is a semantic AST.
256
+ class SemanticFunction < SemanticASTNode
257
+ def initialize(children = [], **properties)
258
+ super(:function, children, properties)
259
+ end
260
+
261
+ def match(parse_node, _)
262
+ type == parse_node.type ? success(parse_node) : failure(parse_node)
263
+ end
264
+ end
265
+
266
+ # AST node for Argument is a semantic AST.
267
+ class SemanticArgument < SemanticASTNode
268
+ def initialize(children = [], **properties)
269
+ super(:argument, children, properties)
270
+ end
271
+
272
+ def match(parse_node, _)
273
+ return failure(nil) if parse_node.nil?
274
+ type == parse_node.type ? success(parse_node) : failure(parse_node)
275
+ end
276
+ end
277
+
278
+ # AST node for VariadicArguments is a semantic AST.
279
+ class SemanticVariadicArguments < SemanticASTNode
280
+ def initialize(children = [], **properties)
281
+ super(:variadic_arguments, children, properties)
282
+ end
283
+
284
+ def match(parse_node, _)
285
+ if parse_node.type == BELParser::Parsers::AST::Argument.ast_type
286
+ success(parse_node)
287
+ else
288
+ failure(parse_node)
289
+ end
290
+ end
291
+ end
292
+
293
+ # AST node for Prefix is a semantic AST.
294
+ class SemanticPrefix < SemanticASTNode
295
+ def initialize(children = [], **properties)
296
+ super(:prefix, children, properties)
297
+ end
298
+
299
+ def match(parse_node, _)
300
+ type == parse_node.type ? success(parse_node) : failure(parse_node)
301
+ end
302
+ end
303
+
304
+ # AST node for Value is a semantic AST.
305
+ class SemanticValue < SemanticASTNode
306
+ def initialize(children = [], **properties)
307
+ super(:value, children, properties)
308
+ end
309
+
310
+ def match(parse_node, _)
311
+ type == parse_node.type ? success(parse_node) : failure(parse_node)
312
+ end
313
+ end
314
+
315
+ # AST node for Nil is a semantic AST.
316
+ class SemanticIsNil < SemanticASTNode
317
+ def initialize(**properties)
318
+ super(:is_nil, [], properties)
319
+ end
320
+
321
+ def terminal?
322
+ true
323
+ end
324
+
325
+ def match(parse_node, _)
326
+ parse_node.nil? ? success(parse_node) : failure(nil)
327
+ end
328
+ end
329
+
330
+ # AST node for Identifier is a semantic AST.
331
+ class SemanticIdentifier < SemanticASTNode
332
+ def initialize(children = [], **properties)
333
+ super(:identifier, children, properties)
334
+ end
335
+
336
+ def terminal?
337
+ true
338
+ end
339
+
340
+ def value_patterns
341
+ children
342
+ end
343
+
344
+ def match(identifier, spec)
345
+ return failure(nil) if identifier.nil?
346
+ return failure(identifier) if type != identifier.type
347
+
348
+ value_results = value_patterns.map do |pattern|
349
+ pattern.match(identifier, spec)
350
+ end
351
+
352
+ failure_result = value_results.flatten.find(&:failure?)
353
+
354
+ if failure_result
355
+ [failure(identifier), failure_result]
356
+ else
357
+ value_results.unshift(success(identifier))
358
+ end
359
+ end
360
+ end
361
+
362
+ # AST node for Any is a semantic AST.
363
+ class SemanticAny < SemanticASTNode
364
+ def initialize(**properties)
365
+ super(:any, [], properties)
366
+ end
367
+
368
+ def match(parse_node, _)
369
+ success(parse_node)
370
+ end
371
+ end
372
+
373
+ # AST node for HasNamespace is a semantic AST.
374
+ class SemanticHasNamespace < SemanticASTNode
375
+ def initialize(**properties)
376
+ super(:has_namespace, [], properties)
377
+ end
378
+
379
+ def match(identifier, _)
380
+ # FIXME: Implement :namespace property assignment to AST.
381
+ return success(identifier)
382
+ if identifier.respond_to?(:namespace)
383
+ success(identifier)
384
+ else
385
+ failure(identifier)
386
+ end
387
+ end
388
+ end
389
+
390
+ # AST node for NamespaceOf is a semantic AST.
391
+ class SemanticNamespaceOf < SemanticASTNode
392
+ def initialize(namespaces, **properties)
393
+ super(:namespace_of, namespaces, properties)
394
+ end
395
+
396
+ def match(identifier, _)
397
+ # FIXME: Implement :namespace property assignment to AST.
398
+ return success(identifier)
399
+ return failure(identifier) unless identifier.respond_to?(:namespace)
400
+ input_namespace = identifier.namespace
401
+ return failure(identifier) if input_namespace.nil?
402
+
403
+ namespace_set = children
404
+ if namespace_set.any? { |i| i == :* || i == input_namespace }
405
+ success(identifier)
406
+ else
407
+ failure(identifier)
408
+ end
409
+ end
410
+ end
411
+
412
+ # AST node for HasEncoding is a semantic AST.
413
+ class SemanticHasEncoding < SemanticASTNode
414
+ def initialize(**properties)
415
+ super(:has_encoding, [], properties)
416
+ end
417
+
418
+ def match(value_type, _)
419
+ # FIXME: Implement :encoding property assignment to AST.
420
+ return success(value_type)
421
+ if value_type.respond_to?(:encoding)
422
+ success(value_type)
423
+ else
424
+ failure(value_type)
425
+ end
426
+ end
427
+ end
428
+
429
+ # AST node for EncodingOf is a semantic AST.
430
+ class SemanticEncodingOf < SemanticASTNode
431
+ def initialize(encodings, **properties)
432
+ super(:encoding_of, encodings, properties)
433
+ end
434
+
435
+ def match(value_type, _)
436
+ # FIXME: Implement :encoding property assignment to AST.
437
+ return success(value_type)
438
+ encoding_set = children
439
+ return success(value_type) if encoding_set.include?(:*)
440
+
441
+ return failure(value_type) unless value_type.respond_to?(:encoding)
442
+
443
+ input_encoding = value_type.encoding
444
+ return failure(value_type) if input_encoding.nil?
445
+
446
+ if encoding_set.include?(input_encoding)
447
+ success(value_type)
448
+ else
449
+ failure(value_type)
450
+ end
451
+ end
452
+ end
453
+
454
+ # AST node for FunctionOf is a semantic AST.
455
+ class SemanticFunctionOf < SemanticASTNode
456
+ def initialize(functions, **properties)
457
+ super(:function_of, functions, properties)
458
+ end
459
+
460
+ def functions
461
+ children
462
+ end
463
+
464
+ def match(identifier, spec)
465
+ return success(identifier) if functions.include?(:*)
466
+
467
+ function = spec.function(identifier.children[0].to_sym)
468
+ if functions.include?(function)
469
+ success(identifier)
470
+ else
471
+ failure(identifier)
472
+ end
473
+ end
474
+ end
475
+
476
+ # AST node for ReturnTypeOf is a semantic AST.
477
+ class SemanticReturnTypeOf < SemanticASTNode
478
+ def initialize(return_types, **properties)
479
+ super(:return_type_of, return_types, properties)
480
+ end
481
+
482
+ def return_types
483
+ children
484
+ end
485
+
486
+ def match(identifier, spec)
487
+ return success(identifier) if return_types.include?(:*)
488
+
489
+ fx_return = spec.function(identifier.children[0].to_sym).return_type
490
+ if return_types.any? { |rt| fx_return <= rt }
491
+ success(identifier)
492
+ else
493
+ failure(identifier)
494
+ end
495
+ end
496
+ end
497
+
498
+ # AST node for ValueType is a semantic AST.
499
+ class SemanticValueType < SemanticASTNode
500
+ TYPES = [
501
+ BELParser::Parsers::AST::Identifier.ast_type,
502
+ BELParser::Parsers::AST::String.ast_type
503
+ ].freeze
504
+
505
+ def initialize(children = [], **properties)
506
+ super(:value_type, children, properties)
507
+ end
508
+
509
+ def terminal?
510
+ true
511
+ end
512
+
513
+ def value_patterns
514
+ children
515
+ end
516
+
517
+ def match(value_type, spec)
518
+ return failure(value_type) unless TYPES.include?(value_type.type)
519
+
520
+ value_results = value_patterns.map do |pattern|
521
+ pattern.match(value_type, spec)
522
+ end
523
+
524
+ failure_result = value_results.flatten.find(&:failure?)
525
+
526
+ if failure_result
527
+ [failure(value_type), failure_result]
528
+ else
529
+ value_results.unshift(success(value_type))
530
+ end
531
+ end
532
+ end
533
+
534
+ # AST node for CovalentModificationOf is a semantic AST.
535
+ class SemanticCovalentModificationOf < SemanticASTNode
536
+ def initialize(covalent_mod_types, **properties)
537
+ properties[:hashed] = Hash[covalent_mod_types.map { |t| [t, true] }]
538
+ super(:covalent_modification_of, covalent_mod_types, properties)
539
+ end
540
+
541
+ def covalent_mod_types
542
+ children
543
+ end
544
+
545
+ def match(value_type, _)
546
+ string_literal_sym = value_type.children[0].to_sym
547
+ return success(value_type) if @hashed[:*]
548
+
549
+ if @hashed.key?(string_literal_sym)
550
+ success(value_type)
551
+ else
552
+ failure(value_type)
553
+ end
554
+ end
555
+ end
556
+
557
+ # AST node for AminoAcidOf is a semantic AST.
558
+ class SemanticAminoAcidOf < SemanticASTNode
559
+ def initialize(amino_acids, **properties)
560
+ properties[:hashed] = Hash[amino_acids.map { |t| [t, true] }]
561
+ super(:amino_acid_of, amino_acids, properties)
562
+ end
563
+
564
+ def amino_acids
565
+ children
566
+ end
567
+
568
+ def match(value_type, _)
569
+ string_literal_sym = value_type.children[0].to_sym
570
+ return success(value_type) if @hashed[:*]
571
+
572
+ if @hashed.key?(string_literal_sym)
573
+ success(value_type)
574
+ else
575
+ failure(value_type)
576
+ end
577
+ end
578
+ end
579
+
580
+ # AST node for IsAminoAcidRange is a semantic AST.
581
+ class SemanticIsAminoAcidRange < SemanticASTNode
582
+ START_STOP = /[1-9][0-9]*_[1-9][0-9]*/
583
+ UNDETERMINED = /[1?]_[?*]/
584
+ UNKNOWN_START_STOP = "?"
585
+
586
+ include BELParser::Quoting
587
+
588
+ def initialize(**properties)
589
+ super(:is_amino_acid_range, [], properties)
590
+ end
591
+
592
+ def match(value_type, _)
593
+ string_literal = unquote(value_type.children[0])
594
+ case string_literal
595
+ when START_STOP, UNDETERMINED, UNKNOWN_START_STOP
596
+ success(value_type)
597
+ else
598
+ failure(value_type)
599
+ end
600
+ end
601
+ end
602
+ end
603
+ end
604
+ end