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