Add support for pointers of char, int and long types
This commit is contained in:
parent
822376d142
commit
a27d3dd782
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
extern_ struct SymbolTable Symbols[SYMBOLS];
|
extern_ struct SymbolTable Symbols[SYMBOLS];
|
||||||
|
|
||||||
|
extern_ char* TypeNames[9];
|
||||||
|
|
||||||
extern_ char* TokenStrings[];
|
extern_ char* TokenStrings[];
|
||||||
extern_ char* TokenNames[];
|
extern_ char* TokenNames[];
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
* TYpes are prefixed TY.
|
* TYpes are prefixed TY.
|
||||||
* CoMParisons are prefixed CMP.
|
* CoMParisons are prefixed CMP.
|
||||||
*
|
*
|
||||||
|
*
|
||||||
* NOTE: Tokens are different from Syntax Operations!
|
* NOTE: Tokens are different from Syntax Operations!
|
||||||
*
|
*
|
||||||
* Tokens should represent the characters that invoke them,
|
* Tokens should represent the characters that invoke them,
|
||||||
|
@ -49,6 +50,8 @@ enum TokenTypes {
|
||||||
LI_LPARE, // (
|
LI_LPARE, // (
|
||||||
LI_RPARE, // )
|
LI_RPARE, // )
|
||||||
|
|
||||||
|
LI_AMP, // &
|
||||||
|
|
||||||
TY_IDENTIFIER, // Identifier name. Variable, function, etc.
|
TY_IDENTIFIER, // Identifier name. Variable, function, etc.
|
||||||
TY_NONE, // No return type. Literal void.
|
TY_NONE, // No return type. Literal void.
|
||||||
TY_CHAR, // "char" type keyword
|
TY_CHAR, // "char" type keyword
|
||||||
|
@ -94,6 +97,9 @@ enum SyntaxOps {
|
||||||
|
|
||||||
OP_ASSIGN, // Assign an l-value
|
OP_ASSIGN, // Assign an l-value
|
||||||
|
|
||||||
|
OP_ADDRESS, // Fetch the address of a var
|
||||||
|
OP_DEREF, // Get the value of the address in a pointer
|
||||||
|
|
||||||
TERM_INTLITERAL, // Integer Literal. This is a virtual operation, so it's a terminal.
|
TERM_INTLITERAL, // Integer Literal. This is a virtual operation, so it's a terminal.
|
||||||
|
|
||||||
REF_IDENT, // Reference (read) an identifier (variable).
|
REF_IDENT, // Reference (read) an identifier (variable).
|
||||||
|
@ -149,11 +155,17 @@ struct SymbolTable {
|
||||||
* //TODO: Move back into TokenTypes
|
* //TODO: Move back into TokenTypes
|
||||||
*/
|
*/
|
||||||
enum DataTypes {
|
enum DataTypes {
|
||||||
RET_NONE, // No return type. Literal void.
|
RET_NONE, // No return type. Literal void.
|
||||||
RET_CHAR, // "char" type keyword
|
RET_CHAR, // "char" type keyword
|
||||||
RET_INT, // "int" type keyword
|
RET_INT, // "int" type keyword
|
||||||
RET_LONG, // "long" type keyword
|
RET_LONG, // "long" type keyword
|
||||||
RET_VOID, // "void" type keyword
|
RET_VOID, // "void" type keyword
|
||||||
|
|
||||||
|
// Pointer types
|
||||||
|
PTR_CHAR,
|
||||||
|
PTR_INT,
|
||||||
|
PTR_LONG,
|
||||||
|
PTR_VOID,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -212,8 +224,10 @@ struct ASTNode* ParsePrecedenceASTNode(int PreviousTokenPrecedence);
|
||||||
|
|
||||||
int ParseAST(struct ASTNode* Node);
|
int ParseAST(struct ASTNode* Node);
|
||||||
|
|
||||||
|
struct ASTNode* ParsePrimary(void);
|
||||||
//void ParseStatements(void);
|
//void ParseStatements(void);
|
||||||
struct ASTNode* ParseStatement(void);
|
struct ASTNode* ParseStatement(void);
|
||||||
|
struct ASTNode* PrefixStatement();
|
||||||
|
|
||||||
struct ASTNode* ParseFunction();
|
struct ASTNode* ParseFunction();
|
||||||
struct ASTNode* ParseCompound();
|
struct ASTNode* ParseCompound();
|
||||||
|
@ -222,7 +236,9 @@ struct ASTNode* ParseCompound();
|
||||||
struct ASTNode* CallFunction();
|
struct ASTNode* CallFunction();
|
||||||
struct ASTNode* ReturnStatement();
|
struct ASTNode* ReturnStatement();
|
||||||
|
|
||||||
int ParseType(int Token);
|
int ParsePointer();
|
||||||
|
int ValueAt(int Type);
|
||||||
|
int PointerTo(int Type);
|
||||||
|
|
||||||
|
|
||||||
int ParseTokenToOperation(int Token);
|
int ParseTokenToOperation(int Token);
|
||||||
|
@ -279,6 +295,9 @@ int AsDiv(int Left, int Right);
|
||||||
int AsLdVar(int ID);
|
int AsLdVar(int ID);
|
||||||
int AsStrVar(int Register, int ID);
|
int AsStrVar(int Register, int ID);
|
||||||
|
|
||||||
|
int AsDeref(int Reg, int Type);
|
||||||
|
int AsAddr(int ID);
|
||||||
|
|
||||||
void AsNewSymb(int ID);
|
void AsNewSymb(int ID);
|
||||||
|
|
||||||
int AsEqual(int Left, int Right);
|
int AsEqual(int Left, int Right);
|
||||||
|
|
|
@ -26,8 +26,6 @@ static char* ByteRegisters[4] = { "%r8b", "%r9b", "%r10b", "%r11b" };
|
||||||
static char* Comparisons[6] = { "sete", "setne", "setl", "setg", "setle", "setge" };
|
static char* Comparisons[6] = { "sete", "setne", "setl", "setg", "setle", "setge" };
|
||||||
static char* InvComparisons[6] = { "jne", "je", "jge", "jle", "jg", "jl"};
|
static char* InvComparisons[6] = { "jne", "je", "jge", "jle", "jg", "jl"};
|
||||||
|
|
||||||
static char* Types[5] = { "none", "char", "int", "long", "void" };
|
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
* * * * R O O T O F A S S E M B L E R * * * *
|
* * * * R O O T O F A S S E M B L E R * * * *
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
@ -85,6 +83,12 @@ int AssembleTree(struct ASTNode* Node, int Register, int ParentOp) {
|
||||||
case OP_DIVIDE:
|
case OP_DIVIDE:
|
||||||
return AsDiv(LeftVal, RightVal);
|
return AsDiv(LeftVal, RightVal);
|
||||||
|
|
||||||
|
case OP_ADDRESS:
|
||||||
|
return AsAddr(Node->Value.ID);
|
||||||
|
|
||||||
|
case OP_DEREF:
|
||||||
|
return AsDeref(LeftVal, Node->Left->ExprType);
|
||||||
|
|
||||||
case OP_ASSIGN:
|
case OP_ASSIGN:
|
||||||
return RightVal;
|
return RightVal;
|
||||||
|
|
||||||
|
@ -356,18 +360,22 @@ int AsLdVar(int ID) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RET_LONG:
|
case RET_LONG:
|
||||||
|
case PTR_CHAR:
|
||||||
|
case PTR_INT:
|
||||||
|
case PTR_LONG:
|
||||||
|
case PTR_VOID:
|
||||||
fprintf(OutputFile, "\tmovq\t%s(%%rip), %s\n", Symbols[ID].Name, Registers[Reg]);
|
fprintf(OutputFile, "\tmovq\t%s(%%rip), %s\n", Symbols[ID].Name, Registers[Reg]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DieMessage("Bad type for loading", Types[Symbols[ID].Type]);
|
DieMessage("Bad type for loading", TypeNames[Symbols[ID].Type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Reg;
|
return Reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AsStrVar(int Register, int ID) {
|
int AsStrVar(int Register, int ID) {
|
||||||
printf("\tStoring contents of %s into %s\n", Registers[Register], Symbols[ID].Name);
|
printf("\tStoring contents of %s into %s, type %d\n", Registers[Register], Symbols[ID].Name, Symbols[ID].Type);
|
||||||
|
|
||||||
switch(Symbols[ID].Type) {
|
switch(Symbols[ID].Type) {
|
||||||
case RET_CHAR:
|
case RET_CHAR:
|
||||||
|
@ -380,16 +388,44 @@ int AsStrVar(int Register, int ID) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RET_LONG:
|
case RET_LONG:
|
||||||
|
case PTR_CHAR:
|
||||||
|
case PTR_INT:
|
||||||
|
case PTR_LONG:
|
||||||
|
case PTR_VOID:
|
||||||
fprintf(OutputFile, "\tmovq\t%s, %s(%%rip)\n", Registers[Register], Symbols[ID].Name);
|
fprintf(OutputFile, "\tmovq\t%s, %s(%%rip)\n", Registers[Register], Symbols[ID].Name);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DieMessage("Bad type for saving", Types[Symbols[ID].Type]);
|
DieMessage("Bad type for saving", TypeNames[Symbols[ID].Type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Register;
|
return Register;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AsAddr(int ID) {
|
||||||
|
int Register = RetrieveRegister();
|
||||||
|
printf("\tSaving pointer of %s into %s\n", Symbols[ID].Name, Registers[Register]);
|
||||||
|
|
||||||
|
fprintf(OutputFile, "\tleaq\t%s(%%rip), %s\n", Symbols[ID].Name, Registers[Register]);
|
||||||
|
return Register;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AsDeref(int Reg, int Type) {
|
||||||
|
|
||||||
|
printf("\tDereferencing %s\n", Registers[Reg]);
|
||||||
|
switch(Type) {
|
||||||
|
case PTR_CHAR:
|
||||||
|
fprintf(OutputFile, "\tmovzbq\t(%s), %s\n", Registers[Reg], Registers[Reg]);
|
||||||
|
break;
|
||||||
|
case PTR_INT:
|
||||||
|
case PTR_LONG:
|
||||||
|
fprintf(OutputFile, "\tmovq\t(%s), %s\n", Registers[Reg], Registers[Reg]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reg;
|
||||||
|
}
|
||||||
|
|
||||||
void AsNewSymb(int ID) {
|
void AsNewSymb(int ID) {
|
||||||
int TypeSize;
|
int TypeSize;
|
||||||
|
|
||||||
|
@ -432,7 +468,7 @@ int AsReturn(int Register, int FuncID) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DieMessage("Bad function type in generating return", Types[Symbols[FuncID].Type]);
|
DieMessage("Bad function type in generating return", TypeNames[Symbols[FuncID].Type]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,10 @@ int Tokenise(struct Token* Token) {
|
||||||
Token->type = AR_SLASH;
|
Token->type = AR_SLASH;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '&':
|
||||||
|
Token->type = LI_AMP;
|
||||||
|
break;
|
||||||
|
|
||||||
case '=':
|
case '=':
|
||||||
Char = NextChar();
|
Char = NextChar();
|
||||||
// If the next char is =, we have ==, the compare equality token.
|
// If the next char is =, we have ==, the compare equality token.
|
||||||
|
|
17
src/Main.c
17
src/Main.c
|
@ -35,6 +35,8 @@ char* TokenNames[] = {
|
||||||
"Logical Block Start",
|
"Logical Block Start",
|
||||||
"Logical Block End",
|
"Logical Block End",
|
||||||
|
|
||||||
|
"Dereference operator",
|
||||||
|
|
||||||
"Identifier",
|
"Identifier",
|
||||||
"None Type",
|
"None Type",
|
||||||
"Char Type",
|
"Char Type",
|
||||||
|
@ -52,21 +54,8 @@ char* TokenNames[] = {
|
||||||
"Return keyword"
|
"Return keyword"
|
||||||
};
|
};
|
||||||
|
|
||||||
static void TokeniseFile() {
|
char* TypeNames[9] = { "none", "char", "int", "long", "void", "charptr", "intptr", "longptr", "voidptr"};
|
||||||
|
|
||||||
struct Token Token;
|
|
||||||
|
|
||||||
while(Tokenise(&Token)) {
|
|
||||||
|
|
||||||
printf("Token %s", TokenStrings[Token.type]);
|
|
||||||
if(Token.type == LI_INT) {
|
|
||||||
printf(", value %d", Token.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
Line = 1;
|
Line = 1;
|
||||||
|
|
|
@ -99,7 +99,7 @@ int ParseTokenToOperation(int Token) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static struct ASTNode* ParsePrimary(void) {
|
struct ASTNode* ParsePrimary(void) {
|
||||||
struct ASTNode* Node;
|
struct ASTNode* Node;
|
||||||
int ID;
|
int ID;
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ struct ASTNode* ParsePrecedenceASTNode(int PreviousTokenPrecedence) {
|
||||||
int LeftType, RightType;
|
int LeftType, RightType;
|
||||||
int NodeType;
|
int NodeType;
|
||||||
|
|
||||||
LeftNode = ParsePrimary();
|
LeftNode = PrefixStatement();
|
||||||
|
|
||||||
NodeType = CurrentToken.type;
|
NodeType = CurrentToken.type;
|
||||||
if(NodeType == LI_SEMIC || NodeType == LI_RPARE)
|
if(NodeType == LI_SEMIC || NodeType == LI_RPARE)
|
||||||
|
|
66
src/Pointers.c
Normal file
66
src/Pointers.c
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
|
||||||
|
/*************/
|
||||||
|
/*GEMWIRE */
|
||||||
|
/* ERYTHRO*/
|
||||||
|
/*************/
|
||||||
|
|
||||||
|
#include <Defs.h>
|
||||||
|
#include <Data.h>
|
||||||
|
|
||||||
|
int PointerTo(int Type) {
|
||||||
|
|
||||||
|
printf("\t\tPointerising a %s\n", TypeNames[Type]);
|
||||||
|
// As it stands, the conversion between
|
||||||
|
// RET and PTR is +4.
|
||||||
|
// TODO: if we add types, increase this number
|
||||||
|
// TODO: Make this a proper translation table
|
||||||
|
// TODO: More checks! This can go wrong easily!
|
||||||
|
if(Type >= RET_CHAR && Type <= RET_VOID) {
|
||||||
|
return Type + 4;
|
||||||
|
} else {
|
||||||
|
DieDecimal("Unable to create a pointer to the desired type", Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ValueAt(int Type) {
|
||||||
|
|
||||||
|
printf("\t\tDereferencing a %s\n", TypeNames[Type]);
|
||||||
|
//TODO: this is still bullshittery!
|
||||||
|
if(Type >= PTR_CHAR && Type <= PTR_VOID) {
|
||||||
|
return Type - 4;
|
||||||
|
} else {
|
||||||
|
DieDecimal("Unable to dereference type", Type);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ParsePointer() {
|
||||||
|
|
||||||
|
int Type;
|
||||||
|
// TODO: THIS IS WRONG AND SHOULD NOT EXIST
|
||||||
|
// TY_CHAR is 21, RET_CHAR is 1.
|
||||||
|
// Offset is 20. Rest are in order
|
||||||
|
|
||||||
|
if(CurrentToken.type >= TY_CHAR && CurrentToken.type <= TY_VOID) {
|
||||||
|
Type = CurrentToken.type - 20;
|
||||||
|
} else {
|
||||||
|
DieDecimal("Illegal type for pointerisation", CurrentToken.type);
|
||||||
|
}
|
||||||
|
// Recursively scan more *s
|
||||||
|
// This makes things like:
|
||||||
|
// x = **y;
|
||||||
|
// possible.
|
||||||
|
while(1) {
|
||||||
|
Tokenise(&CurrentToken);
|
||||||
|
printf("\t\t\tType on parsing is %d\n", CurrentToken.type);
|
||||||
|
if(CurrentToken.type != AR_STAR)
|
||||||
|
break;
|
||||||
|
|
||||||
|
Type = PointerTo(Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Type;
|
||||||
|
}
|
||||||
|
|
|
@ -20,28 +20,11 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static char* Types[5] = { "none", "char", "int", "long", "void" };
|
static int TypeSize[9] = { 0, 1, 4, 8, 0, 8, 8, 8, 8}; // in BYTES
|
||||||
static int TypeSize[5] = { 0, 1, 4, 8, 0}; // in BYTES
|
|
||||||
|
|
||||||
int ParseType(int Token) {
|
|
||||||
switch(Token) {
|
|
||||||
case TY_CHAR:
|
|
||||||
return RET_CHAR;
|
|
||||||
|
|
||||||
case TY_VOID:
|
|
||||||
return RET_VOID;
|
|
||||||
|
|
||||||
case TY_INT:
|
|
||||||
return RET_INT;
|
|
||||||
|
|
||||||
default:
|
|
||||||
DieDecimal("Illegal variable type", Token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int PrimitiveSize(int Type) {
|
int PrimitiveSize(int Type) {
|
||||||
if(Type < RET_NONE || Type > RET_VOID)
|
if(Type < RET_NONE || Type > PTR_VOID)
|
||||||
DieMessage("Checking size of bad data type", Types[Type]);
|
DieDecimal("Checking size of bad data type", Type);
|
||||||
|
|
||||||
return TypeSize[Type];
|
return TypeSize[Type];
|
||||||
}
|
}
|
||||||
|
@ -145,10 +128,9 @@ int TypesCompatible(int* Left, int* Right, int STRICT) {
|
||||||
*/
|
*/
|
||||||
void BeginVariableDeclaration(void) {
|
void BeginVariableDeclaration(void) {
|
||||||
int ID;
|
int ID;
|
||||||
|
int Type = ParsePointer(CurrentToken.type);
|
||||||
int Type = ParseType(CurrentToken.type);
|
|
||||||
//printf("type: %s\n", Types[Type]);
|
//printf("type: %s\n", Types[Type]);
|
||||||
Tokenise(&CurrentToken);
|
|
||||||
VerifyToken(TY_IDENTIFIER, "ident");
|
VerifyToken(TY_IDENTIFIER, "ident");
|
||||||
//printf("Identifier: %s\n", CurrentIdentifier);
|
//printf("Identifier: %s\n", CurrentIdentifier);
|
||||||
|
|
||||||
|
@ -163,8 +145,8 @@ struct ASTNode* ParseFunction() {
|
||||||
struct ASTNode* FinalStatement;
|
struct ASTNode* FinalStatement;
|
||||||
int SymbolSlot, BreakLabel, Type;
|
int SymbolSlot, BreakLabel, Type;
|
||||||
|
|
||||||
Type = ParseType(CurrentToken.type);
|
Type = ParsePointer(CurrentToken.type);
|
||||||
Tokenise(&CurrentToken);
|
|
||||||
VerifyToken(KW_FUNC, "::");
|
VerifyToken(KW_FUNC, "::");
|
||||||
VerifyToken(TY_IDENTIFIER, "ident");
|
VerifyToken(TY_IDENTIFIER, "ident");
|
||||||
|
|
||||||
|
@ -251,7 +233,7 @@ struct ASTNode* ParseIdentifier() {
|
||||||
|
|
||||||
VerifyToken(TY_IDENTIFIER, "ident");
|
VerifyToken(TY_IDENTIFIER, "ident");
|
||||||
|
|
||||||
printf("After parsing, the identifier name is %s, id %d in the symbol table.\n", CurrentIdentifier, FindSymbol(CurrentIdentifier));
|
printf("\t\tAfter parsing, the identifier name is %s, id %d in the symbol table.\n", CurrentIdentifier, FindSymbol(CurrentIdentifier));
|
||||||
|
|
||||||
if(CurrentToken.type == LI_LPARE)
|
if(CurrentToken.type == LI_LPARE)
|
||||||
return CallFunction();
|
return CallFunction();
|
||||||
|
@ -386,3 +368,40 @@ struct ASTNode* PrintStatement(void) {
|
||||||
return Tree;
|
return Tree;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ASTNode* PrefixStatement() {
|
||||||
|
struct ASTNode* Tree;
|
||||||
|
|
||||||
|
switch (CurrentToken.type) {
|
||||||
|
case LI_AMP:
|
||||||
|
Tokenise(&CurrentToken);
|
||||||
|
|
||||||
|
// To allow things like:
|
||||||
|
// x = &&y;
|
||||||
|
// We need to recursively parse prefixes;
|
||||||
|
Tree = PrefixStatement();
|
||||||
|
|
||||||
|
if(Tree->Operation != REF_IDENT)
|
||||||
|
Die("& must be followed by another & or an identifier.");
|
||||||
|
|
||||||
|
Tree->Operation = OP_ADDRESS;
|
||||||
|
Tree->ExprType = PointerTo(Tree->ExprType);
|
||||||
|
break;
|
||||||
|
case AR_STAR:
|
||||||
|
Tokenise(&CurrentToken);
|
||||||
|
|
||||||
|
Tree = PrefixStatement();
|
||||||
|
|
||||||
|
if(Tree->Operation != REF_IDENT && Tree->Operation != OP_DEREF)
|
||||||
|
Die("* must be followed by another * or an identifier.");
|
||||||
|
|
||||||
|
Tree = ConstructASTBranch(OP_DEREF, ValueAt(Tree->ExprType), Tree, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Tree = ParsePrimary();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tree;
|
||||||
|
}
|
22
tests/pointers
Normal file
22
tests/pointers
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
int :: main() {
|
||||||
|
char a;
|
||||||
|
char *b;
|
||||||
|
char c;
|
||||||
|
int d;
|
||||||
|
int *e;
|
||||||
|
int f;
|
||||||
|
|
||||||
|
a= 18;
|
||||||
|
PrintInteger(a);
|
||||||
|
b= &a;
|
||||||
|
c= *b;
|
||||||
|
PrintInteger(c);
|
||||||
|
|
||||||
|
d= 12;
|
||||||
|
PrintInteger(d);
|
||||||
|
e= &d;
|
||||||
|
f= *e;
|
||||||
|
PrintInteger(f);
|
||||||
|
|
||||||
|
return(0);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user