plympton 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.document +5 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +24 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/bin/func-auto.py +431 -0
- data/bin/func.py +435 -0
- data/bin/func.py.new +440 -0
- data/bin/idascript.py +21 -0
- data/lib/plympton.rb +26 -0
- data/lib/plympton/Solver.tokens +25 -0
- data/lib/plympton/SolverLexer.rb +704 -0
- data/lib/plympton/SolverParser.rb +550 -0
- data/lib/plympton/block.rb +19 -0
- data/lib/plympton/chunk.rb +20 -0
- data/lib/plympton/disassembly.rb +105 -0
- data/lib/plympton/function.rb +31 -0
- data/lib/plympton/matrix.rb +59 -0
- data/lib/plympton/object.rb +153 -0
- data/lib/plympton/solver.g +118 -0
- data/plympton.gemspec +95 -0
- data/spec/libFontParser.64.dylib.fz +152170 -0
- data/spec/libauto.dylib.fz +127001 -0
- data/spec/plympton_spec.rb +220 -0
- data/spec/rufus-test.32bit.trace.xml +53 -0
- data/spec/rufus-test.64bit.trace.xml +50 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/steady-state.64bit.trace.xml +12070 -0
- metadata +195 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
module Plympton
|
2
|
+
# Class responsible for parsing a YAML serialized block object
|
3
|
+
class Block
|
4
|
+
# YAML Entries
|
5
|
+
attr_accessor :startEA, :endEA, :numInstructions
|
6
|
+
|
7
|
+
# Defines the objects YAML tag
|
8
|
+
# @return [String] A string signifying the start of an object of this class
|
9
|
+
def to_yaml_type
|
10
|
+
"!fuzz.io,2011/Block"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Convenience method for printing a block
|
14
|
+
# @return [String] A string representation of a block object
|
15
|
+
def to_s()
|
16
|
+
"#{self.class} (#{self.__id__}): Start Address: #{startEA}, End Address #{endEA}, No. of Instructions: #{numInstructions}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Plympton
|
2
|
+
# Class responsible for parsing a YAML serialized chunk object
|
3
|
+
class Chunk
|
4
|
+
# YAML entries
|
5
|
+
attr_accessor :startEA, :endEA, :blockList, :numBlocks
|
6
|
+
|
7
|
+
# Defines the objects YAML tag
|
8
|
+
# @return [String] A string signifying the start of an object of this class
|
9
|
+
def to_yaml_type
|
10
|
+
"!fuzz.io,2011/Chunk"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Convenience method for printing a chunk
|
14
|
+
# @return [String] A string representation of a chunk object
|
15
|
+
def to_s()
|
16
|
+
"#{self.class} (#{self.__id__}):"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Plympton
|
2
|
+
# Class responsible for working with a disassembly
|
3
|
+
class Disassembly
|
4
|
+
attr_accessor :attributes
|
5
|
+
attr_accessor :expression
|
6
|
+
|
7
|
+
# Create an instance of the disassembly class
|
8
|
+
# Reads in a YAML serialized disassembly
|
9
|
+
# @param [String] Path to the YAML serialized disassembly
|
10
|
+
# @param [String] A mathematical expression for object evaluation
|
11
|
+
def initialize(yamlDisassembly, literalExpression="")
|
12
|
+
|
13
|
+
# Check for existence of the file
|
14
|
+
if(!File.exists?(yamlDisassembly)) then
|
15
|
+
return(nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Unserialize the YAML disassembly
|
19
|
+
@attributes = YAML.load(File.open(yamlDisassembly))
|
20
|
+
@attributes.setup()
|
21
|
+
|
22
|
+
# Create an instance of the expression
|
23
|
+
initialize_solver(literalExpression)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Wrapper function for the solver's evaluate function
|
27
|
+
# After expression evaluation it resets for another turn
|
28
|
+
# @param [BigDecimal] Test case runtime, defaults to zero
|
29
|
+
# @return [BigDecimal] The results of the evaluated expression
|
30
|
+
def evaluate(runtime="0")
|
31
|
+
@attributes.runtime = BigDecimal(runtime)
|
32
|
+
result = @expression.evaluate()
|
33
|
+
initialize_solver(@expression.expressionCache)
|
34
|
+
|
35
|
+
# Catch Not A Number or Infinite cases
|
36
|
+
if(result.nan?() or result.infinite?()) then
|
37
|
+
result = BigDecimal("0")
|
38
|
+
end
|
39
|
+
|
40
|
+
return(result)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Function to process hit tracing recorded by Valgrind tools (rufus and callgrind)
|
44
|
+
# @param [String] Path to a valgrind trace file
|
45
|
+
def valgrind_coverage(valgrindTrace)
|
46
|
+
|
47
|
+
# Open the valgrind xml trace file
|
48
|
+
xmlFile = File.open(valgrindTrace, "r")
|
49
|
+
xmlDoc = Nokogiri::XML(xmlFile)
|
50
|
+
|
51
|
+
# Delete any previous hit traces
|
52
|
+
@attributes.functionHitTrace.clear()
|
53
|
+
|
54
|
+
# Parse all the function hits
|
55
|
+
xmlDoc.xpath("//hit").each do |hit|
|
56
|
+
functionOffset = hit.search("offset").first().inner_text()
|
57
|
+
#puts "Function offset hit: #{functionOffset}"
|
58
|
+
if(@attributes.functionHash.has_key?(functionOffset)) then
|
59
|
+
#puts "Function offset found: #{functionOffset}"
|
60
|
+
if(!@attributes.functionHitTrace.has_key?(functionOffset)) then
|
61
|
+
@attributes.functionHitTrace[functionOffset] = [1]
|
62
|
+
else
|
63
|
+
@attributes.functionHitTrace[functionOffset][0] = @attributes.functionHitTrace[functionOffset][0] + 1
|
64
|
+
end
|
65
|
+
|
66
|
+
# Parse all the functions this function called
|
67
|
+
hit.xpath("callee").each do |callee|
|
68
|
+
calleeOffset = callee.search("offset").first().inner_text()
|
69
|
+
numberOfCalls = callee.search("numberOfCalls").first().inner_text().to_i()
|
70
|
+
if(@attributes.functionHash.has_key?(calleeOffset)) then
|
71
|
+
if(!@attributes.functionHitTrace.has_key?(functionOffset)) then
|
72
|
+
@attributes.functionHitTrace[functionOffset] = [numberOfCalls]
|
73
|
+
else
|
74
|
+
@attributes.functionHitTrace[functionOffset][0] = @attributes.functionHitTrace[functionOffset][0] + numberOfCalls
|
75
|
+
end
|
76
|
+
|
77
|
+
#puts "Function offset: #{functionOffset} -> #{calleeOffset}"
|
78
|
+
# Increment the number of transitions for a state
|
79
|
+
@attributes.functionHash[functionOffset].numTransitions += BigDecimal("#{numberOfCalls}")
|
80
|
+
|
81
|
+
# Update the transition matrix
|
82
|
+
@attributes.transitionMatrix[@attributes.functionHash[functionOffset].markovIdx, @attributes.functionHash[calleeOffset].markovIdx] = @attributes.transitionMatrix[@attributes.functionHash[functionOffset].markovIdx, @attributes.functionHash[calleeOffset].markovIdx] + BigDecimal("#{numberOfCalls}")
|
83
|
+
|
84
|
+
# Keep track of call trace and number of times called
|
85
|
+
@attributes.trace << "#{@attributes.functionHash[functionOffset].markovIdx}:#{@attributes.functionHash[calleeOffset].markovIdx}:#{numberOfCalls}"
|
86
|
+
end
|
87
|
+
end # end callee xpath
|
88
|
+
end # end function hash check for hit function
|
89
|
+
end # end hit xpath
|
90
|
+
|
91
|
+
# Cleanup open file
|
92
|
+
xmlFile.close()
|
93
|
+
end
|
94
|
+
|
95
|
+
# Parses a mathematical expression and setups for function evaluation
|
96
|
+
# @param [String] A mathematical expression for object evaluation
|
97
|
+
def initialize_solver(literalExpression)
|
98
|
+
# Allocate an expression to solve
|
99
|
+
@expression = Solver::Parser.new(Solver::Lexer.new(literalExpression))
|
100
|
+
@expression.expressionCache = literalExpression
|
101
|
+
@expression.objectCache = @attributes
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Plympton
|
2
|
+
# Class responsible for parsing a YAML serialized function object
|
3
|
+
class Function
|
4
|
+
# YAML Entries
|
5
|
+
attr_accessor :name, :startAddress, :endAddress, :savedRegSize, :localVarSize, :argSize, :frameSize, :numArgs, :numLocalVars, :isImport, :numChunks, :chunkList, :cyclomaticComplexity
|
6
|
+
|
7
|
+
attr_accessor :numInstructions
|
8
|
+
attr_accessor :numTransitions, :markovIdx
|
9
|
+
|
10
|
+
# Defines the objects YAML tag
|
11
|
+
# @return [String] A string signifying the start of an object of this class
|
12
|
+
def to_yaml_type
|
13
|
+
"!fuzz.io,2011/Function"
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_total_number_of_instructions()
|
17
|
+
@numInstructions = 0
|
18
|
+
@chunkList.each do |chunk|
|
19
|
+
chunk.blockList.each do |block|
|
20
|
+
@numInstructions = @numInstructions + block.numInstructions
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convenience method for printing a function
|
26
|
+
# @return [String] A string representation of a function object
|
27
|
+
def to_s()
|
28
|
+
"#{self.class} (#{self.__id__}):#{name}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class NMatrix
|
2
|
+
# Class Methods
|
3
|
+
|
4
|
+
# Create a new square matrix of BigDecimal 0 objects
|
5
|
+
# @param [Fixnum] Number of rows and columns
|
6
|
+
# def self.square(dimension)
|
7
|
+
# matrix = PlymptonMatrix.object(dimension, dimension)
|
8
|
+
# matrix.fill!(BigDecimal("0"))
|
9
|
+
# puts matrix.class()
|
10
|
+
# return(matrix)
|
11
|
+
# end
|
12
|
+
|
13
|
+
def investigate()
|
14
|
+
dimensions = shape()
|
15
|
+
rowDimension = dimensions[0]
|
16
|
+
colDimension = dimensions[1]
|
17
|
+
puts ""
|
18
|
+
for row in 0...rowDimension
|
19
|
+
for column in 0...colDimension
|
20
|
+
print " %10f" % self[row, column].to_s('F')
|
21
|
+
end
|
22
|
+
puts""
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Allocate a matrix of BigDecimal zeros
|
27
|
+
# @param [Fixnum] Size of the square matrix n x n
|
28
|
+
# @returns [PlymptonMatrix] An n x n matrix with every entry a BigDecimal("0")
|
29
|
+
# def self.zero(size)
|
30
|
+
# PlymptonMatrix.build(size) do
|
31
|
+
# BigDecimal("0")
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
|
35
|
+
# Instance Methods
|
36
|
+
# Prints the string representation of a Matrix object
|
37
|
+
# @params [Hash] Hash of functions defined by an object
|
38
|
+
# def to_s()
|
39
|
+
# rowSize = row_size()
|
40
|
+
# colSize = column_size()
|
41
|
+
#
|
42
|
+
# # Print Matrix Header
|
43
|
+
# print "%12s" % ""
|
44
|
+
# for colIdx in 0...colSize do
|
45
|
+
# print "%10d" % colIdx
|
46
|
+
# end
|
47
|
+
# spacer = "-" * 100
|
48
|
+
# print "\n %s\n" % spacer
|
49
|
+
#
|
50
|
+
# for rowIdx in 0...rowSize do
|
51
|
+
# print "%10d |" % rowIdx
|
52
|
+
# for colIdx in 0...colSize do
|
53
|
+
# print" %10f" % self[rowIdx, colIdx].to_s('10F')
|
54
|
+
# end
|
55
|
+
# print(" |\n")
|
56
|
+
# end
|
57
|
+
# nil
|
58
|
+
# end
|
59
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Plympton
|
2
|
+
# Class responsible for parsing a YAML serialized object
|
3
|
+
class Object
|
4
|
+
# YAML entries
|
5
|
+
attr_accessor :textSegmentStart, :textSegmentEnd, :functionList, :importList, :numFunctions, :numImports, :numBlocks, :name
|
6
|
+
|
7
|
+
# Class Attributes not imported via YAML
|
8
|
+
attr_accessor :runtime
|
9
|
+
attr_accessor :functionHash
|
10
|
+
attr_accessor :functionHitTrace
|
11
|
+
attr_accessor :transitionMatrix
|
12
|
+
attr_accessor :trace
|
13
|
+
|
14
|
+
# Defines the objects YAML tag
|
15
|
+
# @return [String] A string signifying the start of an object of this class
|
16
|
+
def to_yaml_type
|
17
|
+
"!fuzz.io,2011/Object"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates hashes for function lookup and cleans up data imported via YAML::load
|
21
|
+
def setup()
|
22
|
+
# Housekeeping on library name
|
23
|
+
@name.chomp!()
|
24
|
+
|
25
|
+
# Allocate a hash for function hit tracing
|
26
|
+
@functionHitTrace = Hash.new()
|
27
|
+
|
28
|
+
# Create a function hash for lookups
|
29
|
+
init_function_hash()
|
30
|
+
|
31
|
+
# Add in the markov function (any function not defined by a shared library)
|
32
|
+
markovFunction = Plympton::Function.new()
|
33
|
+
markovFunction.markovIdx = 0
|
34
|
+
markovFunction.numTransitions = BigDecimal("0")
|
35
|
+
@functionHash["-1"] = markovFunction
|
36
|
+
|
37
|
+
# Precompute number of instructions per function and set markovIdx
|
38
|
+
markovIdx = 1
|
39
|
+
@functionList.each do |function|
|
40
|
+
function.set_total_number_of_instructions()
|
41
|
+
function.markovIdx = markovIdx
|
42
|
+
function.numTransitions = BigDecimal("0")
|
43
|
+
markovIdx += 1
|
44
|
+
end
|
45
|
+
|
46
|
+
# Allocate transition matrix (# functions + special state 0)
|
47
|
+
# Transition matrix persists across test case runs
|
48
|
+
# dimension = @functionHash.size() + 1
|
49
|
+
dimension = @functionHash.size()
|
50
|
+
@transitionMatrix = NMatrix.object(dimension, dimension).fill!(BigDecimal("0"))
|
51
|
+
|
52
|
+
# Allocate a trace for Markov chains
|
53
|
+
@trace = Array.new()
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# @param [String] Base variable name to create a sum and average function
|
58
|
+
# @param [Symbol] Associated instance variable to calculate the sum and average
|
59
|
+
def self.define_sum_avg(variable, attribute)
|
60
|
+
sum_function = variable + "s"
|
61
|
+
define_method(sum_function.to_sym()) do
|
62
|
+
result = BigDecimal("0")
|
63
|
+
if(@functionHitTrace !=nil and @functionHitTrace.length() > 0) then
|
64
|
+
# Iterate through the function's hit hash
|
65
|
+
@functionHitTrace.each do |offset, numberTimesExecuted|
|
66
|
+
result = result + BigDecimal("#{@functionHash[offset].send(attribute.to_sym())}") * BigDecimal("#{numberTimesExecuted[0]}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
return(result)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Define the average function
|
73
|
+
avg_function = variable + "a"
|
74
|
+
define_method(avg_function.to_sym()) do
|
75
|
+
# Catch if the functionHitTrace length is zero
|
76
|
+
numFunctionsExecuted = BigDecimal("0")
|
77
|
+
@functionHitTrace.each do |offset, numberTimesExecuted|
|
78
|
+
numFunctionsExecuted = numFunctionsExecuted + BigDecimal("#{numberTimesExecuted[0]}")
|
79
|
+
end
|
80
|
+
|
81
|
+
send(sum_function.to_sym())/numFunctionsExecuted
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Define all the sum and average functions
|
86
|
+
{"A" => :numArgs, "B" => :numLocalVars, "C" => :argSize, "D" => :localVarSize,"E" => :numInstructions,"F" => :frameSize,"G" => :cyclomaticComplexity}.each do |variable, attribute|
|
87
|
+
define_sum_avg(variable, attribute)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Function to calculate execution path uniqueness:
|
91
|
+
# http://www.cs.ucf.edu/~czou/research/EvolutionaryInputCrafting-ACSAC07.pdf
|
92
|
+
# Had to scale due to overflow: log 1/product(pi) = log(productpi)-1 = -log(productpi) = - summation log(pi)
|
93
|
+
# @return [BigDecimal] The steady state transition probability
|
94
|
+
def U()
|
95
|
+
dimensions = @transitionMatrix.shape()
|
96
|
+
rowDimension = dimensions[0]
|
97
|
+
colDimension = dimensions[1]
|
98
|
+
pMatrix = NMatrix.object(rowDimension, colDimension).fill!(BigDecimal("0"))
|
99
|
+
|
100
|
+
# Calculate the new transition probabilities
|
101
|
+
@functionHash.each_value do |function|
|
102
|
+
rowIdx = function.markovIdx
|
103
|
+
# puts "#{function.startAddress}: #{function.numTransitions.to_s("F")}"
|
104
|
+
for column in 0...colDimension
|
105
|
+
if(function.numTransitions != BigDecimal("0")) then
|
106
|
+
pMatrix[rowIdx,column] = @transitionMatrix[rowIdx,column]/function.numTransitions
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Calculate the statistic for function path uniqueness
|
112
|
+
pathUniqueness = BigDecimal("0")
|
113
|
+
@trace.each do |callTrace|
|
114
|
+
traceArr = callTrace.split(":")
|
115
|
+
traceArr[2].to_i.times do
|
116
|
+
#pathUniqueness += BigMath.log(pMatrix[traceArr[0].to_i(), traceArr[1].to_i()], 6)
|
117
|
+
pathUniqueness += Math.log(pMatrix[traceArr[0].to_i(), traceArr[1].to_i()].to_f())
|
118
|
+
end
|
119
|
+
# puts "#{pMatrix[traceArr[0].to_i(), traceArr[1].to_i()].to_f()}"
|
120
|
+
end
|
121
|
+
|
122
|
+
#puts "Path Uniqueness is: #{pathUniqueness.to_s("F")}"
|
123
|
+
pathUniqueness = BigDecimal(pathUniqueness.abs.to_s())
|
124
|
+
#puts "Path Uniqueness is: #{pathUniqueness.to_s("F")}"
|
125
|
+
@trace.clear()
|
126
|
+
return(pathUniqueness)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Function to calculate the function coverage of a test case run
|
130
|
+
# Total number of unique functions/Total number of unique functions executed
|
131
|
+
# @return [BigDecimal] The percentage
|
132
|
+
def V()
|
133
|
+
functionCoverage = @functionHitTrace.length()/@numFunctions
|
134
|
+
return(BigDecimal(functionCoverage.to_s()))
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
# Convenience method for creating a lookup hash for functions based on hex address
|
140
|
+
def init_function_hash()
|
141
|
+
@functionHash = Hash.new()
|
142
|
+
@functionList.each do |function|
|
143
|
+
@functionHash[function.startAddress] = function
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Convenience method for printing a chromosome
|
148
|
+
# @return [String] A string representation of the chromosome object
|
149
|
+
def to_s()
|
150
|
+
"#{self.class} (#{self.__id__}):\n#{name}\t#{textSegmentStart}\t#{textSegmentEnd}\t#{numFunctions}\t#{numImports}\t#{numBlocks}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
grammar Solver;
|
2
|
+
|
3
|
+
options {
|
4
|
+
language = Ruby;
|
5
|
+
}
|
6
|
+
|
7
|
+
tokens {
|
8
|
+
PLUS = '+';
|
9
|
+
MINUS = '-';
|
10
|
+
MULT = '*';
|
11
|
+
DIV = '/';
|
12
|
+
MOD = '%';
|
13
|
+
EXP = '^';
|
14
|
+
LPAREN = '(';
|
15
|
+
RPAREN = ')';
|
16
|
+
}
|
17
|
+
|
18
|
+
@header {
|
19
|
+
require 'bigdecimal/math'
|
20
|
+
}
|
21
|
+
|
22
|
+
@members {
|
23
|
+
attr_accessor :expressionCache
|
24
|
+
attr_accessor :objectCache
|
25
|
+
|
26
|
+
# Recovery function handling errors
|
27
|
+
def recover( error = $! )
|
28
|
+
puts "Parser Recovering: #{error}"
|
29
|
+
exit(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Reporting function handling errors
|
33
|
+
def report_error( error = $! )
|
34
|
+
puts "Parser Reporting: #{error}"
|
35
|
+
exit(1)
|
36
|
+
end
|
37
|
+
}
|
38
|
+
|
39
|
+
|
40
|
+
@lexer::members {
|
41
|
+
# Recovery function handling errors
|
42
|
+
def recover( error = $! )
|
43
|
+
puts "Lexer Recovering: #{error}"
|
44
|
+
exit(1)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Reporting function handling errors
|
48
|
+
def report_error( error = $! )
|
49
|
+
puts "Lexer Reporting: #{error}"
|
50
|
+
exit(1)
|
51
|
+
end
|
52
|
+
}
|
53
|
+
|
54
|
+
/*------------------------------------------------------------------
|
55
|
+
* PARSER RULES
|
56
|
+
*------------------------------------------------------------------*/
|
57
|
+
evaluate returns [result]
|
58
|
+
: r=expression {$result = $r.result }
|
59
|
+
;
|
60
|
+
expression returns [result]
|
61
|
+
: r=mult
|
62
|
+
( '+' r2=mult { $r.result += $r2.result }
|
63
|
+
| '-' r2=mult { $r.result -= $r2.result }
|
64
|
+
)* { $result = $r.result }
|
65
|
+
;
|
66
|
+
|
67
|
+
mult returns [result]
|
68
|
+
: r=log
|
69
|
+
( '*' r2=log { $r.result *= $r2.result }
|
70
|
+
| '/' r2=log { $r.result /= $r2.result }
|
71
|
+
)* { $result = $r.result };
|
72
|
+
|
73
|
+
log returns [result]
|
74
|
+
: 'ln' r=exp { $result = BigMath.log(r, 2)}
|
75
|
+
| r=exp { $result = $r.result }
|
76
|
+
;
|
77
|
+
|
78
|
+
exp returns [result]
|
79
|
+
: r=atom ( '^' r2=atom { $r.result **= $r2.result.to_i() } )? { $result = $r.result }
|
80
|
+
;
|
81
|
+
|
82
|
+
atom returns [result]
|
83
|
+
: (a=INT {$result = BigDecimal($a.text)}
|
84
|
+
| a=FLOAT {$result = BigDecimal($a.text)}
|
85
|
+
| LPAREN r=expression { $result = $r.result } RPAREN
|
86
|
+
| a=MODVAR {
|
87
|
+
$result = @objectCache.send($a.text)
|
88
|
+
}
|
89
|
+
| a=UNMODVAR {
|
90
|
+
case $a.text
|
91
|
+
when 'R'
|
92
|
+
$result = @objectCache.runtime
|
93
|
+
when 'U'
|
94
|
+
$result = @objectCache.send($a.text)
|
95
|
+
when 'V'
|
96
|
+
$result = @objectCache.send($a.text)
|
97
|
+
else
|
98
|
+
$result = BigDecimal("0")
|
99
|
+
end
|
100
|
+
}
|
101
|
+
)
|
102
|
+
;
|
103
|
+
|
104
|
+
/*------------------------------------------------------------------
|
105
|
+
* LEXER RULES
|
106
|
+
*------------------------------------------------------------------*/
|
107
|
+
INT : (DIGIT)+ ;
|
108
|
+
FLOAT : INT '.' INT;
|
109
|
+
MODVAR : 'A'..'G'(MODIFIER);
|
110
|
+
UNMODVAR : ( 'R' | 'U' | 'V' );
|
111
|
+
WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ { $channel = HIDDEN; };
|
112
|
+
|
113
|
+
/*------------------------------------------------------------------
|
114
|
+
* Fragment RULES (Only used as part of another Lexer Rule)
|
115
|
+
*------------------------------------------------------------------*/
|
116
|
+
|
117
|
+
fragment DIGIT : '0'..'9';
|
118
|
+
fragment MODIFIER: 'a'|'s';
|