jcaladh-rubycalc 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e15edd0a3329e9e444b190344f2718189b69dc19
4
+ data.tar.gz: 06d8176f96f44e5343a0a4b33c1f9934baeb0e92
5
+ SHA512:
6
+ metadata.gz: c3a4ad40b5ef057962aa986e6a9c7a7eebbf37555f52f13f7ab3fa8dea4a31c316444767840f986b8d3622d2b13356f54a62a2529bdc2bc373762596c179bef3
7
+ data.tar.gz: 5904c4aa1ae0c5e837b69fd560cda627717050557885f1fdd26d25516d610e56dceb13bb64c362a8b7e3fe94a7d93e01e6f663879b7cb5fb37ce9ed5687d65b6
data/bin/rubycalc ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'calculator'
5
+ require 'calcex'
6
+
7
+ $stdout.print "> "
8
+ $stdout.flush
9
+
10
+ text = gets
11
+
12
+ $calc = Calculator.new()
13
+
14
+ begin
15
+ puts "= " + $calc.eval(text).to_s
16
+ rescue ParseError
17
+ puts "Parse Error"
18
+ rescue UnrecognizedTokenException
19
+ puts "UnrecognizedTokenException"
20
+ rescue
21
+ puts "Unkown exception"
22
+ end
data/lib/ast.rb ADDED
@@ -0,0 +1,85 @@
1
+ require 'set'
2
+ class BinaryNode
3
+ attr_reader :left, :right
4
+
5
+ def initialize(left,right)
6
+ @left = left
7
+ @right = right
8
+ end
9
+ end
10
+
11
+ class UnaryNode
12
+ attr_reader :subTree
13
+
14
+ def initialize(subTree)
15
+ @subTree = subTree
16
+ end
17
+ end
18
+
19
+ class AddNode < BinaryNode
20
+ def initialize(left, right)
21
+ super(left,right)
22
+ end
23
+
24
+ def evaluate()
25
+ return @left.evaluate() + @right.evaluate()
26
+ end
27
+ end
28
+
29
+ class SubNode < BinaryNode
30
+ def initialize(left, right)
31
+ super(left,right)
32
+ end
33
+
34
+ def evaluate()
35
+ return @left.evaluate() - @right.evaluate()
36
+ end
37
+ end
38
+
39
+ class TimesNode < BinaryNode
40
+ def initialize(left, right)
41
+ super(left,right)
42
+ end
43
+
44
+ def evaluate()
45
+ return @left.evaluate() * @right.evaluate()
46
+ end
47
+ end
48
+
49
+ class DivideNode < BinaryNode
50
+ def initialize(left, right)
51
+ super(left,right)
52
+ end
53
+
54
+ def evaluate()
55
+ return @left.evaluate() / @right.evaluate()
56
+ end
57
+ end
58
+
59
+ class NumNode
60
+ def initialize(num)
61
+ @num = num
62
+ end
63
+
64
+ def evaluate()
65
+ return @num
66
+ end
67
+ end
68
+
69
+
70
+ class StoreNode < UnaryNode
71
+ def initialize(sub)
72
+ super(sub)
73
+ end
74
+
75
+ def evaluate()
76
+ $calc.memory = @subTree.evaluate
77
+ end
78
+ end
79
+
80
+ class RecallNode
81
+
82
+ def evaluate()
83
+ $calc.memory
84
+ end
85
+ end
data/lib/calcex.rb ADDED
@@ -0,0 +1,3 @@
1
+
2
+ class ParseError < Exception; end
3
+ class UnrecognizedTokenException < Exception; end
data/lib/calculator.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'parser'
2
+ require 'ast'
3
+
4
+ class Calculator
5
+ attr_accessor :memory
6
+
7
+ def initialize()
8
+ @memory = 0
9
+ end
10
+
11
+ def eval(expr)
12
+ parser = Parser.new(StringIO.new(expr))
13
+ ast = parser.parse()
14
+ return ast.evaluate()
15
+ end
16
+ end
data/lib/parser.rb ADDED
@@ -0,0 +1,106 @@
1
+ require 'ast'
2
+ require 'scanner'
3
+ require 'token'
4
+ require 'calcex'
5
+
6
+ class Parser
7
+ def initialize(istream)
8
+ @scan = Scanner.new(istream)
9
+ end
10
+
11
+ def parse()
12
+ return Prog()
13
+ end
14
+
15
+ # private public protected
16
+ def Prog()
17
+ result = Expr()
18
+ t = @scan.sgetToken()
19
+
20
+ unless t.type == :eof then
21
+ print "Expected EOF. Found ", t.type, ".\n"
22
+ raise ParseError.new
23
+ end
24
+
25
+ return result
26
+ end
27
+
28
+ def Expr()
29
+ return RestExpr(Term())
30
+ end
31
+
32
+ def RestExpr(e)
33
+ t = @scan.getToken()
34
+
35
+ if t.type == :add then
36
+ return RestExpr(AddNode.new(e,Term()))
37
+ end
38
+
39
+ if t.type == :sub then
40
+ return RestExpr(SubNode.new(e,Term()))
41
+ end
42
+
43
+ @scan.putBackToken()
44
+
45
+ return e
46
+ end
47
+
48
+ def Term()
49
+ return RestTerm(Storable())
50
+ end
51
+
52
+ def RestTerm(e)
53
+ t= @scan.getToken
54
+ if t.type==:times then
55
+ return RestTerm(TimesNode.new(e,Storable()))
56
+ end
57
+ if t.type==:divide then
58
+ return RestTerm(DivideNode.new(e,Storable()))
59
+ end
60
+ @scan.putBackToken
61
+
62
+ return e
63
+ end
64
+
65
+ def Storable()
66
+ fact= Factor()
67
+ t=@scan.getToken
68
+ if t.type == :keyword then
69
+ if t.lex "S" then
70
+ return StoreNode.new(fact)
71
+ else
72
+ raise ParseError.new
73
+ end
74
+ end
75
+ @scan.putBackToken
76
+ return fact
77
+ end
78
+
79
+ def Factor()
80
+ t=@scan.getToken
81
+
82
+ if t.type == :number then
83
+ return NumNode.new(t.lex.to_i)
84
+ end
85
+
86
+ if t.type == :keyword then
87
+ if t.lex =="R" then
88
+ return RecallNode.new
89
+ else
90
+ raise ParseError.new
91
+ end
92
+ end
93
+
94
+ if t.type == :lparen then
95
+ expr = Expr()
96
+ t=@scan.getToken
97
+ if t.type == :rparen then
98
+ return expr
99
+ else
100
+ raise ParseError.new
101
+ end
102
+ end
103
+ raise ParseError.new
104
+ end
105
+
106
+ end
data/lib/scanner.rb ADDED
@@ -0,0 +1,128 @@
1
+ require 'stringio'
2
+ require 'calcex'
3
+ require 'set'
4
+
5
+ class Scanner
6
+ def initialize(inStream)
7
+ @istream = inStream
8
+ @keywords = Set.new(["S","R"])
9
+ @lineCount = 1
10
+ @colCount = -1
11
+ @needToken = true
12
+ @lastToken = nil
13
+ end
14
+
15
+ def putBackToken()
16
+ @needToken = false
17
+ end
18
+
19
+ def getToken()
20
+ if !@needToken
21
+ @needToken = true
22
+ return @lastToken
23
+ end
24
+
25
+ state = 0
26
+ foundOne = false
27
+ c = @istream.getc()
28
+
29
+ if @istream.eof() then
30
+ @lastToken = Token.new(:eof,@lineCount,@colCount)
31
+ return @lastToken
32
+ end
33
+
34
+ while !foundOne
35
+ @colCount = @colCount + 1
36
+ case state
37
+ when 0
38
+ lex = ""
39
+ column = @colCount
40
+ line = @lineCount
41
+ if isLetter(c) then state=1
42
+ elsif isDigit(c) then state=2
43
+ elsif c == ?+ then state = 3
44
+ elsif c == ?- then state = 4
45
+ elsif c == ?* then state = 5
46
+ elsif c == ?/ then state = 6
47
+ elsif c == ?( then state = 7
48
+ elsif c == ?) then state = 8
49
+ elsif c == ?\n then
50
+ @colCount = -1
51
+ @lineCount = @lineCount+1
52
+ elsif isWhiteSpace(c) then state = state #ignore whitespace
53
+ elsif @istream.eof() then
54
+ @foundOne = true
55
+ type = :eof
56
+ else
57
+ puts "Unrecognized Token found at line ",line," and column ",column,"\n"
58
+ raise UnrecognizedTokenException # "Unrecognized Token"
59
+ end
60
+ when 1
61
+ if isLetter(c) or isDigit(c) then state = 1
62
+ else
63
+ if @keywords.include?(lex) then
64
+ foundOne = true
65
+ type = :keyword
66
+ else
67
+ foundOne = true
68
+ type = :identifier
69
+ end
70
+ end
71
+ when 2
72
+ if isDigit(c) then state = 2
73
+ else
74
+ type = :number
75
+ foundOne = true
76
+ end
77
+ when 3
78
+ type = :add
79
+ foundOne = true
80
+ when 4
81
+ type = :sub
82
+ foundOne = true
83
+ when 5
84
+ type = :times
85
+ foundOne = true
86
+ when 6
87
+ type = :divide
88
+ foundOne = true
89
+ when 7
90
+ type = :lparen
91
+ foundOne = true
92
+ when 8
93
+ type = :rparen
94
+ foundOne = true
95
+ end
96
+
97
+ if !foundOne then
98
+ lex.concat(c)
99
+ c = @istream.getc()
100
+ end
101
+
102
+ end
103
+
104
+ @istream.ungetc(c)
105
+ @colCount = @colCount - 1
106
+ if type == :number or type == :identifier or type == :keyword then
107
+ t = LexicalToken.new(type,lex,line,column)
108
+ else
109
+ t = Token.new(type,line,column)
110
+ end
111
+
112
+ @lastToken = t
113
+ return t
114
+ end
115
+
116
+ private
117
+ def isLetter(c)
118
+ return ((?a <= c and c <= ?z) or (?A <= c and c <= ?Z))
119
+ end
120
+
121
+ def isDigit(c)
122
+ return (?0 <= c and c <= ?9)
123
+ end
124
+
125
+ def isWhiteSpace(c)
126
+ return (c == ?\ or c == ?\n or c == ?\t)
127
+ end
128
+ end
data/lib/token.rb ADDED
@@ -0,0 +1,18 @@
1
+ class Token
2
+ attr_reader :type, :line, :col
3
+
4
+ def initialize(type,lineNum,colNum)
5
+ @type = type
6
+ @line = lineNum
7
+ @col = colNum
8
+ end
9
+ end
10
+
11
+ class LexicalToken < Token
12
+ attr_reader :lex
13
+
14
+ def initialize(type,lex,lineNum,colNum)
15
+ super(type,lineNum,colNum)
16
+ @lex = lex
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jcaladh-rubycalc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Kent D. Lee - Juan Pablo Calad Henao
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: An calculator implementation on ruby
14
+ email: jcaladh@eafit.edu.co
15
+ executables:
16
+ - rubycalc
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/rubycalc
21
+ - lib/ast.rb
22
+ - lib/calcex.rb
23
+ - lib/calculator.rb
24
+ - lib/parser.rb
25
+ - lib/scanner.rb
26
+ - lib/token.rb
27
+ homepage: http://www1.eafit.edu.co/fcardona/cursos/st0244/rubycal
28
+ licenses:
29
+ - ARTISTIC
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 2.4.8
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: Another calculator in ruby
51
+ test_files: []