electr 0.0.2
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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +196 -0
- data/Rakefile +7 -0
- data/bin/electr +27 -0
- data/electr.gemspec +25 -0
- data/lib/electr.rb +20 -0
- data/lib/electr/ast.rb +9 -0
- data/lib/electr/ast/ast.rb +62 -0
- data/lib/electr/ast/constant_ast.rb +14 -0
- data/lib/electr/ast/func_args_ast.rb +11 -0
- data/lib/electr/ast/func_ast.rb +12 -0
- data/lib/electr/ast/func_name_ast.rb +14 -0
- data/lib/electr/ast/numeric_ast.rb +13 -0
- data/lib/electr/ast/operator_ast.rb +13 -0
- data/lib/electr/ast/root_ast.rb +11 -0
- data/lib/electr/ast/value_ast.rb +12 -0
- data/lib/electr/compiler.rb +24 -0
- data/lib/electr/evaluator.rb +64 -0
- data/lib/electr/exceptions.rb +6 -0
- data/lib/electr/option.rb +70 -0
- data/lib/electr/parser.rb +5 -0
- data/lib/electr/parser/lexer.rb +50 -0
- data/lib/electr/parser/lexical_unit.rb +67 -0
- data/lib/electr/parser/rules/base_rule.rb +60 -0
- data/lib/electr/parser/rules/expression_rule.rb +79 -0
- data/lib/electr/parser/rules/root_rule.rb +21 -0
- data/lib/electr/parser/sya.rb +105 -0
- data/lib/electr/parser/syntaxer.rb +21 -0
- data/lib/electr/parser/tokenizer.rb +79 -0
- data/lib/electr/repl.rb +56 -0
- data/lib/electr/rules.rb +3 -0
- data/lib/electr/version.rb +3 -0
- data/spec/compiler_spec.rb +42 -0
- data/spec/electr_spec.rb +7 -0
- data/spec/evaluator_spec.rb +47 -0
- data/spec/lexer_spec.rb +29 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/sya_spec.rb +153 -0
- data/spec/tokenizer_spec.rb +102 -0
- metadata +138 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 129c7c8560ec169ade1a934c574de8a045e3dbc0
|
4
|
+
data.tar.gz: c04ece5fd979bad30c69e5d67b64899b841ac72e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: faee90b0d4a72a8bf8c74c9530c697062e7edde33c6c96b0135d56aa93bda5573e1d78647279b7104622300bab369900f1ffd5d2d2be0b30915af20bc5430b6e
|
7
|
+
data.tar.gz: c04497b5911bb940656a495eaab0232be5394b456baf27c4f1a209a3aa2e86f27630e986b59b2499ba3c417a0bf557305c1f04fbe698a1795f6dc3a6e23fd23f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
This project adheres to [Semantic Versioning](http://semver.org/).
|
4
|
+
|
5
|
+
## [Unreleased] - unreleased
|
6
|
+
|
7
|
+
## [0.0.2] - 2015-09-04
|
8
|
+
### Added
|
9
|
+
- Basic Read-Eval-Print-Loop as a proof of concept. It recognizes some
|
10
|
+
units but not all
|
11
|
+
- Options -v (version), -h (help), --ast (display the AST instead of the
|
12
|
+
result) and -e (compute this expression and quit, do not run
|
13
|
+
interactively)
|
14
|
+
- Grammar of Electr (in `grammar.md`)
|
15
|
+
- Beginning of documentation for developers
|
16
|
+
- Precedence table
|
17
|
+
- This change log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 lkdjiin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
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:
|
12
|
+
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
Electr
|
2
|
+
======
|
3
|
+
|
4
|
+
Interactive language for electronic formulas.
|
5
|
+
|
6
|
+
## Rationale
|
7
|
+
|
8
|
+
- I have not found an open source language or calculator at the command line,
|
9
|
+
specialized in electronic formulas
|
10
|
+
- I'm always confused with milli, micro, pico, nano, etc
|
11
|
+
- I prefer to type 100k rather than 100000, or 100M rather than 100000000
|
12
|
+
- If, for any reason, I want to write 100000000, then I should be able to write
|
13
|
+
100_000_000 or 100,000,000
|
14
|
+
|
15
|
+
I don't want a big monster but a tiny specialized language.
|
16
|
+
|
17
|
+
## What do you think?
|
18
|
+
|
19
|
+
I started to write this tiny language. First, a quick prototype in Ruby, as a
|
20
|
+
proof of concept. And then, maybe, a more portable version in C.
|
21
|
+
|
22
|
+
Tell me what you think on [twitter](https://twitter.com/lkdjiin) or better,
|
23
|
+
open an issue here on Github. In any cases feel free to start a discussion.
|
24
|
+
|
25
|
+
The current version is an early one:
|
26
|
+
|
27
|
+
1. Don't expect too much
|
28
|
+
2. Expect a lot of bugs
|
29
|
+
3. Please be kind enough to report any bugs here in Github
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
Install it with:
|
34
|
+
|
35
|
+
$ gem install electr
|
36
|
+
|
37
|
+
## Examples of what Electr can do right now
|
38
|
+
|
39
|
+
### Start Electr
|
40
|
+
|
41
|
+
Just type `electr`:
|
42
|
+
|
43
|
+
$ electr
|
44
|
+
|
45
|
+
And you'll see the prompt waiting for you:
|
46
|
+
|
47
|
+
E>
|
48
|
+
|
49
|
+
To compute an expression without entering the interactive mode, use the `-e`
|
50
|
+
switch:
|
51
|
+
|
52
|
+
$ electr -e "3V / 25mA"
|
53
|
+
120
|
54
|
+
|
55
|
+
To display the AST instead of doing the computation, use the `--ast` switch
|
56
|
+
(*this is intended only for the developers*):
|
57
|
+
|
58
|
+
$ electr --ast -e "3V / 25mA"
|
59
|
+
ast
|
60
|
+
root
|
61
|
+
operator (/)
|
62
|
+
value ::= 3V
|
63
|
+
value ::= 25mA
|
64
|
+
|
65
|
+
### Resistors in serie
|
66
|
+
|
67
|
+
Start simple to illustrate the addition. We have a 10,000 Ohm resistor (10k) and
|
68
|
+
a 200 Ohm resistor (200R):
|
69
|
+
|
70
|
+
E> 10k + 200R
|
71
|
+
10200
|
72
|
+
|
73
|
+
*Should it be `K`, `k`, `kΩ` or the three is still open to debate.*
|
74
|
+
|
75
|
+
### Ohm's law
|
76
|
+
|
77
|
+
Divide Volts (V) by milliamps (mA) to get some Ohms:
|
78
|
+
|
79
|
+
E> 3V / 25mA
|
80
|
+
120
|
81
|
+
|
82
|
+
There is no symbol for the multiplication. Simply put values side by side:
|
83
|
+
|
84
|
+
E> 1mA 3k
|
85
|
+
3
|
86
|
+
|
87
|
+
### Frequency of an oscillator
|
88
|
+
|
89
|
+
A little bit more complex is the computation of a frequency for an oscillator.
|
90
|
+
We've got two constants (`2` and `pi`), a value in micro Farad (`0.5uF`) and
|
91
|
+
a square root (`sqrt`) of the product of two resistors (`11k 22k`). The result
|
92
|
+
is in Hertz.
|
93
|
+
|
94
|
+
E> 1 / (2 pi 0.5uF sqrt(11k 22k))
|
95
|
+
20.4617344581
|
96
|
+
|
97
|
+
### Units
|
98
|
+
|
99
|
+
Currently Electr knows the following units:
|
100
|
+
|
101
|
+
Prefix and/or Symbol | Name
|
102
|
+
:-------------------: | -----
|
103
|
+
k K kΩ | kilo ohm
|
104
|
+
Ω R | ohm
|
105
|
+
A | ampere
|
106
|
+
V | volt
|
107
|
+
W | watt
|
108
|
+
mA | milli ampere
|
109
|
+
mV | milli volt
|
110
|
+
mW | milli watt
|
111
|
+
μ u μF uF | micro farad
|
112
|
+
p pF | pico farad
|
113
|
+
|
114
|
+
### What is missing?
|
115
|
+
|
116
|
+
Electr is at a very early stage and it miss a lot of (basic) things!
|
117
|
+
You can expect that the following features will be implemented in the
|
118
|
+
next couple of days/weeks:
|
119
|
+
|
120
|
+
[ ] Negative numbers
|
121
|
+
[ ] `*` for the multiplication if one want to
|
122
|
+
[ ] √ for an alternative to square root
|
123
|
+
[ ] Floating point number without a leading zero (ie `.678`)
|
124
|
+
[ ] More buitin functions (sin, cos, tan, etc)
|
125
|
+
[ ] Exponent
|
126
|
+
[ ] Shortcuts for function's names (ie sq and sqr for sqrt)
|
127
|
+
[ ] Readline lib in the REPL for a better user experience
|
128
|
+
[ ] 10_000 or 10,000 will be the same as 10000
|
129
|
+
[ ] All units and prefix used in electronic
|
130
|
+
|
131
|
+
## What's next?
|
132
|
+
|
133
|
+
Maybe Electr could infer the resulting unit:
|
134
|
+
|
135
|
+
E> 10k + 200R
|
136
|
+
10.2kΩ
|
137
|
+
E> 3V / 25mA
|
138
|
+
120Ω
|
139
|
+
E> 3V / 1mA
|
140
|
+
3kΩ
|
141
|
+
|
142
|
+
One are less prone to typing errors (less parenthesis) if one enter a complex
|
143
|
+
expression on two lines:
|
144
|
+
|
145
|
+
E> 1 /
|
146
|
+
E> 2 pi 0.5uF sq(11K 22K)
|
147
|
+
20.46Hz
|
148
|
+
|
149
|
+
### Beyond the calculator
|
150
|
+
|
151
|
+
Electr could be more **interactive**. In the following session we've got a
|
152
|
+
resistor R1 and two capacitors C1 and C2. Once the formula is entered, Electr
|
153
|
+
ask us for the components values, then gives us the result.
|
154
|
+
|
155
|
+
E> 1/
|
156
|
+
E> 2 pi R1 sq(C1 C2)
|
157
|
+
R1=?> 33K
|
158
|
+
C1=?> 1000pF
|
159
|
+
C2=?> 470pF
|
160
|
+
7.04kHz
|
161
|
+
|
162
|
+
Or acts like a tiny programming language.
|
163
|
+
|
164
|
+
E> R1 = 33k
|
165
|
+
E> C1 = 1000pF
|
166
|
+
E> C2 = 470pF
|
167
|
+
E> 1 / (2 pi R1 sq(C1 C2))
|
168
|
+
7.04kHz
|
169
|
+
|
170
|
+
Why not having custom functions?
|
171
|
+
|
172
|
+
E> func = { 1 / (2 pi R1 sq(C1 C2)) }
|
173
|
+
E> func(R1=33k, C1=1000pF, C2=470pF)
|
174
|
+
7.04kHz
|
175
|
+
|
176
|
+
*The above syntax is just one possibility amongst a lot of others.*
|
177
|
+
|
178
|
+
## Contributing
|
179
|
+
|
180
|
+
1. Fork it ( https://github.com/[my-github-username]/electr/fork )
|
181
|
+
2. **PLEASE Create your feature branch** (`git checkout -b my-new-feature`)
|
182
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
183
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
184
|
+
5. Create a new Pull Request
|
185
|
+
|
186
|
+
## Alternatives
|
187
|
+
|
188
|
+
Some people point me to two existing softwares:
|
189
|
+
|
190
|
+
- [Frink](https://futureboy.us/frinkdocs/)
|
191
|
+
- [GNU Units](https://en.wikipedia.org/wiki/GNU_Units)
|
192
|
+
|
193
|
+
Frink is closed source, so it doesn't meet my requirements. GNU Units is
|
194
|
+
awesome and close to what I want but it's so huge and not at all specialized!
|
195
|
+
I want to deal with ohms, farads, volts, etc and not with kilograms nor with
|
196
|
+
furlongs ;)
|
data/Rakefile
ADDED
data/bin/electr
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'electr'
|
4
|
+
|
5
|
+
opt = Electr::Option.new
|
6
|
+
|
7
|
+
if opt[:expression]
|
8
|
+
if opt[:ast]
|
9
|
+
compiler = Electr::Compiler.new
|
10
|
+
ast = compiler.compile_to_ast(opt[:expression])
|
11
|
+
ast.display
|
12
|
+
else
|
13
|
+
# FIXME Should be under lib/ to be tested and there is a lot of
|
14
|
+
# common code with Repl.
|
15
|
+
compiler = Electr::Compiler.new
|
16
|
+
temp = compiler.compile_to_pn(opt[:expression])
|
17
|
+
evaluator = Electr::Evaluator.new
|
18
|
+
temp = evaluator.evaluate_pn(temp)
|
19
|
+
if temp == temp.truncate
|
20
|
+
puts temp.truncate
|
21
|
+
else
|
22
|
+
puts temp.round(10)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
Electr::Repl.new(opt).run
|
27
|
+
end
|
data/electr.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'electr/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "electr"
|
8
|
+
spec.version = Electr::VERSION
|
9
|
+
spec.authors = ["lkdjiin"]
|
10
|
+
spec.email = ["xavier.nayrac@gmail.com"]
|
11
|
+
spec.summary = %q{Interactive language for electronic formulas}
|
12
|
+
spec.homepage = "https://github.com/lkdjiin/electr"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.required_ruby_version = ">= 2.2"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
end
|
data/lib/electr.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
require "electr/version"
|
4
|
+
require "electr/exceptions"
|
5
|
+
require "electr/parser"
|
6
|
+
require "electr/compiler"
|
7
|
+
require "electr/ast"
|
8
|
+
require "electr/rules"
|
9
|
+
require "electr/evaluator"
|
10
|
+
require "electr/repl"
|
11
|
+
require "electr/option"
|
12
|
+
|
13
|
+
module Electr
|
14
|
+
|
15
|
+
# f - function
|
16
|
+
SYMBOL_TABLE = {
|
17
|
+
'sqrt' => 'f',
|
18
|
+
}
|
19
|
+
|
20
|
+
end
|
data/lib/electr/ast.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require "electr/ast/ast"
|
2
|
+
require "electr/ast/root_ast"
|
3
|
+
require "electr/ast/numeric_ast"
|
4
|
+
require "electr/ast/constant_ast"
|
5
|
+
require "electr/ast/value_ast"
|
6
|
+
require "electr/ast/operator_ast"
|
7
|
+
require "electr/ast/func_ast"
|
8
|
+
require "electr/ast/func_name_ast"
|
9
|
+
require "electr/ast/func_args_ast"
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Electr
|
2
|
+
|
3
|
+
class AST
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
if name
|
7
|
+
@name = name
|
8
|
+
else
|
9
|
+
@name = self.class.name.split('::').last
|
10
|
+
end
|
11
|
+
@children = []
|
12
|
+
@value = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
# Public: Add a child node to the end of the children's list.
|
16
|
+
#
|
17
|
+
# child - An AST node to add to the list of children.
|
18
|
+
#
|
19
|
+
# Returns self.
|
20
|
+
def add_child child
|
21
|
+
@children << child
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Returns True if this node is a leaf.
|
26
|
+
def leaf?
|
27
|
+
@children.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def size
|
31
|
+
@children.size
|
32
|
+
end
|
33
|
+
|
34
|
+
def display(indent = 0)
|
35
|
+
print " " * indent + @name
|
36
|
+
if leaf?
|
37
|
+
print " ::= #@value"
|
38
|
+
else
|
39
|
+
print " (#@value)" if @value
|
40
|
+
end
|
41
|
+
puts
|
42
|
+
@children.each {|child| child.display(indent + 2) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_pn
|
46
|
+
[* Pn.new(@value, @name)] + @children.map {|child| child.to_pn }.flatten
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
# TODO Pn is not a good name!
|
52
|
+
class Pn
|
53
|
+
|
54
|
+
def initialize(value, name)
|
55
|
+
@value = value
|
56
|
+
@name = name
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_reader :value, :name
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|