electr 0.0.2 → 0.0.3
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/.coco.yml +4 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +8 -0
- data/README.md +12 -13
- data/bin/electr +10 -5
- data/documentation/developer.md +21 -0
- data/documentation/grammar.md +34 -0
- data/documentation/precedence.md +8 -0
- data/electr.gemspec +1 -0
- data/lib/electr.rb +13 -2
- data/lib/electr/ast/ast.rb +24 -6
- data/lib/electr/ast/constant_ast.rb +1 -0
- data/lib/electr/ast/func_args_ast.rb +1 -0
- data/lib/electr/ast/func_ast.rb +1 -0
- data/lib/electr/ast/func_name_ast.rb +2 -0
- data/lib/electr/ast/numeric_ast.rb +1 -0
- data/lib/electr/ast/operator_ast.rb +1 -0
- data/lib/electr/ast/root_ast.rb +1 -0
- data/lib/electr/ast/value_ast.rb +1 -0
- data/lib/electr/compiler.rb +10 -6
- data/lib/electr/exceptions.rb +1 -0
- data/lib/electr/parser.rb +2 -0
- data/lib/electr/parser/lexer.rb +14 -37
- data/lib/electr/parser/lexical_unit.rb +7 -4
- data/lib/electr/{rules.rb → parser/rules.rb} +0 -0
- data/lib/electr/parser/rules/base_rule.rb +5 -13
- data/lib/electr/parser/rules/expression_rule.rb +18 -17
- data/lib/electr/parser/rules/root_rule.rb +5 -2
- data/lib/electr/parser/sya.rb +22 -21
- data/lib/electr/parser/syntaxer.rb +4 -1
- data/lib/electr/parser/token.rb +26 -0
- data/lib/electr/parser/tokenizer.rb +47 -7
- data/lib/electr/repl.rb +8 -56
- data/lib/electr/repl/ast_printer.rb +11 -0
- data/lib/electr/repl/ast_reader.rb +17 -0
- data/lib/electr/repl/base_reader.rb +23 -0
- data/lib/electr/{evaluator.rb → repl/evaluator.rb} +15 -1
- data/lib/electr/repl/nil_evaluator.rb +11 -0
- data/lib/electr/repl/printer.rb +12 -0
- data/lib/electr/repl/reader.rb +16 -0
- data/lib/electr/repl/repl.rb +28 -0
- data/lib/electr/version.rb +1 -1
- data/spec/ast/ast_spec.rb +17 -0
- data/spec/compiler_spec.rb +4 -6
- data/spec/parser/base_rule_spec.rb +13 -0
- data/spec/{lexer_spec.rb → parser/lexer_spec.rb} +5 -2
- data/spec/parser/pn_spec.rb +17 -0
- data/spec/{sya_spec.rb → parser/sya_spec.rb} +35 -0
- data/spec/{tokenizer_spec.rb → parser/tokenizer_spec.rb} +89 -0
- data/spec/repl/ast_printer_spec.rb +18 -0
- data/spec/repl/ast_reader_spec.rb +19 -0
- data/spec/{evaluator_spec.rb → repl/evaluator_spec.rb} +15 -5
- data/spec/repl/nil_evaluator_spec.rb +11 -0
- data/spec/repl/printer_spec.rb +19 -0
- data/spec/repl/reader_spec.rb +25 -0
- data/spec/repl/repl_spec.rb +32 -0
- data/spec/spec_helper.rb +1 -0
- metadata +56 -12
@@ -0,0 +1,17 @@
|
|
1
|
+
module Electr
|
2
|
+
|
3
|
+
# Special reader for a REPL that display an AST instead of the
|
4
|
+
# computation.
|
5
|
+
class ASTReader < BaseReader
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
super("E--ast> ")
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
prompt
|
13
|
+
Compiler.compile_to_ast(STDIN.gets.chomp)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Electr
|
2
|
+
|
3
|
+
# Responsible of the terminal interface, that is printing the prompt
|
4
|
+
# and reading a line of code (I know, I know, there is an «and» in the
|
5
|
+
# description…).
|
6
|
+
class BaseReader
|
7
|
+
|
8
|
+
def initialize(prompt = "No prompt defined> ")
|
9
|
+
@prompt = prompt
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
raise(NotImplementedError)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def prompt
|
19
|
+
print(@prompt)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Electr
|
2
2
|
|
3
|
+
# Evaluate (compute) a value.
|
3
4
|
class Evaluator
|
4
5
|
|
5
6
|
def initialize
|
@@ -9,7 +10,7 @@ module Electr
|
|
9
10
|
def evaluate_pn(list)
|
10
11
|
while item = list.pop
|
11
12
|
case item.name
|
12
|
-
when "numeric" then @stack.push(item.
|
13
|
+
when "numeric" then @stack.push(item.to_f)
|
13
14
|
when "constant" then @stack.push(constant(item.value))
|
14
15
|
when "value" then do_value(item.value)
|
15
16
|
when "operator" then operation(item.value)
|
@@ -37,6 +38,19 @@ module Electr
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def operation(operand)
|
41
|
+
if operand == UNARY_MINUS_INTERNAL_SYMBOL
|
42
|
+
unary_minus
|
43
|
+
else
|
44
|
+
classic_operation(operand)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def unary_minus
|
49
|
+
a = @stack.pop
|
50
|
+
@stack.push(-a)
|
51
|
+
end
|
52
|
+
|
53
|
+
def classic_operation(operand)
|
40
54
|
a = @stack.pop
|
41
55
|
b = @stack.pop
|
42
56
|
@stack.push(a.send(operand, b))
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Electr
|
2
|
+
|
3
|
+
# The Read Eval Print Loop starting point.
|
4
|
+
class Repl
|
5
|
+
|
6
|
+
BANNER = "\nElectr version #{Electr::VERSION}\n" +
|
7
|
+
"A tiny language for electronic formulas\n\n" +
|
8
|
+
"Hit Ctrl+C to quit Electr\n\n"
|
9
|
+
|
10
|
+
def initialize(reader = Reader.new,
|
11
|
+
evaluator = Evaluator.new,
|
12
|
+
printer = Printer)
|
13
|
+
@reader = reader
|
14
|
+
@eval = evaluator
|
15
|
+
@printer = printer
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
puts BANNER
|
20
|
+
run_once while true
|
21
|
+
end
|
22
|
+
|
23
|
+
def run_once
|
24
|
+
@printer.run(@eval.evaluate_pn(@reader.run))
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/electr/version.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include Electr
|
4
|
+
|
5
|
+
describe AST do
|
6
|
+
|
7
|
+
it 'has a name' do
|
8
|
+
node = AST.new('foobar')
|
9
|
+
expect(node.name).to eq 'foobar'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'has a default name' do
|
13
|
+
node = AST.new(nil)
|
14
|
+
expect(node.name).to eq 'AST'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/spec/compiler_spec.rb
CHANGED
@@ -16,26 +16,24 @@ describe Compiler do
|
|
16
16
|
"sqrt(40 + 9)",
|
17
17
|
"3 (2 + 1)",
|
18
18
|
"2 sqrt(3)",
|
19
|
+
"2 pi 0.5uF sqrt(11K 22K)",
|
19
20
|
]
|
20
21
|
|
21
22
|
specify "#compile_to_ast" do
|
22
23
|
CODES_TO_COMPILE.each do |code|
|
23
|
-
|
24
|
-
result = compiler.compile_to_ast(code)
|
24
|
+
result = Compiler.compile_to_ast(code)
|
25
25
|
expect(result).to be_a AST
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
specify do
|
30
|
-
|
31
|
-
result = compiler.compile_to_ast("2 pi 0.5uF sqrt(11K 22K)")
|
30
|
+
result = Compiler.compile_to_ast("-pi 2")
|
32
31
|
result.display
|
33
32
|
expect(result).to be_a AST
|
34
33
|
end
|
35
34
|
|
36
35
|
specify "#compile_to_pn" do
|
37
|
-
|
38
|
-
result = compiler.compile_to_pn("2 pi")
|
36
|
+
result = Compiler.compile_to_pn("2 pi")
|
39
37
|
expect(result).to be_a Array
|
40
38
|
end
|
41
39
|
|
@@ -3,12 +3,15 @@ require 'spec_helper'
|
|
3
3
|
include Electr
|
4
4
|
|
5
5
|
describe Lexer do
|
6
|
-
before { @lexer = Lexer.new }
|
7
6
|
|
8
7
|
TOKENS = [
|
9
8
|
[ :numeric, "123" ],
|
10
9
|
[ :numeric, "0.6" ],
|
10
|
+
[ :numeric, ".6" ],
|
11
11
|
[ :operator, "+" ],
|
12
|
+
[ :operator, "-" ],
|
13
|
+
[ :operator, "/" ],
|
14
|
+
[ :operator, UNARY_MINUS_INTERNAL_SYMBOL ],
|
12
15
|
[ :constant, "pi" ],
|
13
16
|
[ :value, "11K" ],
|
14
17
|
[ :value, "11kΩ" ],
|
@@ -20,7 +23,7 @@ describe Lexer do
|
|
20
23
|
|
21
24
|
it "lexify" do
|
22
25
|
TOKENS.each do |token|
|
23
|
-
lexeme =
|
26
|
+
lexeme = Lexer.lexify(token.last)
|
24
27
|
expect(lexeme.type).to eq token.first
|
25
28
|
expect(lexeme.value).to eq token.last
|
26
29
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include Electr
|
4
|
+
|
5
|
+
describe Pn do
|
6
|
+
|
7
|
+
describe '#to_f' do
|
8
|
+
|
9
|
+
it { expect(Pn.new('1000', 'n').to_f).to eq 1000.0 }
|
10
|
+
|
11
|
+
it { expect(Pn.new('1_000', 'n').to_f).to eq 1000.0 }
|
12
|
+
|
13
|
+
it { expect(Pn.new('1,000', 'n').to_f).to eq 1000.0 }
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -47,6 +47,17 @@ describe Sya do
|
|
47
47
|
expect(sya.run).to eq [added, a, b]
|
48
48
|
end
|
49
49
|
|
50
|
+
specify '2 -pi' do
|
51
|
+
a = LexicalUnit.numeric('2')
|
52
|
+
b = LexicalUnit.operator(UNARY_MINUS_INTERNAL_SYMBOL)
|
53
|
+
c = LexicalUnit.constant('pi')
|
54
|
+
added = LexicalUnit.operator('*')
|
55
|
+
sya = Sya.new([a, b, c])
|
56
|
+
|
57
|
+
# * 2 um pi
|
58
|
+
expect(sya.run).to eq [added, a, b, c]
|
59
|
+
end
|
60
|
+
|
50
61
|
specify '2 11K' do
|
51
62
|
a = LexicalUnit.numeric('2')
|
52
63
|
b = LexicalUnit.constant('11K')
|
@@ -150,4 +161,28 @@ describe Sya do
|
|
150
161
|
expect(sya.run).to eq [a, d, c, e]
|
151
162
|
end
|
152
163
|
|
164
|
+
specify 'um pi' do
|
165
|
+
a = LexicalUnit.operator(UNARY_MINUS_INTERNAL_SYMBOL)
|
166
|
+
b = LexicalUnit.constant('pi')
|
167
|
+
sya = Sya.new([a, b])
|
168
|
+
|
169
|
+
# Note that the symbol used internally isn't `-`.
|
170
|
+
# - pi
|
171
|
+
expect(sya.run).to eq [a, b]
|
172
|
+
end
|
173
|
+
|
174
|
+
specify 'um sqrt(49) + 1' do
|
175
|
+
a = LexicalUnit.operator(UNARY_MINUS_INTERNAL_SYMBOL)
|
176
|
+
b = LexicalUnit.fname('sqrt')
|
177
|
+
c = LexicalUnit.open_parenthesis
|
178
|
+
d = LexicalUnit.numeric('49')
|
179
|
+
e = LexicalUnit.closed_parenthesis
|
180
|
+
f = LexicalUnit.operator('+')
|
181
|
+
g = LexicalUnit.numeric('1')
|
182
|
+
sya = Sya.new([a, b, c, d, e, f, g])
|
183
|
+
|
184
|
+
# + um sqrt 49 1
|
185
|
+
expect(sya.run).to eq [f, a, b, d, g]
|
186
|
+
end
|
187
|
+
|
153
188
|
end
|
@@ -19,6 +19,33 @@ describe Tokenizer do
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
specify "21" do
|
23
|
+
tkr = Tokenizer.new("21")
|
24
|
+
expect(tkr.next_token).to eq "21"
|
25
|
+
end
|
26
|
+
|
27
|
+
specify "-21" do
|
28
|
+
tkr = Tokenizer.new("-21")
|
29
|
+
expect(tkr.next_token).to eq "-21"
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "-.1" do
|
33
|
+
tkr = Tokenizer.new("-.1")
|
34
|
+
expect(tkr.next_token).to eq "-.1"
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "- 21" do
|
38
|
+
tkr = Tokenizer.new("- 21")
|
39
|
+
expect(tkr.next_token).to eq "-"
|
40
|
+
expect(tkr.next_token).to eq "21"
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "- .1" do
|
44
|
+
tkr = Tokenizer.new("- .1")
|
45
|
+
expect(tkr.next_token).to eq "-"
|
46
|
+
expect(tkr.next_token).to eq ".1"
|
47
|
+
end
|
48
|
+
|
22
49
|
specify "21 300" do
|
23
50
|
tkr = Tokenizer.new("21 300")
|
24
51
|
expect(tkr.next_token).to eq "21"
|
@@ -31,6 +58,17 @@ describe Tokenizer do
|
|
31
58
|
expect(tkr.next_token).to eq "0.6"
|
32
59
|
end
|
33
60
|
|
61
|
+
specify ".6" do
|
62
|
+
tkr = Tokenizer.new(".6")
|
63
|
+
expect(tkr.next_token).to eq ".6"
|
64
|
+
end
|
65
|
+
|
66
|
+
specify "2 .6" do
|
67
|
+
tkr = Tokenizer.new("2 .6")
|
68
|
+
expect(tkr.next_token).to eq "2"
|
69
|
+
expect(tkr.next_token).to eq ".6"
|
70
|
+
end
|
71
|
+
|
34
72
|
specify "2 0.6 3" do
|
35
73
|
tkr = Tokenizer.new("2 0.6 3")
|
36
74
|
expect(tkr.next_token).to eq "2"
|
@@ -45,12 +83,48 @@ describe Tokenizer do
|
|
45
83
|
expect(tkr.next_token).to eq "3"
|
46
84
|
end
|
47
85
|
|
86
|
+
specify "2 + -3" do
|
87
|
+
tkr = Tokenizer.new("2 + -3")
|
88
|
+
expect(tkr.next_token).to eq "2"
|
89
|
+
expect(tkr.next_token).to eq "+"
|
90
|
+
expect(tkr.next_token).to eq "-3"
|
91
|
+
end
|
92
|
+
|
48
93
|
specify "2 pi" do
|
49
94
|
tkr = Tokenizer.new("2 pi")
|
50
95
|
expect(tkr.next_token).to eq "2"
|
51
96
|
expect(tkr.next_token).to eq "pi"
|
52
97
|
end
|
53
98
|
|
99
|
+
specify "pi" do
|
100
|
+
tkr = Tokenizer.new("pi")
|
101
|
+
expect(tkr.next_token).to eq "pi"
|
102
|
+
end
|
103
|
+
|
104
|
+
specify "-pi" do
|
105
|
+
tkr = Tokenizer.new("-pi")
|
106
|
+
expect(tkr.next_token).to eq UNARY_MINUS_INTERNAL_SYMBOL
|
107
|
+
expect(tkr.next_token).to eq "pi"
|
108
|
+
end
|
109
|
+
|
110
|
+
specify "2 -pi" do
|
111
|
+
tkr = Tokenizer.new("2 -pi")
|
112
|
+
expect(tkr.next_token).to eq "2"
|
113
|
+
expect(tkr.next_token).to eq UNARY_MINUS_INTERNAL_SYMBOL
|
114
|
+
expect(tkr.next_token).to eq "pi"
|
115
|
+
end
|
116
|
+
|
117
|
+
specify "-2V" do
|
118
|
+
tkr = Tokenizer.new("-2V")
|
119
|
+
expect(tkr.next_token).to eq "-2V"
|
120
|
+
end
|
121
|
+
|
122
|
+
specify "-sqrt(7)" do
|
123
|
+
tkr = Tokenizer.new("-sqrt(7)")
|
124
|
+
expect(tkr.next_token).to eq UNARY_MINUS_INTERNAL_SYMBOL
|
125
|
+
expect(tkr.next_token).to eq "sqrt"
|
126
|
+
end
|
127
|
+
|
54
128
|
specify "11K 22k 33kΩ" do
|
55
129
|
tkr = Tokenizer.new("11K 22k 33kΩ")
|
56
130
|
expect(tkr.next_token).to eq "11K"
|
@@ -99,4 +173,19 @@ describe Tokenizer do
|
|
99
173
|
expect(tkr.next_token).to eq ")"
|
100
174
|
end
|
101
175
|
|
176
|
+
specify "-(2 3)" do
|
177
|
+
tkr = Tokenizer.new("-(2 3)")
|
178
|
+
expect(tkr.next_token).to eq UNARY_MINUS_INTERNAL_SYMBOL
|
179
|
+
expect(tkr.next_token).to eq "("
|
180
|
+
expect(tkr.next_token).to eq "2"
|
181
|
+
expect(tkr.next_token).to eq "3"
|
182
|
+
expect(tkr.next_token).to eq ")"
|
183
|
+
end
|
184
|
+
|
185
|
+
specify "22_000_000 11,000" do
|
186
|
+
tkr = Tokenizer.new("22_000_000 11,000")
|
187
|
+
expect(tkr.next_token).to eq "22_000_000"
|
188
|
+
expect(tkr.next_token).to eq "11,000"
|
189
|
+
end
|
190
|
+
|
102
191
|
end
|