Add typedefs in codegen, globals is working
This commit is contained in:
@@ -277,8 +277,12 @@ gen_ast(Ast *ast){
|
|||||||
else if(sym->type == type_string){
|
else if(sym->type == type_string){
|
||||||
gen("String %s = LIT(\"%s\");", node->name.str, sym->intern_val.str);
|
gen("String %s = LIT(\"%s\");", node->name.str, sym->intern_val.str);
|
||||||
}
|
}
|
||||||
|
else if(sym->type == type_type){
|
||||||
|
gen("typedef ");
|
||||||
|
gen_simple_decl(sym->type_val, node->name);
|
||||||
|
}
|
||||||
else{
|
else{
|
||||||
parsing_error(node->pos, "Unhandled type of constant expression");
|
parsing_error(node->pos, "C_Codegen: Unhandled type of constant expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast_End();
|
Ast_End();
|
||||||
|
|||||||
20
globals.kl
20
globals.kl
@@ -1,10 +1,10 @@
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Function types
|
// Function types
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
test_function :: (thing: int): ^int
|
test_function :: (thing: int): *int
|
||||||
function_type: (thing: int): ^int = test_function
|
function_type: test_function
|
||||||
const_function_alias :: test_function
|
const_function_alias :: test_function
|
||||||
null_function: (t: int): ^int = null
|
null_function: (t: int): *int = null
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Booleans
|
// Booleans
|
||||||
@@ -35,10 +35,10 @@ array_item_imp: int = array2[2]
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Pointers
|
// Pointers
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
pointer_decl : ^int
|
pointer_decl : *int
|
||||||
variable_from_deref: int = pointer_decl^
|
variable_from_deref: int = *pointer_decl
|
||||||
pointer_from_var : ^int = ^variable_from_deref
|
pointer_from_var : *int = &variable_from_deref
|
||||||
boolean_pointer := ^boolean
|
boolean_pointer := &boolean
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Implicit type
|
// Implicit type
|
||||||
@@ -49,9 +49,9 @@ implicit_str :: "Hello world"
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Pointers
|
// Pointers
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
pointer1: ^int = null
|
pointer1: *int = null
|
||||||
pointer2: ^int = pointer1
|
pointer2: *int = pointer1
|
||||||
pointer3: ^^int = null
|
pointer3: **int = null
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// String types
|
// String types
|
||||||
|
|||||||
3
main.cpp
3
main.cpp
@@ -23,7 +23,8 @@ int main(){
|
|||||||
test_intern_table();
|
test_intern_table();
|
||||||
lex_test();
|
lex_test();
|
||||||
|
|
||||||
String result = compile_file("lambdas.kl"_s);
|
String result = compile_file("globals.kl"_s);
|
||||||
|
// String result = compile_file("lambdas.kl"_s);
|
||||||
// String result = compile_file("order_independent_globals.kl"_s);
|
// String result = compile_file("order_independent_globals.kl"_s);
|
||||||
printf("%s", result.str);
|
printf("%s", result.str);
|
||||||
|
|
||||||
|
|||||||
@@ -363,9 +363,9 @@ ast_init(Token *pos, Token_Kind op, Ast_Atom *ident, Ast_Expr *expr){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Ast_Array *
|
function Ast_Array *
|
||||||
ast_array(Token *pos, Ast_Expr *base){
|
ast_array(Token *pos, Ast_Expr *expr){
|
||||||
AST_NEW(Array, ARRAY, pos);
|
AST_NEW(Array, ARRAY, pos);
|
||||||
result->base = base;
|
result->expr = expr;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -288,6 +288,7 @@ null_denotation(Token *token){
|
|||||||
case TK_OpenBracket: {
|
case TK_OpenBracket: {
|
||||||
Ast_Array *result = ast_array(token, parse_expr());
|
Ast_Array *result = ast_array(token, parse_expr());
|
||||||
token_expect(TK_CloseBracket);
|
token_expect(TK_CloseBracket);
|
||||||
|
result->base = parse_expr();
|
||||||
return result;
|
return result;
|
||||||
}break;
|
}break;
|
||||||
case TK_Keyword: {
|
case TK_Keyword: {
|
||||||
|
|||||||
180
new_resolve.cpp
180
new_resolve.cpp
@@ -14,6 +14,14 @@ enum Sym_State{
|
|||||||
SYM_RESOLVED,
|
SYM_RESOLVED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define VALUE_FIELDS \
|
||||||
|
S64 int_val; \
|
||||||
|
Intern_String intern_val; \
|
||||||
|
Ast_Resolved_Type *type_val;
|
||||||
|
#define INLINE_VALUE_FIELDS union{Value value; union{VALUE_FIELDS};}
|
||||||
|
|
||||||
|
union Value{VALUE_FIELDS};
|
||||||
|
|
||||||
struct Sym{
|
struct Sym{
|
||||||
Intern_String name;
|
Intern_String name;
|
||||||
Sym_Kind kind;
|
Sym_Kind kind;
|
||||||
@@ -21,24 +29,19 @@ struct Sym{
|
|||||||
Ast *ast;
|
Ast *ast;
|
||||||
|
|
||||||
Ast_Resolved_Type *type;
|
Ast_Resolved_Type *type;
|
||||||
union{
|
INLINE_VALUE_FIELDS;
|
||||||
S64 int_val;
|
|
||||||
Intern_String intern_val;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Operand{
|
struct Operand{
|
||||||
Ast_Resolved_Type *type;
|
Ast_Resolved_Type *type;
|
||||||
bool is_const;
|
bool is_const;
|
||||||
union {
|
INLINE_VALUE_FIELDS;
|
||||||
S64 int_val;
|
|
||||||
Intern_String intern_val;
|
|
||||||
Ast_Resolved_Type *type_type;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
global Ast_Named empty_decl = {};
|
global Ast_Named empty_decl = {};
|
||||||
|
|
||||||
|
// @todo: currently inserting into sym table type of [Type] instead of what type represents
|
||||||
|
|
||||||
function void
|
function void
|
||||||
sym_insert(Sym *sym){
|
sym_insert(Sym *sym){
|
||||||
U64 hash = hash_string(sym->name.s);
|
U64 hash = hash_string(sym->name.s);
|
||||||
@@ -148,62 +151,6 @@ sym_insert_builtins(){
|
|||||||
function Sym *resolve_name(Token *pos, Intern_String name);
|
function Sym *resolve_name(Token *pos, Intern_String name);
|
||||||
function Operand eval_expr(Ast_Expr *ast, Ast_Resolved_Type *compound_required_type = 0, Sym *lambda_to_complete = 0);
|
function Operand eval_expr(Ast_Expr *ast, Ast_Resolved_Type *compound_required_type = 0, Sym *lambda_to_complete = 0);
|
||||||
|
|
||||||
// function Ast_Resolved_Type *
|
|
||||||
// eval_typespec(Ast_Expr *ast){
|
|
||||||
// if(!ast) return 0;
|
|
||||||
|
|
||||||
// switch(ast->kind){
|
|
||||||
// Ast_Begin(AST_IDENT, Ast_Atom){
|
|
||||||
// Sym *type_sym = sym_get(node->intern_val);
|
|
||||||
// if(!type_sym){
|
|
||||||
// parsing_error(node->pos, "This type is not defined");
|
|
||||||
// }
|
|
||||||
// if(type_sym->kind != SYM_TYPE){
|
|
||||||
// parsing_error(node->pos, "This identifier is not a type");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// sym_new_resolved(SYM_TYPESPEC, {}, type_sym->type, node);
|
|
||||||
// return type_sym->type;
|
|
||||||
// Ast_End();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ast_Begin(AST_TYPESPEC_LAMBDA, Ast_Lambda){
|
|
||||||
// Scratch scratch;
|
|
||||||
// Ast_Resolved_Type *ret = eval_typespec(node->lambda->ret);
|
|
||||||
// Array<Ast_Resolved_Type *> args = {scratch};
|
|
||||||
// For(node->lambda->args) args.add(eval_typespec(it[0]->typespec));
|
|
||||||
|
|
||||||
// Ast_Resolved_Type *resolved_type = type_lambda(ret, args);
|
|
||||||
// sym_new_resolved(SYM_TYPESPEC, {}, resolved_type, node);
|
|
||||||
// return resolved_type;
|
|
||||||
// Ast_End();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ast_Begin(AST_TYPESPEC_POINTER, Ast_Typespec){
|
|
||||||
// Ast_Resolved_Type *type = eval_typespec(node->base);
|
|
||||||
// Ast_Resolved_Type *resolved_type = type_pointer(type);
|
|
||||||
|
|
||||||
// sym_new_resolved(SYM_TYPESPEC, {}, resolved_type, node);
|
|
||||||
// return resolved_type;
|
|
||||||
// Ast_End();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ast_Begin(AST_TYPESPEC_ARRAY, Ast_Typespec){
|
|
||||||
// Ast_Resolved_Type *type = eval_typespec(node->arr.base);
|
|
||||||
// Operand expr = eval_expr(node->arr.expr);
|
|
||||||
// if(!expr.is_const) parsing_error(node->pos, "Array size is not a constant");
|
|
||||||
// if(expr.type != type_int) parsing_error(node->pos, "Array size is expected to be of type [Int] is instead of type %s", type_names[expr.type->kind]);
|
|
||||||
|
|
||||||
// Ast_Resolved_Type *resolved_type = type_array(type, expr.int_val);
|
|
||||||
// sym_new_resolved(SYM_TYPESPEC, {}, resolved_type, node);
|
|
||||||
// return resolved_type;
|
|
||||||
// Ast_End();
|
|
||||||
// }
|
|
||||||
// invalid_default_case;
|
|
||||||
// }
|
|
||||||
// return 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
function Ast_Resolved_Type *
|
function Ast_Resolved_Type *
|
||||||
resolve_type_pair(Token *pos, Ast_Resolved_Type *a, Ast_Resolved_Type *b){
|
resolve_type_pair(Token *pos, Ast_Resolved_Type *a, Ast_Resolved_Type *b){
|
||||||
Ast_Resolved_Type *result = 0;
|
Ast_Resolved_Type *result = 0;
|
||||||
@@ -278,6 +225,15 @@ eval_stmt(Ast *ast, Ast_Resolved_Type *ret){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum{AST_CAN_BE_NULL = 1};
|
||||||
|
function Ast_Resolved_Type *
|
||||||
|
eval_typespec(Ast_Expr *ast, B32 ast_can_be_null){
|
||||||
|
if(ast_can_be_null && ast == 0) return 0;
|
||||||
|
Operand resolved = eval_expr(ast);
|
||||||
|
if(resolved.type != type_type) parsing_error(ast->pos, "Expected [Type] got instead %s", resolved.type->kind);
|
||||||
|
return resolved.type_val;
|
||||||
|
}
|
||||||
|
|
||||||
function Operand
|
function Operand
|
||||||
eval_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_complete){
|
eval_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_complete){
|
||||||
switch(ast->kind){
|
switch(ast->kind){
|
||||||
@@ -333,7 +289,7 @@ eval_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_comple
|
|||||||
|
|
||||||
else if(sym->kind == SYM_TYPE){
|
else if(sym->kind == SYM_TYPE){
|
||||||
result.type = type_type;
|
result.type = type_type;
|
||||||
result.type_type = sym->type;
|
result.type_val = sym->type;
|
||||||
sym_new_resolved(SYM_TYPE, sym->name, sym->type, node);
|
sym_new_resolved(SYM_TYPE, sym->name, sym->type, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,7 +310,8 @@ eval_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_comple
|
|||||||
if(!expr.is_const) parsing_error(node->pos, "Array operator requires a constant value");
|
if(!expr.is_const) parsing_error(node->pos, "Array operator requires a constant value");
|
||||||
if(expr.type != type_int) parsing_error(node->pos, "Array index requires type [Int]");
|
if(expr.type != type_int) parsing_error(node->pos, "Array index requires type [Int]");
|
||||||
|
|
||||||
type.type_type = type_array(type.type_type, expr.int_val);
|
type.type_val = type_array(type.type_val, expr.int_val);
|
||||||
|
sym_new_resolved(SYM_TYPE, {}, type.type_val, node);
|
||||||
return type;
|
return type;
|
||||||
Ast_End();
|
Ast_End();
|
||||||
}
|
}
|
||||||
@@ -369,11 +326,14 @@ eval_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_comple
|
|||||||
For(node->args){
|
For(node->args){
|
||||||
Operand type = eval_expr(it[0]->typespec);
|
Operand type = eval_expr(it[0]->typespec);
|
||||||
if(type.type != type_type) parsing_error(it[0]->pos, "Required expression of kind [type]");
|
if(type.type != type_type) parsing_error(it[0]->pos, "Required expression of kind [type]");
|
||||||
args.add(type.type_type);
|
args.add(type.type_val);
|
||||||
}
|
}
|
||||||
lambda_type = type_lambda(ret_op.type_type, args);
|
lambda_type = type_lambda(ret_op.type_val, args);
|
||||||
|
sym_new_resolved(SYM_TYPE, {}, lambda_type, node);
|
||||||
assert(lambda_type);
|
assert(lambda_type);
|
||||||
|
|
||||||
|
Operand result = {type_type, true};
|
||||||
|
result.type_val = lambda_type;
|
||||||
// @todo: We also need to make sure there is a return value when ret type is not void
|
// @todo: We also need to make sure there is a return value when ret type is not void
|
||||||
// @note: then try resolving the block of lambda
|
// @note: then try resolving the block of lambda
|
||||||
if(node->block){
|
if(node->block){
|
||||||
@@ -386,12 +346,13 @@ eval_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_comple
|
|||||||
sym_insert(arg_sym);
|
sym_insert(arg_sym);
|
||||||
}
|
}
|
||||||
For(node->block->stmts){
|
For(node->block->stmts){
|
||||||
eval_stmt(it[0], ret_op.type_type);
|
eval_stmt(it[0], ret_op.type_val);
|
||||||
}
|
}
|
||||||
scope_close(scope_index);
|
scope_close(scope_index);
|
||||||
|
result.type = lambda_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {lambda_type, true};
|
return result;
|
||||||
Ast_End();
|
Ast_End();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,38 +367,35 @@ eval_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_comple
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ast_Begin(AST_COMPOUND, Ast_Compound){
|
Ast_Begin(AST_COMPOUND, Ast_Compound){
|
||||||
unused(node);
|
Ast_Resolved_Type *type = eval_typespec(node->typespec, AST_CAN_BE_NULL);
|
||||||
// Ast_Resolved_Type *type = eval_typespec(node->typespec);
|
if(!type && expected_type) type = expected_type;
|
||||||
// Ast_Resolved_Type *variable_type = expected_type;
|
else if(!expected_type && type);
|
||||||
// if(!type && variable_type) type = variable_type;
|
else if(expected_type != type) parsing_error(node->pos, "Variable type different from explicit compound type");
|
||||||
// else if(!variable_type && type);
|
node->type = type;
|
||||||
// else if(variable_type != type) parsing_error(node->pos, "Variable type different from explicit compound type");
|
|
||||||
// node->type = type;
|
|
||||||
|
|
||||||
// if(type->kind == TYPE_Array){
|
if(type->kind == TYPE_Array){
|
||||||
// if(node->exprs.len > type->arr.size) parsing_error(node->pos, "compound statement has too many items for this type");
|
if(node->exprs.len > type->arr.size) parsing_error(node->pos, "compound statement has too many items for this type");
|
||||||
// Ast_Resolved_Type *item_type = type->arr.base;
|
Ast_Resolved_Type *item_type = type->arr.base;
|
||||||
|
|
||||||
// For(node->exprs){
|
For(node->exprs){
|
||||||
// assert(it[0]->kind == AST_COMPOUND_ITEM);
|
assert(it[0]->kind == AST_COMPOUND_ITEM);
|
||||||
// Ast_Compound_Item *i = (Ast_Compound_Item *)it[0];
|
Ast_Compound_Item *i = (Ast_Compound_Item *)it[0];
|
||||||
// assert(i->kind == AST_COMPOUND_ITEM);
|
assert(i->kind == AST_COMPOUND_ITEM);
|
||||||
// if(i->name) parsing_error(i->pos, "Invalid indexing kind in a compound expression of type %s", type_names[type->kind]);
|
if(i->name) parsing_error(i->pos, "Invalid indexing kind in a compound expression of type %s", type_names[type->kind]);
|
||||||
// if(i->index){
|
if(i->index){
|
||||||
// Operand index_op = eval_expr(i->index);
|
Operand index_op = eval_expr(i->index);
|
||||||
// if(!index_op.is_const) parsing_error(i->pos, "Index in a compound expression is not a constant");
|
if(!index_op.is_const) parsing_error(i->pos, "Index in a compound expression is not a constant");
|
||||||
// if(index_op.type != type_int) parsing_error(i->pos, "Index should be of type int");
|
if(index_op.type != type_int) parsing_error(i->pos, "Index should be of type int");
|
||||||
// if(index_op.int_val > (type->arr.size - 1)) parsing_error(i->pos, "Invalid index in compound expression, larger then type can store");
|
if(index_op.int_val > (type->arr.size - 1)) parsing_error(i->pos, "Invalid index in compound expression, larger then type can store");
|
||||||
// }
|
}
|
||||||
// Operand expr = eval_expr(i->item, item_type);
|
Operand expr = eval_expr(i->item, item_type);
|
||||||
// resolve_type_pair(i->pos, expr.type, item_type);
|
resolve_type_pair(i->pos, expr.type, item_type);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// else parsing_error(node->pos, "Invalid compound expression type");
|
else parsing_error(node->pos, "Invalid compound expression type");
|
||||||
|
|
||||||
// Operand result = {type, false};
|
Operand result = {type, false};
|
||||||
// return result;
|
return result;
|
||||||
not_implemented;
|
|
||||||
Ast_End();
|
Ast_End();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,7 +403,7 @@ eval_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_comple
|
|||||||
Operand expr = eval_expr(node->expr);
|
Operand expr = eval_expr(node->expr);
|
||||||
Operand typespec = eval_expr(node->typespec);
|
Operand typespec = eval_expr(node->typespec);
|
||||||
if(typespec.type != type_type) parsing_error(node->pos, "Expected type in left of cast got instead %s", type_names[typespec.type->kind]);
|
if(typespec.type != type_type) parsing_error(node->pos, "Expected type in left of cast got instead %s", type_names[typespec.type->kind]);
|
||||||
Ast_Resolved_Type *type = typespec.type;
|
Ast_Resolved_Type *type = typespec.type_val;
|
||||||
|
|
||||||
if(type == expr.type) return expr;
|
if(type == expr.type) return expr;
|
||||||
|
|
||||||
@@ -473,9 +431,16 @@ eval_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_comple
|
|||||||
Operand value = eval_expr(node->expr);
|
Operand value = eval_expr(node->expr);
|
||||||
switch(node->op){
|
switch(node->op){
|
||||||
case TK_Pointer:{
|
case TK_Pointer:{
|
||||||
if(value.type->kind != TYPE_Pointer) parsing_error(node->pos, "Dereferencing a value that is not a pointer");
|
if(value.type->kind == TYPE_Pointer){
|
||||||
Operand result = {value.type->base};
|
Operand result = {value.type->base};
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
else if(value.type->kind == TYPE_Type){
|
||||||
|
Operand result = {type_type, true};
|
||||||
|
result.type_val = type_pointer(value.type_val);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else{ parsing_error(node->pos, "Dereferencing expression %s that is not a [Pointer] or [Type]", type_names[value.type->kind]); return {}; }
|
||||||
}break;
|
}break;
|
||||||
case TK_Dereference:{
|
case TK_Dereference:{
|
||||||
Operand result = {type_pointer(value.type)};
|
Operand result = {type_pointer(value.type)};
|
||||||
@@ -523,8 +488,8 @@ eval_decl(Ast *ast, Sym *sym){
|
|||||||
Ast_Begin(AST_VAR, Ast_Var){
|
Ast_Begin(AST_VAR, Ast_Var){
|
||||||
Operand type = node->typespec ? eval_expr(node->typespec) : Operand{};
|
Operand type = node->typespec ? eval_expr(node->typespec) : Operand{};
|
||||||
if(type.type && (type.type != type_type)) parsing_error(node->typespec->pos, "Expected [Type] got instead %s", type_names[type.type->kind]);
|
if(type.type && (type.type != type_type)) parsing_error(node->typespec->pos, "Expected [Type] got instead %s", type_names[type.type->kind]);
|
||||||
Operand expr = node->expr ? eval_expr(node->expr, type.type_type) : Operand{};
|
Operand expr = node->expr ? eval_expr(node->expr, type.type_val) : Operand{};
|
||||||
expr.type = resolve_type_pair(node->pos, type.type_type, expr.type);
|
expr.type = resolve_type_pair(node->pos, type.type_val, expr.type);
|
||||||
return expr;
|
return expr;
|
||||||
Ast_End();
|
Ast_End();
|
||||||
}
|
}
|
||||||
@@ -576,8 +541,7 @@ resolve_sym(Sym *sym){
|
|||||||
sym->type = op.type;
|
sym->type = op.type;
|
||||||
if(sym->kind == SYM_CONST){
|
if(sym->kind == SYM_CONST){
|
||||||
assert(op.is_const);
|
assert(op.is_const);
|
||||||
if(op.type == type_int) sym->int_val = op.int_val;
|
sym->value = op.value;
|
||||||
else if(op.type == type_string) sym->intern_val = op.intern_val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sym->state = SYM_RESOLVED;
|
sym->state = SYM_RESOLVED;
|
||||||
|
|||||||
Reference in New Issue
Block a user