rley 0.7.03 → 0.7.08

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +362 -62
  3. data/.travis.yml +6 -7
  4. data/CHANGELOG.md +20 -1
  5. data/LICENSE.txt +1 -1
  6. data/README.md +6 -7
  7. data/Rakefile +2 -0
  8. data/appveyor.yml +2 -4
  9. data/examples/NLP/benchmark_pico_en.rb +2 -0
  10. data/examples/NLP/engtagger.rb +193 -188
  11. data/examples/NLP/nano_eng/nano_en_demo.rb +2 -0
  12. data/examples/NLP/nano_eng/nano_grammar.rb +7 -5
  13. data/examples/NLP/pico_en_demo.rb +2 -0
  14. data/examples/data_formats/JSON/cli_options.rb +3 -1
  15. data/examples/data_formats/JSON/json_ast_builder.rb +14 -9
  16. data/examples/data_formats/JSON/json_ast_nodes.rb +14 -21
  17. data/examples/data_formats/JSON/json_demo.rb +2 -0
  18. data/examples/data_formats/JSON/json_grammar.rb +4 -2
  19. data/examples/data_formats/JSON/json_lexer.rb +10 -8
  20. data/examples/data_formats/JSON/json_minifier.rb +3 -1
  21. data/examples/general/calc_iter1/calc_ast_builder.rb +15 -10
  22. data/examples/general/calc_iter1/calc_ast_nodes.rb +25 -37
  23. data/examples/general/calc_iter1/calc_demo.rb +2 -0
  24. data/examples/general/calc_iter1/calc_grammar.rb +4 -2
  25. data/examples/general/calc_iter1/calc_lexer.rb +8 -4
  26. data/examples/general/calc_iter1/spec/calculator_spec.rb +7 -5
  27. data/examples/general/calc_iter2/calc_ast_builder.rb +7 -3
  28. data/examples/general/calc_iter2/calc_ast_nodes.rb +29 -43
  29. data/examples/general/calc_iter2/calc_demo.rb +2 -0
  30. data/examples/general/calc_iter2/calc_grammar.rb +5 -3
  31. data/examples/general/calc_iter2/calc_lexer.rb +13 -10
  32. data/examples/general/calc_iter2/spec/calculator_spec.rb +28 -26
  33. data/examples/general/left.rb +4 -2
  34. data/examples/general/right.rb +4 -2
  35. data/lib/rley.rb +2 -0
  36. data/lib/rley/base/base_parser.rb +2 -0
  37. data/lib/rley/base/dotted_item.rb +38 -41
  38. data/lib/rley/base/grm_items_builder.rb +2 -0
  39. data/lib/rley/constants.rb +5 -3
  40. data/lib/rley/engine.rb +22 -24
  41. data/lib/rley/formatter/asciitree.rb +6 -4
  42. data/lib/rley/formatter/base_formatter.rb +2 -0
  43. data/lib/rley/formatter/bracket_notation.rb +3 -8
  44. data/lib/rley/formatter/debug.rb +8 -6
  45. data/lib/rley/formatter/json.rb +4 -2
  46. data/lib/rley/gfg/call_edge.rb +3 -1
  47. data/lib/rley/gfg/edge.rb +7 -5
  48. data/lib/rley/gfg/end_vertex.rb +4 -6
  49. data/lib/rley/gfg/epsilon_edge.rb +3 -5
  50. data/lib/rley/gfg/grm_flow_graph.rb +31 -25
  51. data/lib/rley/gfg/item_vertex.rb +12 -22
  52. data/lib/rley/gfg/non_terminal_vertex.rb +6 -4
  53. data/lib/rley/gfg/return_edge.rb +2 -0
  54. data/lib/rley/gfg/scan_edge.rb +3 -1
  55. data/lib/rley/gfg/shortcut_edge.rb +4 -2
  56. data/lib/rley/gfg/start_vertex.rb +6 -8
  57. data/lib/rley/gfg/vertex.rb +47 -41
  58. data/lib/rley/lexical/token.rb +3 -1
  59. data/lib/rley/lexical/token_range.rb +8 -6
  60. data/lib/rley/parse_forest_visitor.rb +7 -5
  61. data/lib/rley/parse_rep/ast_base_builder.rb +11 -11
  62. data/lib/rley/parse_rep/cst_builder.rb +7 -4
  63. data/lib/rley/parse_rep/parse_forest_builder.rb +36 -25
  64. data/lib/rley/parse_rep/parse_forest_factory.rb +5 -3
  65. data/lib/rley/parse_rep/parse_rep_creator.rb +18 -13
  66. data/lib/rley/parse_rep/parse_tree_builder.rb +15 -15
  67. data/lib/rley/parse_rep/parse_tree_factory.rb +27 -25
  68. data/lib/rley/parse_tree_visitor.rb +3 -1
  69. data/lib/rley/parser/error_reason.rb +9 -8
  70. data/lib/rley/parser/gfg_chart.rb +54 -22
  71. data/lib/rley/parser/gfg_earley_parser.rb +3 -1
  72. data/lib/rley/parser/gfg_parsing.rb +51 -31
  73. data/lib/rley/parser/parse_entry.rb +29 -33
  74. data/lib/rley/parser/parse_entry_set.rb +32 -27
  75. data/lib/rley/parser/parse_entry_tracker.rb +6 -4
  76. data/lib/rley/parser/parse_state.rb +18 -21
  77. data/lib/rley/parser/parse_state_tracker.rb +6 -4
  78. data/lib/rley/parser/parse_tracer.rb +15 -13
  79. data/lib/rley/parser/parse_walker_factory.rb +28 -29
  80. data/lib/rley/parser/state_set.rb +11 -10
  81. data/lib/rley/ptree/non_terminal_node.rb +10 -6
  82. data/lib/rley/ptree/parse_tree.rb +6 -4
  83. data/lib/rley/ptree/parse_tree_node.rb +7 -5
  84. data/lib/rley/ptree/terminal_node.rb +9 -7
  85. data/lib/rley/rley_error.rb +12 -10
  86. data/lib/rley/sppf/alternative_node.rb +8 -6
  87. data/lib/rley/sppf/composite_node.rb +9 -7
  88. data/lib/rley/sppf/epsilon_node.rb +5 -3
  89. data/lib/rley/sppf/leaf_node.rb +5 -3
  90. data/lib/rley/sppf/non_terminal_node.rb +2 -0
  91. data/lib/rley/sppf/parse_forest.rb +19 -17
  92. data/lib/rley/sppf/sppf_node.rb +9 -8
  93. data/lib/rley/sppf/token_node.rb +5 -3
  94. data/lib/rley/syntax/grammar.rb +7 -5
  95. data/lib/rley/syntax/grammar_builder.rb +11 -9
  96. data/lib/rley/syntax/grm_symbol.rb +8 -6
  97. data/lib/rley/syntax/literal.rb +2 -0
  98. data/lib/rley/syntax/non_terminal.rb +11 -15
  99. data/lib/rley/syntax/production.rb +13 -11
  100. data/lib/rley/syntax/symbol_seq.rb +10 -10
  101. data/lib/rley/syntax/terminal.rb +6 -5
  102. data/lib/rley/syntax/verbatim_symbol.rb +5 -3
  103. data/lib/support/base_tokenizer.rb +23 -20
  104. data/spec/rley/base/dotted_item_spec.rb +4 -2
  105. data/spec/rley/base/grm_items_builder_spec.rb +2 -0
  106. data/spec/rley/engine_spec.rb +47 -9
  107. data/spec/rley/formatter/asciitree_spec.rb +11 -9
  108. data/spec/rley/formatter/bracket_notation_spec.rb +16 -14
  109. data/spec/rley/formatter/debug_spec.rb +4 -2
  110. data/spec/rley/formatter/json_spec.rb +5 -3
  111. data/spec/rley/gfg/call_edge_spec.rb +2 -0
  112. data/spec/rley/gfg/edge_spec.rb +2 -0
  113. data/spec/rley/gfg/end_vertex_spec.rb +7 -5
  114. data/spec/rley/gfg/epsilon_edge_spec.rb +2 -0
  115. data/spec/rley/gfg/grm_flow_graph_spec.rb +2 -0
  116. data/spec/rley/gfg/item_vertex_spec.rb +12 -10
  117. data/spec/rley/gfg/non_terminal_vertex_spec.rb +5 -3
  118. data/spec/rley/gfg/return_edge_spec.rb +2 -0
  119. data/spec/rley/gfg/scan_edge_spec.rb +2 -0
  120. data/spec/rley/gfg/shortcut_edge_spec.rb +3 -1
  121. data/spec/rley/gfg/start_vertex_spec.rb +7 -5
  122. data/spec/rley/gfg/vertex_spec.rb +5 -3
  123. data/spec/rley/lexical/token_range_spec.rb +18 -16
  124. data/spec/rley/lexical/token_spec.rb +4 -2
  125. data/spec/rley/parse_forest_visitor_spec.rb +167 -163
  126. data/spec/rley/parse_rep/ambiguous_parse_spec.rb +46 -44
  127. data/spec/rley/parse_rep/ast_builder_spec.rb +8 -6
  128. data/spec/rley/parse_rep/cst_builder_spec.rb +7 -5
  129. data/spec/rley/parse_rep/groucho_spec.rb +25 -25
  130. data/spec/rley/parse_rep/parse_forest_builder_spec.rb +28 -26
  131. data/spec/rley/parse_rep/parse_forest_factory_spec.rb +8 -6
  132. data/spec/rley/parse_rep/parse_tree_factory_spec.rb +4 -2
  133. data/spec/rley/parse_tree_visitor_spec.rb +12 -8
  134. data/spec/rley/parser/error_reason_spec.rb +8 -6
  135. data/spec/rley/parser/gfg_chart_spec.rb +17 -4
  136. data/spec/rley/parser/gfg_earley_parser_spec.rb +16 -11
  137. data/spec/rley/parser/gfg_parsing_spec.rb +41 -252
  138. data/spec/rley/parser/parse_entry_set_spec.rb +2 -0
  139. data/spec/rley/parser/parse_entry_spec.rb +21 -19
  140. data/spec/rley/parser/parse_state_spec.rb +7 -5
  141. data/spec/rley/parser/parse_tracer_spec.rb +16 -14
  142. data/spec/rley/parser/parse_walker_factory_spec.rb +10 -8
  143. data/spec/rley/parser/state_set_spec.rb +24 -22
  144. data/spec/rley/ptree/non_terminal_node_spec.rb +7 -3
  145. data/spec/rley/ptree/parse_tree_node_spec.rb +6 -4
  146. data/spec/rley/ptree/parse_tree_spec.rb +2 -0
  147. data/spec/rley/ptree/terminal_node_spec.rb +8 -6
  148. data/spec/rley/sppf/alternative_node_spec.rb +8 -6
  149. data/spec/rley/sppf/non_terminal_node_spec.rb +5 -3
  150. data/spec/rley/sppf/token_node_spec.rb +6 -4
  151. data/spec/rley/support/ambiguous_grammar_helper.rb +5 -4
  152. data/spec/rley/support/expectation_helper.rb +2 -0
  153. data/spec/rley/support/grammar_abc_helper.rb +4 -4
  154. data/spec/rley/support/grammar_ambig01_helper.rb +6 -5
  155. data/spec/rley/support/grammar_arr_int_helper.rb +6 -5
  156. data/spec/rley/support/grammar_b_expr_helper.rb +6 -5
  157. data/spec/rley/support/grammar_helper.rb +2 -0
  158. data/spec/rley/support/grammar_l0_helper.rb +15 -16
  159. data/spec/rley/support/grammar_pb_helper.rb +8 -5
  160. data/spec/rley/support/grammar_sppf_helper.rb +3 -1
  161. data/spec/rley/syntax/grammar_builder_spec.rb +7 -5
  162. data/spec/rley/syntax/grammar_spec.rb +8 -6
  163. data/spec/rley/syntax/grm_symbol_spec.rb +3 -1
  164. data/spec/rley/syntax/literal_spec.rb +2 -0
  165. data/spec/rley/syntax/non_terminal_spec.rb +10 -8
  166. data/spec/rley/syntax/production_spec.rb +15 -13
  167. data/spec/rley/syntax/symbol_seq_spec.rb +4 -2
  168. data/spec/rley/syntax/terminal_spec.rb +7 -5
  169. data/spec/rley/syntax/verbatim_symbol_spec.rb +3 -1
  170. data/spec/spec_helper.rb +2 -12
  171. data/spec/support/base_tokenizer_spec.rb +9 -2
  172. metadata +21 -63
  173. data/.simplecov +0 -7
  174. data/Gemfile +0 -8
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'calc_lexer'
2
4
  require_relative 'calc_ast_builder'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Grammar for simple arithmetical expressions
2
4
  require 'rley' # Load the gem
3
5
 
@@ -16,9 +18,9 @@ builder = Rley::Syntax::GrammarBuilder.new do
16
18
  rule 'factor' => 'NUMBER'
17
19
  rule 'factor' => %w[LPAREN expression RPAREN]
18
20
  rule 'add_operator' => 'PLUS'
19
- rule 'add_operator' => 'MINUS'
21
+ rule 'add_operator' => 'MINUS'
20
22
  rule 'mul_operator' => 'STAR'
21
- rule 'mul_operator' => 'DIVIDE'
23
+ rule 'mul_operator' => 'DIVIDE'
22
24
  end
23
25
 
24
26
  # And now build the grammar...
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # File: calc_lexer.rb
2
4
  # Lexer for a basic arithmetical expression parser
3
5
  require 'strscan'
@@ -26,7 +28,7 @@ class CalcLexer
26
28
  @lineno = 1
27
29
  end
28
30
 
29
- def tokens()
31
+ def tokens
30
32
  tok_sequence = []
31
33
  until @scanner.eos?
32
34
  token = _next_token
@@ -38,7 +40,8 @@ class CalcLexer
38
40
 
39
41
  private
40
42
 
41
- def _next_token()
43
+ # rubocop: disable Lint/DuplicateBranch
44
+ def _next_token
42
45
  skip_whitespaces
43
46
  curr_ch = scanner.peek(1)
44
47
  return nil if curr_ch.nil?
@@ -61,18 +64,19 @@ class CalcLexer
61
64
  erroneous = curr_ch.nil? ? '' : curr_ch
62
65
  sequel = scanner.scan(/.{1,20}/)
63
66
  erroneous += sequel unless sequel.nil?
64
- raise ScanError.new("Unknown token #{erroneous}")
67
+ raise ScanError, "Unknown token #{erroneous}"
65
68
  end
66
69
 
67
70
  return token
68
71
  end
72
+ # rubocop: enable Lint/DuplicateBranch
69
73
 
70
74
  def build_token(aSymbolName, aLexeme)
71
75
  pos = Rley::Lexical::Position.new(1, scanner.pos)
72
76
  return Rley::Lexical::Token.new(aLexeme, aSymbolName, pos)
73
77
  end
74
78
 
75
- def skip_whitespaces()
79
+ def skip_whitespaces
76
80
  scanner.scan(/[ \t\f\n\r]+/)
77
81
  end
78
82
  end # class
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rspec' # Use the RSpec framework
2
4
  require_relative '../calc_lexer'
3
5
  require_relative '../calc_grammar'
@@ -10,13 +12,13 @@ describe 'Calculator' do
10
12
  engine = Rley::Engine.new do |cfg|
11
13
  cfg.repr_builder = CalcASTBuilder
12
14
  end
13
-
15
+
14
16
  engine.use_grammar(CalcGrammar)
15
17
  raw_result = parse_expression(engine, anExpression)
16
18
  ast = engine.to_ptree(raw_result)
17
19
  return expect(ast.root.interpret)
18
20
  end
19
-
21
+
20
22
  def parse_expression(anEngine, anExpression)
21
23
  lexer = CalcLexer.new(anExpression)
22
24
  result = anEngine.parse(lexer.tokens)
@@ -29,7 +31,7 @@ describe 'Calculator' do
29
31
  end
30
32
 
31
33
  return result
32
- end
34
+ end
33
35
 
34
36
  it 'should evaluate simple number literals' do
35
37
  expect_expr('2').to eq(2)
@@ -54,7 +56,7 @@ describe 'Calculator' do
54
56
  it 'should evaluate parentheses' do
55
57
  expect_expr('2 * (2.1 + 1)').to eq(6.2)
56
58
  end
57
-
59
+
58
60
  it 'should evaluate regardless of whitespace' do
59
61
  expect_expr("2*(1+\t1)").to eq(4)
60
62
  end
@@ -65,6 +67,6 @@ describe 'Calculator' do
65
67
 
66
68
  it 'should evaluate multiple levels of parentheses' do
67
69
  expect_expr('2*(1/(1+3))').to eq(0.5)
68
- end
70
+ end
69
71
  end # describe
70
72
  # End of file
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'calc_ast_nodes'
2
4
 
3
5
  # The purpose of a CalcASTBuilder is to build piece by piece an AST
@@ -13,8 +15,7 @@ class CalcASTBuilder < Rley::ParseRep::ASTBaseBuilder
13
15
  # The unary negation operator on one hand, the binary substraction operator
14
16
  'MINUS' => { 'add_operator_1' => Rley::PTree::TerminalNode,
15
17
  'simple_factor_2' => CalcNegateNode,
16
- 'sign_1' => CalcNegateNode
17
- },
18
+ 'sign_1' => CalcNegateNode },
18
19
  'NUMBER' => CalcNumberNode,
19
20
  'PI' => CalcConstantNode,
20
21
  'E' => CalcConstantNode,
@@ -23,7 +24,7 @@ class CalcASTBuilder < Rley::ParseRep::ASTBaseBuilder
23
24
 
24
25
  protected
25
26
 
26
- def terminal2node()
27
+ def terminal2node
27
28
  Terminal2NodeClass
28
29
  end
29
30
 
@@ -34,6 +35,8 @@ class CalcASTBuilder < Rley::ParseRep::ASTBaseBuilder
34
35
  return operator_node
35
36
  end
36
37
 
38
+ # rubocop: disable Naming/VariableNumber
39
+
37
40
  # rule 'simple_expression' => %w[simple_expression add_operator term]
38
41
  def reduce_simple_expression_1(_production, _range, _tokens, theChildren)
39
42
  reduce_binary_operator(theChildren)
@@ -104,5 +107,6 @@ class CalcASTBuilder < Rley::ParseRep::ASTBaseBuilder
104
107
  def reduce_mul_operator_1(_production, aRange, _tokens, theChildren)
105
108
  return CalcDivideNode.new(theChildren[0].symbol, aRange)
106
109
  end
110
+ # rubocop: enable Naming/VariableNumber
107
111
  end # class
108
112
  # End of file
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Classes that implement nodes of Abstract Syntax Trees (AST) representing
2
4
  # calculator parse results.
3
5
 
4
-
5
6
  CalcTerminalNode = Struct.new(:token, :value, :position) do
6
7
  def initialize(aToken, aPosition)
7
8
  self.token = aToken
@@ -14,15 +15,15 @@ CalcTerminalNode = Struct.new(:token, :value, :position) do
14
15
  self.value = aLiteral.dup
15
16
  end
16
17
 
17
- def symbol()
18
+ def symbol
18
19
  token.terminal
19
20
  end
20
21
 
21
- def interpret()
22
- return value
22
+ def interpret
23
+ value
23
24
  end
24
-
25
- def done!()
25
+
26
+ def done!
26
27
  # Do nothing
27
28
  end
28
29
 
@@ -43,9 +44,9 @@ class CalcNumberNode < CalcTerminalNode
43
44
  self.value = aLiteral.to_f
44
45
  end
45
46
  end
46
-
47
+
47
48
  # Overriding the unary minus operator
48
- def -@()
49
+ def -@
49
50
  self.value = - value
50
51
  return self
51
52
  end
@@ -81,8 +82,8 @@ class CalcCompositeNode
81
82
  def accept(aVisitor)
82
83
  aVisitor.visit_nonterminal(self)
83
84
  end
84
-
85
- def done!()
85
+
86
+ def done!
86
87
  # Do nothing
87
88
  end
88
89
 
@@ -90,44 +91,36 @@ class CalcCompositeNode
90
91
  end # class
91
92
 
92
93
  class CalcUnaryOpNode < CalcCompositeNode
93
- def initialize(aSymbol, aPosition)
94
- super(aSymbol, aPosition)
95
- end
96
-
97
94
  alias members children
98
95
  end # class
99
96
 
100
- class CalcNegateNode < CalcUnaryOpNode
101
- def interpret()
97
+ class CalcNegateNode < CalcUnaryOpNode
98
+ def interpret
102
99
  return -children[0].interpret
103
100
  end
104
101
  end # class
105
102
 
106
103
  class CalcUnaryFunction < CalcCompositeNode
107
- @@name_mapping = begin
104
+ @@name_mapping = begin
108
105
  map = Hash.new { |me, key| me[key] = key }
109
106
  map['ln'] = 'log'
110
107
  map['log'] = 'log10'
111
108
  map
112
109
  end
113
110
  attr_accessor(:func_name)
114
-
115
-
116
- def interpret()
111
+
112
+
113
+ def interpret
117
114
  argument = children[0].interpret
118
115
  internal_name = @@name_mapping[@func_name]
119
116
  return Math.send(internal_name.to_sym, argument)
120
- end
117
+ end
121
118
  end
122
119
 
123
120
  class CalcBinaryOpNode < CalcCompositeNode
124
- def initialize(aSymbol, aRange)
125
- super(aSymbol, aRange)
126
- end
127
-
128
121
  protected
129
122
 
130
- def retrieve_operands()
123
+ def retrieve_operands
131
124
  operands = []
132
125
  children.each do |child|
133
126
  oper = child.respond_to?(:interpret) ? child.interpret : child
@@ -140,52 +133,45 @@ end # class
140
133
 
141
134
  class CalcAddNode < CalcBinaryOpNode
142
135
  # TODO
143
- def interpret()
136
+ def interpret
144
137
  operands = retrieve_operands
145
138
 
146
- sum = operands[0] + operands[1]
147
- return sum
139
+ operands[0] + operands[1]
148
140
  end
149
141
  end # class
150
142
 
151
-
152
143
  class CalcSubtractNode < CalcBinaryOpNode
153
144
  # TODO
154
- def interpret()
145
+ def interpret
155
146
  operands = retrieve_operands
156
147
 
157
- substraction = operands[0] - operands[1]
158
- return substraction
148
+ operands[0] - operands[1]
159
149
  end
160
150
  end # class
161
151
 
162
152
  class CalcMultiplyNode < CalcBinaryOpNode
163
153
  # TODO
164
- def interpret()
154
+ def interpret
165
155
  operands = retrieve_operands
166
- multiplication = operands[0] * operands[1]
167
- return multiplication
156
+ operands[0] * operands[1]
168
157
  end
169
158
  end # class
170
159
 
171
160
  class CalcDivideNode < CalcBinaryOpNode
172
161
  # TODO
173
- def interpret()
162
+ def interpret
174
163
  operands = retrieve_operands
175
164
  numerator = operands[0].to_f
176
165
  denominator = operands[1]
177
- division = numerator / denominator
178
- return division
166
+ numerator / denominator
179
167
  end
180
168
  end # class
181
169
 
182
-
183
170
  class PowerNode < CalcBinaryOpNode
184
171
  # TODO
185
- def interpret()
172
+ def interpret
186
173
  operands = retrieve_operands
187
- exponentiation = operands[0]**operands[1]
188
- return exponentiation
174
+ operands[0]**operands[1]
189
175
  end
190
176
  end # class
191
177
  # End of file
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'calc_lexer'
2
4
  require_relative 'calc_ast_builder'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Grammar for simple arithmetical expressions
2
4
  require 'rley' # Load the gem
3
5
 
@@ -7,7 +9,7 @@ builder = Rley::Syntax::GrammarBuilder.new do
7
9
  add_terminals('NUMBER')
8
10
  add_terminals('PLUS', 'MINUS') # For '+', '-' operators or sign
9
11
  add_terminals('STAR', 'DIVIDE', 'POWER') # For '*', '/', '**' operators
10
- add_terminals('LPAREN', 'RPAREN') # For '(', ')' delimiters
12
+ add_terminals('LPAREN', 'RPAREN') # For '(', ')' delimiters
11
13
  add_terminals('PI', 'E') # For numeric constants
12
14
  add_terminals('RESERVED') # Reserved identifiers
13
15
 
@@ -21,13 +23,13 @@ builder = Rley::Syntax::GrammarBuilder.new do
21
23
  rule 'simple_factor' => %w[sign scalar]
22
24
  rule 'simple_factor' => %w[unary_function in_parenthesis]
23
25
  rule 'simple_factor' => %w[MINUS in_parenthesis]
24
- rule 'simple_factor' => 'in_parenthesis'
26
+ rule 'simple_factor' => 'in_parenthesis'
25
27
  rule 'sign' => 'PLUS'
26
28
  rule 'sign' => 'MINUS'
27
29
  rule 'sign' => []
28
30
  rule 'scalar' => 'NUMBER'
29
31
  rule 'scalar' => 'PI'
30
- rule 'scalar' => 'E'
32
+ rule 'scalar' => 'E'
31
33
  rule 'unary_function' => 'RESERVED'
32
34
  rule 'in_parenthesis' => %w[LPAREN expression RPAREN]
33
35
  rule 'add_operator' => 'PLUS'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # File: calc_lexer.rb
2
4
  # Lexer for a basic arithmetical expression parser
3
5
  require 'strscan'
@@ -8,7 +10,6 @@ class CalcLexer
8
10
  attr_reader(:lineno)
9
11
  attr_reader(:line_start)
10
12
 
11
-
12
13
  @@lexeme2name = {
13
14
  '(' => 'LPAREN',
14
15
  ')' => 'RPAREN',
@@ -20,7 +21,7 @@ class CalcLexer
20
21
  'PI' => 'PI',
21
22
  'E' => 'E'
22
23
  }.freeze
23
-
24
+
24
25
  @@unary_functions = %w[
25
26
  sin cos tan asin acos atan
26
27
  sqrt cbrt exp ln log
@@ -33,7 +34,7 @@ class CalcLexer
33
34
  @lineno = 1
34
35
  end
35
36
 
36
- def tokens()
37
+ def tokens
37
38
  @unary_f_pattern = Regexp.new(@@unary_functions.join('|'))
38
39
  tok_sequence = []
39
40
  until @scanner.eos?
@@ -46,11 +47,12 @@ class CalcLexer
46
47
 
47
48
  private
48
49
 
49
- def _next_token()
50
+ # rubocop: disable Lint/DuplicateBranch
51
+ def _next_token
50
52
  skip_whitespaces
51
53
  curr_ch = scanner.peek(1)
52
54
  return nil if curr_ch.nil?
53
-
55
+
54
56
  token = nil
55
57
 
56
58
  if '()+-/'.include? curr_ch
@@ -61,25 +63,26 @@ class CalcLexer
61
63
  elsif (lexeme = scanner.scan(/\*|PI|E/))
62
64
  token = build_token(@@lexeme2name[lexeme], lexeme)
63
65
  elsif (lexeme = scanner.scan(@unary_f_pattern))
64
- token = build_token('RESERVED', lexeme)
66
+ token = build_token('RESERVED', lexeme)
65
67
  elsif (lexeme = scanner.scan(/[0-9]+(\.[0-9]+)?([eE][-+]?[0-9])?/))
66
68
  token = build_token('NUMBER', lexeme)
67
69
  else # Unknown token
68
70
  erroneous = curr_ch.nil? ? '' : curr_ch
69
71
  sequel = scanner.scan(/.{1,20}/)
70
72
  erroneous += sequel unless sequel.nil?
71
- raise ScanError.new("Unknown token #{erroneous}")
73
+ raise ScanError, "Unknown token #{erroneous}"
72
74
  end
73
75
 
74
76
  return token
75
77
  end
76
-
78
+ # rubocop: enable Lint/DuplicateBranch
79
+
77
80
  def build_token(aSymbolName, aLexeme)
78
81
  pos = Rley::Lexical::Position.new(1, scanner.pos)
79
- return Rley::Lexical::Token.new(aLexeme, aSymbolName, pos)
82
+ return Rley::Lexical::Token.new(aLexeme, aSymbolName, pos)
80
83
  end
81
84
 
82
- def skip_whitespaces()
85
+ def skip_whitespaces
83
86
  scanner.scan(/[ \t\f\n\r]+/)
84
87
  end
85
88
  end # class
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rspec' # Use the RSpec framework
2
4
  require_relative '../calc_lexer'
3
5
  require_relative '../calc_grammar'
@@ -15,13 +17,13 @@ describe 'Calculator' do
15
17
  engine = Rley::Engine.new do |cfg|
16
18
  cfg.repr_builder = CalcASTBuilder
17
19
  end
18
-
20
+
19
21
  engine.use_grammar(CalcGrammar)
20
22
  raw_result = parse_expression(engine, anExpression)
21
23
  ast = engine.to_ptree(raw_result)
22
24
  return expect(ast.root.interpret)
23
25
  end
24
-
26
+
25
27
  def parse_expression(anEngine, anExpression)
26
28
  lexer = CalcLexer.new(anExpression)
27
29
  result = anEngine.parse(lexer.tokens)
@@ -38,7 +40,7 @@ describe 'Calculator' do
38
40
 
39
41
  context 'Parsing valid expressions' do
40
42
  let(:epsilon) { 0.0000000001 }
41
-
43
+
42
44
  it 'should evaluate simple integer literals' do
43
45
  expect_expr('2').to eq(2)
44
46
  end
@@ -70,18 +72,18 @@ describe 'Calculator' do
70
72
  it 'should evaluate the pi constant' do
71
73
  expect_expr('PI').to eq(3.141592653589793)
72
74
  end
73
-
75
+
74
76
  it 'should evaluate the negated pi constant' do
75
77
  expect_expr('-PI').to eq(-3.141592653589793)
76
- end
78
+ end
77
79
 
78
80
  it "should evaluate Neper's e constant" do
79
81
  expect_expr('E').to eq(2.718281828459045)
80
82
  end
81
-
83
+
82
84
  it "should evaluate negated Neper's e constant" do
83
85
  expect_expr('-E').to eq(-2.718281828459045)
84
- end
86
+ end
85
87
 
86
88
  it 'should evaluate integer addition' do
87
89
  expect_expr('2 + 2').to eq(4)
@@ -154,7 +156,7 @@ describe 'Calculator' do
154
156
  it 'should evaluate multiple levels of parentheses' do
155
157
  expect_expr('2*(1/(1+3))').to eq(0.5)
156
158
  end
157
-
159
+
158
160
  # Some special functions
159
161
  it 'should evaluate square root of expressions' do
160
162
  expect_expr('sqrt(0)').to eq(0)
@@ -168,69 +170,69 @@ describe 'Calculator' do
168
170
  expect_expr('cbrt(1)').to eq(1)
169
171
  expect_expr('cbrt(1 + 1)').to eq(Math.cbrt(2))
170
172
  expect_expr('cbrt(5 * 5 * 5)').to eq(5)
171
- end
173
+ end
172
174
 
173
175
  it 'should evaluate exponential of expressions' do
174
- expect_expr('exp(-1)').to eq(1 / Math::E)
176
+ expect_expr('exp(-1)').to eq(1 / Math::E)
175
177
  expect_expr('exp(0)').to eq(1)
176
178
  expect_expr('exp(1)').to eq(Math::E)
177
179
  expect_expr('exp(2)').to be_within(epsilon).of(Math::E * Math::E)
178
180
  end
179
181
 
180
182
  it 'should evaluate natural logarithm of expressions' do
181
- expect_expr('ln(1/E)').to eq(-1)
182
- expect_expr('ln(1)').to eq(0)
183
+ expect_expr('ln(1/E)').to eq(-1)
184
+ expect_expr('ln(1)').to eq(0)
183
185
  expect_expr('ln(E)').to eq(1)
184
186
  expect_expr('ln(E * E)').to eq(2)
185
- end
187
+ end
186
188
 
187
189
  it 'should evaluate the logarithm base 10 of expressions' do
188
- expect_expr('log(1/10)').to eq(-1)
189
- expect_expr('log(1)').to eq(0)
190
+ expect_expr('log(1/10)').to eq(-1)
191
+ expect_expr('log(1)').to eq(0)
190
192
  expect_expr('log(10)').to eq(1)
191
193
  expect_expr('log(10 * 10 * 10)').to eq(3)
192
- end
193
-
194
+ end
195
+
194
196
  # Trigonometric functions
195
-
197
+
196
198
  it 'should compute the sinus of an expression' do
197
199
  expect_expr('sin(0)').to eq(0)
198
200
  expect_expr('sin(PI/6)').to be_within(epsilon).of(0.5)
199
201
  expect_expr('sin(PI/2)').to eq(1)
200
202
  end
201
-
203
+
202
204
  it 'should compute the cosinus of an expression' do
203
205
  expect_expr('cos(0)').to eq(1)
204
206
  expect_expr('cos(PI/3)').to be_within(epsilon).of(0.5)
205
207
  expect_expr('cos(PI/2)').to be_within(epsilon).of(0)
206
- end
208
+ end
207
209
 
208
210
  it 'should compute the tangent of an expression' do
209
211
  expect_expr('tan(0)').to eq(0)
210
212
  expect_expr('tan(PI/4)').to be_within(epsilon).of(1)
211
213
  expect_expr('tan(5*PI/12)').to be_within(epsilon).of(2 + Math.sqrt(3))
212
- end
213
-
214
+ end
215
+
214
216
  # Inverse trigonometric functions
215
-
217
+
216
218
  it 'should compute the arcsinus of an expression' do
217
219
  expect_expr('asin(0)').to eq(0)
218
220
  expect_expr('asin(0.5)').to be_within(epsilon).of(Math::PI / 6)
219
221
  expect_expr('asin(1)').to eq(Math::PI / 2)
220
222
  end
221
-
223
+
222
224
  it 'should compute the arccosinus of an expression' do
223
225
  expect_expr('acos(1)').to eq(0)
224
226
  expect_expr('acos(0.5)').to be_within(epsilon).of(Math::PI / 3)
225
227
  expect_expr('acos(0)').to be_within(epsilon).of(Math::PI / 2)
226
- end
228
+ end
227
229
 
228
230
  it 'should compute the tangent of an expression' do
229
231
  expect_expr('atan(0)').to eq(0)
230
232
  pi = Math::PI
231
233
  expect_expr('atan(1)').to be_within(epsilon).of(pi / 4)
232
234
  expect_expr('atan(2 + sqrt(3))').to be_within(epsilon).of(5 * pi / 12)
233
- end
235
+ end
234
236
  end # context
235
237
  end # describe
236
238
  # End of file