jriasco2-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 +7 -0
- data/bin/rubycalc +25 -0
- data/lib/ast.rb +84 -0
- data/lib/calcex.rb +3 -0
- data/lib/calculator.rb +16 -0
- data/lib/parser.rb +145 -0
- data/lib/scanner.rb +138 -0
- data/lib/token.rb +19 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6a9c25081bb2a79adf6b95a1fba4d332ae95cc98
|
4
|
+
data.tar.gz: f07ba77bd4da1e756ad1b2cb3001c01538f2e925
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 35bd0e42e0a4ea45849ca0fbdc4c50a03113519c24df0f3bf647541bd032e16160c72ceebcbf82e4117baefa35beaafb4ff446526b911795cc067f80d27db8a5
|
7
|
+
data.tar.gz: 4bd709a24925f5638ba7c8ef4aab6c3bf0913d88bfdb54519b7c6f5ed68b20af0222d9c9aa57a6413845ca884fd3e5fc72eafc6540a552fef4ea417d99f65984
|
data/bin/rubycalc
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'calculator'
|
5
|
+
require 'calcex' #Excepciones
|
6
|
+
|
7
|
+
$stdout.print "> " #Imprime
|
8
|
+
$stdout.flush
|
9
|
+
|
10
|
+
text = gets #Obtenemos el texto
|
11
|
+
|
12
|
+
$calc = Calculator.new()
|
13
|
+
|
14
|
+
begin #Begin para manejar las excepciones de las mas bajas en gerarquia a la mas
|
15
|
+
#Forma 1
|
16
|
+
puts "= " + $calc.eval(text).to_s
|
17
|
+
#Forma 2
|
18
|
+
|
19
|
+
rescue ParseError => pe #=> pe nos captura el objeto de la excepcion
|
20
|
+
puts "Parse Error"
|
21
|
+
rescue UnrecognizedTokenException => ute
|
22
|
+
puts "UnrecognizedTokenException"
|
23
|
+
rescue
|
24
|
+
puts "Unkown exception"
|
25
|
+
end
|
data/lib/ast.rb
ADDED
@@ -0,0 +1,84 @@
|
|
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 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
|
+
def initialize(subTree)
|
72
|
+
super(subTree)
|
73
|
+
end
|
74
|
+
def evaluate()
|
75
|
+
$calc.memoy = @subTree.evaluate()
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class RecallNode
|
80
|
+
def evaluate()
|
81
|
+
$calc.memory
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
data/lib/calcex.rb
ADDED
data/lib/calculator.rb
ADDED
data/lib/parser.rb
ADDED
@@ -0,0 +1,145 @@
|
|
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 -> Fuera de un metodo
|
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
|
+
# def Term()
|
52
|
+
# # Write your Term() code here. This code is just temporary
|
53
|
+
# # so you can try the calculator out before finishing it.
|
54
|
+
|
55
|
+
# t = @scan.getToken()
|
56
|
+
|
57
|
+
# if t.type == :number then
|
58
|
+
# val = t.lex.to_i
|
59
|
+
# return NumNode.new(val)
|
60
|
+
# end
|
61
|
+
|
62
|
+
# puts "Term not implemented\n"
|
63
|
+
|
64
|
+
# raise ParseError.new
|
65
|
+
# end
|
66
|
+
|
67
|
+
# def RestTerm(e)
|
68
|
+
|
69
|
+
# puts "RestTerm not implemented"
|
70
|
+
# raise ParseError.new # "Parse Error"
|
71
|
+
# end
|
72
|
+
|
73
|
+
def RestTerm (e)
|
74
|
+
t = @scan.getToken
|
75
|
+
if t.type == :times then
|
76
|
+
return RestTerm(TimesNode.new(e,Storable()))
|
77
|
+
end
|
78
|
+
|
79
|
+
if t.type == :divide then
|
80
|
+
return RestTerm(DivideNode.new(e,Storable()))
|
81
|
+
end
|
82
|
+
|
83
|
+
@scan.putBackToken
|
84
|
+
|
85
|
+
e
|
86
|
+
end
|
87
|
+
# def Storable()
|
88
|
+
|
89
|
+
# puts "Storable not implemented"
|
90
|
+
# raise ParseError.new # "Parse Error"
|
91
|
+
# end
|
92
|
+
|
93
|
+
def Storable()
|
94
|
+
factor = Factor()
|
95
|
+
|
96
|
+
t = @scan.getToken
|
97
|
+
|
98
|
+
if t.type == :keyword then
|
99
|
+
if t.lex == "S" then
|
100
|
+
return StoreNode.new(factor)
|
101
|
+
else
|
102
|
+
raise.ParseError.new
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
@scan.putBackToken
|
107
|
+
|
108
|
+
factor
|
109
|
+
end
|
110
|
+
# def Factor()
|
111
|
+
|
112
|
+
# puts "Factor not implemented"
|
113
|
+
# raise ParserError.new # "Parse Error"
|
114
|
+
# end
|
115
|
+
|
116
|
+
def Factor()
|
117
|
+
|
118
|
+
t = @scan.getToken
|
119
|
+
|
120
|
+
if t.type == :number then
|
121
|
+
return NumNode.new(t.lex.to_i)
|
122
|
+
end
|
123
|
+
|
124
|
+
if t.type == :keyword then
|
125
|
+
if t.lex == "R" then
|
126
|
+
return RecallNode.new
|
127
|
+
else
|
128
|
+
raise.ParseError.new
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if t.type == :lparen then
|
133
|
+
expr = Expr()
|
134
|
+
t = @scan.getToken
|
135
|
+
if t.type == :rparen then
|
136
|
+
return expr
|
137
|
+
else
|
138
|
+
raise.ParseError.new
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
raise ParseError.new
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
data/lib/scanner.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'calcex'
|
3
|
+
|
4
|
+
#Conjunto -> sirve para guardar las palabras reservadas
|
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
|
+
#A menos que necesite un token retorna el ultimo token almacenado o sino hace la comprobacion del flujo de entrada
|
20
|
+
def getToken()
|
21
|
+
unless @needToken
|
22
|
+
@needToken = true
|
23
|
+
return @lastToken
|
24
|
+
end
|
25
|
+
|
26
|
+
state = 0
|
27
|
+
foundOne = false
|
28
|
+
c = @istream.getc #optiene un caracter
|
29
|
+
|
30
|
+
if @istream.eof then #es final de fichero
|
31
|
+
@lastToken = Token.new(:eof,@lineCount,@colCount)
|
32
|
+
return @lastToken
|
33
|
+
end
|
34
|
+
|
35
|
+
until foundOne do #logica negativa -> while !foundOne
|
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? then
|
120
|
+
return false
|
121
|
+
end
|
122
|
+
(?a <= c and c <= ?z) or (?A <= c and c <= ?Z)
|
123
|
+
end
|
124
|
+
|
125
|
+
def isDigit(c)
|
126
|
+
if c.nil? then
|
127
|
+
return false
|
128
|
+
end
|
129
|
+
?0 <= c and c <= ?9
|
130
|
+
end
|
131
|
+
|
132
|
+
def isWhiteSpace(c)
|
133
|
+
if c.nil? then
|
134
|
+
return false
|
135
|
+
end
|
136
|
+
c == ?\ or c == ?\n or c == ?\t
|
137
|
+
end
|
138
|
+
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: jriasco2-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 - John Jairo Riascos
|
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: jriasco2@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: []
|