|
@@ -0,0 +1,372 @@
|
|
|
+from __future__ import print_function
|
|
|
+
|
|
|
+import os
|
|
|
+import copy;
|
|
|
+from pycparser import c_ast
|
|
|
+
|
|
|
+INT_TYPES = ['int', 'short', 'float']
|
|
|
+FLOAT_TYPES = ['float', 'double']
|
|
|
+STRING_TYPES = ['char']
|
|
|
+
|
|
|
+VALID_TPYES = list()
|
|
|
+VALID_TPYES.extend(INT_TYPES)
|
|
|
+VALID_TPYES.extend(FLOAT_TYPES)
|
|
|
+VALID_TPYES.extend(STRING_TYPES)
|
|
|
+
|
|
|
+VALID_LOGIC_OPS = ['||', '&&', '!']
|
|
|
+VALID_REL_OPS = ['==', '!=', '>=', '>', '<=', '<']
|
|
|
+VALID_OPS = list(['*', '-', '/', '+', '%', '++', '--', 'p--', 'p++'])
|
|
|
+VALID_OPS.extend(VALID_LOGIC_OPS)
|
|
|
+VALID_OPS.extend(VALID_REL_OPS)
|
|
|
+
|
|
|
+def normalizeType(type):
|
|
|
+ if type in INT_TYPES:
|
|
|
+ return 'int'
|
|
|
+ elif type in FLOAT_TYPES:
|
|
|
+ return 'float'
|
|
|
+ else:
|
|
|
+ return 'string'
|
|
|
+
|
|
|
+class DeclarationInfo:
|
|
|
+ def __init__ (self, type, dimensions, isPointer = False):
|
|
|
+ self.type = type
|
|
|
+ self.dimensions = dimensions
|
|
|
+ self.isPointer = isPointer
|
|
|
+
|
|
|
+ def isVector (self) :
|
|
|
+ return self.dimensions == 1
|
|
|
+
|
|
|
+ def isMatrix (self):
|
|
|
+ return self.dimensions == 2
|
|
|
+
|
|
|
+ def isMultiDimension (self):
|
|
|
+ return self.dimensions > 2
|
|
|
+
|
|
|
+ def isArray (self):
|
|
|
+ return self.dimensions > 0
|
|
|
+
|
|
|
+class CommandInfo:
|
|
|
+ def __init__ (self, condType, numLogicOps, numRelOps, opList):
|
|
|
+ self.condType = condType
|
|
|
+ self.numLogicOps = numLogicOps
|
|
|
+ self.numRelOps = numRelOps
|
|
|
+ self.opList = opList
|
|
|
+
|
|
|
+ def __str__(self):
|
|
|
+ return "Type:%s LogicOpCount:%d RelOpCount:%d OpList:%s"%( self.condType, self.numLogicOps, self.numRelOps, self.opList)
|
|
|
+
|
|
|
+class ForCommandInfo (CommandInfo):
|
|
|
+ def __init__ (self, useAssignment, useNext, condType, numLogicOps, numRelOps, opList):
|
|
|
+ CommandInfo.__init__(self, condType, numLogicOps, numRelOps, opList)
|
|
|
+ self.useAssignment = useAssignment
|
|
|
+ self.useNext = useNext
|
|
|
+
|
|
|
+ def __str__(self):
|
|
|
+ return "hasInit:%s hasNext:%s Type:%s LogicOpCount:%d RelOpCount:%d OpList:%s"%(self.useAssignment, self.useNext, self.condType, self.numLogicOps, self.numRelOps, self.opList)
|
|
|
+
|
|
|
+class ASTAnalyser:
|
|
|
+
|
|
|
+ def __init__(self, assignment, student, nodeList):
|
|
|
+ self.assignment = assignment
|
|
|
+ self.student = student
|
|
|
+ self.nodes = nodeList
|
|
|
+ self.commandCount = dict()
|
|
|
+ self.declarationData = list()
|
|
|
+ self.forCommandData = list()
|
|
|
+ self.conditionCommandData = list()
|
|
|
+ self.operatorsCount = dict()
|
|
|
+ self.constantInitCount = dict()
|
|
|
+ #<helpers
|
|
|
+ self.logicOpCount = 0
|
|
|
+ self.relOpCount = 0
|
|
|
+ self.opList = list()
|
|
|
+ #helpers />
|
|
|
+ self.declarations = dict()
|
|
|
+ self.declarationsPointers = dict()
|
|
|
+ self.declarationsVectors = dict()
|
|
|
+ self.declarationsMatrixes = dict()
|
|
|
+
|
|
|
+ def conditionCommandStr (self) :
|
|
|
+ return [ s.__str__() for s in self.conditionCommandData]
|
|
|
+
|
|
|
+ def forCommandStr (self) :
|
|
|
+ return [ s.__str__() for s in self.forCommandData]
|
|
|
+
|
|
|
+ def beginAnalysis(self):
|
|
|
+ for n in self.nodes:
|
|
|
+ self.proccessCommand(n)
|
|
|
+ self.countDeclarations()
|
|
|
+
|
|
|
+ def countDeclarations(self):
|
|
|
+ pointers = [i for i in self.declarationData if i.isPointer]
|
|
|
+ vectors = [i for i in self.declarationData if i.isVector()]
|
|
|
+ matrixs = [i for i in self.declarationData if i.isMatrix()]
|
|
|
+ others = [i for i in self.declarationData if not (i.isArray() or i.isPointer)]
|
|
|
+ for p in pointers:
|
|
|
+ type = normalizeType(p.type)
|
|
|
+ if type == 'string':
|
|
|
+ if type in self.declarations:
|
|
|
+ self.declarations[type] += 1
|
|
|
+ else:
|
|
|
+ self.declarations[type] = 1
|
|
|
+ elif type in self.declarationsPointers:
|
|
|
+ self.declarationsPointers[type] += 1
|
|
|
+ else:
|
|
|
+ self.declarationsPointers[type] = 1
|
|
|
+
|
|
|
+ for v in vectors:
|
|
|
+ type = normalizeType(v.type)
|
|
|
+ if type in self.declarationsVectors:
|
|
|
+ self.declarationsVectors[type] += 1
|
|
|
+ else:
|
|
|
+ self.declarationsVectors[type] = 1
|
|
|
+ for m in matrixs:
|
|
|
+ type = normalizeType(m.type)
|
|
|
+ if type in self.declarationsMatrixes:
|
|
|
+ self.declarationsMatrixes[type] += 1
|
|
|
+ else:
|
|
|
+ self.declarationsMatrixes[type] = 1
|
|
|
+ for o in others:
|
|
|
+ type = normalizeType(o.type)
|
|
|
+ if type in self.declarations:
|
|
|
+ self.declarations[type] += 1
|
|
|
+ else:
|
|
|
+ self.declarations[type] = 1
|
|
|
+
|
|
|
+ def proccessDecl (self, node):
|
|
|
+ type = node.type
|
|
|
+ dimensions = 0
|
|
|
+ isPointer = False
|
|
|
+ while type.__class__.__name__ != 'IdentifierType':
|
|
|
+ if type.__class__.__name__ == 'ArrayDecl':
|
|
|
+ dimensions += 1
|
|
|
+ elif type.__class__.__name__ == 'PtrDecl':
|
|
|
+ isPointer = True
|
|
|
+ type = type.type
|
|
|
+ actualType = type.names[-1]
|
|
|
+ if actualType in VALID_TPYES:
|
|
|
+ self.declarationData.append(DeclarationInfo(actualType, dimensions, isPointer))
|
|
|
+ init = node.init
|
|
|
+ initName = init.__class__.__name__
|
|
|
+ if initName == 'Constant' and dimensions == 0 and not isPointer:
|
|
|
+ if init.value in self.constantInitCount:
|
|
|
+ self.constantInitCount[init.value] += 1
|
|
|
+ else:
|
|
|
+ self.constantInitCount[init.value] = 1
|
|
|
+
|
|
|
+ def proccessFuncDef (self, node):
|
|
|
+ commandList = node.body.block_items
|
|
|
+ for cmd in commandList:
|
|
|
+ self.proccessCommand(cmd)
|
|
|
+
|
|
|
+ def proccessFuncCall (self, node):
|
|
|
+ name = node.__class__.__name__
|
|
|
+ self.incCmdCount(name)
|
|
|
+ epxrs = node.args.exprs
|
|
|
+ for e in epxrs:
|
|
|
+ self.countOperators(e)
|
|
|
+
|
|
|
+ def proccessAssignment (self, node):
|
|
|
+ name = node.__class__.__name__
|
|
|
+ self.incCmdCount(name)
|
|
|
+ epxr = node.rvalue
|
|
|
+ self.countOperators(epxr)
|
|
|
+
|
|
|
+ def proccessReturn (self, node):
|
|
|
+ name = node.__class__.__name__
|
|
|
+ self.incCmdCount(name)
|
|
|
+ epxr = node.expr
|
|
|
+ self.countOperators(epxr)
|
|
|
+
|
|
|
+ def proccessSwitch (self, node):
|
|
|
+ name = node.__class__.__name__
|
|
|
+ self.incCmdCount(name)
|
|
|
+ epxr = node.cond
|
|
|
+ self.countOperators(epxr)
|
|
|
+ cmdList = node.stmt.block_items
|
|
|
+ for cmd in cmdList:
|
|
|
+ self.proccessCommand(cmd)
|
|
|
+
|
|
|
+ def proccessDoWhile (self, name, node):
|
|
|
+ self.incCmdCount(name)
|
|
|
+ epxr = node.cond
|
|
|
+ self.logicOpCount = 0
|
|
|
+ self.relOpCount = 0
|
|
|
+ self.opList[:] = []
|
|
|
+ condType = self.checkCondType(epxr)
|
|
|
+ self.countOperators(epxr)
|
|
|
+ logicCount = self.logicOpCount
|
|
|
+ relCount = self.relOpCount
|
|
|
+ opList = copy.deepcopy(self.opList)
|
|
|
+ cmdList = node.stmt
|
|
|
+ cmdName = cmdList.__class__.__name__
|
|
|
+ if cmdName == 'Compound':
|
|
|
+ for cmd in cmdList:
|
|
|
+ self.proccessCommand(cmd)
|
|
|
+ else:
|
|
|
+ self.proccessCommand(cmdList)
|
|
|
+ self.conditionCommandData.append(CommandInfo(condType,logicCount, relCount, opList))
|
|
|
+
|
|
|
+ def proccessFor (self, node):
|
|
|
+ name = node.__class__.__name__
|
|
|
+ self.incCmdCount(name)
|
|
|
+ epxr = node.cond
|
|
|
+ self.logicOpCount = 0
|
|
|
+ self.relOpCount = 0
|
|
|
+ self.opList[:] = []
|
|
|
+ condType = self.checkCondType(epxr)
|
|
|
+ self.countOperators(epxr)
|
|
|
+ logicCount = self.logicOpCount
|
|
|
+ relCount = self.relOpCount
|
|
|
+ opList = copy.deepcopy(self.opList)
|
|
|
+
|
|
|
+ hasInit = node.init.__class__.__name__ != 'NoneType'
|
|
|
+ if hasInit:
|
|
|
+ self.proccessCommand(node.init)
|
|
|
+
|
|
|
+ hasNext = node.next.__class__.__name__ != 'NoneType'
|
|
|
+ if hasNext:
|
|
|
+ self.proccessCommand(node.next)
|
|
|
+ if node.next.__class__.__name__ != 'FuncCall':
|
|
|
+ self.countOperators(node.next)
|
|
|
+
|
|
|
+ cmdList = node.stmt
|
|
|
+ cmdName = cmdList.__class__.__name__
|
|
|
+ if cmdName == 'Compound':
|
|
|
+ for cmd in cmdList:
|
|
|
+ self.proccessCommand(cmd)
|
|
|
+ elif name != 'NoneType':
|
|
|
+ self.proccessCommand(cmdList)
|
|
|
+ self.forCommandData.append(ForCommandInfo(hasInit, hasNext, condType, logicCount, relCount, opList))
|
|
|
+
|
|
|
+ def proccessIf (self, node):
|
|
|
+ name = node.__class__.__name__
|
|
|
+ self.incCmdCount(name)
|
|
|
+ epxr = node.cond
|
|
|
+ self.logicOpCount = 0
|
|
|
+ self.relOpCount = 0
|
|
|
+ self.opList[:] = []
|
|
|
+ condType = self.checkCondType(epxr)
|
|
|
+ self.countOperators(epxr)
|
|
|
+ logicCount = self.logicOpCount
|
|
|
+ relCount = self.relOpCount
|
|
|
+ opList = copy.deepcopy(self.opList)
|
|
|
+
|
|
|
+ iftrue = node.iftrue
|
|
|
+ ifCompound = iftrue.__class__.__name__
|
|
|
+ if ifCompound == 'Compound':
|
|
|
+ cmdList = iftrue.block_items
|
|
|
+ for cmd in cmdList:
|
|
|
+ self.proccessCommand(cmd)
|
|
|
+ else:
|
|
|
+ self.proccessCommand(iftrue)
|
|
|
+
|
|
|
+ iffalse = node.iffalse
|
|
|
+ ifCompound = iffalse.__class__.__name__
|
|
|
+ if ifCompound == 'Compound':
|
|
|
+ cmdList = iffalse.block_items
|
|
|
+ for cmd in cmdList:
|
|
|
+ self.proccessCommand(cmd)
|
|
|
+ elif name != 'NoneType':
|
|
|
+ self.proccessCommand(iffalse)
|
|
|
+ self.conditionCommandData.append(CommandInfo(condType, logicCount, relCount, opList))
|
|
|
+
|
|
|
+ def proccessCase (self, node):
|
|
|
+ try:
|
|
|
+ epxr = node.expr
|
|
|
+ self.countOperators(epxr)
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+ cmdList = node.stmts
|
|
|
+ for cmd in cmdList:
|
|
|
+ self.proccessCommand(cmd)
|
|
|
+
|
|
|
+ def countOperators (self, expr):
|
|
|
+ name = expr.__class__.__name__
|
|
|
+ if name == 'UnaryOp' :
|
|
|
+ op = expr.op
|
|
|
+ if not op in VALID_OPS:
|
|
|
+ return
|
|
|
+ self.incOpCount(op)
|
|
|
+ if op in VALID_LOGIC_OPS:
|
|
|
+ self.logicOpCount += 1
|
|
|
+ elif op in VALID_REL_OPS:
|
|
|
+ self.relOpCount += 1
|
|
|
+ self.opList.append(op)
|
|
|
+ self.countOperators(expr.expr)
|
|
|
+ elif name == 'BinaryOp':
|
|
|
+ op = expr.op
|
|
|
+ if op in VALID_OPS:
|
|
|
+ self.incOpCount(op)
|
|
|
+ if op in VALID_LOGIC_OPS:
|
|
|
+ self.logicOpCount += 1
|
|
|
+ elif op in VALID_REL_OPS:
|
|
|
+ self.relOpCount += 1
|
|
|
+ self.opList.append(op)
|
|
|
+ self.countOperators(expr.left)
|
|
|
+ self.countOperators(expr.right)
|
|
|
+ elif name == 'FuncCall':
|
|
|
+ self.proccessFuncCall(expr)
|
|
|
+ elif name == 'Cast':
|
|
|
+ self.countOperators(expr.expr)
|
|
|
+ else:
|
|
|
+ pass
|
|
|
+
|
|
|
+ def incOpCount (self, op):
|
|
|
+ if op in self.operatorsCount:
|
|
|
+ self.operatorsCount[op] += 1
|
|
|
+ else:
|
|
|
+ self.operatorsCount[op] = 1
|
|
|
+
|
|
|
+ def incCmdCount (self, cmd):
|
|
|
+ if cmd in self.commandCount:
|
|
|
+ self.commandCount[cmd] += 1
|
|
|
+ else:
|
|
|
+ self.commandCount[cmd] = 1
|
|
|
+
|
|
|
+ def checkCondType (self, expr):
|
|
|
+ name = expr.__class__.__name__
|
|
|
+ if name == 'BinaryOp':
|
|
|
+ op = expr.op
|
|
|
+ if op in VALID_LOGIC_OPS:
|
|
|
+ return 'LogicOp'
|
|
|
+ elif op in VALID_REL_OPS:
|
|
|
+ return 'RelationalOp'
|
|
|
+ else:
|
|
|
+ return 'OtherOp'
|
|
|
+ elif name == 'UnaryOp':
|
|
|
+ op = expr.op
|
|
|
+ if op in VALID_LOGIC_OPS:
|
|
|
+ return 'LogicOp'
|
|
|
+ else:
|
|
|
+ return 'OtherOp'
|
|
|
+ elif name == 'Cast':
|
|
|
+ return self.checkCondType(expr.expr)
|
|
|
+ else:
|
|
|
+ return 'BooleanValue'
|
|
|
+
|
|
|
+ def proccessCommand (self, node):
|
|
|
+ name = node.__class__.__name__
|
|
|
+ if name == 'Decl':
|
|
|
+ self.proccessDecl(node)
|
|
|
+ elif name == 'FuncDef':
|
|
|
+ self.proccessFuncDef(node)
|
|
|
+ elif name == 'FuncCall':
|
|
|
+ self.proccessFuncCall(node)
|
|
|
+ elif name == 'Assignment':
|
|
|
+ self.proccessAssignment(node)
|
|
|
+ elif name == 'Return':
|
|
|
+ self.proccessReturn(node)
|
|
|
+ elif name == 'Switch':
|
|
|
+ self.proccessSwitch(node)
|
|
|
+ elif name == 'Case' or name == 'Default':
|
|
|
+ self.proccessCase(node)
|
|
|
+ elif name == 'If':
|
|
|
+ self.proccessIf(node)
|
|
|
+ elif name == 'DoWhile' or name == 'While':
|
|
|
+ self.proccessDoWhile(name, node)
|
|
|
+ elif name == 'For':
|
|
|
+ self.proccessFor(node)
|
|
|
+ else:
|
|
|
+ pass
|
|
|
+ #print("Unknown Command: %s" % name )
|