def checkCustom(qtPrimary, qtSecondary, isRight): if not qtPrimary.taxon.isClass() or 'simple' not in qtPrimary.taxon.attrs: return None, None decl = qtPrimary.taxon.findMember(opcode) if not decl: return None, None if decl.type == 'operator' and ('right' in decl.attrs) == isRight: paramsList = decl.getParamsList() if len(paramsList) == 1: res, err = QuasiType.matchTaxons(paramsList[0], qtSecondary) if res: return decl, None if decl.type == 'overload': matches = [] for operDecl in decl.items: paramsList = operDecl.getParamsList() if len(paramsList)==1 and ('right' in operDecl.attrs) == isRight: res, err = QuasiType.matchTaxons(paramsList[0].getTypeTaxon(), qtSecondary) if res: matches.append((res, operDecl)) if len(matches) == 1: return matches[0][1], None elif len(matches) > 0: exacts = [operDecl for res, operDecl in matches if res=='exact'] if len(exacts) == 0: exacts = [operDecl for res, operDecl in matches if res=='constExact'] if len(exacts) == 1: return exacts[0], None return None, 'Multiple declarations for operator %s(%s, %s)' % (opcode, leftQType.getDebugStr(), rightQType.getDebugStr()) return None, None
def match(self, funcTaxon): """ Сопоставить сигнатуру с объявлением функции (Func, Method, Constructor, ...) Возвращается числовой вес """ formalParamsList = funcTaxon.getParams() if len(self.params) > len(formalParamsList): # Если список параметров сигнатуры длиннее, чем список формальных параметров, значит нет соответствия return 0 # Специальный случай - функция без параметров if len(formalParamsList) == 0: return 1 sum = 0 for i, formalParam in enumerate(formalParamsList): if i < len(self.params): # Если параметр сигнатуры соответствует формальному параметру функции, то требуется проверить соответствие типов matchResult, errMessage = QuasiType.matchTaxons( formalParam, self.params[i]) else: # Нужно проверить наличие значение параметра по-умолчанию. val = formalParam.getValueTaxon() # Если его нет, то сигнатура не соответствует функции matchResult = 'default' if val else '' if not matchResult: return 0 sum += weights[matchResult] return sum
def testSizeT(self): core = TaxonCore.createInstance() size_t = core.findItem('size_t') self.assertEqual(size_t.type, 'typedef') self.assertEqual(len(size_t.items), 1) sizetExpr = size_t.getTypeExpr() self.assertIsInstance(sizetExpr, TaxonTypeExpr) constA = TaxonConst('int', 123, '123') res, errMsg = QuasiType.matchTaxons(size_t, constA) self.assertIsNone(errMsg) self.assertEqual(res, 'constExact') constB = TaxonConst('int', -1, '-1') res, errMsg = QuasiType.matchTaxons(size_t, constB) self.assertEqual( errMsg, 'Invalid conversion of negative value "-1" to "unsigned long"')
def exec(self): taxon = self.taxon localType = taxon.getLocalType() valueTaxon = taxon.getValueTaxon() qtValue = valueTaxon.buildQuasiType() qtValue.inst = valueTaxon result, errorMsg = QuasiType.matchTaxons(localType, qtValue) if errorMsg: taxon.throwError(errorMsg)
def matchQuasiType(self, left, right): # right - using: list.push(22.2) # left - declaration: Array.push(value:ArrayItemType) # left: QuasiType # +-taxon --> ArrayItemType # +-inst: TaxonCaller # +-getCaller(): BinOp . # +-getLeft(): TaxonIdExpr qtItem = self.findRealItemType(left.inst) return QuasiType.matchTaxons(qtItem, right)
def matchQuasiTypeReverse(self, left, right): # Предполагается, что это конструкция типа x = list.pop() assert (right.taxon.type == 'ArrayItemType') if right.inst.type != 'Call': self.throwError( 'Expected type "Call" in right part (%s) instead of "%s"' % (right.inst.getDebugStr(), right.inst.type)) qtItem = self.findRealItemType( right.inst ) # examp: var x: double = listOfFloat.pop() --> quasi of Scalar float return QuasiType.matchTaxons(left, qtItem)
def _defaultEquation(self, leftQuasi, rightQuasi): """ Стандартный оператор присваивания """ from core.Operators import createOperatorLow # Левая часть должна допускать присваивание # Нужно, чтобы правый тип матчился в левый matchCode, errorMsg = QuasiType.matchTaxons(leftQuasi, rightQuasi) if errorMsg: self.throwError(errorMsg) self.typeTaxon = leftQuasi.taxon self.refDecl.setTarget( createOperatorLow(self.core, '=', leftQuasi.taxon, rightQuasi.taxon, self.typeTaxon))
def _checkIndexType(self): """ Индекс таксона должен каститься в unsigned long """ # Индексы строгие, как в C++, Java, JS. # В PHP отрицательный индекс используется, как ключ ассоциативного массива. В Python - отсчитывает элемент от конца # Поэтому для совместимости все индексы беззнаковые целые from core.TaxonScalar import TaxonScalar from core.QuasiType import QuasiType requiredType = TaxonScalar.createByName('long') requiredType.attrs.add('unsigned') code, errMsg = QuasiType.matchTaxons(requiredType, self.getIndexTaxon()) if errMsg: self.throwError(errMsg)
def testFindParentByName(self): source = """ class Top class Middle extends Top class Bottom extends Middle """ module = WppCore.createMemModule(source, 'findParentByName.wpp') Top = module.findItem('Top') Middle = module.findItem('Middle') Bottom = module.findItem('Bottom') self.assertIsNone(Top.getParent()) self.assertEqual(Middle.getParent(), Top) self.assertEqual(Bottom.getParent(), Middle) self.assertEqual(Bottom.findParentByName('Top'), Top) self.assertEqual(Bottom.findParentByName('Middle'), Middle) self.assertIsNone(Bottom.findParentByName('Abcd')) self.assertEqual(QuasiType.matchTaxons(Top, Middle), ('upcast', None)) self.assertEqual(QuasiType.matchTaxons(Top, Bottom), ('upcast', None)) self.assertEqual(QuasiType.matchTaxons(Top, Top), ('exact', None))
def testMatch(self): sign = Signature() sign.params = [MyVar('i', typeInt), MyVar('x', typeFloat)] fn1 = createFunc('fn1', ['i:int', 'x:float']) args1 = fn1.getParams() self.assertEqual(len(args1), 2) self.assertEqual(args1[0].type, 'Param') self.assertEqual(QuasiType.matchTaxons(sign.params[0], args1[0]), ('exact', None)) # int exact int self.assertEqual( QuasiType.matchTaxons(sign.params[1], args1[0])[0], None) # int variable can't upcast to float self.assertEqual( QuasiType.matchTaxons(sign.params[0], args1[1])[0], None) # float can't upcast to int self.assertEqual(QuasiType.matchTaxons(sign.params[1], args1[1]), ('exact', None)) # float exact float self.assertEqual(sign.match(fn1), 6) # exact = 3, upcast = 1 fn2 = createFunc('fn2', ['a:long', 'b:float']) self.assertEqual( QuasiType.matchTaxons(fn2.getParams()[0], sign.params[0]), ('upcast', None)) # long = int -> upcast self.assertEqual( QuasiType.matchTaxons(fn2.getParams()[1], sign.params[1]), ('exact', None)) # float = float -> exact self.assertEqual(sign.match(fn2), 4) # exact=3 + upcast(1) = 4
def checkAgrs(funcName, params, args): nArgs = len(args) nParams = len(params) if nArgs > nParams: return '%s takes %d argument but %d were given' % (funcName, nParams, nArgs) if nArgs < nParams and not params[nArgs].getValueTaxon(): return '%s missing required argument: "%s"' % (funcName, params[nArgs].getName()) for i, arg in enumerate(args): result, errorMsg = QuasiType.matchTaxons(params[i], arg) if errorMsg: return errorMsg return ''
def matchQuasiType(self, left, right): #TODO: Проверять unsigned и диаазон констант if left.taxon.type != 'Array': self.throwError('Required Array instead of ' + left.taxon.type) qtItem = left.itemType.buildQuasiType() if right.taxon.type == 'ArrayValue': # Необходимо проверить каждый элемент значения [a, b, c], соответствует ли он типу элементов массива for srcItemTaxon in right.taxon.items: code, errMsg = qtItem.matchTaxons(qtItem, srcItemTaxon) if errMsg: self.throwError(errMsg) # Будем считать, что массив соответствует типу, даже если элементы имеют другие коды, н.р. constUpcast return 'constExact', None if right.taxon.type == 'Array': # Нужно проверить соответствие типов элементов return QuasiType.matchTaxons(left.itemType, right.itemType) return None, None
def findBinOp(self, opcode, leftPart, rightPart): leftQType = leftPart.buildQuasiType() rightQType = rightPart.buildQuasiType() # Особый случай - оператор присваивания. Для дефольной реализации достаточно возможность привести правый тип к левому if opcode == '=': _, errMsg = QuasiType.matchTaxons(leftQType, rightQType) if errMsg: return None, errMsg return self.declAssignBase, None declList = self.binOpMap.get(opcode) if not declList: return None, 'Invalid operator %s' % opcode # Пока ищем первое попавшееся совпадение for decl in declList: leftResult, rightResult = decl.matchTypes(leftQType, rightQType) if leftResult and rightResult: return decl, None return None, 'Not found operator %s(%s, %s)' % ( opcode, leftQType.getDebugStr(), rightQType.getDebugStr())
def checkParams(self): """ Проверка соответствия типов формальных и фактических параметров """ args = self.getArguments() formalParams = self.declTaxon.getParams() # Формальных параметров не может быть больше, чем фактических if len(args) > len(formalParams): self.throwError("Expected %d parameters instead of %d in %s %s" % (len(formalParams), len(args), self.type, self.getDebugStr())) # Проверить соответствие типов. Фактический должен кастоваться к формальному for i, arg in enumerate(args): formal = formalParams[i].buildQuasiType() formal.inst = self result, errMsg = QuasiType.matchTaxons(formal, arg) if not result: self.throwError('Invalid parameter #%d of %s: %s' % (i+1, self.declTaxon.getDebugStr(), errMsg)) # Если фактических параметров меньше, чем формальных, то оставшиеся формальные должны иметь значение по-умолчанию i = len(args) while i < len(formalParams): formal = formalParams[i] i += 1 if not formal.getValueTaxon(): self.throwError('Expected parameter #%d (%s) in %s' % (i, formal.name, self.declTaxon.getDebugStr()))
def testMatch(self): source = """ class public First class public Second var first: First var firstA: First var second: Second """ module = WppCore.createMemModule(source, 'simple.wpp') First = module.findItem('First') Second = module.findItem('Second') first = module.findItem('first') firstA = module.findItem('firstA') second = module.findItem('second') self.assertEqual(QuasiType.matchTaxons(first, First), ('exact', None)) self.assertEqual(QuasiType.matchTaxons(firstA, First), ('exact', None)) self.assertEqual(QuasiType.matchTaxons(first, firstA), ('exact', None)) self.assertEqual(QuasiType.matchTaxons(firstA, first), ('exact', None)) self.assertEqual(QuasiType.matchTaxons(second, Second), ('exact', None)) self.assertEqual(QuasiType.matchTaxons(first, Second), (None, 'Cannot convert from "class Second" to "var first: First"'))
def exec(self): st, errMsg = QuasiType.matchTaxons(self.left, self.right) if errMsg: self.taxon.throwError(errMsg)
def buildQuasiType(self): if self.isReady(): return QuasiType(self) return None
def mkQt(self, typeName): words = typeName.split() return QuasiType(self.findItem(words[-1]), set(words[0:-1]))
def buildQuasiType(self): resultType = self.getResultType() if not resultType: return None return QuasiType.combine(self, resultType)
def buildQuasiType(self): if not self.declTaxon: self.throwError('%s not ready in buildQuasiType' % (self.type)) return QuasiType.combine(self, self.declTaxon)
def checkType(self): left = self.getTypeTaxon() right = self.getValueTaxon() result, errMsg = QuasiType.matchTaxons(left, right) if errMsg: self.throwError(errMsg)
def buildQuasiType(self): typeTaxon = self.getTypeTaxon() return QuasiType.combine(self, typeTaxon) if typeTaxon else None
def matchTypes(self, leftQt, rightQt): leftRes, leftErr = QuasiType.matchTaxons(self.leftType, leftQt) rightRes, rightErr = QuasiType.matchTaxons(self.rightType, rightQt) return leftRes, rightRes
def buildQuasiType(self): return QuasiType.combine(self, self.typeRef)
def buildQuasiType(self): return QuasiType(self)
def buildQuasiType(self): if not self.isReady(): # C isReadyFull зацикливание self.throwError('Var not ready in buildQuasiType') return QuasiType.combine(self, self.getLocalType())
def findSuitablePure(qtArguments, functions): """ qtArguments = список квази-типов для аргументов вызывающего выращения (фактических параметров) functions = список таксонов типа TaxonFunc result = None - значит не готово 'NoSuitable' = 'No suitable _method_ found for fname(type, type)' """ if None in qtArguments: return None nArgs = len(qtArguments) # Собрать список функций, которые подходят по количеству параметров, с учетом дефолтных значений possibleFuncs = [] for func in functions: params = func.getParamsList() if len(params) != nArgs: # У функции число параметров должно точно соответствовать количеству аргументов. # Дефолтные параметры для перегруженных функций запрещены. continue qtList = [param.buildQuasiType() for param in params] if None in qtList: return None possibleFuncs.append((func, qtList)) # Если не подошел ни один вариант if len(possibleFuncs) == 0: return 'NoSuitable' # Теперь нужно проверить соответствие matches = [] for func, qtList in possibleFuncs: resList = [] for i, qtArg in enumerate(qtArguments): res, err = QuasiType.matchTaxons(qtList[i], qtArg) if err: break resList.append(res) else: matches.append((func, resList)) if len(matches) == 0: return 'NoSuitable' # Если найдено одно соответствие, то задача выполнена if len(matches) == 1: return matches[0][0] # Если вариантов несколько то сначала пробуем найти точное соответствие exacts = [] for func, matchList in matches: for res in matchList: if res not in ('exact', 'constExact'): break else: exacts.append(func) if len(exacts) == 1: # Найден один вариант, точно соответствующий по параметрам return exacts[0] if len(exacts) > 1: # Несколько точно подходящих вариантов return 'NoSuitable' # Точных соответствий нет. Попробуем найти лучшее соответствие weights = { 'exact': 10, 'constExact': 10, 'upcast': 1, 'constUpcast': 1 } priors = [] for func, matchList in matches: weight = 0 for res in matchList: weight += weights[res] priors.append((func, weight)) # Сортировка по весу priors.sort(key=lambda pair: pair[1]) # Если первые два равны по весу, то ошибка if priors[0][1] == priors[1][1]: return 'NoSuitable' return priors[0][0]
def exec(self): res, err = QuasiType.matchTaxons(self.left, self.right) if err: self.taxon.throwError(err)