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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.coco.yml +4 -0
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +8 -0
  5. data/README.md +12 -13
  6. data/bin/electr +10 -5
  7. data/documentation/developer.md +21 -0
  8. data/documentation/grammar.md +34 -0
  9. data/documentation/precedence.md +8 -0
  10. data/electr.gemspec +1 -0
  11. data/lib/electr.rb +13 -2
  12. data/lib/electr/ast/ast.rb +24 -6
  13. data/lib/electr/ast/constant_ast.rb +1 -0
  14. data/lib/electr/ast/func_args_ast.rb +1 -0
  15. data/lib/electr/ast/func_ast.rb +1 -0
  16. data/lib/electr/ast/func_name_ast.rb +2 -0
  17. data/lib/electr/ast/numeric_ast.rb +1 -0
  18. data/lib/electr/ast/operator_ast.rb +1 -0
  19. data/lib/electr/ast/root_ast.rb +1 -0
  20. data/lib/electr/ast/value_ast.rb +1 -0
  21. data/lib/electr/compiler.rb +10 -6
  22. data/lib/electr/exceptions.rb +1 -0
  23. data/lib/electr/parser.rb +2 -0
  24. data/lib/electr/parser/lexer.rb +14 -37
  25. data/lib/electr/parser/lexical_unit.rb +7 -4
  26. data/lib/electr/{rules.rb → parser/rules.rb} +0 -0
  27. data/lib/electr/parser/rules/base_rule.rb +5 -13
  28. data/lib/electr/parser/rules/expression_rule.rb +18 -17
  29. data/lib/electr/parser/rules/root_rule.rb +5 -2
  30. data/lib/electr/parser/sya.rb +22 -21
  31. data/lib/electr/parser/syntaxer.rb +4 -1
  32. data/lib/electr/parser/token.rb +26 -0
  33. data/lib/electr/parser/tokenizer.rb +47 -7
  34. data/lib/electr/repl.rb +8 -56
  35. data/lib/electr/repl/ast_printer.rb +11 -0
  36. data/lib/electr/repl/ast_reader.rb +17 -0
  37. data/lib/electr/repl/base_reader.rb +23 -0
  38. data/lib/electr/{evaluator.rb → repl/evaluator.rb} +15 -1
  39. data/lib/electr/repl/nil_evaluator.rb +11 -0
  40. data/lib/electr/repl/printer.rb +12 -0
  41. data/lib/electr/repl/reader.rb +16 -0
  42. data/lib/electr/repl/repl.rb +28 -0
  43. data/lib/electr/version.rb +1 -1
  44. data/spec/ast/ast_spec.rb +17 -0
  45. data/spec/compiler_spec.rb +4 -6
  46. data/spec/parser/base_rule_spec.rb +13 -0
  47. data/spec/{lexer_spec.rb → parser/lexer_spec.rb} +5 -2
  48. data/spec/parser/pn_spec.rb +17 -0
  49. data/spec/{sya_spec.rb → parser/sya_spec.rb} +35 -0
  50. data/spec/{tokenizer_spec.rb → parser/tokenizer_spec.rb} +89 -0
  51. data/spec/repl/ast_printer_spec.rb +18 -0
  52. data/spec/repl/ast_reader_spec.rb +19 -0
  53. data/spec/{evaluator_spec.rb → repl/evaluator_spec.rb} +15 -5
  54. data/spec/repl/nil_evaluator_spec.rb +11 -0
  55. data/spec/repl/printer_spec.rb +19 -0
  56. data/spec/repl/reader_spec.rb +25 -0
  57. data/spec/repl/repl_spec.rb +32 -0
  58. data/spec/spec_helper.rb +1 -0
  59. metadata +56 -12
@@ -0,0 +1,11 @@
1
+ module Electr
2
+
3
+ # Prints the abstract syntax tree.
4
+ class ASTPrinter
5
+
6
+ def self.run(ast)
7
+ ast.display
8
+ end
9
+
10
+ end
11
+ end
@@ -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.value.to_f)
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,11 @@
1
+ module Electr
2
+
3
+ # An Evaluator that does nothing.
4
+ class NilEvaluator
5
+
6
+ def evaluate_pn(arg)
7
+ arg
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module Electr
2
+
3
+ # Prints the computation.
4
+ class Printer
5
+
6
+ def self.run(result)
7
+ truncated = result.truncate
8
+ puts result == truncated ? truncated : result.round(10)
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Electr
2
+
3
+ # Normal reader for the REPL.
4
+ class Reader < BaseReader
5
+
6
+ def initialize
7
+ super("E> ")
8
+ end
9
+
10
+ def run
11
+ prompt
12
+ Compiler.compile_to_pn(STDIN.gets.chomp)
13
+ end
14
+
15
+ end
16
+ end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Electr
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -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
@@ -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
- compiler = Compiler.new
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
- compiler = Compiler.new
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
- compiler = Compiler.new
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
 
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ include Electr
4
+
5
+ describe BaseRule do
6
+
7
+ it "is not implemented" do
8
+ expect {
9
+ BaseRule.new(nil, nil).apply
10
+ }.to raise_error(NotImplementedError)
11
+ end
12
+
13
+ end
@@ -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 = @lexer.lexify(token.last)
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