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: 032e6555fd45f98aa6ecbed768f7e4f167e4cd98
4
+ data.tar.gz: f42953bb6461cc2a61cd0d0eadd4d3f57367b827
5
+ SHA512:
6
+ metadata.gz: 739c596d4758478d9f1983e2280a7f5f2c35e233f86db48eb8b74b738567afdba2bc972b2de73c9e50120783790991f0396edd656007d09892398f492adac8bb
7
+ data.tar.gz: 527bbd487f8f49bbf8f8a945b872e8f881b3838603f5fe49cc5ea876614ab4fbac3f2263242aeecf9721ad5a81a0cea00658355b4d2d90c3c58282750fb380f5
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
+
3
+ class BinaryNode
4
+ attr_reader :left, :right
5
+
6
+ def initialize(left,right)
7
+ @left = left
8
+ @right = right
9
+ end
10
+ end
11
+
12
+ class UnaryNode
13
+ attr_reader :subTree
14
+
15
+ def initialize(subTree)
16
+ @subTree = subTree
17
+ end
18
+ end
19
+
20
+ class AddNode < BinaryNode
21
+ def initialize(left, right)
22
+ super(left,right)
23
+ end
24
+
25
+ def evaluate()
26
+ @left.evaluate() + @right.evaluate()
27
+ end
28
+ end
29
+
30
+ class SubNode < BinaryNode
31
+ def initialize(left, right)
32
+ super(left,right)
33
+ end
34
+
35
+ def evaluate()
36
+ @left.evaluate() - @right.evaluate()
37
+ end
38
+ end
39
+
40
+ class NumNode
41
+ def initialize(num)
42
+ @num = num
43
+ end
44
+
45
+ def evaluate()
46
+ return @num
47
+ end
48
+ end
49
+
50
+ class TimesNode < BinaryNode
51
+ def initialize(left, right)
52
+ super(left,right)
53
+ end
54
+ def evaluate()
55
+ @left.evaluate() * @right.evaluate()
56
+ end
57
+ end
58
+
59
+ class DivideNode < BinaryNode
60
+ def initialize(left, right)
61
+ super(left,right)
62
+ end
63
+ def evaluate()
64
+ @left.evaluate() / @right.evaluate()
65
+ end
66
+ end
67
+
68
+ class StoreNode < UnaryNode
69
+ def initialize(sub)
70
+ super(sub)
71
+
72
+ end
73
+
74
+ def evaluate()
75
+ $calc.memory = $subTree.evaluate
76
+ end
77
+
78
+ end
79
+
80
+ class RecallNode
81
+ def evaluate()
82
+ $calc.memory
83
+ end
84
+ end
85
+
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,137 @@
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.getToken()
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
+
50
+
51
+ RestTerm(Storable())
52
+
53
+
54
+ # Write your Term() code here. This code is just temporary
55
+ # so you can try the calculator out before finishing it.
56
+
57
+ # t = @scan.getToken()
58
+
59
+ # if t.type == :number then
60
+ # val = t.lex.to_i
61
+ # return NumNode.new(val)
62
+ # end
63
+
64
+ # puts "Term not implemented\n"
65
+
66
+ # raise ParseError.new
67
+ end
68
+
69
+ def RestTerm(e)
70
+ t = @scan.getToken
71
+
72
+ if t.type == :times then
73
+ return RestTerm(TimesNode.new(e, Storable()))
74
+ end
75
+
76
+ if t.type == :divide then
77
+ return RestTerm(DivideNode.new(e, Storable()))
78
+ end
79
+
80
+ @scan.putBackToken
81
+
82
+ e
83
+
84
+ # puts "RestTerm not implemented"
85
+ # raise ParseError.new # "Parse Error"
86
+ end
87
+
88
+ def Storable()
89
+
90
+ fact = Factor()
91
+
92
+ t = @scan.getToken
93
+
94
+ if t.type == :keyword then
95
+ if t.lex == "S" then
96
+ return StoreNode.new(fact)
97
+ else
98
+ raise ParseError.new
99
+ end
100
+ end
101
+
102
+ @scan.putBackToken
103
+
104
+ fact
105
+
106
+
107
+ # puts "Storable not implemented"
108
+ # raise ParseError.new # "Parse Error"
109
+ end
110
+
111
+ def Factor()
112
+ t = @scan.getToken
113
+ if t.type == :number then
114
+ return NumNode.new(t.lex.to_i)
115
+ end
116
+ if t.type == :keyword then
117
+ if t.lex == "R" then
118
+ return RecallNode.new
119
+ else
120
+ raise ParseError
121
+ end
122
+ end
123
+ if t.ype == :lparen then
124
+ expr = Expr()
125
+ t = @scan.getToken
126
+ if t.type == :rparen then
127
+ return expr
128
+ else
129
+ raise ParseError.new
130
+ end
131
+ end
132
+ raise ParseError.new
133
+
134
+ # puts "Factor not implemented"
135
+ # raise ParserError.new # "Parse Error"
136
+ end
137
+ end
data/lib/scanner.rb ADDED
@@ -0,0 +1,147 @@
1
+ require 'stringio'
2
+ require 'calcex'
3
+ require 'set'
4
+
5
+
6
+ class Scanner
7
+ def initialize(inStream)
8
+ @istream = inStream
9
+ @keywords = Set.new(["S","R"])
10
+ @lineCount = 1
11
+ @colCount = -1
12
+ @needToken = true
13
+ @lastToken = nil
14
+ end
15
+
16
+ def putBackToken()
17
+ @needToken = false
18
+ end
19
+
20
+ def getToken()
21
+ unless @needToken then
22
+ @needToken = true
23
+ return @lastToken
24
+ end
25
+
26
+ state = 0
27
+ foundOne = false
28
+ c = @istream.getc()
29
+
30
+ if @istream.eof() then
31
+ @lastToken = Token.new(:eof,@lineCount,@colCount)
32
+ return @lastToken
33
+ end
34
+
35
+ until foundOne do
36
+ @colCount = @colCount + 1
37
+ case state
38
+ when 0
39
+ lex = ""
40
+ column = @colCount
41
+ line = @lineCount
42
+ if isLetter(c) then state=1
43
+ elsif isDigit(c) then state=2
44
+ elsif c == ?+ then state = 3
45
+ elsif c == ?- then state = 4
46
+ elsif c == ?* then state = 5
47
+ elsif c == ?/ then state = 6
48
+ elsif c == ?( then state = 7
49
+ elsif c == ?) then state = 8
50
+ elsif c == ?\n then
51
+ @colCount = -1
52
+ @lineCount = @lineCount+1
53
+ elsif isWhiteSpace(c) then state = state #ignore whitespace
54
+ elsif @istream.eof() then
55
+ @foundOne = true
56
+ type = :eof
57
+ else
58
+ puts "Unrecognized Token found at line ",line," and column ",column,"\n"
59
+ raise UnrecognizedTokenException # "Unrecognized Token"
60
+ end
61
+ when 1
62
+ if isLetter(c) or isDigit(c) then state = 1
63
+ else
64
+ if @keywords.include?(lex) then
65
+ foundOne = true
66
+ type = :keyword
67
+ else
68
+ foundOne = true
69
+ type = :identifier
70
+ end
71
+ end
72
+ when 2
73
+ if isDigit(c) then state = 2
74
+ else
75
+ type = :number
76
+ foundOne = true
77
+ end
78
+ when 3
79
+ type = :add
80
+ foundOne = true
81
+ when 4
82
+ type = :sub
83
+ foundOne = true
84
+ when 5
85
+ type = :times
86
+ foundOne = true
87
+ when 6
88
+ type = :divide
89
+ foundOne = true
90
+ when 7
91
+ type = :lparen
92
+ foundOne = true
93
+ when 8
94
+ type = :rparen
95
+ foundOne = true
96
+ end
97
+
98
+ if !foundOne then
99
+ lex.concat(c)
100
+ c = @istream.getc()
101
+ end
102
+
103
+ end
104
+
105
+ @istream.ungetc(c)
106
+ @colCount = @colCount - 1
107
+ if type == :number or type == :identifier or type == :keyword then
108
+ t = LexicalToken.new(type,lex,line,column)
109
+ else
110
+ t = Token.new(type,line,column)
111
+ end
112
+
113
+ @lastToken = t
114
+ return t
115
+ end
116
+
117
+ private
118
+ def isLetter(c)
119
+ if c.nil?
120
+ false
121
+ else
122
+ return ((?a <= c and c <= ?z) or (?A <= c and c <= ?Z))
123
+ end
124
+ end
125
+
126
+ def isDigit(c)
127
+ if c.nil?
128
+ false
129
+ else
130
+ ?0 <= c and c <= ?9
131
+ end
132
+ end
133
+
134
+ def isWhiteSpace(c)
135
+ if c.nil?
136
+ false
137
+ else
138
+ return (c == ?\ or c == ?\n or c == ?\t)
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ def isWhiteSpace(c)
145
+ return (c == ?\ or c == ?\n or c == ?\t)
146
+ end
147
+
data/lib/token.rb ADDED
@@ -0,0 +1,19 @@
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
+
17
+ @lex = lex
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubycalc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Kent D. Lee - Juan Francisco Cardona Mc
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: An implementation of a basic calculator on ruby
14
+ email: fcardona@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: []