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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 129c7c8560ec169ade1a934c574de8a045e3dbc0
4
- data.tar.gz: c04ece5fd979bad30c69e5d67b64899b841ac72e
3
+ metadata.gz: 6be27b7b5efaeb5cb477f1bda270e4d1b95d4fec
4
+ data.tar.gz: 086e216808aa9e6709ee47ff5a648a3d175779f7
5
5
  SHA512:
6
- metadata.gz: faee90b0d4a72a8bf8c74c9530c697062e7edde33c6c96b0135d56aa93bda5573e1d78647279b7104622300bab369900f1ffd5d2d2be0b30915af20bc5430b6e
7
- data.tar.gz: c04497b5911bb940656a495eaab0232be5394b456baf27c4f1a209a3aa2e86f27630e986b59b2499ba3c417a0bf557305c1f04fbe698a1795f6dc3a6e23fd23f
6
+ metadata.gz: 9d206038f0b0f77a76fc388c1f152416f623af280a766cb93f67fdf547e200c4ad3d135f6b2db4a2c4ce2e560eb55ef43bd3dde0bc14cf83c534bc3b352c272c
7
+ data.tar.gz: 3877f48e19951b1b2cb78b4fa0b3ef9197b16229246e63a18e1aefa575b054cade8fd5497815beb237204c5624e58a00a39fcf417d1725743b3747f0eff4adc4
data/.coco.yml ADDED
@@ -0,0 +1,4 @@
1
+ :excludes:
2
+ - lib/electr/version.rb
3
+ - lib/electr/option.rb
4
+ :always_run: false
data/.gitignore CHANGED
@@ -13,3 +13,4 @@
13
13
  *.a
14
14
  mkmf.log
15
15
  TODO
16
+ tags
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 [![Gem Version](https://badge.fury.io/rb/electr.png)](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
- [ ] 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
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/[my-github-username]/electr/fork )
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
- compiler = Electr::Compiler.new
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
- compiler = Electr::Compiler.new
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
- Electr::Repl.new(opt).run
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`.
@@ -0,0 +1,8 @@
1
+ Precedence Table
2
+ ================
3
+
4
+ Operator | Description | Associativity
5
+ :-------: | ------------- | --------------
6
+ () | Function call | Left
7
+ * / | Multiplication and division | Left
8
+ + - | Addition and substraction | Left
data/electr.gemspec CHANGED
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "bundler", "~> 1.7"
23
23
  spec.add_development_dependency "rake", "~> 10.0"
24
24
  spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "coco"
25
26
  end
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
@@ -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 " " * indent + @name
36
- if leaf?
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
@@ -1,5 +1,6 @@
1
1
  module Electr
2
2
 
3
+ # A constant node in the AST, ie the number pi.
3
4
  class ConstantAST < AST
4
5
 
5
6
  def initialize(value)
@@ -1,5 +1,6 @@
1
1
  module Electr
2
2
 
3
+ # A node in the AST to hold arguments of a function.
3
4
  class FuncArgsAST < AST
4
5
 
5
6
  def initialize
@@ -1,5 +1,6 @@
1
1
  module Electr
2
2
 
3
+ # A node in the AST to represent a function.
3
4
  class FuncAST < AST
4
5
 
5
6
  def initialize
@@ -1,5 +1,7 @@
1
1
  module Electr
2
2
 
3
+ # A node in the AST to represent the name of a function, ie sqrt, sin
4
+ # or cos.
3
5
  class FuncNameAST < AST
4
6
 
5
7
  def initialize(value)
@@ -1,5 +1,6 @@
1
1
  module Electr
2
2
 
3
+ # A node in the AST to represent a numeric, ie 234 or 17.89
3
4
  class NumericAST < AST
4
5
 
5
6
  def initialize(value)
@@ -1,5 +1,6 @@
1
1
  module Electr
2
2
 
3
+ # A node in the AST to hold an operator, ie +, -, etc.
3
4
  class OperatorAST < AST
4
5
 
5
6
  def initialize(value)
@@ -1,5 +1,6 @@
1
1
  module Electr
2
2
 
3
+ # The root node of the AST.
3
4
  class RootAST < AST
4
5
 
5
6
  def initialize
@@ -1,5 +1,6 @@
1
1
  module Electr
2
2
 
3
+ # A node in the AST to hold a value, ie 10mA or 3V.
3
4
  class ValueAST < AST
4
5
 
5
6
  def initialize(value)
@@ -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 << lexer.lexify(tokenizer.next_token)
14
+ units << Lexer.lexify(tokenizer.next_token)
11
15
  end
12
16
 
13
17
  syntaxer = Syntaxer.new(units.dup)
14
- ast = syntaxer.run
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
 
@@ -1,5 +1,6 @@
1
1
  module Electr
2
2
 
3
+ # The good old syntax error ;)
3
4
  class SyntaxError < StandardError
4
5
  end
5
6
 
data/lib/electr/parser.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require "electr/parser/tokenizer"
2
+ require "electr/parser/token"
2
3
  require "electr/parser/lexer"
3
4
  require "electr/parser/lexical_unit"
5
+ require "electr/parser/rules"
4
6
  require "electr/parser/syntaxer"
5
7
  require "electr/parser/sya"
@@ -1,50 +1,27 @@
1
1
  module Electr
2
2
 
3
- class Lexer
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
- if numeric?(token)
12
- LexicalUnit.numeric(token)
13
- elsif operator?(token)
14
- LexicalUnit.operator(token)
15
- elsif constant?(token)
16
- LexicalUnit.constant(token)
17
- elsif value?(token)
18
- LexicalUnit.value(token)
19
- elsif token == '('
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