electr 0.0.4 → 0.0.5
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 +11 -0
- data/README.md +40 -19
- data/documentation/precedence.md +2 -0
- data/lib/electr.rb +12 -1
- data/lib/electr/option.rb +1 -1
- data/lib/electr/parser.rb +1 -0
- data/lib/electr/parser/lexical_unit.rb +1 -1
- data/lib/electr/parser/token.rb +1 -1
- data/lib/electr/parser/tokenizer.rb +5 -2
- data/lib/electr/parser/value.rb +64 -0
- data/lib/electr/repl/eval_function.rb +1 -1
- data/lib/electr/repl/repl.rb +1 -1
- data/lib/electr/version.rb +2 -1
- data/spec/parser/lexer_spec.rb +24 -0
- data/spec/parser/tokenizer_spec.rb +36 -0
- data/spec/parser/value_spec.rb +49 -0
- data/spec/repl/evaluator_spec.rb +4 -2
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86d47b60ef5f815792290a73257593f50f6819a1
|
4
|
+
data.tar.gz: 638a91fb8edea30cf2c782a972c21591efea09f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: abdf7b5b3f6282134294825cfeeb277d5d2116573f90aa50e1fb4e836ca9b9d99dcee00fccee571225631f83a3e86e856f70c9a091151edcd304355c8b391354
|
7
|
+
data.tar.gz: 745e6f0654ab7fea487954ebd0f7443ac18471f89a084f7dca7860c2d332c6276b8d794899c96f951fe9b5b3f0fc3ebdedb077d45c681d43b044cd90ff2c07e8
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,17 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
5
5
|
|
6
6
|
## [Unreleased] - unreleased
|
7
7
|
|
8
|
+
## [0.0.5] - 2015-09-28
|
9
|
+
### Added
|
10
|
+
- All units and prefix used in electronic (sort of)
|
11
|
+
- One can use * for a multiplication as well as no symbol
|
12
|
+
- √ as an alias of sqrt
|
13
|
+
- The version now has a codename. The current one is «Units»
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
- A bug where some illegal tokens produced an infinite loop
|
17
|
+
|
18
|
+
|
8
19
|
## [0.0.4] - 2015-09-09
|
9
20
|
### Added
|
10
21
|
- Exponent operator: ^
|
data/README.md
CHANGED
@@ -95,22 +95,43 @@ is in Hertz.
|
|
95
95
|
E> 1 / (2 pi 0.5uF sqrt(11k 22k))
|
96
96
|
20.4617344581
|
97
97
|
|
98
|
-
### Units
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
Ω R
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
98
|
+
### Units, Prefixes and Abbreviations
|
99
|
+
|
100
|
+
Electr knows the following units:
|
101
|
+
|
102
|
+
Symbol | Name
|
103
|
+
:-----: | -----
|
104
|
+
A | ampere
|
105
|
+
Ω R | ohm
|
106
|
+
V | volt
|
107
|
+
W | watt
|
108
|
+
F | farad
|
109
|
+
Hz | hertz
|
110
|
+
C | coulomb
|
111
|
+
S ℧ | siemens
|
112
|
+
H | henry
|
113
|
+
|
114
|
+
Units can be combined with the following prefixes:
|
115
|
+
|
116
|
+
Symbol | Name
|
117
|
+
:-----: | -----
|
118
|
+
k | kilo
|
119
|
+
M | mega
|
120
|
+
G | giga
|
121
|
+
T | tera
|
122
|
+
m | milli
|
123
|
+
μ u | micro
|
124
|
+
n | nano
|
125
|
+
p | pico
|
126
|
+
|
127
|
+
There is also the following abbreviations:
|
128
|
+
|
129
|
+
Symbol | Name
|
130
|
+
:-----: | -----
|
131
|
+
u μ | micro farad
|
132
|
+
n | nano farad
|
133
|
+
p | pico farad
|
134
|
+
k K | kilo ohm
|
114
135
|
|
115
136
|
### What is missing?
|
116
137
|
|
@@ -124,10 +145,10 @@ next couple of days/weeks:
|
|
124
145
|
- [x] More builtin functions (sin, cos, tan)
|
125
146
|
- [x] Exponent
|
126
147
|
- [x] Readline lib in the REPL for a better user experience
|
127
|
-
- [
|
128
|
-
- [
|
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
|
129
151
|
- [ ] Shortcuts for function's names (ie sq and sqr for sqrt)
|
130
|
-
- [ ] All units and prefix used in electronic
|
131
152
|
|
132
153
|
## What's next?
|
133
154
|
|
data/documentation/precedence.md
CHANGED
data/lib/electr.rb
CHANGED
@@ -13,11 +13,12 @@ module Electr
|
|
13
13
|
|
14
14
|
UNARY_MINUS_INTERNAL_SYMBOL = '€'
|
15
15
|
|
16
|
-
ONE_CHAR_OPERATORS = %w( + - / ^ )
|
16
|
+
ONE_CHAR_OPERATORS = %w( + - * / ^ )
|
17
17
|
|
18
18
|
# f - function
|
19
19
|
SYMBOL_TABLE = {
|
20
20
|
'sqrt' => 'f',
|
21
|
+
'√' => 'f',
|
21
22
|
'sin' => 'f',
|
22
23
|
'cos' => 'f',
|
23
24
|
'tan' => 'f',
|
@@ -35,4 +36,14 @@ module Electr
|
|
35
36
|
'+' => {assoc: 'L', val: 1},
|
36
37
|
}
|
37
38
|
|
39
|
+
UNITS = %w( A Hz W C V F R Ω S ℧ H )
|
40
|
+
|
41
|
+
PREFIXES = %w( k M G T m μ u n p )
|
42
|
+
|
43
|
+
# u μ - micro farad
|
44
|
+
# n - nano farad
|
45
|
+
# p - pico farad
|
46
|
+
# k K - kilo ohm
|
47
|
+
ABBREVIATIONS = %w( u μ n p k K )
|
48
|
+
|
38
49
|
end
|
data/lib/electr/option.rb
CHANGED
data/lib/electr/parser.rb
CHANGED
@@ -19,7 +19,7 @@ module Electr
|
|
19
19
|
def self.numeric(value) ; new(:numeric, value) ; end
|
20
20
|
def self.operator(value) ; new(:operator, value) ; end
|
21
21
|
def self.constant(value) ; new(:constant, value) ; end
|
22
|
-
def self.value(value) ; new(:value, value)
|
22
|
+
def self.value(value) ; new(:value, Value.assert(value)) ; end
|
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
|
data/lib/electr/parser/token.rb
CHANGED
@@ -94,13 +94,16 @@ module Electr
|
|
94
94
|
add_look_ahead if @look_ahead == '-'
|
95
95
|
add_look_ahead while @look_ahead =~ /[0-9._,]/
|
96
96
|
if @token[-1] != '.'
|
97
|
-
add_look_ahead while @look_ahead =~ /[
|
97
|
+
add_look_ahead while @look_ahead =~ /[A-Za-zΩ℧μ]/
|
98
98
|
end
|
99
99
|
@token
|
100
100
|
end
|
101
101
|
|
102
102
|
def get_word
|
103
|
-
add_look_ahead while @look_ahead =~
|
103
|
+
add_look_ahead while @look_ahead =~ /[\w√]/
|
104
|
+
# At this step, an empty token signify that we have reached an
|
105
|
+
# illegal word.
|
106
|
+
raise(SyntaxError, @codeline) if @token.empty?
|
104
107
|
@token
|
105
108
|
end
|
106
109
|
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Electr
|
2
|
+
|
3
|
+
class LexicalUnit::Value
|
4
|
+
|
5
|
+
def self.assert(value)
|
6
|
+
new(value).assert
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(value)
|
10
|
+
@value = value
|
11
|
+
@unit = nil
|
12
|
+
@prefix = nil
|
13
|
+
@abbreviation = false
|
14
|
+
find_unit_attributes
|
15
|
+
end
|
16
|
+
|
17
|
+
def assert
|
18
|
+
if good_unit?
|
19
|
+
@value
|
20
|
+
else
|
21
|
+
raise(SyntaxError, "Unknow unit: #@unit in #@value")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def find_unit_attributes
|
28
|
+
find_unit
|
29
|
+
maybe_split_unit
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_unit
|
33
|
+
index = 0
|
34
|
+
index += 1 while @value[index] =~ /[0-9._,-]/
|
35
|
+
@unit = @value[index..-1]
|
36
|
+
end
|
37
|
+
|
38
|
+
def maybe_split_unit
|
39
|
+
if abbreviation?
|
40
|
+
@abbreviation = true
|
41
|
+
elsif prefix?
|
42
|
+
fill_prefix_and_unit
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def abbreviation?
|
47
|
+
@unit.size == 1 && ABBREVIATIONS.include?(@unit)
|
48
|
+
end
|
49
|
+
|
50
|
+
def prefix?
|
51
|
+
PREFIXES.include?(@unit[0])
|
52
|
+
end
|
53
|
+
|
54
|
+
def fill_prefix_and_unit
|
55
|
+
@prefix = @unit[0]
|
56
|
+
@unit = @unit[1..-1]
|
57
|
+
end
|
58
|
+
|
59
|
+
def good_unit?
|
60
|
+
@abbreviation || UNITS.include?(@unit)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
data/lib/electr/repl/repl.rb
CHANGED
@@ -3,7 +3,7 @@ module Electr
|
|
3
3
|
# The Read Eval Print Loop starting point.
|
4
4
|
class Repl
|
5
5
|
|
6
|
-
BANNER = "\nElectr version #{
|
6
|
+
BANNER = "\nElectr version #{VERSION} (codename «#{CODENAME}»)\n" +
|
7
7
|
"A tiny language for electronic formulas\n\n" +
|
8
8
|
"Hit Ctrl+C to quit Electr\n\n"
|
9
9
|
|
data/lib/electr/version.rb
CHANGED
data/spec/parser/lexer_spec.rb
CHANGED
@@ -29,4 +29,28 @@ describe Lexer do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
describe "value" do
|
33
|
+
|
34
|
+
it "rejects unknown units" do
|
35
|
+
expect { Lexer.lexify("1P") }.to raise_error(Electr::SyntaxError)
|
36
|
+
expect { Lexer.lexify("1Q") }.to raise_error(Electr::SyntaxError)
|
37
|
+
expect { Lexer.lexify("1X") }.to raise_error(Electr::SyntaxError)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "rejects unknown prefixes" do
|
41
|
+
expect { Lexer.lexify("1bA") }.to raise_error(Electr::SyntaxError)
|
42
|
+
expect { Lexer.lexify("1wW") }.to raise_error(Electr::SyntaxError)
|
43
|
+
expect { Lexer.lexify("1jHz") }.to raise_error(Electr::SyntaxError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "accepts known units" do
|
47
|
+
expect { Lexer.lexify("1A") }.not_to raise_error
|
48
|
+
end
|
49
|
+
|
50
|
+
it "accepts known prefixes" do
|
51
|
+
expect { Lexer.lexify("1mA") }.not_to raise_error
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
32
56
|
end
|
@@ -52,6 +52,13 @@ describe Tokenizer do
|
|
52
52
|
expect(tkr.next_token).to eq "300"
|
53
53
|
end
|
54
54
|
|
55
|
+
specify "2 * 3" do
|
56
|
+
tkr = Tokenizer.new("2 * 3")
|
57
|
+
expect(tkr.next_token).to eq "2"
|
58
|
+
expect(tkr.next_token).to eq "*"
|
59
|
+
expect(tkr.next_token).to eq "3"
|
60
|
+
end
|
61
|
+
|
55
62
|
specify "2 0.6" do
|
56
63
|
tkr = Tokenizer.new("2 0.6")
|
57
64
|
expect(tkr.next_token).to eq "2"
|
@@ -125,6 +132,11 @@ describe Tokenizer do
|
|
125
132
|
expect(tkr.next_token).to eq "sqrt"
|
126
133
|
end
|
127
134
|
|
135
|
+
specify "√(49)" do
|
136
|
+
tkr = Tokenizer.new("√(49)")
|
137
|
+
expect(tkr.next_token).to eq "√"
|
138
|
+
end
|
139
|
+
|
128
140
|
specify "11K 22k 33kΩ" do
|
129
141
|
tkr = Tokenizer.new("11K 22k 33kΩ")
|
130
142
|
expect(tkr.next_token).to eq "11K"
|
@@ -195,4 +207,28 @@ describe Tokenizer do
|
|
195
207
|
expect(tkr.next_token).to eq "2"
|
196
208
|
end
|
197
209
|
|
210
|
+
it 'tokenize units and prefixes' do
|
211
|
+
units = %w( A Hz W C V F R Ω S ℧ H )
|
212
|
+
prefixes = %w( k M G T m μ u n p )
|
213
|
+
|
214
|
+
units.each do |unit|
|
215
|
+
tkr = Tokenizer.new("1#{unit}")
|
216
|
+
expect(tkr.next_token).to eq "1" + unit
|
217
|
+
end
|
218
|
+
|
219
|
+
prefixes.each do |prefix|
|
220
|
+
units.each do |unit|
|
221
|
+
tkr = Tokenizer.new("1#{prefix}#{unit}")
|
222
|
+
expect(tkr.next_token).to eq "1" + prefix + unit
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'raises on illegal token' do
|
228
|
+
tkr = Tokenizer.new("@")
|
229
|
+
expect {
|
230
|
+
tkr.next_token
|
231
|
+
}.to raise_error(Electr::SyntaxError)
|
232
|
+
end
|
233
|
+
|
198
234
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include Electr
|
4
|
+
|
5
|
+
describe LexicalUnit::Value do
|
6
|
+
|
7
|
+
describe "assert" do
|
8
|
+
|
9
|
+
it "rejects unknown units" do
|
10
|
+
unknown = %w( 1P 1Q 1X )
|
11
|
+
unknown.each do |unit|
|
12
|
+
expect {
|
13
|
+
LexicalUnit::Value.assert(unit)
|
14
|
+
}.to raise_error(Electr::SyntaxError)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "asserts valid units" do
|
19
|
+
known = %w( 1V 1C 1S )
|
20
|
+
known.each do |unit|
|
21
|
+
expect { LexicalUnit::Value.assert(unit) }.not_to raise_error
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "rejects unknown prefixes" do
|
26
|
+
unknown = %w( 1xV 1bV 1wW )
|
27
|
+
unknown.each do |unit|
|
28
|
+
expect {
|
29
|
+
LexicalUnit::Value.assert(unit)
|
30
|
+
}.to raise_error(Electr::SyntaxError)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "accepts valid prefixes" do
|
35
|
+
known = %w( 1mV 1μF 1nF )
|
36
|
+
known.each do |unit|
|
37
|
+
expect { LexicalUnit::Value.assert(unit) }.not_to raise_error
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "accepts unit abbreviations" do
|
42
|
+
abbreviations = %w( 1u 1μ 1n 1p 1k 1K )
|
43
|
+
abbreviations.each do |unit|
|
44
|
+
expect { LexicalUnit::Value.assert(unit) }.not_to raise_error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/spec/repl/evaluator_spec.rb
CHANGED
@@ -34,6 +34,8 @@ describe Evaluator do
|
|
34
34
|
{code: "sin(pi / 2) + 1", result: 2},
|
35
35
|
{code: "cos(0) - 1", result: 0},
|
36
36
|
{code: "tan(0) - 1", result: -1},
|
37
|
+
{code: "10 ^ 2", result: 100},
|
38
|
+
{code: "√(49)", result: 7},
|
37
39
|
]
|
38
40
|
|
39
41
|
specify "#evaluate_pn" do
|
@@ -48,12 +50,12 @@ describe Evaluator do
|
|
48
50
|
end
|
49
51
|
|
50
52
|
specify do
|
51
|
-
pns = Compiler.compile_to_pn("
|
53
|
+
pns = Compiler.compile_to_pn("2 * 3")
|
52
54
|
|
53
55
|
evaluator = Evaluator.new
|
54
56
|
result = evaluator.evaluate_pn(pns)
|
55
57
|
|
56
|
-
expect(result).to eq
|
58
|
+
expect(result).to eq 6
|
57
59
|
end
|
58
60
|
|
59
61
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: electr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- lkdjiin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09-
|
11
|
+
date: 2015-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- lib/electr/parser/syntaxer.rb
|
114
114
|
- lib/electr/parser/token.rb
|
115
115
|
- lib/electr/parser/tokenizer.rb
|
116
|
+
- lib/electr/parser/value.rb
|
116
117
|
- lib/electr/repl.rb
|
117
118
|
- lib/electr/repl/ast_printer.rb
|
118
119
|
- lib/electr/repl/ast_reader.rb
|
@@ -132,6 +133,7 @@ files:
|
|
132
133
|
- spec/parser/pn_spec.rb
|
133
134
|
- spec/parser/sya_spec.rb
|
134
135
|
- spec/parser/tokenizer_spec.rb
|
136
|
+
- spec/parser/value_spec.rb
|
135
137
|
- spec/repl/ast_printer_spec.rb
|
136
138
|
- spec/repl/ast_reader_spec.rb
|
137
139
|
- spec/repl/evaluator_spec.rb
|
@@ -173,6 +175,7 @@ test_files:
|
|
173
175
|
- spec/parser/pn_spec.rb
|
174
176
|
- spec/parser/sya_spec.rb
|
175
177
|
- spec/parser/tokenizer_spec.rb
|
178
|
+
- spec/parser/value_spec.rb
|
176
179
|
- spec/repl/ast_printer_spec.rb
|
177
180
|
- spec/repl/ast_reader_spec.rb
|
178
181
|
- spec/repl/evaluator_spec.rb
|