plympton 1.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/.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';
|