htobonm-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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: efb895b755af2f3501ce5ba3acf4aaa300786678
4
+ data.tar.gz: 0400579fb2207074997bc9eb481825ffcfb222e1
5
+ SHA512:
6
+ metadata.gz: e65425ba29977a851a04dd9c04c644cff265479ff9797a99aeaec1b3a1735df69b37a829b63367ea126777fabfd3e45b137f44898e365d90bac4bc695b389041
7
+ data.tar.gz: 6c7ebae66481b3422066e3342f2e9a70e66713b35255fa2859ae9d2bc09c865dbdfc7846404f00542f1b9c17cded0999477c7480ddf55919adb5ce10fa36605c
@@ -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
@@ -0,0 +1,86 @@
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 TimesNode < BinaryNode
41
+ def initialize(left, right)
42
+ super(left,right)
43
+ end
44
+
45
+ def evaluate()
46
+ @left.evaluate() * @right.evaluate()
47
+ end
48
+ end
49
+
50
+ class DivideNode < BinaryNode
51
+ def initialize(left, right)
52
+ super(left,right)
53
+ end
54
+
55
+ def evaluate()
56
+ @left.evaluate() / @right.evaluate()
57
+ end
58
+ end
59
+
60
+ class NumNode
61
+ def initialize(num)
62
+ @num = num
63
+ end
64
+
65
+ def evaluate()
66
+ return @num
67
+ end
68
+ end
69
+
70
+ class StoreNode < UnaryNode
71
+
72
+ def initialize(sub)
73
+ super(sub)
74
+ end
75
+
76
+ def evaluate()
77
+ $calc.memory = @subTree.evaluate
78
+ end
79
+ end
80
+
81
+ class RecallNode
82
+
83
+ def evaluate()
84
+ $calc.memory
85
+ end
86
+ end
@@ -0,0 +1,3 @@
1
+
2
+ class ParseError < Exception; end
3
+ class UnrecognizedTokenException < Exception; end
@@ -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
@@ -0,0 +1,122 @@
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
+ 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
+ e
46
+ end
47
+
48
+ def Term()
49
+ RestTerm(Storable())
50
+ # Write your Term() code here. This code is just temporary
51
+ # so you can try the calculator out before finishing it.
52
+
53
+ # t = @scan.getToken()
54
+
55
+ # if t.type == :number then
56
+ # val = t.lex.to_i
57
+ # return NumNode.new(val)
58
+ # end
59
+
60
+ # puts "Term not implemented\n"
61
+
62
+ # raise ParseError.new
63
+ end
64
+
65
+ def RestTerm(e)
66
+ t = @scan.getToken()
67
+
68
+ if t.type == :times then
69
+ return RestTerm(TimesNode.new(e, Storable()))
70
+ end
71
+ if t.type == :divide then
72
+ return RestTerm(DivideNode.new(e, Storable()))
73
+ end
74
+ @scan.putBackToken
75
+
76
+ e
77
+ end
78
+
79
+ def Storable()
80
+ fact = Factor()
81
+ t = @scan.getToken
82
+
83
+ if t.type == :keyword then
84
+ if t.lex == "S" then
85
+ return StoreNode.new(fact)
86
+ else
87
+ raise ParseError.new
88
+ end
89
+ end
90
+
91
+ @scan.putBackToken
92
+
93
+ fact
94
+ end
95
+
96
+ def Factor()
97
+ t = @scan.getToken
98
+
99
+ if t.type == :number then
100
+ return NumNode.new(t.lex.to_i)
101
+ end
102
+ if t.type == :keyword then
103
+ if t.lex == "R" then
104
+ return RecallNode.new
105
+ else
106
+ raise ParseError.new
107
+ end
108
+ end
109
+ if t.type == :lparen then
110
+ expr = Expr()
111
+ t = @scan.getToken
112
+
113
+ if t.type == :rparen then
114
+ return expr
115
+ else
116
+ raise ParseError.new
117
+ end
118
+ end
119
+
120
+ raise ParseError.new
121
+ end
122
+ end
@@ -0,0 +1,140 @@
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
+ unless @needToken then
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
+ until 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
+ if c.nil?
119
+ false
120
+ else
121
+ (?a <= c and c <= ?z) or (?A <= c and c <= ?Z)
122
+ end
123
+ end
124
+
125
+ def isDigit(c)
126
+ if c.nil?
127
+ false
128
+ else
129
+ ?0 <= c and c <= ?9
130
+ end
131
+ end
132
+
133
+ def isWhiteSpace(c)
134
+ if c.nil?
135
+ false
136
+ else
137
+ c == ?\ or c == ?\n or c == ?\t
138
+ end
139
+ end
140
+ end
@@ -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: htobonm-rubycalc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Kent D. Lee - Hamilton Tobon Mosquera
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: htobonm@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: []