calc_jossava 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/calc +23 -0
- data/lib/ast.rb +84 -0
- data/lib/calcex.rb +7 -0
- data/lib/calculator.rb +16 -0
- data/lib/parser.rb +124 -0
- data/lib/scanner.rb +127 -0
- data/lib/token.rb +19 -0
- metadata +51 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 109ccfd8c8e6ea54f50ba166806038b4a73a6b66
|
|
4
|
+
data.tar.gz: 2626e1c245225a5c688daa37b66b45767f2ac9b4
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 971a1d1d4cdbaaf04c44735f760b53fb005e733fdb3695d7eb915682c48f92f2e6fbb2b301e2245e7a6686159dcbf0146638d5e85d154bcdc4ded650bc252b9e
|
|
7
|
+
data.tar.gz: ff793a6f9854a89597960daa06e57900f3f0305b7948384ae0896974155475f37edce7041e698713577e233373a1f6e4c0e07f09ea7ead1d63b9a5bc2131a75b
|
data/bin/calc
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#env: enviroment
|
|
3
|
+
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
require 'calculator'
|
|
6
|
+
require 'calcex'
|
|
7
|
+
|
|
8
|
+
$stdout.print "> "
|
|
9
|
+
$stdout.flush
|
|
10
|
+
|
|
11
|
+
text = gets
|
|
12
|
+
|
|
13
|
+
$calc = Calculator.new()
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
puts "= " + $calc.eval(text).to_s
|
|
17
|
+
rescue ParseError
|
|
18
|
+
puts "Parse Error"
|
|
19
|
+
rescue UnrecognizedTokenException
|
|
20
|
+
puts "UnrecognizedTokenException"
|
|
21
|
+
rescue
|
|
22
|
+
puts "Unkown exception"
|
|
23
|
+
end
|
data/lib/ast.rb
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'set'#es como el include pero este incluye el codigo
|
|
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 StoreNode < UnaryNode
|
|
61
|
+
def initialize(sub)
|
|
62
|
+
super(sub)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def evaluate
|
|
66
|
+
$calc.memory = subTree.evaluate
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class RecallNode
|
|
71
|
+
def evaluate
|
|
72
|
+
$calc.memory
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class NumNode
|
|
77
|
+
def initialize(num)
|
|
78
|
+
@num = num
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def evaluate()
|
|
82
|
+
return @num
|
|
83
|
+
end
|
|
84
|
+
end
|
data/lib/calcex.rb
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
class ParseError < Exception; end #para declarar una nueva excepcion se hereda de Exception.
|
|
3
|
+
class UnrecognizedTokenException < Exception; end
|
|
4
|
+
#aqui no hay un bloque try-catch
|
|
5
|
+
#aqui hay un bloque begin-rescue-end
|
|
6
|
+
#en java se lanza una excepcion (throw)
|
|
7
|
+
#en ruby se levanta una excepcion (raise)
|
data/lib/calculator.rb
ADDED
data/lib/parser.rb
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
require 'ast'
|
|
2
|
+
require 'scanner'
|
|
3
|
+
require 'token'
|
|
4
|
+
require 'calcex'
|
|
5
|
+
|
|
6
|
+
class Parser
|
|
7
|
+
def initialize(istream) #flujo de entrada
|
|
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 != :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
|
+
# Write your Term() code here. This code is just temporary
|
|
50
|
+
# so you can try the calculator out before finishing it.
|
|
51
|
+
|
|
52
|
+
# t = @scan.getToken()
|
|
53
|
+
|
|
54
|
+
# if t.type == :number then
|
|
55
|
+
# val = t.lex.to_i
|
|
56
|
+
# return NumNode.new(val)
|
|
57
|
+
# end
|
|
58
|
+
|
|
59
|
+
# puts "Term not implemented\n"
|
|
60
|
+
|
|
61
|
+
# raise ParseError.new
|
|
62
|
+
return RestTerm(Storable())
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def RestTerm(e)
|
|
66
|
+
t = @scan.getToken()
|
|
67
|
+
|
|
68
|
+
if(t.type() == :times)
|
|
69
|
+
return RestTerm(TimesNode.new(e,Storable()))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if(t.type == :divide) then
|
|
73
|
+
return RestTerm(DivideNode.new(e,Storable()))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
@scan.putBackToken
|
|
77
|
+
|
|
78
|
+
return e
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def Storable()
|
|
82
|
+
result=Factor()
|
|
83
|
+
t = @scan.getToken()
|
|
84
|
+
if(t.type == :keyword) then
|
|
85
|
+
if t.lex == "S" then
|
|
86
|
+
return StoreNode.new(result)
|
|
87
|
+
end
|
|
88
|
+
puts "Expected S found: ",t.lex
|
|
89
|
+
raise ParseError.new
|
|
90
|
+
end
|
|
91
|
+
@scan.putBackToken()
|
|
92
|
+
return result
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def Factor()
|
|
96
|
+
t = @scan.getToken()
|
|
97
|
+
|
|
98
|
+
if(t.type == :number) then
|
|
99
|
+
return NumNode.new(t.lex.to_i)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if(t.type == :keyword) then
|
|
103
|
+
if(t.lex == "R") then
|
|
104
|
+
return RecallNode.new
|
|
105
|
+
end
|
|
106
|
+
puts "Espected R found: ",t.lex
|
|
107
|
+
raise ParseError.new
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
if(t.type == :lparen) then
|
|
111
|
+
result = Expr()
|
|
112
|
+
|
|
113
|
+
t = @scan.getToken()
|
|
114
|
+
if(t.type == :rparen) then
|
|
115
|
+
return result
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
puts "Espected ) found: " + t.type.to_s
|
|
119
|
+
raise ParseError.new
|
|
120
|
+
end
|
|
121
|
+
puts "Expected number, R, ( found: " + t.type.to_s
|
|
122
|
+
raise ParseError.new # "Parse Error"
|
|
123
|
+
end
|
|
124
|
+
end
|
data/lib/scanner.rb
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
require 'calcex' #incluye el modulo de las excepciones.
|
|
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 # estaba (if !@needToken) con logica negativa, pero ahora lo hacemos con unless
|
|
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 # estaba con while !foundOne
|
|
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
|
+
return ((?a <= c and c <= ?z) or (?A <= c and c <= ?Z))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def isDigit(c)
|
|
121
|
+
return (?0 <= c and c <= ?9)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def isWhiteSpace(c)
|
|
125
|
+
return (c == ?\ or c == ?\n or c == ?\t)
|
|
126
|
+
end
|
|
127
|
+
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 #Herencia simple
|
|
12
|
+
attr_reader :lex
|
|
13
|
+
|
|
14
|
+
def initialize(type,lex,lineNum,colNum)
|
|
15
|
+
super(type,lineNum,colNum) #se encarga de llamar los metodos de la clase padre.
|
|
16
|
+
|
|
17
|
+
@lex = lex
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: calc_jossava
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Kent D. Lee - Juan Francisco Cardona Mc - Juan Fernando Ossa Vásquez
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-11-12 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: An calculator implementation on ruby
|
|
14
|
+
email: jossava@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.7
|
|
48
|
+
signing_key:
|
|
49
|
+
specification_version: 4
|
|
50
|
+
summary: Another calculator in ruby
|
|
51
|
+
test_files: []
|