rley 0.5.05 → 0.5.06

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/examples/NLP/mini_en_demo.rb +1 -1
  4. data/examples/data_formats/JSON/json_lexer.rb +4 -4
  5. data/examples/general/calc_iter1/calc_lexer.rb +1 -1
  6. data/examples/general/calc_iter2/calc_ast_builder.rb +51 -23
  7. data/examples/general/calc_iter2/calc_ast_nodes.rb +27 -6
  8. data/examples/general/calc_iter2/calc_demo.rb +2 -1
  9. data/examples/general/calc_iter2/calc_grammar.rb +14 -5
  10. data/examples/general/calc_iter2/calc_lexer.rb +14 -5
  11. data/examples/general/calc_iter2/spec/calculator_spec.rb +140 -23
  12. data/lib/rley.rb +1 -1
  13. data/lib/rley/{parser → base}/base_parser.rb +5 -3
  14. data/lib/rley/{parser → base}/dotted_item.rb +27 -16
  15. data/lib/rley/{parser → base}/grm_items_builder.rb +4 -2
  16. data/lib/rley/constants.rb +1 -1
  17. data/lib/rley/formatter/base_formatter.rb +1 -0
  18. data/lib/rley/gfg/item_vertex.rb +16 -5
  19. data/lib/rley/gfg/non_terminal_vertex.rb +5 -0
  20. data/lib/rley/gfg/vertex.rb +1 -2
  21. data/lib/rley/lexical/token.rb +31 -0
  22. data/lib/rley/{tokens → lexical}/token_range.rb +8 -2
  23. data/lib/rley/parser/gfg_earley_parser.rb +2 -2
  24. data/lib/rley/parser/parse_tree_builder.rb +2 -2
  25. data/lib/rley/ptree/parse_tree_node.rb +2 -2
  26. data/lib/rley/sppf/sppf_node.rb +2 -2
  27. data/lib/rley/syntax/grammar.rb +5 -1
  28. data/lib/rley/syntax/grammar_builder.rb +2 -2
  29. data/lib/rley/syntax/terminal.rb +1 -1
  30. data/spec/rley/{parser → base}/dotted_item_spec.rb +2 -2
  31. data/spec/rley/{parser → base}/grm_items_builder_spec.rb +2 -2
  32. data/spec/rley/formatter/asciitree_spec.rb +6 -6
  33. data/spec/rley/formatter/bracket_notation_spec.rb +6 -6
  34. data/spec/rley/formatter/debug_spec.rb +6 -6
  35. data/spec/rley/formatter/json_spec.rb +6 -6
  36. data/spec/rley/gfg/call_edge_spec.rb +2 -2
  37. data/spec/rley/gfg/grm_flow_graph_spec.rb +2 -2
  38. data/spec/rley/gfg/item_vertex_spec.rb +9 -9
  39. data/spec/rley/gfg/return_edge_spec.rb +2 -2
  40. data/spec/rley/{tokens → lexical}/token_range_spec.rb +2 -2
  41. data/spec/rley/{tokens → lexical}/token_spec.rb +2 -2
  42. data/spec/rley/parse_forest_visitor_spec.rb +1 -1
  43. data/spec/rley/parse_tree_visitor_spec.rb +6 -6
  44. data/spec/rley/parser/ast_builder_spec.rb +1 -1
  45. data/spec/rley/parser/cst_builder_spec.rb +1 -1
  46. data/spec/rley/parser/error_reason_spec.rb +3 -3
  47. data/spec/rley/parser/gfg_chart_spec.rb +4 -4
  48. data/spec/rley/parser/gfg_earley_parser_spec.rb +3 -3
  49. data/spec/rley/parser/gfg_parsing_spec.rb +5 -5
  50. data/spec/rley/parser/groucho_spec.rb +1 -1
  51. data/spec/rley/parser/parse_entry_set_spec.rb +4 -4
  52. data/spec/rley/parser/parse_entry_spec.rb +4 -4
  53. data/spec/rley/parser/parse_state_spec.rb +7 -7
  54. data/spec/rley/parser/parse_tracer_spec.rb +5 -5
  55. data/spec/rley/parser/parse_walker_factory_spec.rb +1 -1
  56. data/spec/rley/ptree/non_terminal_node_spec.rb +1 -1
  57. data/spec/rley/sppf/alternative_node_spec.rb +4 -4
  58. data/spec/rley/sppf/non_terminal_node_spec.rb +2 -2
  59. data/spec/rley/support/ambiguous_grammar_helper.rb +2 -2
  60. data/spec/rley/support/expectation_helper.rb +1 -1
  61. data/spec/rley/support/grammar_ambig01_helper.rb +2 -2
  62. data/spec/rley/support/grammar_arr_int_helper.rb +3 -3
  63. data/spec/rley/support/grammar_b_expr_helper.rb +2 -2
  64. data/spec/rley/support/grammar_helper.rb +3 -3
  65. data/spec/rley/support/grammar_l0_helper.rb +2 -2
  66. data/spec/rley/support/grammar_pb_helper.rb +2 -2
  67. metadata +15 -15
  68. data/lib/rley/tokens/token.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5fdf5f08878706a50fe9b024c7ffed95b3cb08fd
4
- data.tar.gz: 92265adccb7cb660b56ac2b060aee4a5facdd777
3
+ metadata.gz: 330046979feed860a18faeb226de2c2aab9ca8ca
4
+ data.tar.gz: 3e8b7ee124edb3db95f6c95c56754a71375dd703
5
5
  SHA512:
6
- metadata.gz: 936959f76b1b2dc9fe6db68e4a0372f6aa421ea143876421c0ee90ab95852f71dad9fc166d80269af070fe84dfcbdef3848cb29be321e474afd40dae9e3bce59
7
- data.tar.gz: 4e2f8a16366d4771e6d8506d3333adc40329c01a21952ee2002d432a5d0205c6ab6658b96d245bd4688227c6cf2c053fa66a57a9bdbeaeca4ef4969425b21457
6
+ metadata.gz: 518f9fb02230a1953c71ac4334cfa71d4c72c3f9e94b546a7dd164a32aa67d0ab2769563cd5cd72b46891cbce08fe9d4319f6645de7db8bd2ddb5d8fab67034a
7
+ data.tar.gz: b468cda9c0d4c0e49537f0fc4cdac5882ea21ee176810c0d529fda1f080e13428f73a328607afff640f51ded1de8c83e4a5715da008d4295a276064d78490ca5
@@ -1,3 +1,9 @@
1
+ ### 0.5.06 / 2017-11-08
2
+ * [CHANGE] Demo calculator in `examples/general/calc_iter2` does much than the basic arithmetic operators, it now
3
+ support trigonometric functions and their inverse, square root, exponential and natural logarithm functions!.
4
+ * [CHANGE] Internal module re-organization. New module `Base`, module `Tokens` renamed to `Lexical`.
5
+ * [CHANGE] Improved YARD Documentation for 10+ classes.
6
+
1
7
  ### 0.5.05 / 2017-11-04
2
8
  * [FIX] Method `GFGParsing#call_rule` didn't handle properly the case of nullable symbols appearing in more than one production rule.
3
9
  * [New] New calculator example. In addition to the basic arithmetic operators (+, -, *, /) it accepts the unary minus
@@ -63,7 +63,7 @@ def tokenizer(aTextToParse, aGrammar)
63
63
  term_name = Lexicon[word]
64
64
  raise StandardError, "Word '#{word}' not found in lexicon" if term_name.nil?
65
65
  terminal = aGrammar.name2symbol[term_name]
66
- Rley::Tokens::Token.new(word, terminal)
66
+ Rley::Lexical::Token.new(word, terminal)
67
67
  end
68
68
 
69
69
  return tokens
@@ -51,7 +51,7 @@ class JSONLexer
51
51
  when '{', '}', '[', ']', ',', ':'
52
52
  type_name = @@lexeme2name[curr_ch]
53
53
  token_type = name2symbol[type_name]
54
- token = Rley::Tokens::Token.new(curr_ch, token_type)
54
+ token = Rley::Lexical::Token.new(curr_ch, token_type)
55
55
 
56
56
  when /[ftn]/ # First letter of keywords
57
57
  @scanner.pos = scanner.pos - 1 # Simulate putback
@@ -61,7 +61,7 @@ class JSONLexer
61
61
  raise ScanError.new("Invalid keyword: #{invalid_keyw}")
62
62
  else
63
63
  token_type = name2symbol[keyw]
64
- token = Rley::Tokens::Token.new(keyw, token_type)
64
+ token = Rley::Lexical::Token.new(keyw, token_type)
65
65
  end
66
66
 
67
67
  # LITERALS
@@ -71,13 +71,13 @@ class JSONLexer
71
71
  err_msg = 'No closing quotes (") found'
72
72
  raise ScanError.new(err_msg) if end_delimiter.nil?
73
73
  token_type = name2symbol['string']
74
- token = Rley::Tokens::Token.new(value, token_type)
74
+ token = Rley::Lexical::Token.new(value, token_type)
75
75
 
76
76
  when /[-0-9]/ # Start character of number literal found
77
77
  @scanner.pos = scanner.pos - 1 # Simulate putback
78
78
  value = scanner.scan(/-?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9])?/)
79
79
  token_type = name2symbol['number']
80
- token = Rley::Tokens::Token.new(value, token_type)
80
+ token = Rley::Lexical::Token.new(value, token_type)
81
81
 
82
82
  else # Unknown token
83
83
  erroneous = curr_ch.nil? ? '' : curr_ch
@@ -71,7 +71,7 @@ class CalcLexer
71
71
 
72
72
  def build_token(aSymbolName, aLexeme)
73
73
  token_type = name2symbol[aSymbolName]
74
- return Rley::Tokens::Token.new(aLexeme, token_type)
74
+ return Rley::Lexical::Token.new(aLexeme, token_type)
75
75
  end
76
76
 
77
77
  def skip_whitespaces()
@@ -12,10 +12,13 @@ class CalcASTBuilder < Rley::Parser::ParseTreeBuilder
12
12
  # Lexical ambiguity: minus sign represents two very concepts:
13
13
  # The unary negation operator on one hand, the binary substraction operator
14
14
  'MINUS' => { 'add_operator_1' => Rley::PTree::TerminalNode,
15
- 'factor_2' => CalcNegateNode,
15
+ 'simple_factor_2' => CalcNegateNode,
16
16
  'sign_1' => CalcNegateNode
17
17
  },
18
- 'NUMBER' => CalcNumberNode
18
+ 'NUMBER' => CalcNumberNode,
19
+ 'PI' => CalcConstantNode,
20
+ 'E' => CalcConstantNode,
21
+ 'RESERVED' => CalcReservedNode
19
22
  }.freeze
20
23
 
21
24
  protected
@@ -87,32 +90,49 @@ class CalcASTBuilder < Rley::Parser::ParseTreeBuilder
87
90
 
88
91
  when 'term_1' # rule 'term' => %w[term mul_operator factor]
89
92
  reduce_term_1(aProduction, aRange, theTokens, theChildren)
90
-
93
+
91
94
  when 'factor_0' # rule 'factor' => 'simple_factor'
92
95
  return_first_child(aRange, theTokens, theChildren)
93
-
94
- when 'factor_1' # rule 'factor' => %w[simple_factor POWER simple_factor]
95
- reduce_factor_1(aProduction, aRange, theTokens, theChildren)
96
96
 
97
- when 'simple_factor_0' # rule 'simple_factor' => %[sign NUMBER]
97
+ when 'factor_1' # rule 'factor' => %w[factor POWER simple_factor]
98
+ reduce_factor_1(aProduction, aRange, theTokens, theChildren)
99
+
100
+ when 'simple_factor_0' # rule 'simple_factor' => %[sign scalar]
98
101
  reduce_simple_factor_0(aProduction, aRange, theTokens, theChildren)
99
102
 
100
- when 'simple_factor_1' # rule 'simple_factor' => %w[LPAREN expression RPAREN]
101
- return_second_child(aRange, theTokens, theChildren)
102
-
103
- when 'simple_factor_2' # rule 'simple_factor' => %w[MINUS LPAREN expression RPAREN]
103
+ when 'simple_factor_1' # rule 'simple_factor' => %w[unary_function in_parenthesis]
104
+ reduce_simple_factor_1(aProduction, aRange, theTokens, theChildren)
105
+
106
+ when 'simple_factor_2' # rule 'simple_factor' => %w[MINUS in_parenthesis]
104
107
  reduce_simple_factor_2(aProduction, aRange, theTokens, theChildren)
105
-
108
+
109
+ when 'simple_factor_3' # rule 'simple_factor' => 'in_parenthesis'
110
+ return_first_child(aRange, theTokens, theChildren)
106
111
 
107
112
  when 'sign_0' # rule 'sign' => 'PLUS'
108
- return_first_child(aRange, theTokens, theChildren)
113
+ return_first_child(aRange, theTokens, theChildren)
109
114
 
110
115
  when 'sign_1' # rule 'sign' => 'MINUS'
111
- return_first_child(aRange, theTokens, theChildren)
116
+ return_first_child(aRange, theTokens, theChildren)
112
117
 
113
- when 'sign_2'
118
+ when 'sign_2' # rule 'sign' => []
114
119
  return_epsilon(aRange, theTokens, theChildren)
115
120
 
121
+ when 'scalar_0' # rule 'scalar' => 'NUMBER'
122
+ return_first_child(aRange, theTokens, theChildren)
123
+
124
+ when 'scalar_1' # rule 'scalar' => 'PI'
125
+ return_first_child(aRange, theTokens, theChildren)
126
+
127
+ when 'scalar_2' # rule 'scalar' => 'E'
128
+ return_first_child(aRange, theTokens, theChildren)
129
+
130
+ when 'unary_function_0' # rule 'unary_function' => 'RESERVED'
131
+ return_first_child(aRange, theTokens, theChildren)
132
+
133
+ when 'in_parenthesis_0' # rule 'in_parenthesis' => %w[LPAREN expression RPAREN]
134
+ return_second_child(aRange, theTokens, theChildren)
135
+
116
136
  when 'add_operator_0' # rule 'add_operator' => 'PLUS'
117
137
  reduce_add_operator_0(aProduction, aRange, theTokens, theChildren)
118
138
 
@@ -120,7 +140,7 @@ class CalcASTBuilder < Rley::Parser::ParseTreeBuilder
120
140
  reduce_add_operator_1(aProduction, aRange, theTokens, theChildren)
121
141
 
122
142
  when 'mul_operator_0' # rule 'mul_operator' => 'STAR'
123
- reduce_mul_operator_0(aProduction, aRange, theTokens, theChildren)
143
+ reduce_mul_operator_0(aProduction, aRange, theTokens, theChildren)
124
144
 
125
145
  when 'mul_operator_1' # rule 'mul_operator' => 'DIVIDE'
126
146
  reduce_mul_operator_1(aProduction, aRange, theTokens, theChildren)
@@ -148,17 +168,17 @@ class CalcASTBuilder < Rley::Parser::ParseTreeBuilder
148
168
  def reduce_term_1(_production, _range, _tokens, theChildren)
149
169
  reduce_binary_operator(theChildren)
150
170
  end
151
-
171
+
152
172
  # rule 'factor' => %w[simple_factor POWER simple_factor]]
153
173
  def reduce_factor_1(aProduction, aRange, theTokens, theChildren)
154
174
  result = PowerNode.new(theChildren[1].symbol, aRange)
155
175
  result.children << theChildren[0]
156
176
  result.children << theChildren[2]
157
-
177
+
158
178
  return result
159
- end
179
+ end
160
180
 
161
- # rule 'simple_factor' => %[sign NUMBER]
181
+ # rule 'simple_factor' => %[sign scalar]
162
182
  def reduce_simple_factor_0(aProduction, aRange, theTokens, theChildren)
163
183
  first_child = theChildren[0]
164
184
  result = if first_child.kind_of?(CalcNegateNode)
@@ -169,11 +189,19 @@ class CalcASTBuilder < Rley::Parser::ParseTreeBuilder
169
189
 
170
190
  return result
171
191
  end
172
-
173
- # rule 'simple_factor' => %w[MINUS LPAREN expression RPAREN]
192
+
193
+ # rule 'simple_factor' => %w[unary_function in_parenthesis]
194
+ def reduce_simple_factor_1(aProduction, aRange, theTokens, theChildren)
195
+ func = CalcUnaryFunction.new(theChildren[0].symbol, aRange.low)
196
+ func.func_name = theChildren[0].value
197
+ func.children << theChildren[1]
198
+ return func
199
+ end
200
+
201
+ # rule 'simple_factor' => %w[MINUS in_parenthesis]
174
202
  def reduce_simple_factor_2(aProduction, aRange, theTokens, theChildren)
175
203
  negation = CalcNegateNode.new(theChildren[0].symbol, aRange.low)
176
- negation.children << theChildren[2]
204
+ negation.children << theChildren[1]
177
205
  return negation
178
206
  end
179
207
 
@@ -47,10 +47,25 @@ class CalcNumberNode < CalcTerminalNode
47
47
  end
48
48
  end
49
49
 
50
+ class CalcConstantNode < CalcNumberNode
51
+ @@constants2val = {
52
+ 'PI' => Math::PI,
53
+ 'E' => Math::E
54
+ }
55
+
56
+ def init_value(aConstantName)
57
+ self.value = @@constants2val[aConstantName]
58
+ end
59
+ end
60
+
61
+ class CalcReservedNode < CalcTerminalNode
62
+
63
+ end
64
+
50
65
  class CalcCompositeNode
51
66
  attr_accessor(:children)
52
67
  attr_accessor(:symbol)
53
- attr_accessor(:aPosition)
68
+ attr_accessor(:position)
54
69
 
55
70
  def initialize(aSymbol, aPosition)
56
71
  @symbol = aSymbol
@@ -75,16 +90,22 @@ class CalcUnaryOpNode < CalcCompositeNode
75
90
  alias members children
76
91
  end # class
77
92
 
78
- class CalcNegateNode < CalcUnaryOpNode
79
- def initialize(aSymbol, aPosition)
80
- super(aSymbol,aPosition)
81
- end
82
-
93
+ class CalcNegateNode < CalcUnaryOpNode
83
94
  def interpret()
84
95
  return -(children[0].interpret)
85
96
  end
86
97
  end # class
87
98
 
99
+ class CalcUnaryFunction < CalcCompositeNode
100
+ attr_accessor(:func_name)
101
+
102
+ def interpret()
103
+ argument = children[0].interpret
104
+ internal_name = @func_name == 'ln' ? 'log' : @func_name
105
+ return Math.send(internal_name.to_sym, argument)
106
+ end
107
+ end
108
+
88
109
  class CalcBinaryOpNode < CalcCompositeNode
89
110
  def initialize(aSymbol, aRange)
90
111
  super(aSymbol, aRange)
@@ -33,8 +33,9 @@ Command-line symtax:
33
33
  where:
34
34
  the arithmetic expression is enclosed between double quotes (")
35
35
 
36
- Example:
36
+ Examples:
37
37
  ruby #{my_name} "2 * 3 + (1 + 3 ** 2)"
38
+ ruby #{my_name} "cos(PI/2) + sqrt(1 + 1)"
38
39
  END_MSG
39
40
  puts msg
40
41
  exit(1)
@@ -5,22 +5,31 @@ require 'rley' # Load the gem
5
5
  # Define a grammar for basic arithmetical expressions
6
6
  builder = Rley::Syntax::GrammarBuilder.new do
7
7
  add_terminals('NUMBER')
8
- add_terminals('LPAREN', 'RPAREN') # For '(', ')' delimiters
9
8
  add_terminals('PLUS', 'MINUS') # For '+', '-' operators or sign
10
9
  add_terminals('STAR', 'DIVIDE', 'POWER') # For '*', '/', '**' operators
10
+ add_terminals('LPAREN', 'RPAREN') # For '(', ')' delimiters
11
+ add_terminals('PI', 'E') # For numeric constants
12
+ add_terminals('RESERVED') # Reserved identifiers
13
+
11
14
  rule 'expression' => %w[simple_expression]
12
15
  rule 'simple_expression' => 'term'
13
16
  rule 'simple_expression' => %w[simple_expression add_operator term]
14
17
  rule 'term' => 'factor'
15
18
  rule 'term' => %w[term mul_operator factor]
16
19
  rule 'factor' => 'simple_factor'
17
- rule 'factor' => %w[simple_factor POWER simple_factor]
18
- rule 'simple_factor' => %w[sign NUMBER]
19
- rule 'simple_factor' => %w[LPAREN expression RPAREN]
20
- rule 'simple_factor' => %w[MINUS LPAREN expression RPAREN]
20
+ rule 'factor' => %w[factor POWER simple_factor]
21
+ rule 'simple_factor' => %w[sign scalar]
22
+ rule 'simple_factor' => %w[unary_function in_parenthesis]
23
+ rule 'simple_factor' => %w[MINUS in_parenthesis]
24
+ rule 'simple_factor' => 'in_parenthesis'
21
25
  rule 'sign' => 'PLUS'
22
26
  rule 'sign' => 'MINUS'
23
27
  rule 'sign' => []
28
+ rule 'scalar' => 'NUMBER'
29
+ rule 'scalar' => 'PI'
30
+ rule 'scalar' => 'E'
31
+ rule 'unary_function' => 'RESERVED'
32
+ rule 'in_parenthesis' => %w[LPAREN expression RPAREN]
24
33
  rule 'add_operator' => 'PLUS'
25
34
  rule 'add_operator' => 'MINUS'
26
35
  rule 'mul_operator' => 'STAR'
@@ -3,7 +3,6 @@
3
3
  require 'strscan'
4
4
  require 'rley' # Load the gem
5
5
 
6
-
7
6
  class CalcLexer
8
7
  attr_reader(:scanner)
9
8
  attr_reader(:lineno)
@@ -17,8 +16,16 @@ class CalcLexer
17
16
  '-' => 'MINUS',
18
17
  '*' => 'STAR',
19
18
  '/' => 'DIVIDE',
20
- '**' => 'POWER'
19
+ '**' => 'POWER',
20
+ 'PI' => 'PI',
21
+ 'E' => 'E'
21
22
  }.freeze
23
+
24
+ @@unary_functions = [
25
+ 'sin', 'cos', 'tan',
26
+ 'asin', 'acos', 'atan',
27
+ 'sqrt', 'exp', 'ln'
28
+ ].freeze
22
29
 
23
30
  class ScanError < StandardError; end
24
31
 
@@ -29,6 +36,7 @@ class CalcLexer
29
36
  end
30
37
 
31
38
  def tokens()
39
+ @unary_f_pattern = Regexp.new(@@unary_functions.join('|'))
32
40
  tok_sequence = []
33
41
  until @scanner.eos?
34
42
  token = _next_token
@@ -50,11 +58,12 @@ class CalcLexer
50
58
  if '()+-/'.include? curr_ch
51
59
  # Single character token
52
60
  token = build_token(@@lexeme2name[curr_ch], scanner.getch)
53
-
54
61
  elsif (lexeme = scanner.scan(/\*\*/))
55
62
  token = build_token(@@lexeme2name[lexeme], lexeme)
56
- elsif (lexeme = scanner.scan(/\*/))
63
+ elsif (lexeme = scanner.scan(/\*|PI|E/))
57
64
  token = build_token(@@lexeme2name[lexeme], lexeme)
65
+ elsif (lexeme = scanner.scan(@unary_f_pattern))
66
+ token = build_token('RESERVED', lexeme)
58
67
  elsif (lexeme = scanner.scan(/[0-9]+(\.[0-9]+)?([eE][-+]?[0-9])?/))
59
68
  token = build_token('NUMBER', lexeme)
60
69
  else # Unknown token
@@ -69,7 +78,7 @@ class CalcLexer
69
78
 
70
79
  def build_token(aSymbolName, aLexeme)
71
80
  token_type = name2symbol[aSymbolName]
72
- return Rley::Tokens::Token.new(aLexeme, token_type)
81
+ return Rley::Lexical::Token.new(aLexeme, token_type)
73
82
  end
74
83
 
75
84
  def skip_whitespaces()
@@ -23,17 +23,17 @@ describe 'Calculator' do
23
23
 
24
24
  return result
25
25
  end
26
-
26
+
27
27
  def print_cst(aParseResult)
28
28
  # Generate a parse tree from the parse result
29
29
  ptree = aParseResult.parse_tree
30
-
30
+
31
31
  # Let's create a parse tree visitor
32
32
  visitor = Rley::ParseTreeVisitor.new(ptree)
33
33
 
34
34
  # Now output formatted parse tree
35
35
  renderer = Rley::Formatter::Asciitree.new($stdout)
36
- renderer.render(visitor)
36
+ renderer.render(visitor)
37
37
  end
38
38
 
39
39
  def build_ast(aParseResult)
@@ -48,55 +48,112 @@ describe 'Calculator' do
48
48
  ast = build_ast(parsing)
49
49
  return expect(ast.interpret)
50
50
  end
51
-
51
+
52
52
  context 'Parsing valid expressions' do
53
- it 'should evaluate simple number literals' do
53
+ it 'should evaluate simple integer literals' do
54
54
  expect_expr('2').to eq(2)
55
55
  end
56
-
57
- it 'should evaluate positive number literals' do
56
+
57
+ it 'should evaluate simple floating-point literals' do
58
+ expect_expr('3.1').to eq(3.1)
59
+ end
60
+
61
+ it 'should evaluate positive integer literals' do
58
62
  expect_expr('+2').to eq(2)
59
63
  expect_expr('+ 2').to eq(2)
60
64
  end
61
65
 
62
- it 'should evaluate negative number literals' do
66
+ it 'should evaluate positive floating-point literals' do
67
+ expect_expr('+3.1').to eq(3.1)
68
+ expect_expr('+ 3.1').to eq(3.1)
69
+ end
70
+
71
+ it 'should evaluate negative integer literals' do
63
72
  expect_expr('-2').to eq(-2)
64
73
  expect_expr('- 2').to eq(-2)
65
- end
74
+ end
75
+
76
+ it 'should evaluate negative floating-point literals' do
77
+ expect_expr('-3.1').to eq(-3.1)
78
+ expect_expr('- 3.1').to eq(-3.1)
79
+ end
66
80
 
67
- it 'should evaluate addition' do
81
+ it 'should evaluate the pi constant' do
82
+ expect_expr('PI').to eq(3.141592653589793)
83
+ end
84
+
85
+ it 'should evaluate the negated pi constant' do
86
+ expect_expr('-PI').to eq(-3.141592653589793)
87
+ end
88
+
89
+ it "should evaluate Neper's e constant" do
90
+ expect_expr('E').to eq(2.718281828459045)
91
+ end
92
+
93
+ it "should evaluate negated Neper's e constant" do
94
+ expect_expr('-E').to eq(-2.718281828459045)
95
+ end
96
+
97
+ it 'should evaluate integer addition' do
68
98
  expect_expr('2 + 2').to eq(4)
69
99
  end
70
100
 
71
- it 'should evaluate subtraction' do
72
- expect_expr('2.1 - 2').to be_within(0.000000000000001).of(0.1)
101
+ it 'should evaluate floating-point addition' do
102
+ expect_expr('2.0 + 3.1').to eq(5.1)
73
103
  end
74
104
 
75
- it 'handles negative numbers' do
105
+ it 'should evaluate integer subtraction' do
106
+ expect_expr('2 - 3').to eq(-1)
107
+ end
108
+
109
+ it 'should evaluate floating-point subtraction' do
110
+ expect_expr('3.1 - 2').to eq(1.1)
111
+ end
112
+
113
+ it 'should handle negation of negative numbers' do
76
114
  expect_expr('3--2').to eq(5)
77
- end
78
-
79
- it 'should evaluate division' do
80
- expect_expr('10.5 / 5').to eq(2.1)
81
115
  end
82
116
 
83
- it 'should evaluate multiplication' do
117
+ it 'should evaluate integer multiplication' do
118
+ expect_expr('2 * 3').to eq(6)
119
+ end
120
+
121
+ it 'should evaluate floating-point multiplication' do
84
122
  expect_expr('2 * 3.1').to eq(6.2)
85
123
  end
86
124
 
87
- it 'should evaluate exponentiation' do
125
+ it 'should evaluate division of integers' do
126
+ expect_expr('5 / 2').to eq(2.5)
127
+ end
128
+
129
+ it 'should evaluate floating-point division' do
130
+ expect_expr('10.5 / 5').to eq(2.1)
131
+ end
132
+
133
+ it 'should evaluate integer exponent' do
88
134
  expect_expr('5 ** (3 - 1)').to eq(25)
135
+ end
136
+
137
+ it 'should evaluate floating-point exponent' do
89
138
  expect_expr('25 ** 0.5').to eq(5)
90
- end
139
+ end
140
+
141
+ it 'should evaluate negative exponent' do
142
+ expect_expr('5 ** -2').to eq(0.04)
143
+ end
144
+
145
+ it 'should handle nested exponentiations' do
146
+ expect_expr('2 ** 2**2)').to eq(16)
147
+ end
91
148
 
92
149
  it 'should change sign of expression in parentheses' do
93
150
  expect_expr('- (2 * 5)').to eq(-10)
94
151
  end
95
-
152
+
96
153
  it 'should evaluate parentheses' do
97
154
  expect_expr('2 * (2.1 + 1)').to eq(6.2)
98
- end
99
-
155
+ end
156
+
100
157
  it 'should evaluate regardless of whitespace' do
101
158
  expect_expr("2*(1+\t1)").to eq(4)
102
159
  end
@@ -108,6 +165,66 @@ describe 'Calculator' do
108
165
  it 'should evaluate multiple levels of parentheses' do
109
166
  expect_expr('2*(1/(1+3))').to eq(0.5)
110
167
  end
168
+
169
+ # Some special functions
170
+ it 'should evaluate square root of expressions' do
171
+ expect_expr('sqrt(1 + 1)').to eq(Math.sqrt(2))
172
+ end
173
+
174
+ it 'should evaluate exponential of expressions' do
175
+ expect_expr('exp(-1)').to eq(1/Math::E)
176
+ expect_expr('exp(0)').to eq(1)
177
+ expect_expr('exp(1)').to eq(Math::E)
178
+ expect_expr('exp(2)').to be_within(0.0000000001).of(Math::E * Math::E)
179
+ end
180
+
181
+ it 'should evaluate natural logarithm of expressions' do
182
+ expect_expr('ln(1/E)').to eq(-1)
183
+ expect_expr('ln(1)').to eq(0)
184
+ expect_expr('ln(E)').to eq(1)
185
+ expect_expr('ln(E * E)').to eq(2)
186
+ end
187
+
188
+ # Trigonometric functions
189
+
190
+ it 'should compute the sinus of an expression' do
191
+ expect_expr('sin(0)').to eq(0)
192
+ expect_expr('sin(PI/6)').to be_within(0.0000000001).of(0.5)
193
+ expect_expr('sin(PI/2)').to eq(1)
194
+ end
195
+
196
+ it 'should compute the cosinus of an expression' do
197
+ expect_expr('cos(0)').to eq(1)
198
+ expect_expr('cos(PI/3)').to be_within(0.0000000001).of(0.5)
199
+ expect_expr('cos(PI/2)').to be_within(0.0000000001).of(0)
200
+ end
201
+
202
+ it 'should compute the tangent of an expression' do
203
+ expect_expr('tan(0)').to eq(0)
204
+ expect_expr('tan(PI/4)').to be_within(0.0000000001).of(1)
205
+ expect_expr('tan(5*PI/12)').to be_within(0.0000000001).of(2 + Math.sqrt(3))
206
+ end
207
+
208
+ # Inverse trigonometric functions
209
+
210
+ it 'should compute the arcsinus of an expression' do
211
+ expect_expr('asin(0)').to eq(0)
212
+ expect_expr('asin(0.5)').to be_within(0.0000000001).of(Math::PI/6)
213
+ expect_expr('asin(1)').to eq(Math::PI/2)
214
+ end
215
+
216
+ it 'should compute the arccosinus of an expression' do
217
+ expect_expr('acos(1)').to eq(0)
218
+ expect_expr('acos(0.5)').to be_within(0.0000000001).of(Math::PI/3)
219
+ expect_expr('acos(0)').to be_within(0.0000000001).of(Math::PI/2)
220
+ end
221
+
222
+ it 'should compute the tangent of an expression' do
223
+ expect_expr('atan(0)').to eq(0)
224
+ expect_expr('atan(1)').to be_within(0.0000000001).of(Math::PI/4)
225
+ expect_expr('atan(2 + sqrt(3))').to be_within(0.0000000001).of(5*Math::PI/12)
226
+ end
227
+
111
228
  end # context
112
229
  end # describe
113
230
  # End of file