electr 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6be27b7b5efaeb5cb477f1bda270e4d1b95d4fec
4
- data.tar.gz: 086e216808aa9e6709ee47ff5a648a3d175779f7
3
+ metadata.gz: ccc906f3bbacaccdd300849c16843c4088b34b6f
4
+ data.tar.gz: 6e23419eb9b1435832b10a5f2179930b6e06ca0a
5
5
  SHA512:
6
- metadata.gz: 9d206038f0b0f77a76fc388c1f152416f623af280a766cb93f67fdf547e200c4ad3d135f6b2db4a2c4ce2e560eb55ef43bd3dde0bc14cf83c534bc3b352c272c
7
- data.tar.gz: 3877f48e19951b1b2cb78b4fa0b3ef9197b16229246e63a18e1aefa575b054cade8fd5497815beb237204c5624e58a00a39fcf417d1725743b3747f0eff4adc4
6
+ metadata.gz: 96d87d01a204d8437fc511155240ba7f4cf2fc775c2ab16ff6f293cecadbd5805eb37ab97caa5c9e801cade75af14e981f7d2ebd85c3e63eac5723d59259ab0b
7
+ data.tar.gz: c884b9a67e9b481abc08541983ff8ca272ab2a54003e961755596375f4ceba4a6814ac46acd3874aa98802775d9649ab3513957b7201a92589973b76c009b6b0
@@ -2,16 +2,39 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
+
5
6
  ## [Unreleased] - unreleased
7
+
8
+ ## [0.0.4] - 2015-09-09
9
+ ### Added
10
+ - Exponent operator: ^
11
+ - New functions: sin, cos and tan
12
+ - Using the [readline library][readline]
13
+ - There's now a branch «development» on Github
14
+ [readline]: https://cnswww.cns.cwru.edu/php/chet/readline/rltop.html
15
+
16
+ ### Modified
17
+ - In order to use the readline library the license of Electr switch from
18
+ MIT to [Apache 2.0][apache2]
19
+ [apache2]: http://www.apache.org/licenses/LICENSE-2.0
20
+
21
+ ### Fixed
22
+ - Temporarily limits function's arity to one. This fixes some minor bugs in
23
+ the AST.
24
+
25
+
26
+ ## [0.0.3] - 2015-09-07
6
27
  ### Added
7
28
  - Quit gracefuly with Ctrl-C
8
29
  - 10,000 and 10_000 are the same as 10000
9
30
  - Electr supports negative numbers
10
31
  - Coco, a code coverage tool as a developer dependency
11
32
  - Enable Travis Continuous Integration tool
33
+
12
34
  ### Modified
13
35
  - One can write floating point number without a leading zero (ie `.678`).
14
36
 
37
+
15
38
  ## [0.0.2] - 2015-09-04
16
39
  ### Added
17
40
  - Basic Read-Eval-Print-Loop as a proof of concept. It recognizes some
@@ -1,22 +1,13 @@
1
- Copyright (c) 2015 lkdjiin
1
+ Copyright 2015 Xavier Nayrac
2
2
 
3
- MIT License
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
4
6
 
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
7
+ http://www.apache.org/licenses/LICENSE-2.0
12
8
 
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
- # Electr [![Gem Version](https://badge.fury.io/rb/electr.png)](http://badge.fury.io/rb/electr)
1
+ # Electr [![Gem Version](https://badge.fury.io/rb/electr.png)](http://badge.fury.io/rb/electr) [![Build Status](https://travis-ci.org/lkdjiin/electr.png)](https://travis-ci.org/lkdjiin/electr)
2
2
 
3
3
  Interactive language for electronic formulas.
4
4
 
5
+ What's new? Take a look at [CHANGELOG.md](CHANGELOG.md).
6
+
5
7
  ## Rationale
6
8
 
7
9
  - I have not found an open source language or calculator at the command line,
@@ -119,12 +121,12 @@ next couple of days/weeks:
119
121
  - [x] Negative numbers
120
122
  - [x] Floating point number without a leading zero (ie `.678`)
121
123
  - [x] 10_000 or 10,000 will be the same as 10000
124
+ - [x] More builtin functions (sin, cos, tan)
125
+ - [x] Exponent
126
+ - [x] Readline lib in the REPL for a better user experience
122
127
  - [ ] `*` for the multiplication if one want to
123
128
  - [ ] √ for an alternative to square root
124
- - [ ] More builtin functions (sin, cos, tan, etc)
125
- - [ ] Exponent
126
129
  - [ ] Shortcuts for function's names (ie sq and sqr for sqrt)
127
- - [ ] Readline lib in the REPL for a better user experience
128
130
  - [ ] All units and prefix used in electronic
129
131
 
130
132
  ## What's next?
@@ -193,3 +195,7 @@ Frink is closed source, so it doesn't meet my requirements. GNU Units is
193
195
  awesome and close to what I want but it's so huge and not at all specialized!
194
196
  I want to deal with ohms, farads, volts, etc and not with kilograms nor with
195
197
  furlongs ;)
198
+
199
+ ## License
200
+
201
+ [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0)
@@ -1,4 +1,5 @@
1
1
  require "optparse"
2
+ require "readline"
2
3
 
3
4
  require "electr/version"
4
5
  require "electr/exceptions"
@@ -12,15 +13,21 @@ module Electr
12
13
 
13
14
  UNARY_MINUS_INTERNAL_SYMBOL = '€'
14
15
 
16
+ ONE_CHAR_OPERATORS = %w( + - / ^ )
17
+
15
18
  # f - function
16
19
  SYMBOL_TABLE = {
17
20
  'sqrt' => 'f',
21
+ 'sin' => 'f',
22
+ 'cos' => 'f',
23
+ 'tan' => 'f',
18
24
  }
19
25
 
20
26
  PRECEDENCE = {
21
- '()' => {assoc: 'L', val: 99},
22
- ')' => {assoc: 'L', val: 99},
23
- '(' => {assoc: 'L', val: 99},
27
+ '()' => {assoc: 'L', val: 100},
28
+ ')' => {assoc: 'L', val: 100},
29
+ '(' => {assoc: 'L', val: 100},
30
+ '^' => {assoc: 'L', val: 90},
24
31
  UNARY_MINUS_INTERNAL_SYMBOL => {assoc: 'R', val: 80},
25
32
  '*' => {assoc: 'L', val: 10},
26
33
  '/' => {assoc: 'L', val: 10},
@@ -13,7 +13,7 @@ module Electr
13
13
  @value = nil
14
14
  end
15
15
 
16
- attr_reader :value, :name
16
+ attr_reader :value, :name, :children
17
17
 
18
18
  # Public: Add a child node to the end of the children's list.
19
19
  #
@@ -72,8 +72,13 @@ module Electr
72
72
  end
73
73
 
74
74
  def there_is_room?(node)
75
- (node.value == UNARY_MINUS_INTERNAL_SYMBOL && node.size < 1) ||
76
- (node.value != UNARY_MINUS_INTERNAL_SYMBOL && node.size < 2)
75
+ case node.name
76
+ when 'funcargs'
77
+ node.size < 1
78
+ else
79
+ (node.value == UNARY_MINUS_INTERNAL_SYMBOL && node.size < 1) ||
80
+ (node.value != UNARY_MINUS_INTERNAL_SYMBOL && node.size < 2)
81
+ end
77
82
  end
78
83
 
79
84
  end
@@ -8,7 +8,7 @@ module Electr
8
8
  end
9
9
 
10
10
  def operator?
11
- ['+', '-', '/', UNARY_MINUS_INTERNAL_SYMBOL].include?(self)
11
+ [*ONE_CHAR_OPERATORS, UNARY_MINUS_INTERNAL_SYMBOL].include?(self)
12
12
  end
13
13
 
14
14
  def constant?
@@ -12,8 +12,6 @@ module Electr
12
12
  # - `)`
13
13
  class Tokenizer
14
14
 
15
- ONE_CHAR_SYMBOLS = %w( + - / )
16
-
17
15
  # Create a new Tokenizer for a specific code.
18
16
  #
19
17
  # string - The String code to tokenize.
@@ -59,7 +57,7 @@ module Electr
59
57
  get_number
60
58
  elsif unary_minus?
61
59
  get_unary_minus
62
- elsif ONE_CHAR_SYMBOLS.include?(@look_ahead)
60
+ elsif ONE_CHAR_OPERATORS.include?(@look_ahead)
63
61
  add_this_char
64
62
  elsif @look_ahead == '('
65
63
  add_this_char
@@ -1,4 +1,5 @@
1
1
  require "electr/repl/evaluator"
2
+ require "electr/repl/eval_function"
2
3
  require "electr/repl/nil_evaluator"
3
4
  require "electr/repl/base_reader"
4
5
  require "electr/repl/reader"
@@ -4,13 +4,13 @@ module Electr
4
4
  # computation.
5
5
  class ASTReader < BaseReader
6
6
 
7
- def initialize
8
- super("E--ast> ")
7
+ def initialize(readline_lib = Readline)
8
+ super("E--ast> ", readline_lib)
9
9
  end
10
10
 
11
11
  def run
12
- prompt
13
- Compiler.compile_to_ast(STDIN.gets.chomp)
12
+ line = @readline.readline(@prompt, true)
13
+ Compiler.compile_to_ast(line)
14
14
  end
15
15
 
16
16
  end
@@ -2,22 +2,23 @@ module Electr
2
2
 
3
3
  # Responsible of the terminal interface, that is printing the prompt
4
4
  # and reading a line of code (I know, I know, there is an «and» in the
5
- # description…).
5
+ # description…). This is a base class intended for inheritance.
6
6
  class BaseReader
7
7
 
8
- def initialize(prompt = "No prompt defined> ")
8
+ # Creates a new BaseReader.
9
+ #
10
+ # prompt - String prompt to be displayed to the user.
11
+ # readline_lib - The app should always use Readline, but injected
12
+ # the Module here could ease the tests…
13
+ def initialize(prompt = "No prompt defined> ",
14
+ readline_lib = Readline)
9
15
  @prompt = prompt
16
+ @readline = readline_lib
10
17
  end
11
18
 
12
19
  def run
13
20
  raise(NotImplementedError)
14
21
  end
15
22
 
16
- private
17
-
18
- def prompt
19
- print(@prompt)
20
- end
21
-
22
23
  end
23
24
  end
@@ -0,0 +1,23 @@
1
+ module Electr
2
+
3
+ module EvalFunction
4
+
5
+ def self.eval(function_name, stack)
6
+ case function_name
7
+ when "sqrt"
8
+ a = stack.pop
9
+ stack.push(Math::sqrt(a))
10
+ when "sin"
11
+ a = stack.pop
12
+ stack.push(Math::sin(a))
13
+ when "cos"
14
+ a = stack.pop
15
+ stack.push(Math::cos(a))
16
+ when "tan"
17
+ a = stack.pop
18
+ stack.push(Math::tan(a))
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -14,7 +14,7 @@ module Electr
14
14
  when "constant" then @stack.push(constant(item.value))
15
15
  when "value" then do_value(item.value)
16
16
  when "operator" then operation(item.value)
17
- when "funcname" then func(item.value)
17
+ when "funcname" then EvalFunction.eval(item.value, @stack)
18
18
  end
19
19
  end
20
20
 
@@ -29,14 +29,6 @@ module Electr
29
29
  end
30
30
  end
31
31
 
32
- def func(name)
33
- case name
34
- when "sqrt"
35
- a = @stack.pop
36
- @stack.push(Math::sqrt(a))
37
- end
38
- end
39
-
40
32
  def operation(operand)
41
33
  if operand == UNARY_MINUS_INTERNAL_SYMBOL
42
34
  unary_minus
@@ -51,6 +43,7 @@ module Electr
51
43
  end
52
44
 
53
45
  def classic_operation(operand)
46
+ operand = "**" if operand == '^'
54
47
  a = @stack.pop
55
48
  b = @stack.pop
56
49
  @stack.push(a.send(operand, b))
@@ -3,13 +3,13 @@ module Electr
3
3
  # Normal reader for the REPL.
4
4
  class Reader < BaseReader
5
5
 
6
- def initialize
7
- super("E> ")
6
+ def initialize(readline_lib = Readline)
7
+ super("E> ", readline_lib)
8
8
  end
9
9
 
10
10
  def run
11
- prompt
12
- Compiler.compile_to_pn(STDIN.gets.chomp)
11
+ line = @readline.readline(@prompt, true)
12
+ Compiler.compile_to_pn(line)
13
13
  end
14
14
 
15
15
  end
@@ -1,3 +1,3 @@
1
1
  module Electr
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -26,15 +26,44 @@ describe Compiler do
26
26
  end
27
27
  end
28
28
 
29
- specify do
30
- result = Compiler.compile_to_ast("-pi 2")
31
- result.display
32
- expect(result).to be_a AST
33
- end
34
-
35
29
  specify "#compile_to_pn" do
36
30
  result = Compiler.compile_to_pn("2 pi")
37
31
  expect(result).to be_a Array
38
32
  end
39
33
 
34
+ describe "Bug in the AST with `sin(pi / 2) + 1`" do
35
+
36
+ # I want the AST as follow:
37
+ #
38
+ # $ electr --ast -e "sin(2) + 1"
39
+ # ast
40
+ # root
41
+ # operator (+)
42
+ # func
43
+ # funcname ::= sin
44
+ # funcargs
45
+ # numeric ::= 2
46
+ # numeric ::= 1
47
+ #
48
+ # and not as follow (this is the bug result):
49
+ #
50
+ # $ electr --ast -e "sin(2) + 1"
51
+ # ast
52
+ # root
53
+ # operator (+)
54
+ # func
55
+ # funcname ::= sin
56
+ # funcargs
57
+ # numeric ::= 2
58
+ # numeric ::= 1
59
+ specify "sin(pi / 2) + 1" do
60
+ result = Compiler.compile_to_ast("sin(pi / 2) + 1")
61
+ operator = result.children[0].children[0]
62
+ expect(operator.size).to eq 2
63
+ funcargs = operator.children[0].children[1]
64
+ expect(funcargs.size).to eq 1
65
+ end
66
+
67
+ end
68
+
40
69
  end
@@ -188,4 +188,11 @@ describe Tokenizer do
188
188
  expect(tkr.next_token).to eq "11,000"
189
189
  end
190
190
 
191
+ specify "10 ^ 2" do
192
+ tkr = Tokenizer.new("10 ^ 2")
193
+ expect(tkr.next_token).to eq "10"
194
+ expect(tkr.next_token).to eq "^"
195
+ expect(tkr.next_token).to eq "2"
196
+ end
197
+
191
198
  end
@@ -2,13 +2,19 @@ require 'spec_helper'
2
2
 
3
3
  include Electr
4
4
 
5
+ class TestReadline
6
+ def self.readline(a, b)
7
+ print a
8
+ "2 3"
9
+ end
10
+ end
11
+
5
12
  describe ASTReader do
6
13
 
7
14
  describe ".run" do
8
15
 
9
16
  it 'prompts' do
10
- allow(STDIN).to receive(:gets).and_return("2 3\n")
11
- reader = ASTReader.new
17
+ reader = ASTReader.new(TestReadline)
12
18
 
13
19
  expect { reader.run }.to output("E--ast> ").to_stdout
14
20
  end
@@ -30,6 +30,10 @@ describe Evaluator do
30
30
  {code: "2 -pi", result: -(2 * Math::PI)},
31
31
  {code: "1 - 1 + 4 - 1 - 1", result: 2},
32
32
  {code: "-(2 3)", result: -6},
33
+ {code: "11,000 + 22_000", result: 33_000},
34
+ {code: "sin(pi / 2) + 1", result: 2},
35
+ {code: "cos(0) - 1", result: 0},
36
+ {code: "tan(0) - 1", result: -1},
33
37
  ]
34
38
 
35
39
  specify "#evaluate_pn" do
@@ -44,12 +48,12 @@ describe Evaluator do
44
48
  end
45
49
 
46
50
  specify do
47
- pns = Compiler.compile_to_pn("11,000 + 22_000")
51
+ pns = Compiler.compile_to_pn("10 ^ 2")
48
52
 
49
53
  evaluator = Evaluator.new
50
54
  result = evaluator.evaluate_pn(pns)
51
55
 
52
- expect(result).to eq 33_000
56
+ expect(result).to eq 100
53
57
  end
54
58
 
55
59
 
@@ -2,15 +2,18 @@ require 'spec_helper'
2
2
 
3
3
  include Electr
4
4
 
5
+ class TestReadline
6
+ def self.readline(a, b)
7
+ print a
8
+ "2 3"
9
+ end
10
+ end
11
+
5
12
  describe Reader do
6
13
 
7
14
  describe ".run" do
8
15
 
9
- before do
10
- allow(STDIN).to receive(:gets).and_return("2 3\n")
11
- end
12
-
13
- let(:reader) { Reader.new }
16
+ let(:reader) { Reader.new(TestReadline) }
14
17
 
15
18
  it 'compiles the expression from STDIN' do
16
19
  expect(reader.run.first.name).to eq 'ast'
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.3
4
+ version: 0.0.4
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-07 00:00:00.000000000 Z
11
+ date: 2015-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -117,6 +117,7 @@ files:
117
117
  - lib/electr/repl/ast_printer.rb
118
118
  - lib/electr/repl/ast_reader.rb
119
119
  - lib/electr/repl/base_reader.rb
120
+ - lib/electr/repl/eval_function.rb
120
121
  - lib/electr/repl/evaluator.rb
121
122
  - lib/electr/repl/nil_evaluator.rb
122
123
  - lib/electr/repl/printer.rb