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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/CHANGELOG.md +17 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +196 -0
  9. data/Rakefile +7 -0
  10. data/bin/electr +27 -0
  11. data/electr.gemspec +25 -0
  12. data/lib/electr.rb +20 -0
  13. data/lib/electr/ast.rb +9 -0
  14. data/lib/electr/ast/ast.rb +62 -0
  15. data/lib/electr/ast/constant_ast.rb +14 -0
  16. data/lib/electr/ast/func_args_ast.rb +11 -0
  17. data/lib/electr/ast/func_ast.rb +12 -0
  18. data/lib/electr/ast/func_name_ast.rb +14 -0
  19. data/lib/electr/ast/numeric_ast.rb +13 -0
  20. data/lib/electr/ast/operator_ast.rb +13 -0
  21. data/lib/electr/ast/root_ast.rb +11 -0
  22. data/lib/electr/ast/value_ast.rb +12 -0
  23. data/lib/electr/compiler.rb +24 -0
  24. data/lib/electr/evaluator.rb +64 -0
  25. data/lib/electr/exceptions.rb +6 -0
  26. data/lib/electr/option.rb +70 -0
  27. data/lib/electr/parser.rb +5 -0
  28. data/lib/electr/parser/lexer.rb +50 -0
  29. data/lib/electr/parser/lexical_unit.rb +67 -0
  30. data/lib/electr/parser/rules/base_rule.rb +60 -0
  31. data/lib/electr/parser/rules/expression_rule.rb +79 -0
  32. data/lib/electr/parser/rules/root_rule.rb +21 -0
  33. data/lib/electr/parser/sya.rb +105 -0
  34. data/lib/electr/parser/syntaxer.rb +21 -0
  35. data/lib/electr/parser/tokenizer.rb +79 -0
  36. data/lib/electr/repl.rb +56 -0
  37. data/lib/electr/rules.rb +3 -0
  38. data/lib/electr/version.rb +3 -0
  39. data/spec/compiler_spec.rb +42 -0
  40. data/spec/electr_spec.rb +7 -0
  41. data/spec/evaluator_spec.rb +47 -0
  42. data/spec/lexer_spec.rb +29 -0
  43. data/spec/spec_helper.rb +8 -0
  44. data/spec/sya_spec.rb +153 -0
  45. data/spec/tokenizer_spec.rb +102 -0
  46. metadata +138 -0
@@ -0,0 +1,11 @@
1
+ module Electr
2
+
3
+ class FuncArgsAST < AST
4
+
5
+ def initialize
6
+ super("funcargs")
7
+ end
8
+
9
+ end
10
+ end
11
+
@@ -0,0 +1,12 @@
1
+ module Electr
2
+
3
+ class FuncAST < AST
4
+
5
+ def initialize
6
+ super("func")
7
+ end
8
+
9
+ end
10
+ end
11
+
12
+
@@ -0,0 +1,14 @@
1
+ module Electr
2
+
3
+ class FuncNameAST < AST
4
+
5
+ def initialize(value)
6
+ super("funcname")
7
+ @value = value
8
+ end
9
+
10
+ end
11
+ end
12
+
13
+
14
+
@@ -0,0 +1,13 @@
1
+ module Electr
2
+
3
+ class NumericAST < AST
4
+
5
+ def initialize(value)
6
+ super("numeric")
7
+ @value = value
8
+ end
9
+
10
+ end
11
+ end
12
+
13
+
@@ -0,0 +1,13 @@
1
+ module Electr
2
+
3
+ class OperatorAST < AST
4
+
5
+ def initialize(value)
6
+ super("operator")
7
+ @value = value
8
+ end
9
+
10
+ end
11
+ end
12
+
13
+
@@ -0,0 +1,11 @@
1
+ module Electr
2
+
3
+ class RootAST < AST
4
+
5
+ def initialize
6
+ super("root")
7
+ end
8
+
9
+ end
10
+ end
11
+
@@ -0,0 +1,12 @@
1
+ module Electr
2
+
3
+ class ValueAST < AST
4
+
5
+ def initialize(value)
6
+ super("value")
7
+ @value = value
8
+ end
9
+
10
+ end
11
+ end
12
+
@@ -0,0 +1,24 @@
1
+ module Electr
2
+
3
+ class Compiler
4
+
5
+ def compile_to_ast(code)
6
+ units = []
7
+ tokenizer = Tokenizer.new(code)
8
+ lexer = Lexer.new
9
+ while tokenizer.has_more_token?
10
+ units << lexer.lexify(tokenizer.next_token)
11
+ end
12
+
13
+ syntaxer = Syntaxer.new(units.dup)
14
+ ast = syntaxer.run
15
+ end
16
+
17
+ # To Prefix Notation.
18
+ def compile_to_pn(code)
19
+ ast = compile_to_ast(code)
20
+ ast.to_pn
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,64 @@
1
+ module Electr
2
+
3
+ class Evaluator
4
+
5
+ def initialize
6
+ @stack = []
7
+ end
8
+
9
+ def evaluate_pn(list)
10
+ while item = list.pop
11
+ case item.name
12
+ when "numeric" then @stack.push(item.value.to_f)
13
+ when "constant" then @stack.push(constant(item.value))
14
+ when "value" then do_value(item.value)
15
+ when "operator" then operation(item.value)
16
+ when "funcname" then func(item.value)
17
+ end
18
+ end
19
+
20
+ @stack.pop
21
+ end
22
+
23
+ private
24
+
25
+ def constant(value)
26
+ case value
27
+ when "pi" then Math::PI
28
+ end
29
+ end
30
+
31
+ def func(name)
32
+ case name
33
+ when "sqrt"
34
+ a = @stack.pop
35
+ @stack.push(Math::sqrt(a))
36
+ end
37
+ end
38
+
39
+ def operation(operand)
40
+ a = @stack.pop
41
+ b = @stack.pop
42
+ @stack.push(a.send(operand, b))
43
+ end
44
+
45
+ def do_value(str)
46
+ # FIXME It's going to be better to separate the check for the unit
47
+ # (ohm, volt, ampere, etc) and for the prefix ([m]illi, [k]ilo,
48
+ # etc).
49
+ val = str.to_f
50
+ if %w( k K ).include?(str[-1]) || %w( kΩ ).include?(str[-2..-1])
51
+ val *= 1_000
52
+ elsif %w( mA mV mW ).include?(str[-2..-1])
53
+ val *= 0.001
54
+ elsif %w( u μ ).include?(str[-1]) || %w( μF uF ).include?(str[-2..-1])
55
+ val *= 0.000_001
56
+ elsif str[-1] == 'p' || str[-2..-1] == 'pF'
57
+ val *= 0.000_000_001
58
+ end
59
+ @stack.push(val)
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,6 @@
1
+ module Electr
2
+
3
+ class SyntaxError < StandardError
4
+ end
5
+
6
+ end
@@ -0,0 +1,70 @@
1
+ module Electr
2
+
3
+ # Process command line switches.
4
+ #
5
+ # The keys you are going to use:
6
+ #
7
+ # :ast - Boolean, if true the user want to print the Abstract
8
+ # Syntax Tree instead of the result.
9
+ # :expression - Don't run interactively, parse this String and quit.
10
+ #
11
+ # Examples
12
+ #
13
+ # opt = Option.new
14
+ # if opt[:expression]
15
+ # puts "Doing the job with #{opt[:expression]}"
16
+ # end
17
+ class Option
18
+
19
+ # Creates a new Option instance.
20
+ def initialize
21
+ @options = { ast: false }
22
+
23
+ optparse = OptionParser.new {|opts| parse(opts) }
24
+
25
+ begin
26
+ optparse.parse!
27
+ rescue OptionParser::InvalidOption => exception
28
+ puts exception.to_s
29
+ exit 1
30
+ end
31
+
32
+ print_version if @options[:version]
33
+ end
34
+
35
+ # Get an option.
36
+ #
37
+ # key - The Symbol name of the option to get.
38
+ #
39
+ # Returns Any value corresponding of the key, or nil if the key
40
+ # doesn't exists.
41
+ def [](key)
42
+ @options[key]
43
+ end
44
+
45
+ def parse(opts)
46
+ opts.on(nil, '--ast', 'Display AST and quit') do
47
+ @options[:ast] = true
48
+ end
49
+ opts.on('-e', '--expression EXP', 'Compute EXP and quit') do |arg|
50
+ @options[:expression] = arg
51
+ end
52
+ opts.on('-v', '--version', 'Print version number and exit') do
53
+ @options[:version] = true
54
+ end
55
+ opts.on('-h', '--help', 'Display this screen') do
56
+ puts opts
57
+ exit
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def print_version
64
+ puts "Electr version #{VERSION}"
65
+ exit
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,5 @@
1
+ require "electr/parser/tokenizer"
2
+ require "electr/parser/lexer"
3
+ require "electr/parser/lexical_unit"
4
+ require "electr/parser/syntaxer"
5
+ require "electr/parser/sya"
@@ -0,0 +1,50 @@
1
+ module Electr
2
+
3
+ class Lexer
4
+
5
+ # Public: Build a LexicalUnit from a given token.
6
+ #
7
+ # token - A String token to transform in LexicalUnit.
8
+ #
9
+ # 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)
27
+ end
28
+ end
29
+
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
+ end
50
+ end
@@ -0,0 +1,67 @@
1
+ module Electr
2
+
3
+ class LexicalUnit
4
+
5
+ private_class_method :new
6
+
7
+ attr_reader :type, :value
8
+
9
+ # type - Symbol
10
+ # value - String
11
+ def initialize(type, value)
12
+ @type = type
13
+ @value = value
14
+ end
15
+
16
+ def self.numeric(value) ; new(:numeric, value) ; end
17
+ def self.operator(value) ; new(:operator, value) ; end
18
+ def self.constant(value) ; new(:constant, value) ; end
19
+ def self.value(value) ; new(:value, value) ; end
20
+ def self.name(value) ; new(:name, value) ; end
21
+ def self.fname(value) ; new(:fname, value) ; end
22
+ def self.fcall(value) ; new(:fcall, value) ; end
23
+ def self.open_parenthesis ; new(:open_parenthesis, "(") ; end
24
+ def self.closed_parenthesis ; new(:closed_parenthesis, ")") ; end
25
+
26
+ def ==(other)
27
+ @type == other.type && @value == other.value
28
+ end
29
+
30
+ def numeric?
31
+ @type == :numeric
32
+ end
33
+
34
+ def operator?
35
+ @type == :operator
36
+ end
37
+
38
+ def constant?
39
+ @type == :constant
40
+ end
41
+
42
+ def value?
43
+ @type == :value
44
+ end
45
+
46
+ def fname?
47
+ @type == :fname
48
+ end
49
+
50
+ def fcall?
51
+ @type == :fcall
52
+ end
53
+
54
+ def number?
55
+ numeric? || constant? || value?
56
+ end
57
+
58
+ def closed_parenthesis?
59
+ @type == :closed_parenthesis
60
+ end
61
+
62
+ def open_parenthesis?
63
+ @type == :open_parenthesis
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,60 @@
1
+ module Electr
2
+
3
+ class BaseRule
4
+
5
+ # Initialize a new rule. The units and ast_node objects
6
+ # given in arguments will be modified by #apply!.
7
+ #
8
+ # units - Array of LexicalUnits.
9
+ # ast_node - An AST object, for adding children, etc.
10
+ def initialize(units, ast_node)
11
+ @units = units
12
+ @ast_node = ast_node
13
+ @series = []
14
+ end
15
+
16
+ # Public: Apply the rule to the list of lexical units, creating
17
+ # and/or modifying the ast node. #apply! modify the units and
18
+ # ast_node objects given to #initialize.
19
+ def apply!
20
+ raise NotImplementedError
21
+ end
22
+
23
+ private
24
+
25
+ # Accepts the next lexical unit, given type match the unit type.
26
+ # If value is given, it has to match the unit value.
27
+ #
28
+ # type - Expected Symbol type of the next unit.
29
+ # value - Expected String value of the next unit. Default is the
30
+ # empty string, meaning the value doesn't matter.
31
+ #
32
+ # Returns nothing.
33
+ # Raises SyntaxError if type or value doesn't match.
34
+ def accept(type, value = '')
35
+ unit = @units.slice!(0)
36
+ @series << unit
37
+ unless unit.type == type
38
+ raise(SyntaxError, "Expected type : #{type}\n" +
39
+ "Expected value: #{value}\n" +
40
+ "Got type : #{unit.type}\n" +
41
+ "Got value : #{unit.value}")
42
+ end
43
+ if value != ''
44
+ raise SyntaxError unless unit.value == value
45
+ end
46
+ end
47
+
48
+ # A shortcut method to #accept. Usefull when we have several calls
49
+ # to #accept. Calls #accept for each of the symbol in args.
50
+ #
51
+ # args - A series of Symbol.
52
+ #
53
+ # Returns nothing.
54
+ def accept_all(*args)
55
+ args.each {|arg| accept(arg) }
56
+ end
57
+
58
+ end
59
+ end
60
+