Added automatic distructor calling for going out of scope, found out that += is broken (and just comes through as =)
This commit is contained in:
@@ -338,8 +338,8 @@ obj code_block (Object) {
|
|||||||
return children == other.children && scope == other.scope
|
return children == other.children && scope == other.scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun ast_statement_ptr(): *ast_node {
|
fun ast_statement_ptr(child: *ast_node): *ast_node {
|
||||||
var to_ret.construct(): statement
|
var to_ret.construct(child): statement
|
||||||
var ptr = new<ast_node>()
|
var ptr = new<ast_node>()
|
||||||
ptr->copy_construct(&ast_node::statement(to_ret))
|
ptr->copy_construct(&ast_node::statement(to_ret))
|
||||||
return ptr
|
return ptr
|
||||||
@@ -353,9 +353,10 @@ fun is_statement(node: *ast_node): bool {
|
|||||||
obj statement (Object) {
|
obj statement (Object) {
|
||||||
var scope: map<string, vector<*ast_node>>
|
var scope: map<string, vector<*ast_node>>
|
||||||
var child: *ast_node
|
var child: *ast_node
|
||||||
fun construct(): *statement {
|
fun construct(child_in: *ast_node): *statement {
|
||||||
child = null<ast_node>()
|
child = null<ast_node>()
|
||||||
scope.construct()
|
scope.construct()
|
||||||
|
child = child_in
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
fun copy_construct(old: *statement) {
|
fun copy_construct(old: *statement) {
|
||||||
|
|||||||
+334
-325
@@ -160,344 +160,353 @@ obj ast_transformation (Object) {
|
|||||||
fun fourth_pass(parse_tree: *tree<symbol>, translation_unit: *ast_node) {
|
fun fourth_pass(parse_tree: *tree<symbol>, translation_unit: *ast_node) {
|
||||||
println(string("Fourth Pass for ") + translation_unit->translation_unit.name)
|
println(string("Fourth Pass for ") + translation_unit->translation_unit.name)
|
||||||
}
|
}
|
||||||
fun transform_type(node: *tree<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *type {
|
}
|
||||||
// check for references and step down
|
|
||||||
// always get to pre-reffed level
|
|
||||||
var real_node = get_node("pre_reffed", node)
|
|
||||||
// check for indirection and step down
|
|
||||||
var indirection = 0
|
|
||||||
while (get_node("pre_reffed", real_node)) {
|
|
||||||
real_node = get_node("pre_reffed", real_node)
|
|
||||||
indirection++
|
|
||||||
}
|
|
||||||
var type_syntax_str = concat_symbol_tree(real_node)
|
|
||||||
println(type_syntax_str + " *************************")
|
|
||||||
// should take into account indirection and references...
|
|
||||||
if (type_syntax_str == "void")
|
|
||||||
return type_ptr(base_type::void_return(), indirection)
|
|
||||||
else if (type_syntax_str == "bool")
|
|
||||||
return type_ptr(base_type::boolean(), indirection)
|
|
||||||
else if (type_syntax_str == "int")
|
|
||||||
return type_ptr(base_type::integer(), indirection)
|
|
||||||
else if (type_syntax_str == "float")
|
|
||||||
return type_ptr(base_type::floating(), indirection)
|
|
||||||
else if (type_syntax_str == "double")
|
|
||||||
return type_ptr(base_type::double_precision(), indirection)
|
|
||||||
else if (type_syntax_str == "char")
|
|
||||||
return type_ptr(base_type::character(), indirection)
|
|
||||||
else if (/* check for function type*/ false)
|
|
||||||
return type_ptr(base_type::function(), indirection)
|
|
||||||
else {
|
|
||||||
// do lookup for objects, ADTs, templates, etc
|
|
||||||
var possibilities = scope_lookup(type_syntax_str, scope)
|
|
||||||
print("There are "); print(possibilities.size); println(" possibilites for this object type lookup")
|
|
||||||
for (var i = 0; i < possibilities.size; i++;) {
|
|
||||||
match(*possibilities[i]) {
|
|
||||||
ast_node::type_def(backing) return backing.self_type->clone_with_indirection(indirection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println("No objects in lookup, returning none")
|
|
||||||
return type_ptr(base_type::none(), indirection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*NodeTree<ASTData>* ASTTransformation::transform(NodeTree<Symbol>* from, NodeTree<ASTData>* scope, std::vector<Type> types, bool limitToFunction, std::map<std::string, Type*> templateTypeReplacements) {*/
|
|
||||||
fun transform(node: *tree<symbol>, scope: *ast_node): *ast_node return transform(node, scope, search_type::none())
|
|
||||||
|
|
||||||
fun transform(node: *tree<symbol>, scope: *ast_node, searching_for: search_type): *ast_node {
|
fun transform_type(node: *tree<symbol>, scope: *ast_node, template_replacements: map<string, *type>): *type {
|
||||||
var name = node->data.name
|
// check for references and step down
|
||||||
if (name == "identifier" || name == "scoped_identifier") {
|
// always get to pre-reffed level
|
||||||
return transform_identifier(node, scope, searching_for)
|
var real_node = get_node("pre_reffed", node)
|
||||||
} else if (name == "code_block") {
|
// check for indirection and step down
|
||||||
return transform_code_block(node, scope)
|
var indirection = 0
|
||||||
} else if (name == "if_comp") {
|
while (get_node("pre_reffed", real_node)) {
|
||||||
return transform_if_comp(node, scope)
|
real_node = get_node("pre_reffed", real_node)
|
||||||
} else if (name == "simple_passthrough") {
|
indirection++
|
||||||
return transform_simple_passthrough(node, scope)
|
|
||||||
} else if (name == "statement") {
|
|
||||||
return transform_statement(node, scope)
|
|
||||||
} else if (name == "declaration_statement") {
|
|
||||||
return transform_declaration_statement(node, scope)
|
|
||||||
} else if (name == "assignment_statement") {
|
|
||||||
return transform_assignment_statement(node, scope)
|
|
||||||
} else if (name == "if_statement") {
|
|
||||||
return transform_if_statement(node, scope)
|
|
||||||
} else if (name == "while_loop") {
|
|
||||||
return transform_while_loop(node, scope)
|
|
||||||
} else if (name == "for_loop") {
|
|
||||||
return transform_for_loop(node, scope)
|
|
||||||
} else if (name == "return_statement") {
|
|
||||||
return transform_return_statement(node, scope)
|
|
||||||
} else if (name == "continue_statement" || name == "break_statement") {
|
|
||||||
return transform_branching_statement(node, scope)
|
|
||||||
} else if (name == "defer_statement") {
|
|
||||||
return transform_defer_statement(node, scope)
|
|
||||||
} else if (name == "function_call") {
|
|
||||||
return transform_function_call(node, scope)
|
|
||||||
} else if (name == "boolean_expression" || name == "and_boolean_expression"
|
|
||||||
|| name == "bool_exp" || name == "expression"
|
|
||||||
|| name == "shiftand" || name == "term"
|
|
||||||
|| name == "factor" || name == "unarad"
|
|
||||||
|| name == "access_operation"
|
|
||||||
) {
|
|
||||||
// for now, assume passthrough and just transform underneath
|
|
||||||
return transform_expression(node, scope, searching_for)
|
|
||||||
} else if (name == "bool" || name == "string"
|
|
||||||
|| name == "character" || name == "number"
|
|
||||||
) {
|
|
||||||
println(string("transforming value: ") + name)
|
|
||||||
return transform_value(node, scope)
|
|
||||||
}
|
|
||||||
print("FAILED TO TRANSFORM: "); print(name + ": "); println(concat_symbol_tree(node))
|
|
||||||
return null<ast_node>()
|
|
||||||
}
|
}
|
||||||
fun transform_all(nodes: vector<*tree<symbol>>, scope: *ast_node): vector<*ast_node> {
|
var type_syntax_str = concat_symbol_tree(real_node)
|
||||||
return nodes.map(fun(node: *tree<symbol>): *ast_node return transform(node, scope);)
|
println(type_syntax_str + " *************************")
|
||||||
}
|
// should take into account indirection and references...
|
||||||
fun transform_identifier(node: *tree<symbol>, scope: *ast_node, searching_for: search_type): *ast_node {
|
if (type_syntax_str == "void")
|
||||||
// first, we check for and generate this
|
return type_ptr(base_type::void_return(), indirection)
|
||||||
var name = concat_symbol_tree(node)
|
else if (type_syntax_str == "bool")
|
||||||
if (name == "this") {
|
return type_ptr(base_type::boolean(), indirection)
|
||||||
while (!is_type_def(scope)) scope = get_ast_scope(scope)->get(string("~enclosing_scope"))[0]
|
else if (type_syntax_str == "int")
|
||||||
return ast_identifier_ptr("this", scope->type_def.self_type->clone_with_indirection(1))
|
return type_ptr(base_type::integer(), indirection)
|
||||||
|
else if (type_syntax_str == "float")
|
||||||
|
return type_ptr(base_type::floating(), indirection)
|
||||||
|
else if (type_syntax_str == "double")
|
||||||
|
return type_ptr(base_type::double_precision(), indirection)
|
||||||
|
else if (type_syntax_str == "char")
|
||||||
|
return type_ptr(base_type::character(), indirection)
|
||||||
|
else if (/* check for function type*/ false)
|
||||||
|
return type_ptr(base_type::function(), indirection)
|
||||||
|
else {
|
||||||
|
// do lookup for objects, ADTs, templates, etc
|
||||||
|
var possibilities = scope_lookup(type_syntax_str, scope)
|
||||||
|
print("There are "); print(possibilities.size); println(" possibilites for this object type lookup")
|
||||||
|
for (var i = 0; i < possibilities.size; i++;) {
|
||||||
|
match(*possibilities[i]) {
|
||||||
|
ast_node::type_def(backing) return backing.self_type->clone_with_indirection(indirection)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
match (searching_for) {
|
println("No objects in lookup, returning none")
|
||||||
search_type::none() return identifier_lookup(name, scope)
|
return type_ptr(base_type::none(), indirection)
|
||||||
search_type::function(type_vec) return function_lookup(name, scope, type_vec)
|
|
||||||
}
|
|
||||||
println("FAILED SEARCH FOR")
|
|
||||||
return null<ast_node>()
|
|
||||||
}
|
}
|
||||||
fun transform_value(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
}
|
||||||
var value_str = concat_symbol_tree(node)
|
fun transform(node: *tree<symbol>, scope: *ast_node): *ast_node return transform(node, scope, search_type::none())
|
||||||
var value_type = null<type>()
|
fun transform(node: *tree<symbol>, scope: *ast_node, searching_for: search_type): *ast_node {
|
||||||
if (value_str[0] == '"')
|
var name = node->data.name
|
||||||
value_type = type_ptr(base_type::character(), 1)
|
if (name == "identifier" || name == "scoped_identifier") {
|
||||||
else if (value_str[0] == '\'') //'// lol, comment hack for vim syntax highlighting (my fault, of course)
|
return transform_identifier(node, scope, searching_for)
|
||||||
value_type = type_ptr(base_type::character())
|
} else if (name == "code_block") {
|
||||||
else {
|
return transform_code_block(node, scope)
|
||||||
// should differentiate between float and double...
|
} else if (name == "if_comp") {
|
||||||
var contains_dot = false
|
return transform_if_comp(node, scope)
|
||||||
for (var i = 0; i < value_str.length(); i++;) {
|
} else if (name == "simple_passthrough") {
|
||||||
if (value_str[i] == '.') {
|
return transform_simple_passthrough(node, scope)
|
||||||
contains_dot = true
|
} else if (name == "statement") {
|
||||||
|
return transform_statement(node, scope)
|
||||||
|
} else if (name == "declaration_statement") {
|
||||||
|
return transform_declaration_statement(node, scope)
|
||||||
|
} else if (name == "assignment_statement") {
|
||||||
|
return transform_assignment_statement(node, scope)
|
||||||
|
} else if (name == "if_statement") {
|
||||||
|
return transform_if_statement(node, scope)
|
||||||
|
} else if (name == "while_loop") {
|
||||||
|
return transform_while_loop(node, scope)
|
||||||
|
} else if (name == "for_loop") {
|
||||||
|
return transform_for_loop(node, scope)
|
||||||
|
} else if (name == "return_statement") {
|
||||||
|
return transform_return_statement(node, scope)
|
||||||
|
} else if (name == "continue_statement" || name == "break_statement") {
|
||||||
|
return transform_branching_statement(node, scope)
|
||||||
|
} else if (name == "defer_statement") {
|
||||||
|
return transform_defer_statement(node, scope)
|
||||||
|
} else if (name == "function_call") {
|
||||||
|
return transform_function_call(node, scope)
|
||||||
|
} else if (name == "boolean_expression" || name == "and_boolean_expression"
|
||||||
|
|| name == "bool_exp" || name == "expression"
|
||||||
|
|| name == "shiftand" || name == "term"
|
||||||
|
|| name == "factor" || name == "unarad"
|
||||||
|
|| name == "access_operation"
|
||||||
|
) {
|
||||||
|
// for now, assume passthrough and just transform underneath
|
||||||
|
return transform_expression(node, scope, searching_for)
|
||||||
|
} else if (name == "bool" || name == "string"
|
||||||
|
|| name == "character" || name == "number"
|
||||||
|
) {
|
||||||
|
println(string("transforming value: ") + name)
|
||||||
|
return transform_value(node, scope)
|
||||||
|
}
|
||||||
|
print("FAILED TO TRANSFORM: "); print(name + ": "); println(concat_symbol_tree(node))
|
||||||
|
return null<ast_node>()
|
||||||
|
}
|
||||||
|
fun transform_all(nodes: vector<*tree<symbol>>, scope: *ast_node): vector<*ast_node> {
|
||||||
|
return nodes.map(fun(node: *tree<symbol>): *ast_node return transform(node, scope);)
|
||||||
|
}
|
||||||
|
fun transform_identifier(node: *tree<symbol>, scope: *ast_node, searching_for: search_type): *ast_node {
|
||||||
|
// first, we check for and generate this
|
||||||
|
var name = concat_symbol_tree(node)
|
||||||
|
if (name == "this") {
|
||||||
|
while (!is_type_def(scope)) scope = get_ast_scope(scope)->get(string("~enclosing_scope"))[0]
|
||||||
|
return ast_identifier_ptr("this", scope->type_def.self_type->clone_with_indirection(1))
|
||||||
|
}
|
||||||
|
match (searching_for) {
|
||||||
|
search_type::none() return identifier_lookup(name, scope)
|
||||||
|
search_type::function(type_vec) return function_lookup(name, scope, type_vec)
|
||||||
|
}
|
||||||
|
println("FAILED SEARCH FOR")
|
||||||
|
return null<ast_node>()
|
||||||
|
}
|
||||||
|
fun transform_value(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
var value_str = concat_symbol_tree(node)
|
||||||
|
var value_type = null<type>()
|
||||||
|
if (value_str[0] == '"')
|
||||||
|
value_type = type_ptr(base_type::character(), 1)
|
||||||
|
else if (value_str[0] == '\'') //'// lol, comment hack for vim syntax highlighting (my fault, of course)
|
||||||
|
value_type = type_ptr(base_type::character())
|
||||||
|
else {
|
||||||
|
// should differentiate between float and double...
|
||||||
|
var contains_dot = false
|
||||||
|
for (var i = 0; i < value_str.length(); i++;) {
|
||||||
|
if (value_str[i] == '.') {
|
||||||
|
contains_dot = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (contains_dot)
|
||||||
|
value_type = type_ptr(base_type::floating())
|
||||||
|
else
|
||||||
|
value_type = type_ptr(base_type::integer())
|
||||||
|
}
|
||||||
|
return ast_value_ptr(value_str, value_type)
|
||||||
|
}
|
||||||
|
fun transform_code_block(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
var new_block = ast_code_block_ptr()
|
||||||
|
add_to_scope("~enclosing_scope", scope, new_block)
|
||||||
|
new_block->code_block.children = transform_all(node->children, new_block)
|
||||||
|
return new_block
|
||||||
|
}
|
||||||
|
fun transform_if_comp(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
var new_if_comp = ast_if_comp_ptr()
|
||||||
|
new_if_comp->if_comp.wanted_generator = concat_symbol_tree(get_node("identifier", node))
|
||||||
|
new_if_comp->if_comp.statement = transform_statement(get_node("statement", node), scope)
|
||||||
|
return new_if_comp
|
||||||
|
}
|
||||||
|
fun transform_simple_passthrough(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
var new_passthrough = ast_simple_passthrough_ptr()
|
||||||
|
// setup passthrough params and string
|
||||||
|
new_passthrough->simple_passthrough.passthrough_str = concat_symbol_tree(get_node("triple_quoted_string", node)).slice(3,-4)
|
||||||
|
return new_passthrough
|
||||||
|
}
|
||||||
|
fun transform_statement(node: *tree<symbol>, scope: *ast_node): *ast_node return ast_statement_ptr(transform(node->children[0], scope));
|
||||||
|
fun transform_declaration_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
// this might have an init position method call
|
||||||
|
var identifiers = get_nodes("identifier", node)
|
||||||
|
var name = concat_symbol_tree(identifiers[0])
|
||||||
|
// may have type, or an expression, or both
|
||||||
|
var type_syntax_node = get_node("type", node)
|
||||||
|
var expression_syntax_node = get_node("boolean_expression", node)
|
||||||
|
var ident_type = null<type>()
|
||||||
|
var expression = null<ast_node>()
|
||||||
|
if (type_syntax_node) ident_type = transform_type(type_syntax_node, scope, map<string, *type>())
|
||||||
|
if (expression_syntax_node) {
|
||||||
|
expression = transform(expression_syntax_node, scope)
|
||||||
|
if (!type_syntax_node)
|
||||||
|
ident_type = get_ast_type(expression)
|
||||||
|
}
|
||||||
|
if (!ident_type) error("declaration statement with no type or expression from which to inference type")
|
||||||
|
var identifier = ast_identifier_ptr(name, ident_type)
|
||||||
|
var declaration = ast_declaration_statement_ptr(identifier, expression)
|
||||||
|
// ok, deal with the possible init position method call
|
||||||
|
if (identifiers.size == 2) {
|
||||||
|
var method = transform(identifiers[1], ident_type->type_def)
|
||||||
|
var parameters = get_nodes("parameter", node).map(fun(child: *tree<symbol>): *ast_node return transform(get_node("boolean_expression", child), scope);)
|
||||||
|
declaration->declaration_statement.init_method_call = make_method_call(identifier, method, parameters)
|
||||||
|
}
|
||||||
|
add_to_scope(name, identifier, scope)
|
||||||
|
return declaration
|
||||||
|
}
|
||||||
|
fun has_method(object: *ast_node, name: *char, parameter_types: vector<*type>): bool return has_method(object, string(name), parameter_types);
|
||||||
|
fun has_method(object: *ast_node, name: string, parameter_types: vector<*type>): bool {
|
||||||
|
return function_lookup(name, object, parameter_types) || false
|
||||||
|
}
|
||||||
|
fun make_method_call(object_ident: *ast_node, name: *char, parameters: vector<*ast_node>): *ast_node return make_method_call(object_ident, string(name), parameters);
|
||||||
|
fun make_method_call(object_ident: *ast_node, name: string, parameters: vector<*ast_node>): *ast_node {
|
||||||
|
var method = function_lookup(name, get_ast_type(object_ident)->type_def, parameters.map(fun(param: *ast_node): *type return get_ast_type(param);))
|
||||||
|
print("Here is the Method: ")
|
||||||
|
println(method)
|
||||||
|
return make_method_call(object_ident, method, parameters)
|
||||||
|
}
|
||||||
|
fun make_method_call(object_ident: *ast_node, method: *ast_node, parameters: vector<*ast_node>): *ast_node {
|
||||||
|
var method_access = ast_function_call_ptr(get_builtin_function(string("."), vector(get_ast_type(object_ident), get_ast_type(method))), vector(object_ident, method))
|
||||||
|
return ast_function_call_ptr(method_access, parameters)
|
||||||
|
}
|
||||||
|
fun transform_assignment_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
var assignment = ast_assignment_statement_ptr(transform(get_node("factor", node), scope), transform(get_node("boolean_expression", node), scope))
|
||||||
|
return assignment
|
||||||
|
}
|
||||||
|
fun transform_if_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
var if_statement = ast_if_statement_ptr(transform_expression(get_node("boolean_expression", node), scope))
|
||||||
|
// one variable declarations might be in a code_block-less if statement
|
||||||
|
add_to_scope("~enclosing_scope", scope, if_statement)
|
||||||
|
var statements = transform_all(get_nodes("statement", node), if_statement)
|
||||||
|
if_statement->if_statement.then_part = statements[0]
|
||||||
|
// we have an else
|
||||||
|
if (statements.size == 2)
|
||||||
|
if_statement->if_statement.else_part = statements[1]
|
||||||
|
return if_statement
|
||||||
|
}
|
||||||
|
fun transform_while_loop(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
var while_loop = ast_while_loop_ptr(transform_expression(get_node("boolean_expression", node), scope))
|
||||||
|
add_to_scope("~enclosing_scope", scope, while_loop)
|
||||||
|
while_loop->while_loop.statement = transform(get_node("statement", node), while_loop)
|
||||||
|
return while_loop
|
||||||
|
}
|
||||||
|
fun transform_for_loop(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
var for_loop = ast_for_loop_ptr()
|
||||||
|
add_to_scope("~enclosing_scope", scope, for_loop)
|
||||||
|
var statements = get_nodes("statement", node)
|
||||||
|
for_loop->for_loop.init = transform(statements[0], for_loop)
|
||||||
|
for_loop->for_loop.condition = transform(get_node("boolean_expression", node), for_loop)
|
||||||
|
for_loop->for_loop.update = transform(statements[1], for_loop)
|
||||||
|
for_loop->for_loop.body = transform(statements[2], for_loop)
|
||||||
|
return for_loop
|
||||||
|
}
|
||||||
|
fun transform_return_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
return ast_return_statement_ptr(transform(node->children[0], scope))
|
||||||
|
}
|
||||||
|
fun transform_branching_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
if (node->data.name == "break_statement")
|
||||||
|
return ast_branching_statement_ptr(branching_type::break_stmt())
|
||||||
|
return ast_branching_statement_ptr(branching_type::continue_stmt())
|
||||||
|
}
|
||||||
|
fun transform_defer_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
return ast_defer_statement_ptr(transform(node->children[0], scope))
|
||||||
|
}
|
||||||
|
fun transform_function_call(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
||||||
|
// don't bother with a full transform for parameters with their own function, just get the boolean expression and transform it
|
||||||
|
var parameters = get_nodes("parameter", node).map(fun(child: *tree<symbol>): *ast_node return transform(get_node("boolean_expression", child), scope);)
|
||||||
|
var parameter_types = parameters.map(fun(param: *ast_node): *type return get_ast_type(param);)
|
||||||
|
var f = ast_function_call_ptr(transform(get_node("unarad", node), scope, search_type::function(parameter_types)), parameters)
|
||||||
|
/*print("function call function ")*/
|
||||||
|
/*println(f->function_call.func)*/
|
||||||
|
/*print("function call parameters ")*/
|
||||||
|
/*f->function_call.parameters.for_each(fun(param: *ast_node) print(param);)*/
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
fun transform_expression(node: *tree<symbol>, scope: *ast_node): *ast_node return transform_expression(node, scope, search_type::none())
|
||||||
|
fun transform_expression(node: *tree<symbol>, scope: *ast_node, searching_for: search_type): *ast_node {
|
||||||
|
// figure out what the expression is, handle overloads, or you know
|
||||||
|
// ignore everything and do a passthrough
|
||||||
|
var func_name = string()
|
||||||
|
var parameters = vector<*ast_node>()
|
||||||
|
if (node->children.size == 1)
|
||||||
|
return transform(node->children[0], scope, searching_for)
|
||||||
|
else if (node->children.size == 2) {
|
||||||
|
var check_if_post = concat_symbol_tree(node->children[1])
|
||||||
|
if (check_if_post == "--" || check_if_post == "++") {
|
||||||
|
// give the post-operators a special suffix so the c_generator knows to emit them post
|
||||||
|
func_name = concat_symbol_tree(node->children[1]) + "p"
|
||||||
|
parameters = vector(transform(node->children[0], scope))
|
||||||
|
} else {
|
||||||
|
func_name = concat_symbol_tree(node->children[0])
|
||||||
|
parameters = vector(transform(node->children[1], scope))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
func_name = concat_symbol_tree(node->children[1])
|
||||||
|
var first_param = transform(node->children[0], scope)
|
||||||
|
var second_param = null<ast_node>()
|
||||||
|
if (func_name == "." || func_name == "->") {
|
||||||
|
println("Gonna do the internal scope thing")
|
||||||
|
second_param = transform(node->children[2], get_ast_type(first_param)->type_def, searching_for)
|
||||||
|
} else {
|
||||||
|
println("Gonna do regular scope thing")
|
||||||
|
second_param = transform(node->children[2], scope)
|
||||||
|
}
|
||||||
|
parameters = vector(first_param, second_param)
|
||||||
|
}
|
||||||
|
var parameter_types = parameters.map(fun(param: *ast_node): *type return get_ast_type(param);)
|
||||||
|
return ast_function_call_ptr(get_builtin_function(func_name, parameter_types), parameters)
|
||||||
|
}
|
||||||
|
fun get_builtin_function(name: string, param_types: vector<*type>): *ast_node {
|
||||||
|
if (name == "." || name == "->")
|
||||||
|
return ast_function_ptr(name, type_ptr(param_types, param_types[1]), vector<*ast_node>())
|
||||||
|
return ast_function_ptr(name, type_ptr(param_types, param_types[0]), vector<*ast_node>())
|
||||||
|
}
|
||||||
|
fun function_lookup(name: string, scope: *ast_node, param_types: vector<*type>): *ast_node {
|
||||||
|
println(string("doing function lookup for: ") + name)
|
||||||
|
var param_string = string()
|
||||||
|
param_types.for_each(fun(t: *type) param_string += t->to_string() + ", ";)
|
||||||
|
var results = scope_lookup(name, scope)
|
||||||
|
print(results.size); println(" number of results")
|
||||||
|
for (var i = 0; i < results.size; i++;) {
|
||||||
|
if (is_function(results[i])) {
|
||||||
|
var func_param_types = get_ast_type(results[i])->parameter_types
|
||||||
|
if (func_param_types.size != param_types.size) {
|
||||||
|
println(string("type sizes don't match") + get_ast_type(results[i])->to_string() + " with needed " + param_string)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var param_types_match = true
|
||||||
|
for (var j = 0; j < param_types.size; j++;) {
|
||||||
|
if (*func_param_types[j] != *param_types[j]) {
|
||||||
|
param_types_match = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (contains_dot)
|
if (param_types_match)
|
||||||
value_type = type_ptr(base_type::floating())
|
return results[i]
|
||||||
else
|
|
||||||
value_type = type_ptr(base_type::integer())
|
|
||||||
}
|
}
|
||||||
return ast_value_ptr(value_str, value_type)
|
println(string("either isn't function or types don't match ") + get_ast_type(results[i])->to_string() + " with needed " + param_string)
|
||||||
}
|
}
|
||||||
fun transform_code_block(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
println(string("function lookup failed for ") + name)
|
||||||
var new_block = ast_code_block_ptr()
|
return null<ast_node>()
|
||||||
add_to_scope("~enclosing_scope", scope, new_block)
|
}
|
||||||
new_block->code_block.children = transform_all(node->children, new_block)
|
fun identifier_lookup(name: string, scope: *ast_node): *ast_node {
|
||||||
return new_block
|
println(string("doing identifier lookup for: ") + name)
|
||||||
}
|
var results = scope_lookup(name, scope)
|
||||||
fun transform_if_comp(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
if (!results.size) {
|
||||||
var new_if_comp = ast_if_comp_ptr()
|
println(string("identifier lookup failed for ") + name)
|
||||||
new_if_comp->if_comp.wanted_generator = concat_symbol_tree(get_node("identifier", node))
|
|
||||||
new_if_comp->if_comp.statement = transform_statement(get_node("statement", node), scope)
|
|
||||||
return new_if_comp
|
|
||||||
}
|
|
||||||
fun transform_simple_passthrough(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
var new_passthrough = ast_simple_passthrough_ptr()
|
|
||||||
// setup passthrough params and string
|
|
||||||
new_passthrough->simple_passthrough.passthrough_str = concat_symbol_tree(get_node("triple_quoted_string", node)).slice(3,-4)
|
|
||||||
return new_passthrough
|
|
||||||
}
|
|
||||||
fun transform_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
var new_statement = ast_statement_ptr()
|
|
||||||
new_statement->statement.child = transform(node->children[0], scope)
|
|
||||||
return new_statement
|
|
||||||
}
|
|
||||||
fun transform_declaration_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
// this might have an init position method call
|
|
||||||
var identifiers = get_nodes("identifier", node)
|
|
||||||
var name = concat_symbol_tree(identifiers[0])
|
|
||||||
// may have type, or an expression, or both
|
|
||||||
var type_syntax_node = get_node("type", node)
|
|
||||||
var expression_syntax_node = get_node("boolean_expression", node)
|
|
||||||
var ident_type = null<type>()
|
|
||||||
var expression = null<ast_node>()
|
|
||||||
if (type_syntax_node) ident_type = transform_type(type_syntax_node, scope, map<string, *type>())
|
|
||||||
if (expression_syntax_node) {
|
|
||||||
expression = transform(expression_syntax_node, scope)
|
|
||||||
if (!type_syntax_node)
|
|
||||||
ident_type = get_ast_type(expression)
|
|
||||||
}
|
|
||||||
if (!ident_type) error("declaration statement with no type or expression from which to inference type")
|
|
||||||
var identifier = ast_identifier_ptr(name, ident_type)
|
|
||||||
var declaration = ast_declaration_statement_ptr(identifier, expression)
|
|
||||||
// ok, deal with the possible init position method call
|
|
||||||
if (identifiers.size == 2) {
|
|
||||||
var method = transform(identifiers[1], ident_type->type_def)
|
|
||||||
var parameters = get_nodes("parameter", node).map(fun(child: *tree<symbol>): *ast_node return transform(get_node("boolean_expression", child), scope);)
|
|
||||||
var method_access = ast_function_call_ptr(get_builtin_function(string("."), vector(ident_type, get_ast_type(method))), vector(identifier, method))
|
|
||||||
declaration->declaration_statement.init_method_call = ast_function_call_ptr(method_access, parameters)
|
|
||||||
}
|
|
||||||
add_to_scope(name, identifier, scope)
|
|
||||||
return declaration
|
|
||||||
}
|
|
||||||
fun transform_assignment_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
var assignment = ast_assignment_statement_ptr(transform(get_node("factor", node), scope), transform(get_node("boolean_expression", node), scope))
|
|
||||||
return assignment
|
|
||||||
}
|
|
||||||
fun transform_if_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
var if_statement = ast_if_statement_ptr(transform_expression(get_node("boolean_expression", node), scope))
|
|
||||||
// one variable declarations might be in a code_block-less if statement
|
|
||||||
add_to_scope("~enclosing_scope", scope, if_statement)
|
|
||||||
var statements = transform_all(get_nodes("statement", node), if_statement)
|
|
||||||
if_statement->if_statement.then_part = statements[0]
|
|
||||||
// we have an else
|
|
||||||
if (statements.size == 2)
|
|
||||||
if_statement->if_statement.else_part = statements[1]
|
|
||||||
return if_statement
|
|
||||||
}
|
|
||||||
fun transform_while_loop(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
var while_loop = ast_while_loop_ptr(transform_expression(get_node("boolean_expression", node), scope))
|
|
||||||
add_to_scope("~enclosing_scope", scope, while_loop)
|
|
||||||
while_loop->while_loop.statement = transform(get_node("statement", node), while_loop)
|
|
||||||
return while_loop
|
|
||||||
}
|
|
||||||
fun transform_for_loop(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
var for_loop = ast_for_loop_ptr()
|
|
||||||
add_to_scope("~enclosing_scope", scope, for_loop)
|
|
||||||
var statements = get_nodes("statement", node)
|
|
||||||
for_loop->for_loop.init = transform(statements[0], for_loop)
|
|
||||||
for_loop->for_loop.condition = transform(get_node("boolean_expression", node), for_loop)
|
|
||||||
for_loop->for_loop.update = transform(statements[1], for_loop)
|
|
||||||
for_loop->for_loop.body = transform(statements[2], for_loop)
|
|
||||||
return for_loop
|
|
||||||
}
|
|
||||||
fun transform_return_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
return ast_return_statement_ptr(transform(node->children[0], scope))
|
|
||||||
}
|
|
||||||
fun transform_branching_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
if (node->data.name == "break_statement")
|
|
||||||
return ast_branching_statement_ptr(branching_type::break_stmt())
|
|
||||||
return ast_branching_statement_ptr(branching_type::continue_stmt())
|
|
||||||
}
|
|
||||||
fun transform_defer_statement(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
return ast_defer_statement_ptr(transform(node->children[0], scope))
|
|
||||||
}
|
|
||||||
fun transform_function_call(node: *tree<symbol>, scope: *ast_node): *ast_node {
|
|
||||||
// don't bother with a full transform for parameters with their own function, just get the boolean expression and transform it
|
|
||||||
var parameters = get_nodes("parameter", node).map(fun(child: *tree<symbol>): *ast_node return transform(get_node("boolean_expression", child), scope);)
|
|
||||||
var parameter_types = parameters.map(fun(param: *ast_node): *type return get_ast_type(param);)
|
|
||||||
var f = ast_function_call_ptr(transform(get_node("unarad", node), scope, search_type::function(parameter_types)), parameters)
|
|
||||||
/*print("function call function ")*/
|
|
||||||
/*println(f->function_call.func)*/
|
|
||||||
/*print("function call parameters ")*/
|
|
||||||
/*f->function_call.parameters.for_each(fun(param: *ast_node) print(param);)*/
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
fun transform_expression(node: *tree<symbol>, scope: *ast_node): *ast_node return transform_expression(node, scope, search_type::none())
|
|
||||||
fun transform_expression(node: *tree<symbol>, scope: *ast_node, searching_for: search_type): *ast_node {
|
|
||||||
// figure out what the expression is, handle overloads, or you know
|
|
||||||
// ignore everything and do a passthrough
|
|
||||||
var func_name = string()
|
|
||||||
var parameters = vector<*ast_node>()
|
|
||||||
if (node->children.size == 1)
|
|
||||||
return transform(node->children[0], scope, searching_for)
|
|
||||||
else if (node->children.size == 2) {
|
|
||||||
var check_if_post = concat_symbol_tree(node->children[1])
|
|
||||||
if (check_if_post == "--" || check_if_post == "++") {
|
|
||||||
// give the post-operators a special suffix so the c_generator knows to emit them post
|
|
||||||
func_name = concat_symbol_tree(node->children[1]) + "p"
|
|
||||||
parameters = vector(transform(node->children[0], scope))
|
|
||||||
} else {
|
|
||||||
func_name = concat_symbol_tree(node->children[0])
|
|
||||||
parameters = vector(transform(node->children[1], scope))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
func_name = concat_symbol_tree(node->children[1])
|
|
||||||
var first_param = transform(node->children[0], scope)
|
|
||||||
var second_param = null<ast_node>()
|
|
||||||
if (func_name == "." || func_name == "->") {
|
|
||||||
println("Gonna do the internal scope thing")
|
|
||||||
second_param = transform(node->children[2], get_ast_type(first_param)->type_def, searching_for)
|
|
||||||
} else {
|
|
||||||
println("Gonna do regular scope thing")
|
|
||||||
second_param = transform(node->children[2], scope)
|
|
||||||
}
|
|
||||||
parameters = vector(first_param, second_param)
|
|
||||||
}
|
|
||||||
var parameter_types = parameters.map(fun(param: *ast_node): *type return get_ast_type(param);)
|
|
||||||
return ast_function_call_ptr(get_builtin_function(func_name, parameter_types), parameters)
|
|
||||||
}
|
|
||||||
fun get_builtin_function(name: string, param_types: vector<*type>): *ast_node {
|
|
||||||
if (name == "." || name == "->")
|
|
||||||
return ast_function_ptr(name, type_ptr(param_types, param_types[1]), vector<*ast_node>())
|
|
||||||
return ast_function_ptr(name, type_ptr(param_types, param_types[0]), vector<*ast_node>())
|
|
||||||
}
|
|
||||||
fun function_lookup(name: string, scope: *ast_node, param_types: vector<*type>): *ast_node {
|
|
||||||
println(string("doing function lookup for: ") + name)
|
|
||||||
var param_string = string()
|
|
||||||
param_types.for_each(fun(t: *type) param_string += t->to_string() + ", ";)
|
|
||||||
var results = scope_lookup(name, scope)
|
|
||||||
print(results.size); println(" number of results")
|
|
||||||
for (var i = 0; i < results.size; i++;) {
|
|
||||||
if (is_function(results[i])) {
|
|
||||||
var func_param_types = get_ast_type(results[i])->parameter_types
|
|
||||||
if (func_param_types.size != param_types.size) {
|
|
||||||
println(string("type sizes don't match") + get_ast_type(results[i])->to_string() + " with needed " + param_string)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var param_types_match = true
|
|
||||||
for (var j = 0; j < param_types.size; j++;) {
|
|
||||||
if (*func_param_types[j] != *param_types[j]) {
|
|
||||||
param_types_match = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (param_types_match)
|
|
||||||
return results[i]
|
|
||||||
}
|
|
||||||
println(string("either isn't function or types don't match ") + get_ast_type(results[i])->to_string() + " with needed " + param_string)
|
|
||||||
}
|
|
||||||
println(string("function lookup failed for ") + name)
|
|
||||||
return null<ast_node>()
|
return null<ast_node>()
|
||||||
}
|
}
|
||||||
fun identifier_lookup(name: string, scope: *ast_node): *ast_node {
|
return results[0]
|
||||||
println(string("doing identifier lookup for: ") + name)
|
}
|
||||||
var results = scope_lookup(name, scope)
|
fun scope_lookup(name: string, scope: *ast_node): vector<*ast_node> {
|
||||||
if (!results.size) {
|
println("*****Doing a name lookup for*****")
|
||||||
println(string("identifier lookup failed for ") + name)
|
println(name)
|
||||||
return null<ast_node>()
|
return scope_lookup_helper(name, scope)
|
||||||
}
|
}
|
||||||
return results[0]
|
fun scope_lookup_helper(name: string, scope: *ast_node): vector<*ast_node> {
|
||||||
|
// need to do properly scopded lookups
|
||||||
|
// prevent re-checking the same one...
|
||||||
|
print("scope is: ")
|
||||||
|
get_ast_scope(scope)->for_each(fun(key: string, value: vector<*ast_node>) print(key + " ");)
|
||||||
|
println()
|
||||||
|
var results = vector<*ast_node>()
|
||||||
|
if (get_ast_scope(scope)->contains_key(name)) {
|
||||||
|
println(name + " is in scope, adding to results")
|
||||||
|
results += get_ast_scope(scope)->get(name)
|
||||||
}
|
}
|
||||||
fun scope_lookup(name: string, scope: *ast_node): vector<*ast_node> {
|
if (get_ast_scope(scope)->contains_key(string("~enclosing_scope")))
|
||||||
println("*****Doing a name lookup for*****")
|
results += scope_lookup_helper(name, get_ast_scope(scope)->get(string("~enclosing_scope"))[0])
|
||||||
println(name)
|
if (is_translation_unit(scope)) {
|
||||||
return scope_lookup_helper(name, scope)
|
scope->translation_unit.children.for_each(fun(child: *ast_node) {
|
||||||
}
|
if (is_import(child) && child->import.imported.contains(name)) {
|
||||||
fun scope_lookup_helper(name: string, scope: *ast_node): vector<*ast_node> {
|
println(name + " is indeed imported")
|
||||||
// need to do properly scopded lookups
|
results += scope_lookup_helper(name, child->import.translation_unit)
|
||||||
// prevent re-checking the same one...
|
} else println(name + " is not imported (this time)")
|
||||||
print("scope is: ")
|
})
|
||||||
get_ast_scope(scope)->for_each(fun(key: string, value: vector<*ast_node>) print(key + " ");)
|
|
||||||
println()
|
|
||||||
var results = vector<*ast_node>()
|
|
||||||
if (get_ast_scope(scope)->contains_key(name)) {
|
|
||||||
println(name + " is in scope, adding to results")
|
|
||||||
results += get_ast_scope(scope)->get(name)
|
|
||||||
}
|
|
||||||
if (get_ast_scope(scope)->contains_key(string("~enclosing_scope")))
|
|
||||||
results += scope_lookup_helper(name, get_ast_scope(scope)->get(string("~enclosing_scope"))[0])
|
|
||||||
if (is_translation_unit(scope)) {
|
|
||||||
scope->translation_unit.children.for_each(fun(child: *ast_node) {
|
|
||||||
if (is_import(child) && child->import.imported.contains(name)) {
|
|
||||||
println(name + " is indeed imported")
|
|
||||||
results += scope_lookup_helper(name, child->import.translation_unit)
|
|
||||||
} else println(name + " is not imported (this time)")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
}
|
}
|
||||||
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
fun concat_symbol_tree(node: *tree<symbol>): string {
|
fun concat_symbol_tree(node: *tree<symbol>): string {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import tree:*
|
|||||||
import symbol:*
|
import symbol:*
|
||||||
import ast_nodes:*
|
import ast_nodes:*
|
||||||
import poset:*
|
import poset:*
|
||||||
|
// we import ast_transformation for its make_method_call function
|
||||||
|
import ast_transformation:*
|
||||||
|
|
||||||
fun code_triple(): code_triple return code_triple(string(), string(), string());
|
fun code_triple(): code_triple return code_triple(string(), string(), string());
|
||||||
fun code_triple(only: *char): code_triple return code_triple(string(), string(only), string());
|
fun code_triple(only: *char): code_triple return code_triple(string(), string(only), string());
|
||||||
@@ -172,11 +174,14 @@ obj c_generator (Object) {
|
|||||||
}
|
}
|
||||||
fun generate_statement(node: *ast_node, enclosing_object: *ast_node, defer_stack: *stack<pair<bool,stack<*ast_node>>>): code_triple return generate(node->statement.child, enclosing_object, defer_stack) + ";\n";
|
fun generate_statement(node: *ast_node, enclosing_object: *ast_node, defer_stack: *stack<pair<bool,stack<*ast_node>>>): code_triple return generate(node->statement.child, enclosing_object, defer_stack) + ";\n";
|
||||||
fun generate_declaration_statement(node: *ast_node, enclosing_object: *ast_node, defer_stack: *stack<pair<bool,stack<*ast_node>>>): code_triple {
|
fun generate_declaration_statement(node: *ast_node, enclosing_object: *ast_node, defer_stack: *stack<pair<bool,stack<*ast_node>>>): code_triple {
|
||||||
// add destruct to defer_stac
|
// add destruct to defer_stack
|
||||||
var identifier = node->declaration_statement.identifier
|
var identifier = node->declaration_statement.identifier
|
||||||
|
var ident_type = identifier->identifier.type
|
||||||
var to_ret = code_triple() + type_to_c(identifier->identifier.type) + " " + identifier->identifier.name
|
var to_ret = code_triple() + type_to_c(identifier->identifier.type) + " " + identifier->identifier.name
|
||||||
if (node->declaration_statement.expression) to_ret += code_triple(" = ") + generate(node->declaration_statement.expression, enclosing_object, null<stack<pair<bool,stack<*ast_node>>>>())
|
if (node->declaration_statement.expression) to_ret += code_triple(" = ") + generate(node->declaration_statement.expression, enclosing_object, null<stack<pair<bool,stack<*ast_node>>>>())
|
||||||
if (node->declaration_statement.init_method_call) to_ret += code_triple(";\n") + generate(node->declaration_statement.init_method_call, enclosing_object, null<stack<pair<bool,stack<*ast_node>>>>())
|
if (node->declaration_statement.init_method_call) to_ret += code_triple(";\n") + generate(node->declaration_statement.init_method_call, enclosing_object, null<stack<pair<bool,stack<*ast_node>>>>())
|
||||||
|
if (ident_type->is_object() && has_method(ident_type->type_def, "destruct", vector<*type>()))
|
||||||
|
defer_stack->top().second.push(ast_statement_ptr(make_method_call(identifier, "destruct", vector<*ast_node>())))
|
||||||
return to_ret
|
return to_ret
|
||||||
}
|
}
|
||||||
fun generate_assignment_statement(node: *ast_node, enclosing_object: *ast_node): code_triple {
|
fun generate_assignment_statement(node: *ast_node, enclosing_object: *ast_node): code_triple {
|
||||||
|
|||||||
@@ -121,5 +121,11 @@ obj type (Object) {
|
|||||||
to_ret->indirection = ind
|
to_ret->indirection = ind
|
||||||
return to_ret
|
return to_ret
|
||||||
}
|
}
|
||||||
|
fun is_object(): bool {
|
||||||
|
match (base) {
|
||||||
|
base_type::object() return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -17,8 +17,8 @@ obj Something (ObjectTrait) {
|
|||||||
member = old->member
|
member = old->member
|
||||||
}
|
}
|
||||||
fun destruct() {
|
fun destruct() {
|
||||||
simple_println("Destructing a Something")
|
simple_print("Destructing a Something: ")
|
||||||
member = -800
|
simple_println(member)
|
||||||
}
|
}
|
||||||
fun method(a: int):int {
|
fun method(a: int):int {
|
||||||
return 5+a+member + other_method()
|
return 5+a+member + other_method()
|
||||||
@@ -107,6 +107,7 @@ fun main(): int {
|
|||||||
simple_println(test_methods.member)
|
simple_println(test_methods.member)
|
||||||
simple_println(test_methods_param.member)
|
simple_println(test_methods_param.member)
|
||||||
var second_obj = test_methods
|
var second_obj = test_methods
|
||||||
|
second_obj.member += 5
|
||||||
simple_println(second_obj.member)
|
simple_println(second_obj.member)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user