hasLocalVar($var) and $this->isStdContainerType($this->getVarType($var)); } protected function isStdContainerType(string $type): bool { return in_array($type, [ self::TYPE_STD_ARRAY, self::TYPE_STD_VECTOR, self::TYPE_STD_MAP, self::TYPE_STD_ORDERED_MAP, ], true); } protected function isStdArray(string $var): bool { return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_ARRAY; } protected function isStdVector(string $var): bool { return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_VECTOR; } protected function isStdMap(string $var): bool { return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_MAP; } protected function isStdOrderedMap(string $var): bool { return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_ORDERED_MAP; } protected function getStdTypeKey(array $info): string { $parts = [ 'kind=' . $info['kind'], 'decl=' . $info['decl'], 'type=' . $info['type'], 'class=' . ($info['class'] ?? ''), ]; if (isset($info['keyType'])) { $parts[] = 'keyType=' . $info['keyType']; } return implode(';', $parts); } protected function addStdTypeId(array $info): array { $info['typeId'] = $this->registerStdType($this->getStdTypeKey($info)); return $info; } protected function getStdContainerVarInfo(string $var): array { if ($this->isStdArray($var)) { return $this->context->stdArrays[$var]; } return $this->context->stdContainers[$var]; } protected function getStdContainerKeyType(string $var): string { if ($this->isStdVector($var) or $this->isStdArray($var)) { return self::TYPE_INT; } return $this->getStdContainerVarInfo($var)['keyType']; } protected function getStdContainerValueType(string $var, string $valueVar): string { $info = $this->getStdContainerVarInfo($var); if ($this->isStdArray($var)) { return count($info['sizes']) > 1 ? self::TYPE_ARRAY : $info['type']; } if ($info['type'] === self::TYPE_OBJECT and $info['class']) { $this->addObject($valueVar, $info['class']); } else { unset($this->context->objects[$valueVar]); } return $info['type']; } protected function getStdArrayDecl(string $type, array $sizes): string { $decl = str_repeat(self::TYPE_STD_ARRAY . '<', count($sizes)); $decl .= $this->getStdContainerElementType($type); for ($i = count($sizes) - 1; $i >= 0; $i--) { $decl .= ', ' . $sizes[$i] . '>'; } return $decl; } protected function getStdValueTypeBytes(string $type): int { return match ($type) { self::TYPE_BOOL => 1, self::TYPE_INT, self::TYPE_FLOAT => 8, default => 16, }; } protected function getNestedStdArrayInfo(array $info, int $accessLevel): ?array { $sizes = array_reverse($info['sizes']); if ($accessLevel >= count($sizes)) { return null; } $nestedSizes = array_slice($sizes, $accessLevel); return [ 'kind' => 'array', 'decl' => $this->getStdArrayDecl($info['type'], $nestedSizes), 'type' => $info['type'], 'class' => $info['class'], 'sizes' => array_reverse($nestedSizes), 'bytes' => array_product($nestedSizes) * $this->getStdValueTypeBytes($info['type']), ]; } protected function getStdArrayDimFetchContainerInfo(Expr\ArrayDimFetch $expr): ?array { if (!$expr->hasAttribute('stdArrayDimFetch')) { $this->parseStdArrayDimFetch($expr); } $attr = $expr->getAttribute('stdArrayDimFetch'); return $this->getNestedStdArrayInfo($this->context->stdArrays[$attr['var']], $attr['accessLevel']); } protected function isSameStdContainerInfo(array $leftInfo, array $rightInfo): bool { return $this->getStdTypeKey($leftInfo) === $this->getStdTypeKey($rightInfo); } protected function getStdContainerExprInfo(NodeAbstract $expr): ?array { if ($this->isVarExpr($expr)) { $var = $this->parseVariable($expr); if ($this->isStdContainer($var)) { return $this->getStdContainerVarInfo($var); } return null; } if ($this->isArrayDimFetch($expr) and $this->isStdArrayExpr($expr)) { return $this->getStdArrayDimFetchContainerInfo($expr); } return null; } protected function parseStdContainerCopyExpr(NodeAbstract $expr): string { if ($this->isVarExpr($expr)) { return $this->parseVariable($expr) . '_ref'; } if ($this->isArrayDimFetch($expr) and $this->isStdArrayExpr($expr)) { return $this->parseStdArrayDimFetch($expr); } return $this->parseExpr($expr); } protected function isStdArrayExpr(Expr\ArrayDimFetch $expr): bool { $info = $this->getStdArrayInfo($expr); return $info !== null; } protected function isStdContainerExpr(Expr\ArrayDimFetch $expr): bool { return $this->isStdArrayExpr($expr) || $this->getStdContainerInfo($expr) !== null; } protected function fillStdArray(Expr\StaticCall $expr): string { if (!$this->isVarExpr($expr->args[0]->value) or !$this->isStdArray($this->parseIdentifier($expr->args[0]->value))) { $this->fatalError($expr, 'fill() only support std::array'); } $array = $this->parseIdentifier($expr->args[0]->value); $info = $this->context->stdArrays[$array]; $value = $this->convertStdValueExpr($info, $expr->args[1]->value); return "{$array}_ref.fill({$value})"; } protected function getStdArrayInfo(Expr\ArrayDimFetch $expr): ?array { $tmp = $expr->var; while (true) { if ($this->isArrayDimFetch($tmp)) { $tmp = $tmp->var; } elseif ($this->isVarExpr($tmp) and $this->isStdArray($this->parseVariable($tmp))) { return $this->context->stdArrays[$this->parseVariable($tmp)]; } else { return null; } } } protected function getStdContainerInfo(Expr\ArrayDimFetch $expr): ?array { $tmp = $expr->var; while (true) { if ($this->isArrayDimFetch($tmp)) { $tmp = $tmp->var; } elseif ($this->isVarExpr($tmp)) { $var = $this->parseVariable($tmp); if ($this->isStdVector($var) || $this->isStdMap($var) || $this->isStdOrderedMap($var)) { return $this->context->stdContainers[$var]; } return null; } else { return null; } } } protected function parseStdArrayAssign(NodeAbstract $left, NodeAbstract $right): string { $info = $this->getStdArrayInfo($left); $arrayDimFetch = $this->parseStdArrayDimFetch($left); $attr = $left->getAttribute('stdArrayDimFetch'); if ($attr['accessLevel'] < $attr['totalLevel']) { $leftInfo = $this->getNestedStdArrayInfo($info, $attr['accessLevel']); $rightInfo = $this->getStdContainerExprInfo($right); if ($rightInfo !== null and $this->isSameStdContainerInfo($leftInfo, $rightInfo)) { return $arrayDimFetch . ' = ' . $this->parseStdContainerCopyExpr($right); } $this->fatalError($right, 'Cannot assign non-matching value to nested std::array'); } return $arrayDimFetch . ' = ' . $this->convertStdValueExpr($info, $right); } protected function parseStdContainerAssign(Expr\ArrayDimFetch $left, NodeAbstract $right): string { if ($this->isStdArrayExpr($left)) { return $this->parseStdArrayAssign($left, $right); } $info = $this->getStdContainerInfo($left); if ($info['kind'] === 'vector' && $left->dim === null) { if (!$this->isVarExpr($left->var)) { $this->fatalError($left, 'std::vector append only supports a vector variable'); } $vector = $this->parseVariable($left->var); return $vector . '_ref.push_back(' . $this->convertStdValueExpr($info, $right) . ')'; } if ($left->dim === null) { $this->fatalError($left, 'std map expects a key'); } return $this->parseStdContainerOffsetSet($left, $this->convertStdValueExpr($info, $right)); } protected function parseStdArrayAssignOp(Expr\AssignOp $expr, string $op): string { $binaryOp = $this->removeAssignOp($op); if ($binaryOp === '.') { $this->fatalError($expr, 'Cannot concat string to std::array'); } $info = $this->getStdArrayInfo($expr->var); $arrayDimFetch = $this->parseStdArrayDimFetch($expr->var); $attr = $expr->var->getAttribute('stdArrayDimFetch'); if ($attr['accessLevel'] < $attr['totalLevel']) { $this->fatalError($expr, 'Cannot use assign operator on nested std::array'); } return $arrayDimFetch . ' ' . $binaryOp . '= ' . $this->convertExprFromType($info['type'], $this->parseExpr($expr->expr)); } protected function parseStdContainerAssignOp(Expr\AssignOp $expr, string $op): string { if ($this->isStdArrayExpr($expr->var)) { return $this->parseStdArrayAssignOp($expr, $op); } $binaryOp = $this->removeAssignOp($op); if ($binaryOp === '.') { $this->fatalError($expr, 'Cannot concat string to std container'); } $info = $this->getStdContainerInfo($expr->var); $containerDimFetch = $this->parseStdContainerDimFetch($expr->var); return $containerDimFetch . ' ' . $binaryOp . '= ' . $this->convertExprFromType($info['type'], $this->parseExpr($expr->expr)); } protected function parseStdArrayDimFetch(Expr\ArrayDimFetch $expr): string { $tmp = $expr; $dims = []; $info = $this->getStdArrayInfo($expr); while (true) { if ($this->isArrayDimFetch($tmp)) { if ($tmp->dim === null) { $this->fatalError($tmp, 'std::array expects an index'); } $dims[] = $tmp->dim; $tmp = $tmp->var; } else { break; } } if (!$this->isVarExpr($tmp)) { $this->fatalError($expr, 'std::array expects a variable'); } $dims = array_reverse($dims); $sizes = array_reverse($info['sizes']); if (count($dims) > count($sizes)) { $this->fatalError($expr, 'std::array access level exceeds array dimensions'); } $baseVar = $this->parseVariable($tmp); $nesting = [$baseVar . '_ref']; foreach ($dims as $level => $dim) { $size = $sizes[$level]; if ($this->isScalarInt($dim)) { if ($dim->value < 0 || $dim->value >= $size) { $this->fatalError($dim, "std::array index out of bounds: index {$dim->value}, size {$size}"); } } $index = $this->parseExpr($dim); $nesting[] = '[' . Symbol::safeIndex($this->convertIntExpr($index), $size) . ']'; } $expr->setAttribute('stdArrayDimFetch', ['var' => $baseVar, 'accessLevel' => count($dims), 'totalLevel' => count($sizes)]); return implode('', $nesting); } protected function parseForeachStdContainer(Foreach_ $node): string { $container = $this->parseIdentifier($node->expr); if ($this->isStdMap($container) or $this->isStdOrderedMap($container)) { $this->context->stdContainers[$container]['locking'] = true; } $iterator = $this->genTmpVarName(); $code = "for (auto $iterator = {$container}_ref.begin(); $iterator != {$container}_ref.end(); ++$iterator) {" . PHP_EOL; $this->indentLevel++; if ($node->keyVar) { $keyVar = $this->parseIdentifier($node->keyVar); $this->checkVar($node, $keyVar, $this->getStdContainerKeyType($container)); if ($this->isStdVector($container) or $this->isStdArray($container)) { $code .= $this->getIndent() . "$keyVar = $iterator - {$container}_ref.begin();" . PHP_EOL; } else { $code .= $this->getIndent() . "$keyVar = {$iterator}->first;" . PHP_EOL; } } if ($node->byRef) { $this->fatalError($node, 'Cannot use & with std container foreach'); } if (!$this->isVarExpr($node->valueVar)) { $this->fatalError($node, 'Cannot assign value to std container foreach'); } $valueVar = $this->parseIdentifier($node->valueVar); $this->checkVar($node, $valueVar, $this->getStdContainerValueType($container, $valueVar)); if ($this->isStdVector($container) or $this->isStdArray($container)) { $code .= $this->getIndent() . "$valueVar = *$iterator;" . PHP_EOL; } else { $code .= $this->getIndent() . "$valueVar = {$iterator}->second;" . PHP_EOL; } $body = $this->parseForeachBody($node); $this->indentLevel--; $code .= $this->parseBeforeStmtLines() . PHP_EOL; $code .= $body . PHP_EOL; $code .= $this->getIndent() . '}'; unset($this->context->objects[$valueVar]); if ($this->isStdMap($container) or $this->isStdOrderedMap($container)) { $this->context->stdContainers[$container]['locking'] = false; } return $code; } protected function parseStdContainerDimFetch(Expr\ArrayDimFetch $expr): string { if ($this->isStdArrayExpr($expr)) { return $this->parseStdArrayDimFetch($expr); } $info = $this->getStdContainerInfo($expr); $tmp = $expr; $dims = []; while (true) { if ($this->isArrayDimFetch($tmp)) { $dims[] = $tmp->dim; $tmp = $tmp->var; } else { break; } } if (!$this->isVarExpr($tmp)) { $this->fatalError($expr, 'std container expects a variable'); } if (count($dims) !== 1) { $this->fatalError($expr, 'Nested std::vector/std::map/std::ordered_map access is not supported'); } $dim = $dims[0]; if ($dim === null) { $this->fatalError($expr, 'std container expects an index'); } $container = $this->parseVariable($tmp); $index = $this->parseExpr($dim); $key = $info['kind'] === 'vector' ? $this->convertIntExpr($index) : $this->convertStdContainerKey($info, $index); $access = $container . '_ref.offsetGet(' . $key . ')'; $expr->setAttribute('stdContainerDimFetch', ['var' => $container, 'accessLevel' => 1, 'totalLevel' => 1]); return $access; } protected function parseStdContainerOffsetSet(Expr\ArrayDimFetch $expr, string $value): string { $info = $this->getStdContainerInfo($expr); if ($expr->dim === null) { $this->fatalError($expr, 'std container expects an index'); } if (!$this->isVarExpr($expr->var)) { $this->fatalError($expr, 'std container expects a variable'); } $container = $this->parseVariable($expr->var); $indexExpr = $this->parseExpr($expr->dim); $index = $info['kind'] === 'vector' ? $this->convertIntExpr($indexExpr) : $this->convertStdContainerKey($info, $indexExpr); return $container . '_ref.offsetSet(' . $index . ', ' . $value . ')'; } protected function convertStdContainerKey(array $info, string $index): string { if ($info['keyType'] === self::TYPE_STR) { return $this->convertStringExpr($index); } return $this->convertIntExpr($index); } protected function getStdContainerElementType(string $type): string { return match ($type) { self::TYPE_BIGINT, self::TYPE_BIGFLOAT, self::TYPE_DECIMAL, self::TYPE_STREAM, self::TYPE_BOX => self::TYPE_VAR, default => $type, }; } protected function parseStdNativeType(NodeAbstract $expr, string $owner): string { if (!$this->isClassConstFetch($expr)) { $this->fatalError($expr, "{$owner} expects a native_types class constant"); } if (!$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name) || strtolower($expr->class->toString()) !== 'native_types') { $this->fatalError($expr, "An incorrect `{$owner}` definition"); } return match (strtolower($expr->name->name)) { 'type_int' => self::TYPE_INT, 'type_float' => self::TYPE_FLOAT, 'type_bool' => self::TYPE_BOOL, 'type_bigint' => self::TYPE_BIGINT, 'type_bigfloat' => self::TYPE_BIGFLOAT, 'type_decimal' => self::TYPE_DECIMAL, default => $this->fatalError($expr, "An incorrect `{$owner}` definition"), }; } protected function parseStdValueTypeInfo(NodeAbstract $expr, string $owner): array { if (!$this->isClassConstFetch($expr)) { $this->fatalError($expr, "{$owner} expects a native_types or complex_types class constant"); } if (!$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name)) { $this->fatalError($expr, "An incorrect `{$owner}` definition"); } $className = strtolower($expr->class->toString()); if ($className === 'native_types') { return ['type' => $this->parseStdNativeType($expr, $owner), 'class' => null]; } if ($className === 'complex_types') { return [ 'type' => match (strtolower($expr->name->name)) { 'type_str', 'type_string' => self::TYPE_STR, 'type_array' => self::TYPE_ARRAY, 'type_object' => self::TYPE_OBJECT, 'type_any', 'type_var', 'type_variant' => self::TYPE_VAR, 'type_stream' => self::TYPE_STREAM, 'type_box' => self::TYPE_BOX, default => $this->fatalError($expr, "An incorrect `{$owner}` definition"), }, 'class' => null, ]; } if ($expr->name->name !== 'class') { $this->fatalError($expr, "{$owner} class value only supports ClassName::class"); } $class = $this->parseStdClassValueType($expr, $owner); return ['type' => self::TYPE_OBJECT, 'class' => $class]; } protected function parseStdValueType(NodeAbstract $expr, string $owner): string { return $this->parseStdValueTypeInfo($expr, $owner)['type']; } protected function parseStdClassValueType(Expr\ClassConstFetch $expr, string $owner): string { $class = $this->parseIdentifier($expr->class); if ($class === 'static') { $this->fatalError($expr, "{$owner} class value does not support static::class"); } if ($class === 'self' || $class === 'this_') { if (!$this->classDef) { $this->fatalError($expr, "{$owner} class value cannot use self::class outside class scope"); } $class = $this->getNamespacedClassName($this->class); } elseif ($class === 'parent') { if (!$this->classDef || !$this->classDef->extends) { $this->fatalError($expr, "{$owner} class value cannot use parent::class because current class does not extend any class"); } $class = $this->getNamespacedClassName('\\' . $this->classDef->extends); } else { $class = $this->getNamespacedClassName($class); } return $class; } protected function convertStdValueExpr(array $info, NodeAbstract $expr): string { $valueExpr = $this->parseExpr($expr); $class = $info['class'] ?? null; if ($class === null) { $targetType = $info['type']; if ($targetType === self::TYPE_BIGINT || $targetType === self::TYPE_BIGFLOAT || $targetType === self::TYPE_DECIMAL || $targetType === self::TYPE_STREAM || $targetType === self::TYPE_BOX) { return $this->convertStdVarBackedExpr($targetType, $valueExpr, $expr); } return $this->convertExprFromType($targetType, $valueExpr); } $rightClass = $this->detectClassOfExpr($expr); if ($rightClass !== '') { if (!$this->isObjectClassStaticallyAssignableTo($rightClass, $class)) { $this->fatalError($expr, "Cannot assign object of class `{$rightClass}` to std container value of class `{$class}`"); } } return 'php::toObject(' . $valueExpr . ', ' . $this->getClassEntryPtr($class) . ')'; } protected function convertStdVarBackedExpr(string $targetType, string $valueExpr, NodeAbstract $expr): string { $sourceType = $this->detectTypeOfExpr($expr); if ($sourceType === $targetType) { return $valueExpr; } if ($targetType === self::TYPE_STREAM || $targetType === self::TYPE_BOX) { return $valueExpr; } if ($targetType === self::TYPE_BIGINT) { return $this->convertBigIntExpr($valueExpr, $sourceType); } if ($targetType === self::TYPE_BIGFLOAT) { return $this->convertBigFloatExpr($valueExpr, $sourceType); } if ($targetType === self::TYPE_DECIMAL) { return $this->convertDecimalExpr($valueExpr, $sourceType, $expr); } return $valueExpr; } protected function parseToStdAssign(string $var, Expr\MethodCall $expr): string { $methodName = $expr->name->toString(); $containerType = match ($methodName) { 'toStdArray' => 'array', 'toStdVector' => 'vector', 'toStdMap' => 'map', 'toStdOrderedMap' => 'ordered_map', }; if (!$this->isVarExpr($expr->var)) { $this->fatalError($expr->var, "{$methodName}() must be called on a variable"); } $sourceVar = $this->parseVariable($expr->var); if (!$this->hasVar($sourceVar)) { $this->fatalError($expr->var, 'Undefined variable `$' . $sourceVar . '`'); } $name = new Name('std'); $method = new Identifier($containerType); $fakeCall = new StaticCall($name, $method, $expr->args); if ($containerType === 'array') { $this->addLocalVar($var, self::TYPE_STD_ARRAY); $this->parseStdArray($var, $fakeCall); $this->context->stdArrays[$var]['boxExpr'] = $sourceVar; return '// StdContainer<' . $this->context->stdArrays[$var]['decl'] . '>(' . $sourceVar . ')'; } if ($containerType === 'vector') { $this->addLocalVar($var, self::TYPE_STD_VECTOR); $this->parseStdVector($var, $fakeCall); } elseif ($containerType === 'map') { $this->addLocalVar($var, self::TYPE_STD_MAP); $this->parseStdMap($var, $fakeCall); } else { $this->addLocalVar($var, self::TYPE_STD_ORDERED_MAP); $this->parseStdOrderedMap($var, $fakeCall); } $this->context->stdContainers[$var]['boxExpr'] = $sourceVar; return '// StdContainer<' . $this->context->stdContainers[$var]['decl'] . '>(' . $sourceVar . ')'; } protected function parseStdMapKeyType(NodeAbstract $expr, string $owner): string { if (!$this->isClassConstFetch($expr) || !$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name)) { $this->fatalError($expr, "{$owner} expects a native_types or complex_types class constant"); } $className = strtolower($expr->class->toString()); $constName = strtolower($expr->name->name); if ($className === 'native_types' && $constName === 'type_int') { return self::TYPE_INT; } if ($className === 'complex_types' && in_array($constName, ['type_string', 'type_str'], true)) { return self::TYPE_STR; } $this->fatalError($expr, "{$owner} key only supports native_types::type_int, complex_types::type_string or complex_types::type_str"); } protected function getStdMapDecl(string $containerType, string $keyType, string $valueType): string { return $containerType . '<' . $keyType . ', ' . $this->getStdContainerElementType($valueType) . '>'; } protected function parseStdArray(string $var, Expr\StaticCall $expr): string { $tmp = $expr; $nesting = []; $totalBytes = 0; while (true) { if (count($tmp->args) !== 2) { $this->fatalError($tmp, 'std::array() expects two arguments'); } if (!$this->isScalarInt($tmp->args[1]->value)) { $this->fatalError($tmp, 'std::array() expects second argument to be an integer'); } $byte = 0; $size = $tmp->args[1]->value->value; $nesting[] = $size; $typeExpr = $tmp->args[0]->value; if ($this->isClassConstFetch($typeExpr)) { $typeInfo = $this->parseStdValueTypeInfo($typeExpr, 'std::array'); $type = $typeInfo['type']; $byte = $this->getStdValueTypeBytes($type); break; } if ($this->isStaticCall($typeExpr)) { $tmp = $typeExpr; if (!$this->isNameExpr($tmp->class) || !$this->isIdExpr($tmp->name) || $tmp->class->toString() !== 'std' || $tmp->name->toString() !== 'array') { $this->fatalError($tmp, 'An incorrect `std::array` definition'); } } else { $this->fatalError($tmp, 'std::array() expects first argument to be a class constant'); } } $totalBytes = array_product($nesting) * $byte; $decl = $this->getStdArrayDecl($type, $nesting); $this->context->stdArrays[$var] = $this->addStdTypeId([ 'kind' => 'array', 'decl' => $decl, 'type' => $type, 'class' => $typeInfo['class'], 'sizes' => array_reverse($nesting), 'bytes' => $totalBytes, ]); return '// ' . $decl; } protected function parseStdVector(string $var, Expr\StaticCall $expr): string { if (count($expr->args) < 1 || count($expr->args) > 2) { $this->fatalError($expr, 'std::vector() expects one or two arguments'); } $typeInfo = $this->parseStdValueTypeInfo($expr->args[0]->value, 'std::vector'); $type = $typeInfo['type']; $size = null; if (count($expr->args) === 2) { if (!$this->isScalarInt($expr->args[1]->value)) { $this->fatalError($expr, 'std::vector() expects second argument to be an integer'); } $size = $expr->args[1]->value->value; } $decl = self::TYPE_STD_VECTOR . '<' . $this->getStdContainerElementType($type) . '>'; $this->context->stdContainers[$var] = $this->addStdTypeId([ 'kind' => 'vector', 'decl' => $decl, 'type' => $type, 'class' => $typeInfo['class'], 'size' => $size, ]); return '// ' . $decl; } protected function parseStdMap(string $var, Expr\StaticCall $expr): string { return $this->parseStdMapBase($var, $expr, 'std::map', self::TYPE_STD_MAP, 'map'); } protected function parseStdOrderedMap(string $var, Expr\StaticCall $expr): string { return $this->parseStdMapBase($var, $expr, 'std::ordered_map', self::TYPE_STD_ORDERED_MAP, 'ordered_map'); } private function parseStdMapBase(string $var, Expr\StaticCall $expr, string $funcName, string $containerType, string $kind): string { if (count($expr->args) !== 2) { $this->fatalError($expr, $funcName . '() expects two arguments'); } $keyType = $this->parseStdMapKeyType($expr->args[0]->value, $funcName); $valueTypeInfo = $this->parseStdValueTypeInfo($expr->args[1]->value, $funcName); $valueType = $valueTypeInfo['type']; $decl = $this->getStdMapDecl($containerType, $keyType, $valueType); $this->context->stdContainers[$var] = $this->addStdTypeId([ 'kind' => $kind, 'decl' => $decl, 'type' => $valueType, 'class' => $valueTypeInfo['class'], 'keyType' => $keyType, ]); return '// ' . $decl; } /** * Generate compile-time count for std containers. * Returns the C++ integer literal for the size, or false if not a std container. */ protected function genStdContainerCount(NodeAbstract $expr): string|false { // Simple variable: $var if ($this->isVarExpr($expr)) { $var = $this->parseVariable($expr); if ($this->isStdArray($var)) { $info = $this->context->stdArrays[$var]; $sizes = array_reverse($info['sizes']); return $sizes[0] . $this->getPlatform()->getIntegerLiteralSuffix(); } if ($this->isStdVector($var)) { $info = $this->context->stdContainers[$var]; if ($info['size'] !== null) { return $info['size'] . $this->getPlatform()->getIntegerLiteralSuffix(); } return $var . '_ref.size()'; } if ($this->isStdContainer($var)) { return $var . '_ref.size()'; } return false; } // ArrayDimFetch: $array[idx1][idx2]... if ($this->isArrayDimFetch($expr)) { $tmp = $expr; $dimLevel = 0; while ($this->isArrayDimFetch($tmp)) { $dimLevel++; $tmp = $tmp->var; } if ($this->isVarExpr($tmp)) { $var = $this->parseVariable($tmp); if ($this->isStdArray($var)) { $info = $this->context->stdArrays[$var]; $outerSizes = array_reverse($info['sizes']); if ($dimLevel < count($outerSizes)) { return $outerSizes[$dimLevel] . $this->getPlatform()->getIntegerLiteralSuffix(); } } } if ($this->isVarExpr($tmp)) { $var = $this->parseVariable($tmp); if ($this->isStdVector($var)) { $info = $this->context->stdContainers[$var]; if ($info['size'] !== null) { return $info['size'] . $this->getPlatform()->getIntegerLiteralSuffix(); } return $var . '_ref.size()'; } if ($this->isStdMap($var) || $this->isStdOrderedMap($var)) { return $var . '_ref.size()'; } } } return false; } }