rley 0.5.05 → 0.5.06

Sign up to get free protection for your applications and to get access to all the features.
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