electr 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +77 -50
- data/bin/electr +1 -5
- data/electr.gemspec +1 -1
- data/lib/electr.rb +10 -9
- data/lib/electr/ast.rb +1 -0
- data/lib/electr/ast/ast.rb +1 -0
- data/lib/electr/ast/variable_ast.rb +13 -0
- data/lib/electr/compiler.rb +0 -1
- data/lib/electr/exceptions.rb +6 -0
- data/lib/electr/option.rb +2 -2
- data/lib/electr/parser/lexer.rb +2 -0
- data/lib/electr/parser/lexical_unit.rb +9 -0
- data/lib/electr/parser/rules/expression_rule.rb +17 -2
- data/lib/electr/parser/sya.rb +70 -11
- data/lib/electr/parser/token.rb +7 -2
- data/lib/electr/parser/tokenizer.rb +20 -4
- data/lib/electr/repl.rb +1 -0
- data/lib/electr/repl/electr_value.rb +78 -0
- data/lib/electr/repl/evaluator.rb +92 -7
- data/lib/electr/repl/printer.rb +26 -3
- data/lib/electr/version.rb +2 -2
- data/spec/compiler_spec.rb +62 -3
- data/spec/parser/lexer_spec.rb +5 -0
- data/spec/parser/sya_spec.rb +64 -0
- data/spec/parser/tokenizer_spec.rb +13 -0
- data/spec/repl/electr_value_spec.rb +86 -0
- data/spec/repl/evaluator_spec.rb +112 -3
- data/spec/repl/printer_spec.rb +42 -2
- data/spec/repl/repl_spec.rb +1 -1
- metadata +7 -6
- data/documentation/developer.md +0 -21
- data/documentation/grammar.md +0 -34
- data/documentation/precedence.md +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9db135b700b2ed083ff6ffe4e1d9e70be31f831a
|
4
|
+
data.tar.gz: f3bd4535fbf09f65ca28e2a621ed2c292fd1e0a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c4a03f6968baed4cd6759d33648fa34b4a141bdd04e47fc74af87e4bce658595be2e3cf6f753cde03a98c08d4807d5f05de3e219a7edc5add366bceaf8d3ae1
|
7
|
+
data.tar.gz: e4fadd225f4ccc5f7e1f16f590f702915e0df75f05aecbd1f5c295a08c86164f822af98ae3b382c7b076d8c83eb7b0e400f940b3d71e0a529681b961999bcc3f
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,20 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
5
5
|
|
6
6
|
## [Unreleased] - unreleased
|
7
|
+
### Added
|
8
|
+
- Variables! One can now doing things like `R1 = 100`, `R1 + 100`,
|
9
|
+
`R1 R2` and so forth. The name of a variable must be an uppercase letter
|
10
|
+
followed by 1 (or more) digit.
|
11
|
+
Assignment can be chained, that is you can write `R1 = R2 = 100`.
|
12
|
+
- A new section «What to do?» in the readme, as a TODO list.
|
13
|
+
|
14
|
+
### Modified
|
15
|
+
- Reworded the help screen
|
16
|
+
- Internal result of an evaluation is now a type
|
17
|
+
|
18
|
+
### Removed
|
19
|
+
- Removed the folder `documentation` and its content as it was useless.
|
20
|
+
|
7
21
|
|
8
22
|
## [0.0.5] - 2015-09-28
|
9
23
|
### Added
|
data/README.md
CHANGED
@@ -23,12 +23,6 @@ proof of concept. And then, maybe, a more portable version in C.
|
|
23
23
|
Tell me what you think on [twitter](https://twitter.com/lkdjiin) or better,
|
24
24
|
open an issue here on Github. In any cases feel free to start a discussion.
|
25
25
|
|
26
|
-
The current version is an early one:
|
27
|
-
|
28
|
-
1. Don't expect too much
|
29
|
-
2. Expect a lot of bugs
|
30
|
-
3. Please be kind enough to report any bugs here in Github
|
31
|
-
|
32
26
|
## Installation
|
33
27
|
|
34
28
|
Install it with:
|
@@ -54,7 +48,7 @@ switch:
|
|
54
48
|
120
|
55
49
|
|
56
50
|
To display the AST instead of doing the computation, use the `--ast` switch
|
57
|
-
(*this is intended only for the developers*):
|
51
|
+
(*this is normally intended only for the developers*):
|
58
52
|
|
59
53
|
$ electr --ast -e "3V / 25mA"
|
60
54
|
ast
|
@@ -63,18 +57,15 @@ To display the AST instead of doing the computation, use the `--ast` switch
|
|
63
57
|
value ::= 3V
|
64
58
|
value ::= 25mA
|
65
59
|
|
66
|
-
###
|
60
|
+
### Some simple computations
|
67
61
|
|
68
|
-
|
69
|
-
a 200 Ohm resistor (200R):
|
62
|
+
We have a 10,000 Ohm resistor (10k) and a 200 Ohm resistor (200R):
|
70
63
|
|
71
64
|
E> 10k + 200R
|
72
65
|
10200
|
73
66
|
|
74
67
|
*Should it be `K`, `k`, `kΩ` or the three is still open to debate.*
|
75
68
|
|
76
|
-
### Ohm's law
|
77
|
-
|
78
69
|
Divide Volts (V) by milliamps (mA) to get some Ohms:
|
79
70
|
|
80
71
|
E> 3V / 25mA
|
@@ -85,6 +76,8 @@ There is no symbol for the multiplication. Simply put values side by side:
|
|
85
76
|
E> 1mA 3k
|
86
77
|
3
|
87
78
|
|
79
|
+
Actually you *can* use the `*` for the multiplication if you really want to ;)
|
80
|
+
|
88
81
|
### Frequency of an oscillator
|
89
82
|
|
90
83
|
A little bit more complex is the computation of a frequency for an oscillator.
|
@@ -95,6 +88,28 @@ is in Hertz.
|
|
95
88
|
E> 1 / (2 pi 0.5uF sqrt(11k 22k))
|
96
89
|
20.4617344581
|
97
90
|
|
91
|
+
### Variables
|
92
|
+
|
93
|
+
A variable name must be an uppercase letter followed by a digit or more:
|
94
|
+
|
95
|
+
R1 = 22k
|
96
|
+
|
97
|
+
The same formula as above can be written using variables:
|
98
|
+
|
99
|
+
E> C1 = 0.5uF
|
100
|
+
E> R1 = 11k
|
101
|
+
E> R2 = 22k
|
102
|
+
E> 1 / (2 pi C1 sqrt(R1 R2))
|
103
|
+
20.4617344581
|
104
|
+
|
105
|
+
Assignments can be chained:
|
106
|
+
|
107
|
+
E> R1 = R2 = R3 = 100
|
108
|
+
E> R3
|
109
|
+
100
|
110
|
+
E> R1 + R2 + R3
|
111
|
+
300
|
112
|
+
|
98
113
|
### Units, Prefixes and Abbreviations
|
99
114
|
|
100
115
|
Electr knows the following units:
|
@@ -133,26 +148,11 @@ n | nano farad
|
|
133
148
|
p | pico farad
|
134
149
|
k K | kilo ohm
|
135
150
|
|
136
|
-
### What is missing?
|
137
|
-
|
138
|
-
Electr is at a very early stage and it miss a lot of (basic) things!
|
139
|
-
You can expect that the following features will be implemented in the
|
140
|
-
next couple of days/weeks:
|
141
|
-
|
142
|
-
- [x] Negative numbers
|
143
|
-
- [x] Floating point number without a leading zero (ie `.678`)
|
144
|
-
- [x] 10_000 or 10,000 will be the same as 10000
|
145
|
-
- [x] More builtin functions (sin, cos, tan)
|
146
|
-
- [x] Exponent
|
147
|
-
- [x] Readline lib in the REPL for a better user experience
|
148
|
-
- [x] All units and prefix used in electronic
|
149
|
-
- [x] `*` for the multiplication if one want to
|
150
|
-
- [x] √ for an alternative to square root
|
151
|
-
- [ ] Shortcuts for function's names (ie sq and sqr for sqrt)
|
152
|
-
|
153
151
|
## What's next?
|
154
152
|
|
155
|
-
|
153
|
+
Here are some features I would like to implement soon.
|
154
|
+
|
155
|
+
Electr could infer the resulting unit:
|
156
156
|
|
157
157
|
E> 10k + 200R
|
158
158
|
10.2kΩ
|
@@ -161,8 +161,8 @@ Maybe Electr could infer the resulting unit:
|
|
161
161
|
E> 3V / 1mA
|
162
162
|
3kΩ
|
163
163
|
|
164
|
-
One are less prone to typing errors (less parenthesis) if one enter a
|
165
|
-
expression on two lines:
|
164
|
+
One are less prone to typing errors (less parenthesis) if one could enter a
|
165
|
+
complex expression on two lines:
|
166
166
|
|
167
167
|
E> 1 /
|
168
168
|
E> 2 pi 0.5uF sq(11K 22K)
|
@@ -181,14 +181,6 @@ ask us for the components values, then gives us the result.
|
|
181
181
|
C2=?> 470pF
|
182
182
|
7.04kHz
|
183
183
|
|
184
|
-
Or acts like a tiny programming language.
|
185
|
-
|
186
|
-
E> R1 = 33k
|
187
|
-
E> C1 = 1000pF
|
188
|
-
E> C2 = 470pF
|
189
|
-
E> 1 / (2 pi R1 sq(C1 C2))
|
190
|
-
7.04kHz
|
191
|
-
|
192
184
|
Why not having custom functions?
|
193
185
|
|
194
186
|
E> func = { 1 / (2 pi R1 sq(C1 C2)) }
|
@@ -197,18 +189,8 @@ Why not having custom functions?
|
|
197
189
|
|
198
190
|
*The above syntax is just one possibility amongst a lot of others.*
|
199
191
|
|
200
|
-
## Contributing
|
201
|
-
|
202
|
-
1. Fork it ( https://github.com/lkdjiin/electr/fork )
|
203
|
-
2. **PLEASE Create your feature branch** (`git checkout -b my-new-feature`)
|
204
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
205
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
206
|
-
5. Create a new Pull Request
|
207
|
-
|
208
192
|
## Alternatives
|
209
193
|
|
210
|
-
Some people point me to two existing softwares:
|
211
|
-
|
212
194
|
- [Frink](https://futureboy.us/frinkdocs/)
|
213
195
|
- [GNU Units](https://en.wikipedia.org/wiki/GNU_Units)
|
214
196
|
|
@@ -220,3 +202,48 @@ furlongs ;)
|
|
220
202
|
## License
|
221
203
|
|
222
204
|
[Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
205
|
+
|
206
|
+
## Contributing
|
207
|
+
|
208
|
+
### Good
|
209
|
+
|
210
|
+
1. Fork it ( https://github.com/lkdjiin/electr/fork )
|
211
|
+
2. **PLEASE Create your feature branch** (`git checkout -b my-new-feature`)
|
212
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
213
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
214
|
+
5. Create a new Pull Request
|
215
|
+
|
216
|
+
### Better
|
217
|
+
|
218
|
+
Before anything else, please open an issue to say what you are intended to do.
|
219
|
+
|
220
|
+
### What to do?
|
221
|
+
|
222
|
+
You want to help but you don't know where to start? Here are some ideas.
|
223
|
+
The list is more or less sorted. But feel free to pick any item you want. If
|
224
|
+
an item is marked as *pending* and you want to work on it, please first contact
|
225
|
+
me.
|
226
|
+
|
227
|
+
- [ ] Display a nice message on any error (not an abrupt fail as it is for now)
|
228
|
+
- [ ] Add interactivity (see «Beyond the calculator» section in this readme)
|
229
|
+
- [ ] Reply with a unit (*pending*)
|
230
|
+
- [ ] Write 10mA as well as 10 mA
|
231
|
+
- [ ] Use `;` to separate expressions (useful for command line option -e)
|
232
|
+
- [ ] Underscore value. The `_` refers to the last computed value.
|
233
|
+
- [ ] Add autocompletion with the readline library. For example see http://bogojoker.com/readline/
|
234
|
+
- [ ] `foo()` should display an error message like "Error: Undefined function 'foo'"
|
235
|
+
- [ ] BUG Unknown function `foo(2)` evaluate to 2. Instead it must report an error
|
236
|
+
- [ ] A command to quit, instead of Ctrl-C. Should it be `quit`, `exit`, `quit()`, `exit()`?
|
237
|
+
- [ ] Is there any benefits to have the @name attribute of an AST node as a symbol instead of a string?
|
238
|
+
- [ ] Be sure that the AST can be unparsed (prove it, do it)
|
239
|
+
- [ ] Simplify the sequences of multiplication in the AST
|
240
|
+
- [ ] Quit gracefuly with Ctrl+d
|
241
|
+
|
242
|
+
Here are some ideas to experiment:
|
243
|
+
|
244
|
+
- exponent shortcuts `^^` for `^ 2`, `^^^` for `^ 3`, etc
|
245
|
+
- Give multiplication an higher precedence than division: it can remove the need for parenthesis very often.
|
246
|
+
- Compute Electr code from a source file
|
247
|
+
- Give a try to travelling ruby (http://phusion.github.io/traveling-ruby/). It
|
248
|
+
could be very handy for people without knowledge of Ruby.
|
249
|
+
- Colors in the console!
|
data/bin/electr
CHANGED
@@ -16,11 +16,7 @@ if opt[:expression]
|
|
16
16
|
temp = Electr::Compiler.compile_to_pn(opt[:expression])
|
17
17
|
evaluator = Electr::Evaluator.new
|
18
18
|
temp = evaluator.evaluate_pn(temp)
|
19
|
-
|
20
|
-
puts temp.truncate
|
21
|
-
else
|
22
|
-
puts temp.round(10)
|
23
|
-
end
|
19
|
+
Electr::Printer.run(temp)
|
24
20
|
end
|
25
21
|
else
|
26
22
|
if opt[:ast]
|
data/electr.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Electr::VERSION
|
9
9
|
spec.authors = ["lkdjiin"]
|
10
10
|
spec.email = ["xavier.nayrac@gmail.com"]
|
11
|
-
spec.summary = %q{Interactive language for electronic formulas}
|
11
|
+
spec.summary = %q{Interactive tiny language for electronic formulas}
|
12
12
|
spec.homepage = "https://github.com/lkdjiin/electr"
|
13
13
|
spec.license = "MIT"
|
14
14
|
|
data/lib/electr.rb
CHANGED
@@ -25,15 +25,16 @@ module Electr
|
|
25
25
|
}
|
26
26
|
|
27
27
|
PRECEDENCE = {
|
28
|
-
'()' => {assoc: 'L', val:
|
29
|
-
')' => {assoc: 'L', val:
|
30
|
-
'(' => {assoc: 'L', val:
|
31
|
-
'^' => {assoc: 'L', val:
|
32
|
-
UNARY_MINUS_INTERNAL_SYMBOL => {assoc: 'R', val:
|
33
|
-
'*' => {assoc: 'L', val:
|
34
|
-
'/' => {assoc: 'L', val:
|
35
|
-
'-' => {assoc: 'L', val:
|
36
|
-
'+' => {assoc: 'L', val:
|
28
|
+
'()' => {assoc: 'L', val: 1000},
|
29
|
+
')' => {assoc: 'L', val: 1000},
|
30
|
+
'(' => {assoc: 'L', val: 1000},
|
31
|
+
'^' => {assoc: 'L', val: 900},
|
32
|
+
UNARY_MINUS_INTERNAL_SYMBOL => {assoc: 'R', val: 800},
|
33
|
+
'*' => {assoc: 'L', val: 100},
|
34
|
+
'/' => {assoc: 'L', val: 100},
|
35
|
+
'-' => {assoc: 'L', val: 90},
|
36
|
+
'+' => {assoc: 'L', val: 90},
|
37
|
+
'=' => {assoc: 'R', val: 10},
|
37
38
|
}
|
38
39
|
|
39
40
|
UNITS = %w( A Hz W C V F R Ω S ℧ H )
|
data/lib/electr/ast.rb
CHANGED
data/lib/electr/ast/ast.rb
CHANGED
data/lib/electr/compiler.rb
CHANGED
data/lib/electr/exceptions.rb
CHANGED
data/lib/electr/option.rb
CHANGED
@@ -43,10 +43,10 @@ module Electr
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def parse(opts)
|
46
|
-
opts.on(nil, '--ast', 'Display AST
|
46
|
+
opts.on(nil, '--ast', 'Display AST instead of actual computation') do
|
47
47
|
@options[:ast] = true
|
48
48
|
end
|
49
|
-
opts.on('-e', '--expression EXP', 'Compute EXP and
|
49
|
+
opts.on('-e', '--expression EXP', 'Compute EXP and exit') do |arg|
|
50
50
|
@options[:expression] = arg
|
51
51
|
end
|
52
52
|
opts.on('-v', '--version', 'Print version number and exit') do
|
data/lib/electr/parser/lexer.rb
CHANGED
@@ -16,8 +16,10 @@ module Electr
|
|
16
16
|
when ->(x) { x.operator? } then LexicalUnit.operator(token)
|
17
17
|
when ->(x) { x.constant? } then LexicalUnit.constant(token)
|
18
18
|
when ->(x) { x.value? } then LexicalUnit.value(token)
|
19
|
+
when ->(x) { x.variable? } then LexicalUnit.variable(token)
|
19
20
|
when ->(x) { x == '(' } then LexicalUnit.open_parenthesis
|
20
21
|
when ->(x) { x == ')' } then LexicalUnit.closed_parenthesis
|
22
|
+
when ->(x) { x == '=' } then LexicalUnit.assign
|
21
23
|
when ->(x) { SYMBOL_TABLE[x] == 'f' } then LexicalUnit.fname(token)
|
22
24
|
else LexicalUnit.name(token)
|
23
25
|
end
|
@@ -23,8 +23,10 @@ module Electr
|
|
23
23
|
def self.name(value) ; new(:name, value) ; end
|
24
24
|
def self.fname(value) ; new(:fname, value) ; end
|
25
25
|
def self.fcall(value) ; new(:fcall, value) ; end
|
26
|
+
def self.variable(value) ; new(:variable, value) ; end
|
26
27
|
def self.open_parenthesis ; new(:open_parenthesis, "(") ; end
|
27
28
|
def self.closed_parenthesis ; new(:closed_parenthesis, ")") ; end
|
29
|
+
def self.assign ; new(:assign, "=") ; end
|
28
30
|
|
29
31
|
def ==(other)
|
30
32
|
@type == other.type && @value == other.value
|
@@ -66,5 +68,12 @@ module Electr
|
|
66
68
|
@type == :open_parenthesis
|
67
69
|
end
|
68
70
|
|
71
|
+
def assign?
|
72
|
+
@type == :assign
|
73
|
+
end
|
74
|
+
|
75
|
+
def variable?
|
76
|
+
@type == :variable
|
77
|
+
end
|
69
78
|
end
|
70
79
|
end
|
@@ -27,6 +27,10 @@ module Electr
|
|
27
27
|
dig_series(@ast_node)
|
28
28
|
else
|
29
29
|
unit = @series.shift
|
30
|
+
# If we want to handle = differently than other operators, we
|
31
|
+
# can do something like the following:
|
32
|
+
# node_class = unit.assign? ? AssignAST : OperatorAST
|
33
|
+
# node = node_class.new(unit.value)
|
30
34
|
node = OperatorAST.new(unit.value)
|
31
35
|
dig_series(node) while @series.size > 0
|
32
36
|
@ast_node.add_child(node)
|
@@ -37,7 +41,9 @@ module Electr
|
|
37
41
|
|
38
42
|
def number?
|
39
43
|
first = @series.first
|
40
|
-
|
44
|
+
# Is it make sense to say all this things are «numbers»?
|
45
|
+
first.numeric? || first.constant? || first.value? || first.fname? ||
|
46
|
+
first.variable?
|
41
47
|
end
|
42
48
|
|
43
49
|
def first_unit_fname?
|
@@ -46,12 +52,16 @@ module Electr
|
|
46
52
|
|
47
53
|
def dig_series(node)
|
48
54
|
while unit = @series.shift
|
55
|
+
|
49
56
|
if unit && unit.numeric? && there_is_room?(node)
|
50
57
|
node.add_child(NumericAST.new(unit.value))
|
58
|
+
|
51
59
|
elsif unit && unit.constant? && there_is_room?(node)
|
52
60
|
node.add_child(ConstantAST.new(unit.value))
|
61
|
+
|
53
62
|
elsif unit && unit.value? && there_is_room?(node)
|
54
63
|
node.add_child(ValueAST.new(unit.value))
|
64
|
+
|
55
65
|
elsif unit && unit.fname? && there_is_room?(node)
|
56
66
|
func = FuncAST.new
|
57
67
|
func_name = FuncNameAST.new(unit.value)
|
@@ -60,10 +70,15 @@ module Electr
|
|
60
70
|
func.add_child(func_name)
|
61
71
|
func.add_child(func_args)
|
62
72
|
node.add_child(func)
|
63
|
-
|
73
|
+
|
74
|
+
elsif unit && (unit.operator? || unit.assign?)
|
64
75
|
new_node = OperatorAST.new(unit.value)
|
65
76
|
dig_series(new_node)
|
66
77
|
node.add_child(new_node)
|
78
|
+
|
79
|
+
elsif unit && unit.variable?
|
80
|
+
node.add_child(VariableAST.new(unit.value))
|
81
|
+
|
67
82
|
else
|
68
83
|
@series.unshift(unit)
|
69
84
|
break
|
data/lib/electr/parser/sya.rb
CHANGED
@@ -4,6 +4,8 @@ module Electr
|
|
4
4
|
#
|
5
5
|
# It produces prefix notation instead of the most common reverse
|
6
6
|
# polish notation to ease producing the AST.
|
7
|
+
#
|
8
|
+
# For a starter, see https://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
7
9
|
class Sya
|
8
10
|
|
9
11
|
# Creates a new Sya.
|
@@ -19,10 +21,10 @@ module Electr
|
|
19
21
|
|
20
22
|
# Public: Run the Shunting Yard Algorithm.
|
21
23
|
#
|
22
|
-
# Returns Array of LexicalUnit ordered
|
24
|
+
# Returns Array of LexicalUnit ordered in prefix notation.
|
23
25
|
def run
|
24
26
|
while unit = @units.pop
|
25
|
-
if unit.number?
|
27
|
+
if unit.number? || unit.variable?
|
26
28
|
@output.push(unit)
|
27
29
|
elsif unit.fname?
|
28
30
|
@operator.push(unit)
|
@@ -33,19 +35,17 @@ module Electr
|
|
33
35
|
@operator.last.type != :closed_parenthesis
|
34
36
|
@output.push(@operator.pop)
|
35
37
|
end
|
36
|
-
|
38
|
+
@operator.pop # Pop the right parenthesis.
|
37
39
|
if @operator.last && @operator.last.type == :fname
|
38
40
|
@output.push(@operator.pop)
|
39
41
|
end
|
40
42
|
# If the stack runs out without finding a right parenthesis,
|
41
43
|
# then there are mismatched parentheses.
|
42
|
-
elsif unit.operator?
|
43
|
-
|
44
|
-
|
45
|
-
(precedence(unit) < precedence(@operator.last))
|
46
|
-
@output.push(@operator.pop)
|
47
|
-
end
|
44
|
+
elsif unit.operator? || unit.assign?
|
45
|
+
|
46
|
+
@output.push(@operator.pop) while rules_for_operator_are_met(unit)
|
48
47
|
@operator.push(unit)
|
48
|
+
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -56,6 +56,39 @@ module Electr
|
|
56
56
|
|
57
57
|
private
|
58
58
|
|
59
|
+
def rules_for_operator_are_met(unit)
|
60
|
+
size_rule && (operator_rule && associative_rule(unit))
|
61
|
+
end
|
62
|
+
|
63
|
+
def size_rule
|
64
|
+
@operator.size > 0
|
65
|
+
end
|
66
|
+
|
67
|
+
def operator_rule
|
68
|
+
test = @operator.last
|
69
|
+
test.operator? || test.fname? || test.assign?
|
70
|
+
end
|
71
|
+
|
72
|
+
def associative_rule(test)
|
73
|
+
left_associative_rule(test) || right_associative_rule(test)
|
74
|
+
end
|
75
|
+
|
76
|
+
def left_associative_rule(test)
|
77
|
+
left_associative(test) && left_assoc_precedence_rule(test)
|
78
|
+
end
|
79
|
+
|
80
|
+
def left_assoc_precedence_rule(test)
|
81
|
+
precedence(test) < precedence(@operator.last)
|
82
|
+
end
|
83
|
+
|
84
|
+
def right_associative_rule(test)
|
85
|
+
right_associative(test) && right_assoc_precedence_rule(test)
|
86
|
+
end
|
87
|
+
|
88
|
+
def right_assoc_precedence_rule(test)
|
89
|
+
precedence(test) <= precedence(@operator.last)
|
90
|
+
end
|
91
|
+
|
59
92
|
# Look up the precedence of an operator.
|
60
93
|
#
|
61
94
|
# operator - LexicalUnit operator.
|
@@ -69,6 +102,32 @@ module Electr
|
|
69
102
|
end
|
70
103
|
end
|
71
104
|
|
105
|
+
# Check the left associativity of an operator.
|
106
|
+
#
|
107
|
+
# operator - LexicalUnit operator.
|
108
|
+
#
|
109
|
+
# Returns true if the operator is left associative, false otherwise.
|
110
|
+
def left_associative(operator)
|
111
|
+
if operator.fname?
|
112
|
+
PRECEDENCE['()'][:assoc] == 'L'
|
113
|
+
else
|
114
|
+
PRECEDENCE[operator.value][:assoc] == 'L'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Check the right associativity of an operator.
|
119
|
+
#
|
120
|
+
# operator - LexicalUnit operator.
|
121
|
+
#
|
122
|
+
# Returns true if the operator is right associative, false otherwise.
|
123
|
+
def right_associative(operator)
|
124
|
+
if operator.fname?
|
125
|
+
PRECEDENCE['()'][:assoc] == 'R'
|
126
|
+
else
|
127
|
+
PRECEDENCE[operator.value][:assoc] == 'R'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
72
131
|
# Insert the multiplication operator (`*`) where it is needed inside
|
73
132
|
# the @units member. This is because in Electr the `*` is implicit.
|
74
133
|
#
|
@@ -90,12 +149,12 @@ module Electr
|
|
90
149
|
end
|
91
150
|
|
92
151
|
def maybe_insertion_needed?(unit)
|
93
|
-
unit_ahead && unit.number?
|
152
|
+
unit_ahead && (unit.number? || unit.variable?)
|
94
153
|
end
|
95
154
|
|
96
155
|
def insertion_needed?
|
97
156
|
unit_ahead.number? || unit_ahead.open_parenthesis? ||
|
98
|
-
unit_ahead.fname? || unit_ahead.unary_minus?
|
157
|
+
unit_ahead.fname? || unit_ahead.unary_minus? || unit_ahead.variable?
|
99
158
|
end
|
100
159
|
|
101
160
|
def unit_ahead
|