calc_jmoral33 0.1.0
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 +7 -0
- data/bin/calc +55 -0
- data/lib/ast.rb +147 -0
- data/lib/calcex.rb +3 -0
- data/lib/calculator.rb +50 -0
- data/lib/parser.rb +163 -0
- data/lib/scanner.rb +140 -0
- data/lib/token.rb +19 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b03799e06e05f328a4dcb967e97666f2241a44e6
|
4
|
+
data.tar.gz: 538c6fca6fc7a92a8cc06c494bee2590598011b1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cc1a872f1e934c7cb78d02175b68b18885599bbf91afd4e29f183b726b81eb1c47cf72ce27941fd4abd4fb1f43e88f5872d1f21030ea7c57aacf3455077f30a7
|
7
|
+
data.tar.gz: 6ec3cd874518cd16813631f44496118f5601722343882f139df25652ab525ea5353cd23a1844f6d8f72369f13840856c981d876abed812f5edbd569ae010ca92
|
data/bin/calc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'calculator'
|
5
|
+
require 'calcex'
|
6
|
+
|
7
|
+
|
8
|
+
$calc = Calculator.new()
|
9
|
+
|
10
|
+
file = File.new("bin/operaciones.txt")
|
11
|
+
|
12
|
+
$calc.evalFile(file)
|
13
|
+
|
14
|
+
begin
|
15
|
+
$stdout.print "> "
|
16
|
+
$stdout.flush
|
17
|
+
text = gets
|
18
|
+
if(!text)
|
19
|
+
break
|
20
|
+
end
|
21
|
+
|
22
|
+
# line_num=0
|
23
|
+
# text=File.open('operaciones.txt').read
|
24
|
+
# text.gsub!(/\r\n?/, "\n")
|
25
|
+
# text.each_line do |line|
|
26
|
+
# print "= " + $calc.eval(line).to_s
|
27
|
+
# end
|
28
|
+
|
29
|
+
if(text!="\n")
|
30
|
+
|
31
|
+
print "= " + $calc.eval(text).to_s
|
32
|
+
|
33
|
+
if(!$calc.assigns.empty?)
|
34
|
+
assigns = $calc.assigns
|
35
|
+
print "[ "
|
36
|
+
assigns.each {|key,value| print "#{value} "}
|
37
|
+
print "]"
|
38
|
+
|
39
|
+
puts ""
|
40
|
+
else
|
41
|
+
puts ""
|
42
|
+
end
|
43
|
+
$calc.assigns.clear
|
44
|
+
else
|
45
|
+
puts "Parse Error, needed an expresion"
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
rescue ParseError
|
50
|
+
puts "Parse Error"
|
51
|
+
rescue UnrecognizedTokenException
|
52
|
+
puts "UnrecognizedTokenException"
|
53
|
+
rescue
|
54
|
+
puts "Unkown exception"
|
55
|
+
end until ($calc.eol and $calc.eof)
|
data/lib/ast.rb
ADDED
@@ -0,0 +1,147 @@
|
|
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 AssignNode < UnaryNode
|
21
|
+
def initialize(id,subTree)
|
22
|
+
super(subTree)
|
23
|
+
@id=id
|
24
|
+
end
|
25
|
+
|
26
|
+
def evaluate
|
27
|
+
val = @subTree.evaluate
|
28
|
+
$calc.insert(@id,val)
|
29
|
+
#realizar asignacion
|
30
|
+
return val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class IdentifierNode
|
35
|
+
def initialize(id)
|
36
|
+
@id=id
|
37
|
+
end
|
38
|
+
|
39
|
+
def evaluate
|
40
|
+
return $calc.search(@id)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class AddNode < BinaryNode
|
45
|
+
def initialize(left, right)
|
46
|
+
super(left,right)
|
47
|
+
end
|
48
|
+
|
49
|
+
def evaluate
|
50
|
+
@left.evaluate + @right.evaluate
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class SubNode < BinaryNode
|
55
|
+
def initialize(left, right)
|
56
|
+
super(left,right)
|
57
|
+
end
|
58
|
+
|
59
|
+
def evaluate
|
60
|
+
@left.evaluate - @right.evaluate
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class NumNode
|
65
|
+
def initialize(num)
|
66
|
+
@num = num
|
67
|
+
end
|
68
|
+
|
69
|
+
def evaluate
|
70
|
+
@num
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class StoreNode < UnaryNode
|
75
|
+
def initialize(subTree)
|
76
|
+
super(subTree)
|
77
|
+
end
|
78
|
+
|
79
|
+
def evaluate
|
80
|
+
$calc.memory=subTree.evaluate
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class PlusNode < UnaryNode
|
85
|
+
def initialize(subTree)
|
86
|
+
super(subTree)
|
87
|
+
end
|
88
|
+
|
89
|
+
def evaluate
|
90
|
+
$calc.memory+=subTree.evaluate
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class MinusNode < UnaryNode
|
95
|
+
def initialize(subTree)
|
96
|
+
super(subTree)
|
97
|
+
end
|
98
|
+
|
99
|
+
def evaluate
|
100
|
+
$calc.memory-=subTree.evaluate
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class ClearNode
|
105
|
+
def evaluate
|
106
|
+
$calc.memory2.clear
|
107
|
+
$calc.memory=0
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class RecallNode
|
113
|
+
def evaluate
|
114
|
+
$calc.memory
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
class TimesNode < BinaryNode
|
120
|
+
def initialize(left,right)
|
121
|
+
super(left,right)
|
122
|
+
end
|
123
|
+
|
124
|
+
def evaluate
|
125
|
+
@left.evaluate*@right.evaluate
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class DivideNode < BinaryNode
|
130
|
+
def initialize(left,right)
|
131
|
+
super(left,right)
|
132
|
+
end
|
133
|
+
|
134
|
+
def evaluate
|
135
|
+
@left.evaluate/@right.evaluate
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class ModuloNode < BinaryNode
|
140
|
+
def initialize(left,right)
|
141
|
+
super(left,right)
|
142
|
+
end
|
143
|
+
|
144
|
+
def evaluate
|
145
|
+
@left.evaluate%@right.evaluate
|
146
|
+
end
|
147
|
+
end
|
data/lib/calcex.rb
ADDED
data/lib/calculator.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'parser'
|
2
|
+
require 'ast'
|
3
|
+
|
4
|
+
class Calculator
|
5
|
+
attr_accessor :memory
|
6
|
+
attr_accessor :memory2
|
7
|
+
attr_accessor :eol
|
8
|
+
attr_accessor :eof
|
9
|
+
attr_accessor :countAssigns
|
10
|
+
attr_accessor :assigns
|
11
|
+
def initialize()
|
12
|
+
@memory = 0
|
13
|
+
@memory2 = Hash.new(0)
|
14
|
+
@eol = false
|
15
|
+
@eof = false
|
16
|
+
@assigns = Hash.new("")
|
17
|
+
@countAssigns = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def eval(expr)
|
21
|
+
parser = Parser.new(StringIO.new(expr))
|
22
|
+
ast = parser.parse()
|
23
|
+
return ast.evaluate()
|
24
|
+
end
|
25
|
+
|
26
|
+
def insert(id,val)
|
27
|
+
@assigns[countAssigns] = id.to_s + " <- " + val.to_s
|
28
|
+
@countAssigns += 1
|
29
|
+
@memory2[id] = val
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def evalFile(file)
|
34
|
+
file.each{|line|
|
35
|
+
if(line!="\n")
|
36
|
+
puts "= " + $calc.eval(line).to_s
|
37
|
+
else
|
38
|
+
puts "Needed an expresion"
|
39
|
+
end
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def search(id)
|
44
|
+
if(@memory2.keys.include?(id))
|
45
|
+
return @memory2[id]
|
46
|
+
else
|
47
|
+
return 0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,163 @@
|
|
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
|
16
|
+
def Prog
|
17
|
+
result = Expr()
|
18
|
+
t = @scan.getToken
|
19
|
+
|
20
|
+
if t.type == :eol
|
21
|
+
$calc.eol=true
|
22
|
+
else
|
23
|
+
$calc.eol=false
|
24
|
+
$calc.eof=true
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
return result
|
29
|
+
|
30
|
+
if t.type != :eof then
|
31
|
+
print "Expected EOF. Found ", t.type, ".\n"
|
32
|
+
raise ParseError.new
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def Expr
|
39
|
+
return RestExpr(Term())
|
40
|
+
end
|
41
|
+
|
42
|
+
def RestExpr(e)
|
43
|
+
t = @scan.getToken()
|
44
|
+
|
45
|
+
if t.type == :add then
|
46
|
+
return RestExpr(AddNode.new(e,Term()))
|
47
|
+
end
|
48
|
+
|
49
|
+
if t.type == :sub then
|
50
|
+
return RestExpr(SubNode.new(e,Term()))
|
51
|
+
end
|
52
|
+
|
53
|
+
@scan.putBackToken
|
54
|
+
|
55
|
+
return e
|
56
|
+
end
|
57
|
+
|
58
|
+
def Term
|
59
|
+
# Write your Term() code here. This code is just temporary
|
60
|
+
# so you can try the calculator out before finishing it.
|
61
|
+
|
62
|
+
return RestTerm(Storable())
|
63
|
+
|
64
|
+
|
65
|
+
puts "Term not implemented\n"
|
66
|
+
|
67
|
+
raise ParseError.new
|
68
|
+
end
|
69
|
+
|
70
|
+
def RestTerm(e)
|
71
|
+
t = @scan.getToken
|
72
|
+
|
73
|
+
if t.type == :times then
|
74
|
+
return RestTerm(TimesNode.new(e,Term()))
|
75
|
+
end
|
76
|
+
|
77
|
+
if t.type == :divide then
|
78
|
+
return RestTerm(DivideNode.new(e,Term()))
|
79
|
+
end
|
80
|
+
|
81
|
+
if t.type == :modulo then
|
82
|
+
return RestTerm(ModuloNode.new(e,Term()))
|
83
|
+
end
|
84
|
+
|
85
|
+
@scan.putBackToken
|
86
|
+
|
87
|
+
return e
|
88
|
+
|
89
|
+
puts "RestTerm not implemented"
|
90
|
+
raise ParseError.new # "Parse Error"
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
def Storable
|
95
|
+
return MemOperation(Factor())
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
def MemOperation(e)
|
100
|
+
t=@scan.getToken()
|
101
|
+
if (t.type==:keyword)
|
102
|
+
if (t.lex=="S")
|
103
|
+
return StoreNode.new(e)
|
104
|
+
elsif(t.lex=="M")
|
105
|
+
return MinusNode.new(e)
|
106
|
+
elsif(t.lex=="P")
|
107
|
+
return PlusNode.new(e)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
@scan.putBackToken
|
112
|
+
return e
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
def Assignable
|
117
|
+
t=@scan.getToken
|
118
|
+
if(t.type == :identifier)
|
119
|
+
|
120
|
+
result=Assign(t.lex.to_s)
|
121
|
+
return result
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
def Assign(id)
|
127
|
+
t=@scan.getToken
|
128
|
+
if(t.type==:assign)
|
129
|
+
return AssignNode.new(id,Expr())
|
130
|
+
end
|
131
|
+
@scan.putBackToken
|
132
|
+
return IdentifierNode.new(id)
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def Factor
|
137
|
+
|
138
|
+
t = @scan.getToken
|
139
|
+
|
140
|
+
if t.type == :number then
|
141
|
+
val = t.lex.to_i
|
142
|
+
return NumNode.new(val)
|
143
|
+
elsif(t.type == :keyword)
|
144
|
+
if(t.lex == "R")
|
145
|
+
return RecallNode.new
|
146
|
+
elsif(t.lex== "C")
|
147
|
+
return ClearNode.new
|
148
|
+
end
|
149
|
+
elsif(t.type == :lparen)
|
150
|
+
result=Expr()
|
151
|
+
t=@scan.getToken
|
152
|
+
if(t.type== :rparen)
|
153
|
+
return result
|
154
|
+
else
|
155
|
+
puts "Expected right parentheses"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
@scan.putBackToken
|
160
|
+
return Assignable()
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
data/lib/scanner.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'calcex'
|
3
|
+
|
4
|
+
class Scanner
|
5
|
+
def initialize(inStream)
|
6
|
+
@istream = inStream
|
7
|
+
@keywords = Set.new(["S","R","M","P","C"])
|
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
|
34
|
+
@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 == ?% then state=9
|
49
|
+
elsif c == ?= then state = 10
|
50
|
+
elsif c == ?\n then
|
51
|
+
state =11
|
52
|
+
@colCount = -1
|
53
|
+
@lineCount = @lineCount+1
|
54
|
+
elsif isWhiteSpace(c) then state = state #ignore whitespace
|
55
|
+
elsif @istream.eof() then
|
56
|
+
@foundOne = true
|
57
|
+
type = :eof
|
58
|
+
else
|
59
|
+
puts "Unrecognized Token found at line ",line," and column ",column,"\n"
|
60
|
+
raise UnrecognizedTokenException # "Unrecognized Token"
|
61
|
+
end
|
62
|
+
when 1
|
63
|
+
if isLetter(c) or isDigit(c) then state = 1
|
64
|
+
else
|
65
|
+
if @keywords.include?(lex) then
|
66
|
+
foundOne = true
|
67
|
+
type = :keyword
|
68
|
+
else
|
69
|
+
foundOne = true
|
70
|
+
type = :identifier
|
71
|
+
end
|
72
|
+
end
|
73
|
+
when 2
|
74
|
+
if isDigit(c) then state = 2
|
75
|
+
else
|
76
|
+
type = :number
|
77
|
+
foundOne = true
|
78
|
+
end
|
79
|
+
when 3
|
80
|
+
type = :add
|
81
|
+
foundOne = true
|
82
|
+
when 4
|
83
|
+
type = :sub
|
84
|
+
foundOne = true
|
85
|
+
when 5
|
86
|
+
type = :times
|
87
|
+
foundOne = true
|
88
|
+
when 6
|
89
|
+
type = :divide
|
90
|
+
foundOne = true
|
91
|
+
when 7
|
92
|
+
type = :lparen
|
93
|
+
foundOne = true
|
94
|
+
when 8
|
95
|
+
type = :rparen
|
96
|
+
foundOne = true
|
97
|
+
when 9
|
98
|
+
type = :modulo
|
99
|
+
foundOne = true
|
100
|
+
when 10
|
101
|
+
type = :assign
|
102
|
+
foundOne = true
|
103
|
+
when 11
|
104
|
+
type = :eol
|
105
|
+
foundOne = true
|
106
|
+
end
|
107
|
+
|
108
|
+
if !foundOne then
|
109
|
+
lex.concat(c)
|
110
|
+
c = @istream.getc()
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
@istream.ungetc(c)
|
116
|
+
@colCount = @colCount - 1
|
117
|
+
if type == :number or type == :identifier or type == :keyword then
|
118
|
+
t = LexicalToken.new(type,lex,line,column)
|
119
|
+
else
|
120
|
+
t = Token.new(type,line,column)
|
121
|
+
end
|
122
|
+
|
123
|
+
@lastToken = t
|
124
|
+
return t
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
def isLetter(c)
|
130
|
+
return ((?a <= c and c <= ?z) or (?A <= c and c <= ?Z))
|
131
|
+
end
|
132
|
+
|
133
|
+
def isDigit(c)
|
134
|
+
return (?0 <= c and c <= ?9)
|
135
|
+
end
|
136
|
+
|
137
|
+
def isWhiteSpace(c)
|
138
|
+
return (c == ?\ or c == ?\n or c == ?\t)
|
139
|
+
end
|
140
|
+
end
|
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: calc_jmoral33
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kent D. Lee - Juan Francisco Cardona Mc - Juan Daniel Morales
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-29 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: An calculator implementation on ruby
|
14
|
+
email: jmoral33@eafit.edu.co
|
15
|
+
executables:
|
16
|
+
- calc
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/calc
|
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: []
|