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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/examples/NLP/mini_en_demo.rb +1 -1
- data/examples/data_formats/JSON/json_lexer.rb +4 -4
- data/examples/general/calc_iter1/calc_lexer.rb +1 -1
- data/examples/general/calc_iter2/calc_ast_builder.rb +51 -23
- data/examples/general/calc_iter2/calc_ast_nodes.rb +27 -6
- data/examples/general/calc_iter2/calc_demo.rb +2 -1
- data/examples/general/calc_iter2/calc_grammar.rb +14 -5
- data/examples/general/calc_iter2/calc_lexer.rb +14 -5
- data/examples/general/calc_iter2/spec/calculator_spec.rb +140 -23
- data/lib/rley.rb +1 -1
- data/lib/rley/{parser → base}/base_parser.rb +5 -3
- data/lib/rley/{parser → base}/dotted_item.rb +27 -16
- data/lib/rley/{parser → base}/grm_items_builder.rb +4 -2
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/formatter/base_formatter.rb +1 -0
- data/lib/rley/gfg/item_vertex.rb +16 -5
- data/lib/rley/gfg/non_terminal_vertex.rb +5 -0
- data/lib/rley/gfg/vertex.rb +1 -2
- data/lib/rley/lexical/token.rb +31 -0
- data/lib/rley/{tokens → lexical}/token_range.rb +8 -2
- data/lib/rley/parser/gfg_earley_parser.rb +2 -2
- data/lib/rley/parser/parse_tree_builder.rb +2 -2
- data/lib/rley/ptree/parse_tree_node.rb +2 -2
- data/lib/rley/sppf/sppf_node.rb +2 -2
- data/lib/rley/syntax/grammar.rb +5 -1
- data/lib/rley/syntax/grammar_builder.rb +2 -2
- data/lib/rley/syntax/terminal.rb +1 -1
- data/spec/rley/{parser → base}/dotted_item_spec.rb +2 -2
- data/spec/rley/{parser → base}/grm_items_builder_spec.rb +2 -2
- data/spec/rley/formatter/asciitree_spec.rb +6 -6
- data/spec/rley/formatter/bracket_notation_spec.rb +6 -6
- data/spec/rley/formatter/debug_spec.rb +6 -6
- data/spec/rley/formatter/json_spec.rb +6 -6
- data/spec/rley/gfg/call_edge_spec.rb +2 -2
- data/spec/rley/gfg/grm_flow_graph_spec.rb +2 -2
- data/spec/rley/gfg/item_vertex_spec.rb +9 -9
- data/spec/rley/gfg/return_edge_spec.rb +2 -2
- data/spec/rley/{tokens → lexical}/token_range_spec.rb +2 -2
- data/spec/rley/{tokens → lexical}/token_spec.rb +2 -2
- data/spec/rley/parse_forest_visitor_spec.rb +1 -1
- data/spec/rley/parse_tree_visitor_spec.rb +6 -6
- data/spec/rley/parser/ast_builder_spec.rb +1 -1
- data/spec/rley/parser/cst_builder_spec.rb +1 -1
- data/spec/rley/parser/error_reason_spec.rb +3 -3
- data/spec/rley/parser/gfg_chart_spec.rb +4 -4
- data/spec/rley/parser/gfg_earley_parser_spec.rb +3 -3
- data/spec/rley/parser/gfg_parsing_spec.rb +5 -5
- data/spec/rley/parser/groucho_spec.rb +1 -1
- data/spec/rley/parser/parse_entry_set_spec.rb +4 -4
- data/spec/rley/parser/parse_entry_spec.rb +4 -4
- data/spec/rley/parser/parse_state_spec.rb +7 -7
- data/spec/rley/parser/parse_tracer_spec.rb +5 -5
- data/spec/rley/parser/parse_walker_factory_spec.rb +1 -1
- data/spec/rley/ptree/non_terminal_node_spec.rb +1 -1
- data/spec/rley/sppf/alternative_node_spec.rb +4 -4
- data/spec/rley/sppf/non_terminal_node_spec.rb +2 -2
- data/spec/rley/support/ambiguous_grammar_helper.rb +2 -2
- data/spec/rley/support/expectation_helper.rb +1 -1
- data/spec/rley/support/grammar_ambig01_helper.rb +2 -2
- data/spec/rley/support/grammar_arr_int_helper.rb +3 -3
- data/spec/rley/support/grammar_b_expr_helper.rb +2 -2
- data/spec/rley/support/grammar_helper.rb +3 -3
- data/spec/rley/support/grammar_l0_helper.rb +2 -2
- data/spec/rley/support/grammar_pb_helper.rb +2 -2
- metadata +15 -15
- data/lib/rley/tokens/token.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 330046979feed860a18faeb226de2c2aab9ca8ca
|
4
|
+
data.tar.gz: 3e8b7ee124edb3db95f6c95c56754a71375dd703
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 518f9fb02230a1953c71ac4334cfa71d4c72c3f9e94b546a7dd164a32aa67d0ab2769563cd5cd72b46891cbce08fe9d4319f6645de7db8bd2ddb5d8fab67034a
|
7
|
+
data.tar.gz: b468cda9c0d4c0e49537f0fc4cdac5882ea21ee176810c0d529fda1f080e13428f73a328607afff640f51ded1de8c83e4a5715da008d4295a276064d78490ca5
|
data/CHANGELOG.md
CHANGED
@@ -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::
|
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::
|
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::
|
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::
|
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::
|
80
|
+
token = Rley::Lexical::Token.new(value, token_type)
|
81
81
|
|
82
82
|
else # Unknown token
|
83
83
|
erroneous = curr_ch.nil? ? '' : curr_ch
|
@@ -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
|
-
'
|
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 '
|
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'
|
101
|
-
|
102
|
-
|
103
|
-
when 'simple_factor_2' # rule 'simple_factor' => %w[MINUS
|
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
|
-
|
113
|
+
return_first_child(aRange, theTokens, theChildren)
|
109
114
|
|
110
115
|
when 'sign_1' # rule 'sign' => 'MINUS'
|
111
|
-
|
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
|
-
|
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
|
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[
|
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[
|
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(:
|
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)
|
@@ -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[
|
18
|
-
rule 'simple_factor' => %w[sign
|
19
|
-
rule 'simple_factor' => %w[
|
20
|
-
rule 'simple_factor' => %w[MINUS
|
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::
|
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
|
53
|
+
it 'should evaluate simple integer literals' do
|
54
54
|
expect_expr('2').to eq(2)
|
55
55
|
end
|
56
|
-
|
57
|
-
it 'should evaluate
|
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
|
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
|
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
|
72
|
-
expect_expr('2.
|
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 '
|
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
|
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
|