smendoz3-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: cbdc5ec6096e5cdbd1e12593d335b760034dfc59
4
+ data.tar.gz: 8d55671cf3341bfcf3a31536b1d628f1decd57a9
5
+ SHA512:
6
+ metadata.gz: 451b543e2cc1bb1dc859618276e659adf07099baa5428fcfda5e708a4f3e535e59ecad7586b38e46b5c090f47835d9d4aaf85c22487a31b23c2169139488f0cf
7
+ data.tar.gz: 29b76104ed62d02afc36439f3cc3a31b83d549f1f0484287eadfe763d249008c51264e2794ac3e66af777717237a30115381e0ca8d93fed365ba9067a60967b8
@@ -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)}"
16
+ rescue ParseError => pe
17
+ puts "Parse Error"
18
+ rescue UnrecognizedTokenException => ute
19
+ puts "UnrecognizedTokenException"
20
+ rescue
21
+ puts "Unkown exception"
22
+ end
@@ -0,0 +1,83 @@
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
+ return @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
+ return @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
+ return @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
+ return @left.evaluate() / @right.evaluate()
57
+ end
58
+ end
59
+
60
+ class StoreNode < UnaryNode
61
+ def intialize(subTree)
62
+ super(subTree)
63
+ end
64
+ def evaluate()
65
+ $calc.memory = @subTree.evaluate()
66
+ end
67
+ end
68
+
69
+ class NumNode
70
+ def initialize(num)
71
+ @num = num
72
+ end
73
+
74
+ def evaluate()
75
+ return @num
76
+ end
77
+ end
78
+
79
+ class RecallNode
80
+ def evaluate()
81
+ $calc.memory
82
+ end
83
+ 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,113 @@
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
+ if 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
+ RestTerm(Storable())
50
+ end
51
+
52
+ def RestTerm(e)
53
+ t = @scan.getToken
54
+
55
+ if t.type == :times then
56
+ return RestTerm(TimesNode.new(e, Storable()))
57
+ end
58
+
59
+ if t.type == :divide then
60
+ return RestTerm(DivideNode.new(e, Storable()))
61
+ end
62
+
63
+ @scan.putBackToken
64
+
65
+ e
66
+ end
67
+
68
+ def Storable()
69
+ factor = Factor()
70
+
71
+ t = @scan.getToken
72
+
73
+ if t.type == :keyword then
74
+ if t.lex == "S" then
75
+ return StoreNode.new(factor)
76
+ else
77
+ raise ParseError.new
78
+ end
79
+ end
80
+
81
+ @scan.putBackToken
82
+
83
+ factor
84
+ end
85
+
86
+ def Factor()
87
+ t = @scan.getToken
88
+
89
+ if t.type == :number then
90
+ return NumNode.new(t.lex.to_i)
91
+ end
92
+
93
+ if t.type == :keyword then
94
+ if t.lex == "R" then
95
+ return RecallNode.new
96
+ else
97
+ raise ParseError.new
98
+ end
99
+ end
100
+
101
+ if t.type == :lparen then
102
+ expr = Expr()
103
+ t = @scan.getToken
104
+ if t.type == :rparen then
105
+ return expr
106
+ else
107
+ raise ParseError.new
108
+ end
109
+ end
110
+ raise ParseError.new
111
+
112
+ end
113
+ end
@@ -0,0 +1,136 @@
1
+ require 'stringio'
2
+ require 'calcex'
3
+
4
+ class Scanner
5
+ def initialize(inStream)
6
+ @istream = inStream
7
+ @keywords = Set.new(["S","R"])
8
+ @lineCount = 1
9
+ @colCount = -1
10
+ @needToken = true
11
+ @lastToken = nil
12
+ end
13
+
14
+ def putBackToken()
15
+ @needToken = false
16
+ end
17
+
18
+ def getToken()
19
+ unless @needToken
20
+ @needToken = true
21
+ return @lastToken
22
+ end
23
+
24
+ state = 0
25
+ foundOne = false
26
+ c = @istream.getc
27
+
28
+ if @istream.eof then
29
+ @lastToken = Token.new(:eof,@lineCount,@colCount)
30
+ return @lastToken
31
+ end
32
+
33
+ until foundOne do
34
+ @colCount = @colCount + 1
35
+ case state
36
+ when 0
37
+ lex = ""
38
+ column = @colCount
39
+ line = @lineCount
40
+ if isLetter(c) then state=1
41
+ elsif isDigit(c) then state=2
42
+ elsif c == ?+ then state = 3
43
+ elsif c == ?- then state = 4
44
+ elsif c == ?* then state = 5
45
+ elsif c == ?/ then state = 6
46
+ elsif c == ?( then state = 7
47
+ elsif c == ?) then state = 8
48
+ elsif c == ?\n then
49
+ @colCount = -1
50
+ @lineCount = @lineCount+1
51
+ elsif isWhiteSpace(c) then state = state #ignore whitespace
52
+ elsif @istream.eof() then
53
+ @foundOne = true
54
+ type = :eof
55
+ else
56
+ puts "Unrecognized Token found at line ",line," and column ",column,"\n"
57
+ raise UnrecognizedTokenException # "Unrecognized Token"
58
+ end
59
+ when 1
60
+ if isLetter(c) or isDigit(c) then state = 1
61
+ else
62
+ if @keywords.include?(lex) then
63
+ foundOne = true
64
+ type = :keyword
65
+ else
66
+ foundOne = true
67
+ type = :identifier
68
+ end
69
+ end
70
+ when 2
71
+ if isDigit(c) then state = 2
72
+ else
73
+ type = :number
74
+ foundOne = true
75
+ end
76
+ when 3
77
+ type = :add
78
+ foundOne = true
79
+ when 4
80
+ type = :sub
81
+ foundOne = true
82
+ when 5
83
+ type = :times
84
+ foundOne = true
85
+ when 6
86
+ type = :divide
87
+ foundOne = true
88
+ when 7
89
+ type = :lparen
90
+ foundOne = true
91
+ when 8
92
+ type = :rparen
93
+ foundOne = true
94
+ end
95
+
96
+ if !foundOne then
97
+ lex.concat(c)
98
+ c = @istream.getc()
99
+ end
100
+
101
+ end
102
+
103
+ @istream.ungetc(c)
104
+ @colCount = @colCount - 1
105
+ if type == :number or type == :identifier or type == :keyword then
106
+ t = LexicalToken.new(type,lex,line,column)
107
+ else
108
+ t = Token.new(type,line,column)
109
+ end
110
+
111
+ @lastToken = t
112
+ return t
113
+ end
114
+
115
+ private
116
+ def isLetter(c)
117
+ if c.nil? then
118
+ return false
119
+ end
120
+ (?a <= c and c <= ?z) or (?A <= c and c <= ?Z)
121
+ end
122
+
123
+ def isDigit(c)
124
+ if c.nil? then
125
+ return false
126
+ end
127
+ ?0 <= c and c <= ?9
128
+ end
129
+
130
+ def isWhiteSpace(c)
131
+ if c.nil? then
132
+ return false
133
+ end
134
+ c == ?\ or c == ?\n or c == ?\t
135
+ end
136
+ 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: smendoz3-rubycalc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Kent D. Lee - Santiago Mendoza Montoya
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: An calculator implementation on ruby
14
+ email: smendoz3@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: []