electr 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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