work on string, bug fixes, overloaded assignment operator. Still need to get overloaded copy_construct for declaration assignment
This commit is contained in:
@@ -50,7 +50,7 @@ class ASTTransformation: public NodeTransformation<Symbol,ASTData> {
|
||||
NodeTree<ASTData>* functionLookup(NodeTree<ASTData>* scope, std::string lookup, std::vector<Type> types);
|
||||
NodeTree<ASTData>* templateFunctionLookup(NodeTree<ASTData>* scope, std::string lookup, std::vector<Type*>* templateInstantiationTypes, std::vector<Type> types, std::map<std::string, Type*> scopeTypeMap);
|
||||
std::vector<NodeTree<ASTData>*> scopeLookup(NodeTree<ASTData>* scope, std::string lookup, bool includeModules = false);
|
||||
std::vector<NodeTree<ASTData>*> scopeLookup(NodeTree<ASTData>* scope, std::string lookup, bool includeModules, std::vector<NodeTree<ASTData>*> visited);
|
||||
std::vector<NodeTree<ASTData>*> scopeLookup(NodeTree<ASTData>* scope, std::string lookup, bool includeModules, std::set<NodeTree<ASTData>*> visited);
|
||||
|
||||
NodeTree<ASTData>* getUpperTranslationUnit(NodeTree<ASTData>* node);
|
||||
NodeTree<ASTData>* addToScope(std::string name, NodeTree<ASTData>* toAdd, NodeTree<ASTData>* addTo);
|
||||
|
||||
@@ -12,7 +12,7 @@ class CCodeTriple {
|
||||
CCodeTriple(const char* val);
|
||||
CCodeTriple();
|
||||
~CCodeTriple();
|
||||
std::string oneString();
|
||||
std::string oneString(bool endValue = false);
|
||||
CCodeTriple & operator=(const CCodeTriple &rhs);
|
||||
CCodeTriple & operator+=(const CCodeTriple &rhs);
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ ASTTransformation::ASTTransformation(Importer *importerIn) {
|
||||
languageLevelOperators["||"].push_back(addToScope("~enclosing_scope", builtin_trans_unit, new NodeTree<ASTData>("function", ASTData(function, Symbol("||", true), new Type(boolean)))));
|
||||
languageLevelOperators["!"].push_back(addToScope("~enclosing_scope", builtin_trans_unit, new NodeTree<ASTData>("function", ASTData(function, Symbol("!", true), new Type(boolean)))));
|
||||
languageLevelOperators["*="].push_back(addToScope("~enclosing_scope", builtin_trans_unit, new NodeTree<ASTData>("function", ASTData(function, Symbol("*=", true), NULL))));
|
||||
languageLevelOperators["="].push_back(addToScope("~enclosing_scope", builtin_trans_unit, new NodeTree<ASTData>("function", ASTData(function, Symbol("=", true), NULL))));
|
||||
languageLevelOperators["+="].push_back(addToScope("~enclosing_scope", builtin_trans_unit, new NodeTree<ASTData>("function", ASTData(function, Symbol("+=", true), NULL))));
|
||||
languageLevelOperators["-="].push_back(addToScope("~enclosing_scope", builtin_trans_unit, new NodeTree<ASTData>("function", ASTData(function, Symbol("-=", true), NULL))));
|
||||
languageLevelOperators["."].push_back(addToScope("~enclosing_scope", builtin_trans_unit, new NodeTree<ASTData>("function", ASTData(function, Symbol(".", true), NULL))));
|
||||
@@ -592,16 +593,21 @@ NodeTree<ASTData>* ASTTransformation::transform(NodeTree<Symbol>* from, NodeTree
|
||||
} else if (name == "defer_statement") {
|
||||
newNode = new NodeTree<ASTData>(name, ASTData(defer_statement));
|
||||
} else if (name == "assignment_statement") {
|
||||
newNode = new NodeTree<ASTData>(name, ASTData(assignment_statement));
|
||||
std::string assignFuncName = concatSymbolTree(children[1]);
|
||||
NodeTree<ASTData>* lhs = transform(children[0], scope, types, templateTypeReplacements);
|
||||
NodeTree<ASTData>* rhs = transform(children[2], scope, types, templateTypeReplacements);
|
||||
std::vector<NodeTree<ASTData>*> transformedChildren; transformedChildren.push_back(lhs); transformedChildren.push_back(rhs);
|
||||
|
||||
// see if this is an overloaded assignment
|
||||
NodeTree<ASTData>* function = doFunction(scope, assignFuncName, transformedChildren, templateTypeReplacements);
|
||||
if (function)
|
||||
return function;
|
||||
|
||||
newNode = new NodeTree<ASTData>(name, ASTData(assignment_statement));
|
||||
if (assignFuncName == "=") {
|
||||
newNode->addChild(transform(children[0], scope, types, templateTypeReplacements));
|
||||
newNode->addChild(transform(children[2], scope, types, templateTypeReplacements));
|
||||
newNode->addChildren(transformedChildren);
|
||||
} else {
|
||||
//For assignments like += or *=, expand the syntatic sugar.
|
||||
NodeTree<ASTData>* lhs = transform(children[0], scope, types, templateTypeReplacements);
|
||||
NodeTree<ASTData>* rhs = transform(children[2], scope, types, templateTypeReplacements);
|
||||
std::vector<NodeTree<ASTData>*> transformedChildren; transformedChildren.push_back(lhs); transformedChildren.push_back(rhs);
|
||||
std::string functionName = assignFuncName.substr(0,1);
|
||||
NodeTree<ASTData>* operatorCall = doFunction(scope, functionName, transformedChildren, templateTypeReplacements);
|
||||
if (operatorCall == NULL) {
|
||||
@@ -1218,13 +1224,15 @@ std::map<std::string, Type*> ASTTransformation::makeTemplateFunctionTypeMap(Node
|
||||
|
||||
// We need recursion protection
|
||||
std::vector<NodeTree<ASTData>*> ASTTransformation::scopeLookup(NodeTree<ASTData>* scope, std::string lookup, bool includeModules) {
|
||||
return scopeLookup(scope, lookup, includeModules, std::vector<NodeTree<ASTData>*>());
|
||||
return scopeLookup(scope, lookup, includeModules, std::set<NodeTree<ASTData>*>());
|
||||
}
|
||||
|
||||
std::vector<NodeTree<ASTData>*> ASTTransformation::scopeLookup(NodeTree<ASTData>* scope, std::string lookup, bool includeModules, std::vector<NodeTree<ASTData>*> visited) {
|
||||
std::vector<NodeTree<ASTData>*> ASTTransformation::scopeLookup(NodeTree<ASTData>* scope, std::string lookup, bool includeModules, std::set<NodeTree<ASTData>*> visited) {
|
||||
std::cout << "Scp]|[e looking up " << lookup << std::endl;
|
||||
// Don't visit this node again when looking for the smae lookup. Note that we don't prevent coming back for the scope operator, as that should be able to come back.
|
||||
visited.push_back(scope);
|
||||
if (visited.find(scope) != visited.end())
|
||||
return std::vector<NodeTree<ASTData>*>();
|
||||
visited.insert(scope);
|
||||
//We first check to see if it's one of the special reserved identifiers (only this, for now) and return early if it is.
|
||||
auto LLElementIterator = languageLevelReservedWords.find(lookup);
|
||||
if (LLElementIterator != languageLevelReservedWords.end()) {
|
||||
@@ -1463,6 +1471,7 @@ NodeTree<ASTData>* ASTTransformation::findOrInstantiateFunctionTemplate(std::vec
|
||||
std::cout << "\n\nFinding or instantiating templated function\n\n" << std::endl;
|
||||
std::string functionName = concatSymbolTree(children[0]);
|
||||
std::string fullyInstantiatedName;
|
||||
std::string scopelessFullyInstantiatedName;
|
||||
std::vector<Type*> templateActualTypes;
|
||||
NodeTree<ASTData>* templateDefinition = NULL;
|
||||
|
||||
@@ -1529,6 +1538,7 @@ NodeTree<ASTData>* ASTTransformation::findOrInstantiateFunctionTemplate(std::vec
|
||||
std::cout << functionName << " search turned up null, returing null" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
scopelessFullyInstantiatedName = templateDefinition->getDataRef()->symbol.getName() + "<" + instTypeString + ">";
|
||||
|
||||
NodeTree<Symbol>* templateSyntaxTree = templateDefinition->getDataRef()->valueType->templateDefinition;
|
||||
// Makes a map between the names of the template placeholder parameters and the provided types
|
||||
@@ -1539,14 +1549,10 @@ NodeTree<ASTData>* ASTTransformation::findOrInstantiateFunctionTemplate(std::vec
|
||||
std::cout << ", " << i << " : " << templateChildren[i]->getDataRef()->getName();
|
||||
std::cout << std::endl;
|
||||
|
||||
instantiatedFunction = new NodeTree<ASTData>("function", ASTData(function, Symbol(fullyInstantiatedName, true), typeFromTypeNode(templateChildren[templateChildren.size()-2], scope, newTemplateTypeReplacement)));
|
||||
instantiatedFunction = new NodeTree<ASTData>("function", ASTData(function, Symbol(scopelessFullyInstantiatedName, true), typeFromTypeNode(templateChildren[templateChildren.size()-2], scope, newTemplateTypeReplacement)));
|
||||
addToScope("~enclosing_scope", templateDefinition->getDataRef()->scope["~enclosing_scope"][0], instantiatedFunction);
|
||||
// Arrrrrgh this has a hard time working because the functions will need to see their parameter once they are emitted as C.
|
||||
// HAHAHAHAHA DOESN'T MATTER ALL ONE C FILE NOW, swap back to old way
|
||||
// OR, THIS IS A TEMPLATE METHOD AND ADD TO THE OBJECT
|
||||
auto templateTopScope = objectForTemplateMethod ? objectForTemplateMethod : getUpperTranslationUnit(templateDefinition);
|
||||
addToScope(fullyInstantiatedName, instantiatedFunction, templateTopScope);
|
||||
templateTopScope->addChild(instantiatedFunction); // Add this object the the highest scope's
|
||||
addToScope(scopelessFullyInstantiatedName, instantiatedFunction, templateDefinition->getDataRef()->scope["~enclosing_scope"][0]);
|
||||
templateDefinition->getDataRef()->scope["~enclosing_scope"][0]->addChild(instantiatedFunction); // Add this object the the highest scope's
|
||||
|
||||
std::cout << "About to do children of " << functionName << " to " << fullyInstantiatedName << std::endl;
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ CCodeTriple::CCodeTriple() {
|
||||
CCodeTriple::~CCodeTriple() {
|
||||
}
|
||||
|
||||
std::string CCodeTriple::oneString() {
|
||||
return preValue + value + postValue;
|
||||
std::string CCodeTriple::oneString(bool endValue) {
|
||||
return preValue + value + (endValue ? ";" : "") + postValue;
|
||||
}
|
||||
|
||||
CCodeTriple & CCodeTriple::operator=(const CCodeTriple &rhs) {
|
||||
|
||||
@@ -10,7 +10,7 @@ CGenerator::~CGenerator() {
|
||||
// Note the use of std::pair to hold two strings - the running string for the header file and the running string for the c file.
|
||||
void CGenerator::generateCompSet(std::map<std::string, NodeTree<ASTData>*> ASTs, std::string outputName) {
|
||||
//Generate an entire set of files
|
||||
std::string buildString = "#!/bin/sh\ncc -std=c99 ";
|
||||
std::string buildString = "#!/bin/sh\ncc -g -std=c99 ";
|
||||
std::cout << "\n\n =====GENERATE PASS===== \n\n" << std::endl;
|
||||
std::cout << "\n\nGenerate pass for: " << outputName << std::endl;
|
||||
buildString += outputName + ".c ";
|
||||
@@ -305,8 +305,13 @@ CCodeTriple CGenerator::generate(NodeTree<ASTData>* from, NodeTree<ASTData>* enc
|
||||
parameters += ValueTypeToCType(children[j]->getData().valueType, generate(children[j], enclosingObject, justFuncName).oneString());
|
||||
nameDecoration += "_" + ValueTypeToCTypeDecoration(children[j]->getData().valueType);
|
||||
// add parameters to distructDoubleStack so that their destructors will be called at return (if they exist)
|
||||
std::cout << "HAHA: " << generate(children[j], enclosingObject, justFuncName).oneString() << std::endl;
|
||||
distructDoubleStack.back().push_back(children[j]);
|
||||
}
|
||||
if (children.size() == 1)
|
||||
std::cout << "HEHE: " << data.symbol.getName() << " has only one child" << std::endl;
|
||||
else if (children.size() == 0)
|
||||
std::cout << "HEHE: " << data.symbol.getName() << " has only 0 child" << std::endl;
|
||||
// this is for using functions as values
|
||||
if (justFuncName) {
|
||||
output = ((data.symbol.getName() == "main") ? "" : scopePrefix(from)) + CifyName(data.symbol.getName() + nameDecoration);
|
||||
@@ -455,7 +460,6 @@ CCodeTriple CGenerator::generate(NodeTree<ASTData>* from, NodeTree<ASTData>* enc
|
||||
deferDoubleStack.back().push_back(children[0]);
|
||||
return CCodeTriple("/*defer " + generate(children[0], enclosingObject, justFuncName).oneString() + "*/");
|
||||
case assignment_statement:
|
||||
//if (methodExists(retType, "operator=")) {
|
||||
return generate(children[0], enclosingObject, justFuncName).oneString() + " = " + generate(children[1], enclosingObject, true);
|
||||
case declaration_statement:
|
||||
// adding declaration to the distructDoubleStack so that we can call their destructors when leaving scope (}, return, break, continue)
|
||||
@@ -469,7 +473,8 @@ CCodeTriple CGenerator::generate(NodeTree<ASTData>* from, NodeTree<ASTData>* enc
|
||||
&& children[1]->getChildren()[0]->getChildren()[1] == children[0]) {
|
||||
//That is, if we're a declaration with an init position call (Object a.construct())
|
||||
//We can tell if our function call (children[1])'s access operation([0])'s lhs ([1]) is the thing we just declared (children[0])
|
||||
return ValueTypeToCType(children[0]->getData().valueType, generate(children[0], enclosingObject, justFuncName).oneString()) + "; " + generate(children[1], enclosingObject, true).oneString() + "/*Init Position Call*/";
|
||||
// be sure to end value by passing oneString true
|
||||
return ValueTypeToCType(children[0]->getData().valueType, generate(children[0], enclosingObject, justFuncName).oneString()) + "; " + generate(children[1], enclosingObject, true).oneString(true) + "/*Init Position Call*/";
|
||||
} else {
|
||||
// copy constructor if of the same type
|
||||
if (*children[0]->getDataRef()->valueType == *children[1]->getDataRef()->valueType && methodExists(children[1]->getDataRef()->valueType, "copy_construct")) {
|
||||
@@ -533,7 +538,7 @@ CCodeTriple CGenerator::generate(NodeTree<ASTData>* from, NodeTree<ASTData>* enc
|
||||
if (name == "[]")
|
||||
return "(" + generate(children[1], enclosingObject, true) + ")[" + generate(children[2],enclosingObject, true) + "]";
|
||||
if (name == "+" || name == "-" || name == "*" || name == "/" || name == "==" || name == ">=" || name == "<=" || name == "!="
|
||||
|| name == "<" || name == ">" || name == "%" || name == "+=" || name == "-=" || name == "*=" || name == "/=" || name == "||"
|
||||
|| name == "<" || name == ">" || name == "%" || name == "=" || name == "+=" || name == "-=" || name == "*=" || name == "/=" || name == "||"
|
||||
|| name == "&&") {
|
||||
std::cout << "THIS IS IT NAME: " << name << std::endl;
|
||||
return "((" + generate(children[1], enclosingObject, true).oneString() + ")" + name + "(" + generate(children[2], enclosingObject, true).oneString() + "))";
|
||||
@@ -643,6 +648,8 @@ NodeTree<ASTData>* CGenerator::getMethodsObjectType(NodeTree<ASTData>* scope, st
|
||||
|
||||
// Returns the function prototype in the out param and the full definition normally
|
||||
std::string CGenerator::generateObjectMethod(NodeTree<ASTData>* enclosingObject, NodeTree<ASTData>* from, std::string *functionPrototype) {
|
||||
distructDoubleStack.push_back(std::vector<NodeTree<ASTData>*>());
|
||||
|
||||
ASTData data = from->getData();
|
||||
Type enclosingObjectType = *(enclosingObject->getDataRef()->valueType); //Copy a new type so we can turn it into a pointer if we need to
|
||||
enclosingObjectType.increaseIndirection();
|
||||
@@ -651,12 +658,20 @@ std::string CGenerator::generateObjectMethod(NodeTree<ASTData>* enclosingObject,
|
||||
for (int i = 0; i < children.size()-1; i++) {
|
||||
parameters += ", " + ValueTypeToCType(children[i]->getData().valueType, generate(children[i]).oneString());
|
||||
nameDecoration += "_" + ValueTypeToCTypeDecoration(children[i]->getData().valueType);
|
||||
|
||||
distructDoubleStack.back().push_back(children[i]);
|
||||
}
|
||||
std::string functionSignature = "\n" + ValueTypeToCType(data.valueType->returnType, scopePrefix(from) + CifyName(enclosingObject->getDataRef()->symbol.getName()) +"__"
|
||||
+ CifyName(data.symbol.getName()) + nameDecoration) + "(" + ValueTypeToCType(&enclosingObjectType, "this") + parameters + ")";
|
||||
*functionPrototype += functionSignature + ";\n";
|
||||
// Note that we always wrap out child in {}, as we now allow one statement functions without a codeblock
|
||||
return functionSignature + " {\n" + generate(children[children.size()-1], enclosingObject).oneString() + "}\n"; //Pass in the object so we can properly handle access to member stuff
|
||||
//
|
||||
std::string output;
|
||||
output += functionSignature + " {\n" + generate(children[children.size()-1], enclosingObject).oneString();
|
||||
output += emitDestructors(reverse(distructDoubleStack.back()), enclosingObject);
|
||||
output += "}\n"; //Pass in the object so we can properly handle access to member stuff
|
||||
distructDoubleStack.pop_back();
|
||||
return output;
|
||||
}
|
||||
|
||||
NodeTree<ASTData>* CGenerator::getMethod(Type* type, std::string method) {
|
||||
@@ -683,6 +698,8 @@ std::string CGenerator::generateMethodIfExists(Type* type, std::string method, s
|
||||
for (Type *paramType : methodDef->getDataRef()->valueType->parameterTypes)
|
||||
nameDecoration += "_" + ValueTypeToCTypeDecoration(paramType);
|
||||
return scopePrefix(typeDefinition) + CifyName(typeDefinition->getDataRef()->symbol.getName()) + "__" + method + nameDecoration + "(" + parameter + ");\n";
|
||||
} else {
|
||||
std::cout << method << " DOESN'T EXIST FOR TYPE " << type->toString() << std::endl;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import string:*;
|
||||
import mem:*
|
||||
|
||||
__if_comp__ __C__ simple_passthrough """
|
||||
#include <stdio.h>
|
||||
@@ -8,6 +9,11 @@ fun println() : void {
|
||||
print("\n");
|
||||
}
|
||||
|
||||
fun println<T>(toPrint: T) : void {
|
||||
print(toPrint)
|
||||
print("\n")
|
||||
}
|
||||
|
||||
fun print(toPrint: char*) : void {
|
||||
__if_comp__ __C__ {
|
||||
simple_passthrough(toPrint = toPrint::) """
|
||||
@@ -17,17 +23,10 @@ fun print(toPrint: char*) : void {
|
||||
return;
|
||||
}
|
||||
|
||||
fun println(toPrint: char*) : void {
|
||||
print(toPrint);
|
||||
println();
|
||||
}
|
||||
|
||||
fun print(toPrint: string) : void {
|
||||
print(toPrint.toCharArray());
|
||||
}
|
||||
|
||||
fun println(toPrint: string): void {
|
||||
println(toPrint.toCharArray());
|
||||
var charArr = toPrint.toCharArray()
|
||||
defer delete(charArr)
|
||||
print(charArr);
|
||||
}
|
||||
|
||||
fun print(toPrint: int): void {
|
||||
@@ -39,11 +38,6 @@ fun print(toPrint: int): void {
|
||||
return;
|
||||
}
|
||||
|
||||
fun println(toPrint: int): void {
|
||||
print(toPrint);
|
||||
println();
|
||||
}
|
||||
|
||||
fun print(toPrint: float): void {
|
||||
__if_comp__ __C__ {
|
||||
simple_passthrough(toPrint = toPrint::) """
|
||||
@@ -62,15 +56,3 @@ fun print(toPrint: double) : void{
|
||||
return;
|
||||
}
|
||||
|
||||
fun println(toPrint: float): void {
|
||||
print(toPrint);
|
||||
println();
|
||||
}
|
||||
|
||||
fun println(toPrint: double): void {
|
||||
print(toPrint);
|
||||
println();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,15 +13,45 @@ obj string (Destructable) {
|
||||
data.addEnd(*str);
|
||||
str += 1;
|
||||
}
|
||||
// no null terminator
|
||||
return this;
|
||||
}
|
||||
fun construct(vec: vector::vector<char>): string* {
|
||||
data.copy_construct(&vec);
|
||||
return this;
|
||||
}
|
||||
|
||||
fun copy_construct(old: string*): void {
|
||||
data.copy_construct(&old->data)
|
||||
}
|
||||
|
||||
fun destruct():void {
|
||||
data.destruct()
|
||||
}
|
||||
|
||||
fun operator=(str: char*): void {
|
||||
destruct();
|
||||
construct(str)
|
||||
}
|
||||
|
||||
fun operator+(str: char*): string {
|
||||
var newStr.construct(str):string
|
||||
var ret.construct(data + newStr.data):string
|
||||
return ret
|
||||
}
|
||||
|
||||
fun operator+=(str: char*): void {
|
||||
var newStr.construct(str):string
|
||||
data += newStr.data
|
||||
}
|
||||
|
||||
fun toCharArray(): char* {
|
||||
var out: char* = mem::new<char>(data.size);
|
||||
var out: char* = mem::new<char>(data.size+1);
|
||||
for (var i: int = 0; i < data.size; i++;)
|
||||
out[i] = data.get(i);
|
||||
// null terminator
|
||||
out[data.size] = 0
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,29 @@ obj vector<T> (Destructable) {
|
||||
}
|
||||
|
||||
fun destruct(): void {
|
||||
delete<T>(data);
|
||||
if (data)
|
||||
delete<T>(data);
|
||||
data = 0
|
||||
}
|
||||
|
||||
fun operator=(other:vector<T>):void {
|
||||
resize(other.size)
|
||||
for (var i = 0; i < other.size; i++;)
|
||||
set(i, other.get(i))
|
||||
}
|
||||
|
||||
fun operator+(other:vector<T>):vector<T> {
|
||||
var newVec.construct(size + other.size):vector<T>
|
||||
for (var i = 0; i < size; i++;)
|
||||
newVec.set(i, get(i))
|
||||
for (var i = 0; i < other.size; i++;)
|
||||
newVec.set(i+size, other.get(i))
|
||||
return newVec
|
||||
}
|
||||
|
||||
fun operator+=(other:vector<T>):void {
|
||||
for (var i = 0; i < other.size; i++;)
|
||||
addEnd(other.get(i))
|
||||
}
|
||||
|
||||
fun clone(): vector<T> {
|
||||
@@ -48,6 +70,7 @@ obj vector<T> (Destructable) {
|
||||
delete<T>(data, 0);
|
||||
data = newData;
|
||||
available = newSize;
|
||||
size = lesser(size, newSize)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
hello strings
|
||||
assignment overload
|
||||
assignment overload2
|
||||
assignment overload with additional
|
||||
|
||||
@@ -5,6 +5,14 @@ import string;
|
||||
fun main(): int {
|
||||
var str.construct("hello strings"): string::string;
|
||||
io::println(str);
|
||||
str = "assignment overload"
|
||||
io::println(str);
|
||||
io::println(str + "2");
|
||||
str += " with additional"
|
||||
io::println(str);
|
||||
//var initilized:string::string = "hope"
|
||||
//io::println(initilized)
|
||||
//io::println(initilized+ "3")
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user