electr 0.0.5 → 0.0.6
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 +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
|