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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6be27b7b5efaeb5cb477f1bda270e4d1b95d4fec
|
4
|
+
data.tar.gz: 086e216808aa9e6709ee47ff5a648a3d175779f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d206038f0b0f77a76fc388c1f152416f623af280a766cb93f67fdf547e200c4ad3d135f6b2db4a2c4ce2e560eb55ef43bd3dde0bc14cf83c534bc3b352c272c
|
7
|
+
data.tar.gz: 3877f48e19951b1b2cb78b4fa0b3ef9197b16229246e63a18e1aefa575b054cade8fd5497815beb237204c5624e58a00a39fcf417d1725743b3747f0eff4adc4
|
data/.coco.yml
ADDED
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,14 @@ 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
|
## [Unreleased] - unreleased
|
6
|
+
### Added
|
7
|
+
- Quit gracefuly with Ctrl-C
|
8
|
+
- 10,000 and 10_000 are the same as 10000
|
9
|
+
- Electr supports negative numbers
|
10
|
+
- Coco, a code coverage tool as a developer dependency
|
11
|
+
- Enable Travis Continuous Integration tool
|
12
|
+
### Modified
|
13
|
+
- One can write floating point number without a leading zero (ie `.678`).
|
6
14
|
|
7
15
|
## [0.0.2] - 2015-09-04
|
8
16
|
### Added
|
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
Electr
|
2
|
-
======
|
1
|
+
# Electr [](http://badge.fury.io/rb/electr)
|
3
2
|
|
4
3
|
Interactive language for electronic formulas.
|
5
4
|
|
@@ -117,16 +116,16 @@ Electr is at a very early stage and it miss a lot of (basic) things!
|
|
117
116
|
You can expect that the following features will be implemented in the
|
118
117
|
next couple of days/weeks:
|
119
118
|
|
120
|
-
[
|
121
|
-
[
|
122
|
-
[
|
123
|
-
[ ]
|
124
|
-
[ ]
|
125
|
-
[ ]
|
126
|
-
[ ]
|
127
|
-
[ ]
|
128
|
-
[ ]
|
129
|
-
[ ] All units and prefix used in electronic
|
119
|
+
- [x] Negative numbers
|
120
|
+
- [x] Floating point number without a leading zero (ie `.678`)
|
121
|
+
- [x] 10_000 or 10,000 will be the same as 10000
|
122
|
+
- [ ] `*` for the multiplication if one want to
|
123
|
+
- [ ] √ for an alternative to square root
|
124
|
+
- [ ] More builtin 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
|
+
- [ ] All units and prefix used in electronic
|
130
129
|
|
131
130
|
## What's next?
|
132
131
|
|
@@ -177,7 +176,7 @@ Why not having custom functions?
|
|
177
176
|
|
178
177
|
## Contributing
|
179
178
|
|
180
|
-
1. Fork it ( https://github.com/
|
179
|
+
1. Fork it ( https://github.com/lkdjiin/electr/fork )
|
181
180
|
2. **PLEASE Create your feature branch** (`git checkout -b my-new-feature`)
|
182
181
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
183
182
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/bin/electr
CHANGED
@@ -2,18 +2,18 @@
|
|
2
2
|
|
3
3
|
require 'electr'
|
4
4
|
|
5
|
+
trap("INT") { puts "\nBye."; exit }
|
6
|
+
|
5
7
|
opt = Electr::Option.new
|
6
8
|
|
7
9
|
if opt[:expression]
|
8
10
|
if opt[:ast]
|
9
|
-
|
10
|
-
ast = compiler.compile_to_ast(opt[:expression])
|
11
|
+
ast = Electr::Compiler.compile_to_ast(opt[:expression])
|
11
12
|
ast.display
|
12
13
|
else
|
13
14
|
# FIXME Should be under lib/ to be tested and there is a lot of
|
14
15
|
# common code with Repl.
|
15
|
-
|
16
|
-
temp = compiler.compile_to_pn(opt[:expression])
|
16
|
+
temp = Electr::Compiler.compile_to_pn(opt[:expression])
|
17
17
|
evaluator = Electr::Evaluator.new
|
18
18
|
temp = evaluator.evaluate_pn(temp)
|
19
19
|
if temp == temp.truncate
|
@@ -23,5 +23,10 @@ if opt[:expression]
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
else
|
26
|
-
|
26
|
+
if opt[:ast]
|
27
|
+
Electr::Repl.new(Electr::ASTReader.new, Electr::NilEvaluator.new,
|
28
|
+
Electr::ASTPrinter).run
|
29
|
+
else
|
30
|
+
Electr::Repl.new.run
|
31
|
+
end
|
27
32
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
The parsing process
|
2
|
+
===================
|
3
|
+
|
4
|
+
The aim of the parsing process it to transform a source code into an AST.
|
5
|
+
|
6
|
+
AST: [Abstract Syntax Tree][AST]
|
7
|
+
|
8
|
+
SYA: [Shunting Yard Algorithm][SYA]
|
9
|
+
|
10
|
+
PN: [Prefix Notation][PN]
|
11
|
+
|
12
|
+
This is the stages of the parser:
|
13
|
+
|
14
|
+
1. Tokenizer
|
15
|
+
2. Lexer
|
16
|
+
3. Syntaxer
|
17
|
+
4. AST (using SYA for formulas)
|
18
|
+
|
19
|
+
[AST]: https://en.wikipedia.org/wiki/Abstract_syntax_tree
|
20
|
+
[SYA]: https://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
21
|
+
[PN]: https://en.wikipedia.org/wiki/Polish_notation
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Grammar of Electr in EBNF
|
2
|
+
|
3
|
+
expression = [`(`], number {[op] expression}, [`)`];
|
4
|
+
|
5
|
+
number = numeric | value | constant | f-call;
|
6
|
+
|
7
|
+
numeric = integer | float;
|
8
|
+
|
9
|
+
integer = ? integer ?;
|
10
|
+
|
11
|
+
float = ? floating point number ?;
|
12
|
+
|
13
|
+
value = ? integer or floating point directly followed by a unit ?;
|
14
|
+
|
15
|
+
constant = `pi`;
|
16
|
+
|
17
|
+
(* function call *)
|
18
|
+
f-call = f-name, `(`, expression, `)`;
|
19
|
+
|
20
|
+
(* function name *)
|
21
|
+
f-name = ? regexp: [a-z]{1,} ?;
|
22
|
+
|
23
|
+
(* operators *)
|
24
|
+
op = `-` | `+` | `/`;
|
25
|
+
|
26
|
+
## Precisions
|
27
|
+
|
28
|
+
### Negative numbers
|
29
|
+
|
30
|
+
`-123` is a negative number.<br/>
|
31
|
+
`- 123` is not.
|
32
|
+
|
33
|
+
This is because the multiplication is implicit, ie `2 - 3` equals `-1` and
|
34
|
+
`2 -3` equals `-6`.
|
data/electr.gemspec
CHANGED
data/lib/electr.rb
CHANGED
@@ -5,16 +5,27 @@ require "electr/exceptions"
|
|
5
5
|
require "electr/parser"
|
6
6
|
require "electr/compiler"
|
7
7
|
require "electr/ast"
|
8
|
-
require "electr/rules"
|
9
|
-
require "electr/evaluator"
|
10
8
|
require "electr/repl"
|
11
9
|
require "electr/option"
|
12
10
|
|
13
11
|
module Electr
|
14
12
|
|
13
|
+
UNARY_MINUS_INTERNAL_SYMBOL = '€'
|
14
|
+
|
15
15
|
# f - function
|
16
16
|
SYMBOL_TABLE = {
|
17
17
|
'sqrt' => 'f',
|
18
18
|
}
|
19
19
|
|
20
|
+
PRECEDENCE = {
|
21
|
+
'()' => {assoc: 'L', val: 99},
|
22
|
+
')' => {assoc: 'L', val: 99},
|
23
|
+
'(' => {assoc: 'L', val: 99},
|
24
|
+
UNARY_MINUS_INTERNAL_SYMBOL => {assoc: 'R', val: 80},
|
25
|
+
'*' => {assoc: 'L', val: 10},
|
26
|
+
'/' => {assoc: 'L', val: 10},
|
27
|
+
'-' => {assoc: 'L', val: 1},
|
28
|
+
'+' => {assoc: 'L', val: 1},
|
29
|
+
}
|
30
|
+
|
20
31
|
end
|
data/lib/electr/ast/ast.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Electr
|
2
2
|
|
3
|
+
# Base class for the abstract syntax tree.
|
3
4
|
class AST
|
4
5
|
|
5
6
|
def initialize(name)
|
@@ -12,6 +13,8 @@ module Electr
|
|
12
13
|
@value = nil
|
13
14
|
end
|
14
15
|
|
16
|
+
attr_reader :value, :name
|
17
|
+
|
15
18
|
# Public: Add a child node to the end of the children's list.
|
16
19
|
#
|
17
20
|
# child - An AST node to add to the list of children.
|
@@ -32,12 +35,8 @@ module Electr
|
|
32
35
|
end
|
33
36
|
|
34
37
|
def display(indent = 0)
|
35
|
-
print
|
36
|
-
|
37
|
-
print " ::= #@value"
|
38
|
-
else
|
39
|
-
print " (#@value)" if @value
|
40
|
-
end
|
38
|
+
print name_for_display(indent)
|
39
|
+
print value_for_display
|
41
40
|
puts
|
42
41
|
@children.each {|child| child.display(indent + 2) }
|
43
42
|
end
|
@@ -46,8 +45,23 @@ module Electr
|
|
46
45
|
[* Pn.new(@value, @name)] + @children.map {|child| child.to_pn }.flatten
|
47
46
|
end
|
48
47
|
|
48
|
+
private
|
49
|
+
|
50
|
+
def name_for_display(indent)
|
51
|
+
" " * indent + @name
|
52
|
+
end
|
53
|
+
|
54
|
+
def value_for_display
|
55
|
+
if leaf?
|
56
|
+
" ::= #@value"
|
57
|
+
else
|
58
|
+
" (#@value)" if @value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
49
62
|
end
|
50
63
|
|
64
|
+
# Element of a prefix notation.
|
51
65
|
# TODO Pn is not a good name!
|
52
66
|
class Pn
|
53
67
|
|
@@ -57,6 +71,10 @@ module Electr
|
|
57
71
|
end
|
58
72
|
|
59
73
|
attr_reader :value, :name
|
74
|
+
|
75
|
+
def to_f
|
76
|
+
value.tr(',', '_').to_f
|
77
|
+
end
|
60
78
|
end
|
61
79
|
|
62
80
|
end
|
data/lib/electr/ast/func_ast.rb
CHANGED
data/lib/electr/ast/root_ast.rb
CHANGED
data/lib/electr/ast/value_ast.rb
CHANGED
data/lib/electr/compiler.rb
CHANGED
@@ -1,22 +1,26 @@
|
|
1
1
|
module Electr
|
2
2
|
|
3
|
+
# The toolchain that takes a code at input and outputs it in another
|
4
|
+
# structure.
|
5
|
+
#
|
6
|
+
# The compiler could outputs the code either as an AST or as a list of
|
7
|
+
# element in the prefix notation.
|
3
8
|
class Compiler
|
4
9
|
|
5
|
-
def compile_to_ast(code)
|
10
|
+
def self.compile_to_ast(code)
|
6
11
|
units = []
|
7
12
|
tokenizer = Tokenizer.new(code)
|
8
|
-
lexer = Lexer.new
|
9
13
|
while tokenizer.has_more_token?
|
10
|
-
units <<
|
14
|
+
units << Lexer.lexify(tokenizer.next_token)
|
11
15
|
end
|
12
16
|
|
13
17
|
syntaxer = Syntaxer.new(units.dup)
|
14
|
-
|
18
|
+
syntaxer.run
|
15
19
|
end
|
16
20
|
|
17
21
|
# To Prefix Notation.
|
18
|
-
def compile_to_pn(code)
|
19
|
-
ast = compile_to_ast(code)
|
22
|
+
def self.compile_to_pn(code)
|
23
|
+
ast = self.compile_to_ast(code)
|
20
24
|
ast.to_pn
|
21
25
|
end
|
22
26
|
|
data/lib/electr/exceptions.rb
CHANGED
data/lib/electr/parser.rb
CHANGED
data/lib/electr/parser/lexer.rb
CHANGED
@@ -1,50 +1,27 @@
|
|
1
1
|
module Electr
|
2
2
|
|
3
|
-
|
3
|
+
# Lexical analysis.
|
4
|
+
module Lexer
|
5
|
+
|
6
|
+
using Token
|
4
7
|
|
5
8
|
# Public: Build a LexicalUnit from a given token.
|
6
9
|
#
|
7
10
|
# token - A String token to transform in LexicalUnit.
|
8
11
|
#
|
9
12
|
# Returns a LexicalUnit.
|
10
|
-
def lexify(token)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
LexicalUnit.open_parenthesis()
|
21
|
-
elsif token == ')'
|
22
|
-
LexicalUnit.closed_parenthesis()
|
23
|
-
elsif SYMBOL_TABLE[token] == 'f'
|
24
|
-
LexicalUnit.fname(token)
|
25
|
-
else
|
26
|
-
LexicalUnit.name(token)
|
13
|
+
def self.lexify(token)
|
14
|
+
case token
|
15
|
+
when ->(x) { x.numeric? } then LexicalUnit.numeric(token)
|
16
|
+
when ->(x) { x.operator? } then LexicalUnit.operator(token)
|
17
|
+
when ->(x) { x.constant? } then LexicalUnit.constant(token)
|
18
|
+
when ->(x) { x.value? } then LexicalUnit.value(token)
|
19
|
+
when ->(x) { x == '(' } then LexicalUnit.open_parenthesis
|
20
|
+
when ->(x) { x == ')' } then LexicalUnit.closed_parenthesis
|
21
|
+
when ->(x) { SYMBOL_TABLE[x] == 'f' } then LexicalUnit.fname(token)
|
22
|
+
else LexicalUnit.name(token)
|
27
23
|
end
|
28
24
|
end
|
29
25
|
|
30
|
-
private
|
31
|
-
|
32
|
-
def numeric?(token)
|
33
|
-
token =~ /[0-9.]\Z/
|
34
|
-
end
|
35
|
-
|
36
|
-
def operator?(token)
|
37
|
-
%w( + - / ).include?(token)
|
38
|
-
end
|
39
|
-
|
40
|
-
def constant?(token)
|
41
|
-
%w( pi ).include?(token)
|
42
|
-
end
|
43
|
-
|
44
|
-
def value?(token)
|
45
|
-
# The unit part is redondant with Tokenizer.
|
46
|
-
token =~ /[0-9.][kKRuFpΩμAmWV]{1,}\Z/
|
47
|
-
end
|
48
|
-
|
49
26
|
end
|
50
27
|
end
|