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', 'string'] 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, cmdCount, condType, numLogicOps, numRelOps, opList): self.cmdCount = cmdCount 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, cmdCount, condType, numLogicOps, numRelOps, opList): CommandInfo.__init__(self, cmdCount, 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() # 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 == 'string': if type in self.declarations: self.declarations[type] += 1 else: self.declarations[type] = 1 elif 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): name = node.__class__.__name__ self.incCmdCount(name) 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__ self.cmdCountStack.append(0) if cmdName == 'Compound': for cmd in cmdList: self.proccessCommand(cmd) else: self.proccessCommand(cmdList) self.conditionCommandData.append(CommandInfo(self.cmdCountStack.pop(), 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) self.cmdCountStack.append(0) 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, self.cmdCountStack.pop(), 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) self.cmdCountStack.append(0) 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(self.cmdCountStack.pop(), 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 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.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 'logic_expression' elif op in VALID_REL_OPS: return 'relational_expression' else: return 'other_expression' elif name == 'UnaryOp': op = expr.op if op in VALID_LOGIC_OPS: return 'logic_expression' else: return 'other_expression' elif name == 'Cast': return self.checkCondType(expr.expr) else: return 'boolean_value' def proccessCommand (self, node): name = node.__class__.__name__ self.cmdCountStack[-1] += 1 if name == 'Decl': self.proccessDecl(node) self.cmdCountStack[-1] -= 1 elif name == 'FuncDef': self.proccessFuncDef(node) self.cmdCountStack[-1] -= 1 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) self.cmdCountStack[-1] -= 1 elif name == 'If': self.proccessIf(node) elif name == 'DoWhile' or name == 'While': self.proccessDoWhile(name, node) elif name == 'For': self.proccessFor(node) else: self.cmdCountStack[-1] -= 1 #print("Unknown Command: %s" % name )