diff --git a/src/julec/obj/cxx/derive.jule b/src/julec/obj/cxx/derive.jule index b56826929..a83a6789a 100644 --- a/src/julec/obj/cxx/derive.jule +++ b/src/julec/obj/cxx/derive.jule @@ -4,6 +4,8 @@ use std::jule::sema::{Struct} +/* + fn get_derive_fn_decl_clone(&s: &Struct): str { let mut obj = gen_struct_kind(s) obj += " clone(void) const " @@ -16,3 +18,4 @@ fn get_derive_fn_def_clone(&s: &Struct): str { obj += "::clone(void) const " ret obj } +*/ \ No newline at end of file diff --git a/src/julec/obj/cxx/expr.jule b/src/julec/obj/cxx/expr.jule index 677be2a31..03753f278 100644 --- a/src/julec/obj/cxx/expr.jule +++ b/src/julec/obj/cxx/expr.jule @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD 3-Clause // license that can be found in the LICENSE file. +/* + use env use conv for std::conv @@ -1320,3 +1322,4 @@ fn get_init_expr(mut t: &TypeKind): str { } ret gen_type_kind(t) + "()" } +*/ \ No newline at end of file diff --git a/src/julec/obj/cxx/gen.jule b/src/julec/obj/cxx/gen.jule deleted file mode 100644 index f1439ad7e..000000000 --- a/src/julec/obj/cxx/gen.jule +++ /dev/null @@ -1,1202 +0,0 @@ -// Copyright 2023-2024 The Jule Programming Language. -// Use of this source code is governed by a BSD 3-Clause -// license that can be found in the LICENSE file. - -use env -use obj::{IR} - -use conv for std::conv -use std::jule::{VERSION} -use std::jule::build::{ - Directive, - Derive, - INIT_FN, - PATH_API, - is_std_header_path, - is_valid_cpp_ext, - is_valid_header_ext, -} -use std::jule::lex::{ - TokenId, - TokenKind, - is_ignore_ident, - is_anon_ident, -} -use std::jule::sema::{ - Package, - ImportInfo, - SymbolTable, - Var, - StructIns, - Struct, - FieldIns, - Fn, - FnIns, - Trait, - Param, - ParamIns, - BUILTIN_TRAIT_DISPOSE, -} -use std::time::{Time} -use strings for std::strings - -// The self keyword equavalent of generated cpp. -const CPP_SELF = "this" - -// C++ statement terminator. -const CPP_ST_TERM = ";" - -const INDENT_KIND = '\t' - -// Current indention count. -static mut INDENT: []byte = nil - -// Increase indentation. -fn add_indent() { - INDENT = append(INDENT, INDENT_KIND) -} - -// Decrase indentation. -fn done_indent() { - INDENT = INDENT[:INDENT.len - 1] -} - -// Returns indention string by INDENT. -fn indent(): str { - ret str(INDENT) -} - -fn is_cpp_header_file(path: str): bool { - let offset = strings::find_last_byte(path, '.') - if offset == -1 { - ret false - } - ret is_valid_header_ext(path[offset:]) -} - -// Generates all C/C++ include directives. -fn gen_links(mut &ir: &IR): str { - let mut obj = "" - for (_, mut pkg) in ir.used { - match { - | !pkg.cpp_linked: - continue - - | is_std_header_path(pkg.path): - obj += "#include " - obj += pkg.path - obj += "\n" - } - } - - for (_, mut pkg) in ir.used { - match { - | !pkg.cpp_linked: - continue - - | is_cpp_header_file(pkg.path): - obj += `#include "` - obj += pkg.path - obj += "\"\n" - } - } - ret obj -} - -// Generates C++ code of function's result type. -fn gen_fn_result(mut &f: &Fn): str { - if f.is_void() { - if f.exceptional { - ret "jule::VoidExceptional" - } - ret "void" - } - if f.exceptional { - ret "jule::Exceptional<" + gen_type_kind(f.result.kind.kind) + ">" - } - ret gen_type_kind(f.result.kind.kind) -} - -// Generates C++ code of function instance's result type. -fn gen_fn_ins_result(mut &f: &FnIns): str { - if f.decl.is_void() { - if f.decl.exceptional { - ret "jule::VoidExceptional" - } - ret "void" - } - if f.decl.exceptional { - ret "jule::Exceptional<" + gen_type_kind(f.result) + ">" - } - ret gen_type_kind(f.result) -} - -// Generates C++ prototype code of parameter. -fn gen_param_prototype(mut &p: &Param): str { - let mut obj = "" - if p.variadic { - obj += as_jt("slice") - obj += "<" - obj += gen_type_kind(p.kind.kind) - obj += ">" - } else { - obj += gen_type_kind(p.kind.kind) - } - ret obj -} - -// Generates C++ code of parameter instance. -fn gen_param_ins(mut &p: &ParamIns): str { - let mut obj = gen_param_ins_prototype(p) - obj += " " - obj += param_out_ident(p.decl) - ret obj -} - -// Generates C++ prototype code of parameter instance. -fn gen_param_ins_prototype(mut &p: &ParamIns): str { - let mut obj = "" - if p.decl.variadic { - obj += as_jt("slice") - obj += "<" - obj += gen_type_kind(p.kind) - obj += ">" - } else { - obj += gen_type_kind(p.kind) - } - - if p.decl.reference { - obj += "&" - } - - ret obj -} - -// Generates C++ code of parameter. -fn gen_param(mut &p: &Param): str { - let mut obj = gen_param_prototype(p) - if p.ident != "" && !is_ignore_ident(p.ident) && !is_anon_ident(p.ident) { - obj += " " - obj += param_out_ident(p) - } - ret obj -} - -// Generates C++ code of parameters. -fn gen_params(mut ¶ms: []&Param): str { - match { - | params.len == 0: - ret "(void)" - - | params.len == 1 && params[0].is_self(): - ret "(void)" - } - - let mut obj = "(" - for (_, mut p) in params { - if !p.is_self() { - obj += gen_param(p) + "," - } - } - - // Remove comma. - obj = obj[:obj.len-1] - ret obj + ")" -} - -fn gen_params_ins(mut ¶ms: []&ParamIns): str { - match { - | params.len == 0: - ret "(void)" - - | params.len == 1 && params[0].decl.is_self(): - ret "(void)" - } - - let mut obj = "(" - for (_, mut p) in params { - if !p.decl.is_self() { - obj += gen_param_ins(p) + "," - } - } - - // Remove comma. - obj = obj[:obj.len-1] - ret obj + ")" -} - -// Generates C++ declaration code of parameters. -fn gen_params_prototypes(mut ¶ms: []&ParamIns): str { - match { - | params.len == 0: - ret "(void)" - - | params.len == 1 && params[0].decl.is_self(): - ret "(void)" - } - - let mut obj = "(" - for (_, mut p) in params { - if !p.decl.is_self() { - obj += gen_param_ins_prototype(p) - obj += "," - } - } - - // Remove comma. - obj = obj[:obj.len-1] - ret obj + ")" -} - -// Generates C++ code of trait. -fn gen_trait(mut &t: &Trait): str { - const INDENTION = "\t" - let outid = trait_out_ident(t) - - let mut obj = "struct " - obj += outid - obj += " {\n" - obj += INDENTION - obj += "virtual ~" - obj += outid - obj += "(void) {}\n\n" - for (_, mut f) in t.methods { - obj += INDENTION - obj += "virtual " - obj += gen_fn_result(f) - obj += " _method_" - obj += f.ident - obj += gen_params(f.params) - obj += " {" - if !f.is_void() { - obj += " return {}; " - } - obj += "}\n" - } - obj += "};" - ret obj -} - -// Generates C++ code of SymbolTable's all traits. -fn gen_traits_tbl(mut &tbl: &SymbolTable): str { - let mut obj = "" - for (_, mut t) in tbl.traits { - obj += gen_trait(t) - obj += "\n\n" - } - ret obj -} - -// Generates C++ code of package's all traits. -fn gen_traits_pkg(mut &pkg: &Package): str { - let mut obj = "" - for (_, mut tbl) in pkg.files { - obj += gen_traits_tbl(tbl) - } - ret obj -} - -// Generates C++ code of all traits. -fn gen_traits(mut &ir: &IR): str { - let mut obj = "" - for (_, mut u) in ir.used { - if !u.cpp_linked { - obj += gen_traits_pkg(u.package) - } - } - obj += gen_traits_pkg(ir.main) - ret obj -} - -// Generates C++ declaration code of trait. -fn gen_trait_prototype(&t: &Trait): str { - let mut obj = "struct " - obj += trait_out_ident(t) - obj += CPP_ST_TERM - ret obj -} - -// Generates C++ declaration code of all traits. -fn gen_trait_prototypes(mut &p: &Package): str { - let mut obj = "" - for (_, mut f) in p.files { - for _, t in f.traits { - if t.token.id != TokenId.Na { - obj += gen_trait_prototype(t) - obj += "\n" - } - } - } - ret obj -} - -// Generates C++ plain-prototype code of structure. -fn gen_struct_plain_prototype(mut &s: &Struct): str { - let mut obj = "" - for (_, mut ins) in s.instances { - obj += "\nstruct " - obj += struct_ins_out_ident(ins) - obj += CPP_ST_TERM - obj += "\n" - } - ret obj -} - -// Generates C++ plain-prototype code of all structures. -fn gen_struct_plain_prototypes(mut &structs: []&Struct): str { - let mut obj = "" - for (_, mut s) in structs { - if !s.cpp_linked && s.token.id != TokenId.Na { - obj += gen_struct_plain_prototype(s) - obj += "\n" - } - } - ret obj -} - -// Generates C++ derive code of structure's implemented traits. -fn gen_struct_traits(&s: &Struct): str { - if s.implements.len == 0 { - ret "" - } - - let mut obj = ": " - for _, i in s.implements { - obj += "public " - obj += trait_out_ident(i) - obj += "," - } - obj = obj[:obj.len-1] // Remove last comma. - ret obj -} - -// Generates C++ declaration code of field. -fn gen_field_decl(mut &f: &FieldIns): str { - let mut obj = gen_type_kind(f.kind) - obj += " " - obj += field_out_ident(f.decl) - obj += " = " - if f.default == nil { - // No default expression. - // Use default expression of data-type. - obj += get_init_expr(f.kind) - } else { - obj += gen_expr(f.default.model) - } - obj += CPP_ST_TERM - ret obj -} - -fn gen_struct_self_field_type_kind(mut &s: &StructIns): str { - ret as_sptr_kind(gen_struct_kind_ins(s)) -} - -// Generates C++ field declaration code of structure's self field. -fn gen_struct_self_field(mut &s: &StructIns): str { - let mut obj = gen_struct_self_field_type_kind(s) - obj += " self;" - ret obj -} - -fn gen_struct_self_field_init_st(mut &s: &StructIns): str { - let mut obj = "this->self = " - obj += gen_struct_self_field_type_kind(s) - obj += "::make(this, nullptr);" - ret obj -} - -fn gen_struct_constructor(mut &s: &StructIns): str { - let mut obj = struct_ins_out_ident(s) - - obj += "(" - if s.fields.len > 0 { - for (_, mut f) in s.fields { - obj += gen_type_kind(f.kind) - obj += " __param_" - obj += f.decl.ident - obj += ", " - } - obj = obj[:obj.len-2] // Remove last comma. - } else { - obj += "void" - } - - obj += ")" - if s.fields.len > 0 { - obj += ": " - for _, f in s.fields { - obj += field_out_ident(f.decl) - obj += "(" - obj += "__param_" - obj += f.decl.ident - obj += "), " - } - obj = obj[:obj.len-2] // Remove trailing comma. - } - - obj += " {" - if s.decl.has_ref_accessible() { - obj += "\n" - add_indent() - obj += indent() - obj += gen_struct_self_field_init_st(s) - obj += "\n" - done_indent() - obj += "\n" - obj += indent() - } - obj += "}" - ret obj -} - -fn gen_struct_destructor(mut &s: &StructIns): str { - const STATIC = false // Dispose method must be non-static - let mut disposed = fn_is_dispose(s.decl.find_method("dispose", STATIC)) - let ref_access = s.decl.has_ref_accessible() - // Call destructor if implemented. - if !ref_access && !disposed { - ret "" - } - - let mut obj = "~" - obj += struct_ins_out_ident(s) - obj += "(void) { " - - if disposed { - let dispose_method = s.find_method("dispose", false) - obj += "this->" - obj += fn_out_ident(dispose_method) - obj += "(); " - } - - if ref_access { - obj += "this->self.ref = nullptr; " - } - - obj += "}" - ret obj -} - -fn gen_struct_operators(mut &s: &StructIns): str { - let out_ident = struct_ins_out_ident(s) - let mut obj = "" - - obj += indent() - if env::OPT_INLINE { - obj += "inline " - } - obj += "bool operator==(const " - obj += out_ident - obj += " &_Src) const {" - if s.fields.len > 0 { - add_indent() - obj += "\n" - obj += indent() - obj += "return " - add_indent() - let mut n = 0 - for (_, mut f) in s.fields { - // Skip C++-linked struct kinds. - let strct = f.kind.strct() - if strct != nil && strct.decl != nil && strct.decl.cpp_linked { - continue - } - - n++ - obj += "\n" - obj += indent() - obj += "this->" - let f_ident = field_out_ident(f.decl) - obj += f_ident - obj += " == _Src." - obj += f_ident - obj += " &&" - } - done_indent() - if n > 0 { - obj = obj[:obj.len-3] // Remove last suffix " &&" - } else { - obj += "true" - } - obj += ";\n" - done_indent() - obj += indent() - obj += "}" - } else { - obj += " return true; }" - } - obj += "\n\n" - obj += indent() - if env::OPT_INLINE { - obj += "inline " - } - obj += "bool operator!=(const " - obj += out_ident - obj += " &_Src) const { return !this->operator==(_Src); }" - ret obj -} - -fn gen_struct_derive_defs_prototypes(&s: &StructIns): str { - let mut obj = "" - - if s.decl.is_derives(Derive.Clone) { - obj += indent() - obj += get_derive_fn_decl_clone(s.decl) - obj += ";\n\n" - } - - ret obj -} - -fn gen_struct_ins_prototype(mut &s: &StructIns): str { - let mut obj = "struct " - let out_ident = struct_ins_out_ident(s) - - obj += out_ident - obj += gen_struct_traits(s.decl) - obj += " {\n" - - let ref_access = s.decl.has_ref_accessible() - - add_indent() - if ref_access { - obj += indent() - obj += gen_struct_self_field(s) - obj += "\n\n" - } - if s.fields.len > 0 { - for (_, mut f) in s.fields { - obj += indent() - obj += gen_field_decl(f) - obj += "\n" - } - obj += "\n\n" - obj += indent() - obj += gen_struct_constructor(s) - obj += "\n\n" - } - - obj += indent() - obj += gen_struct_destructor(s) - obj += "\n\n" - - // Default constructor. - obj += indent() - obj += out_ident - if ref_access { - obj += "(void) { " - obj += gen_struct_self_field_init_st(s) - obj += " }\n\n" - } else { - obj += "(void) = default;\n\n" - } - - for (_, mut f) in s.methods { - obj += gen_fn_prototype(f, true) - obj += "\n\n" - } - - obj += gen_struct_derive_defs_prototypes(s) - - obj += gen_struct_operators(s) - obj += "\n" - - done_indent() - obj += indent() + "};" - ret obj -} - -// Generates C++ declaration code of structure. -fn gen_struct_prototype(mut &s: &Struct): str { - let mut obj = "" - for (_, mut ins) in s.instances { - obj += gen_struct_ins_prototype(ins) - obj += "\n\n" - } - ret obj -} - -// Generates C++ declaration code of all structures. -fn gen_struct_prototypes(mut &structs: []&Struct): str { - let mut obj = "" - for (_, mut s) in structs { - if !s.cpp_linked && s.token.id != TokenId.Na { - obj += gen_struct_prototype(s) - obj += "\n" - } - } - ret obj -} - -fn gen_fn_decl_head(mut &f: &FnIns, method: bool): str { - let mut obj = "" - - if method && f.decl.statically { - obj += "static " - } - - if env::OPT_INLINE && !f.decl.is_entry_point() { - obj += "inline " - } - - obj += gen_fn_ins_result(f) + " " - - if !method && f.decl.owner != nil { - obj += struct_ins_out_ident(f.owner) - obj += "::" - } - obj += fn_ins_out_ident(f) - ret obj -} - -// Generates C++ declaration code of function's combinations. -fn gen_fn_prototype(mut &f: &Fn, method: bool): str { - let mut obj = "" - for (_, mut c) in f.instances { - obj += indent() - obj += gen_fn_decl_head(c, method) - obj += gen_params_prototypes(c.params) - obj += CPP_ST_TERM + "\n" - } - ret obj -} - -// Generates C++ declaration code of all functions. -fn gen_fn_prototypes(mut &pkg: &Package): str { - let mut obj = "" - for (_, mut file) in pkg.files { - for (_, mut f) in file.funcs { - if !f.cpp_linked && f.token.id != TokenId.Na { - obj += gen_fn_prototype(f, false) - } - } - } - ret obj -} - -// Generates C++ code of all can-be-prototyped declarations. -fn gen_prototypes(mut &ir: &IR): str { - let mut obj = "" - - for (_, mut u) in ir.used { - if !u.cpp_linked { - obj += gen_trait_prototypes(u.package) - } - } - obj += gen_trait_prototypes(ir.main) - - - obj += gen_struct_plain_prototypes(ir.ordered.structs) - - obj += gen_traits(ir) - obj += "\n" - - obj += gen_struct_prototypes(ir.ordered.structs) - - for (_, mut u) in ir.used { - if !u.cpp_linked { - obj += gen_fn_prototypes(u.package) - } - } - obj += gen_fn_prototypes(ir.main) - - ret obj -} - -// Generats C++ code of variable with initialize expression. -fn gen_var_init_expr(mut &v: &Var, init: str): str { - let mut obj = "" - if v.statically { - obj += "static " - } - - obj += gen_type_kind(v.kind.kind) - obj += " " - if v.reference { - obj += "&" - } - obj += var_out_ident(v) - if init != "" { - obj += " = " - obj += init - } - obj += CPP_ST_TERM - ret obj -} - -// Generates C++ code of variable. -fn gen_var(mut v: &Var): str { - if is_ignore_ident(v.ident) { - ret "" - } - - if v.value != nil && v.value.expr != nil { - if v.value.data.model != nil { - ret gen_var_init_expr(v, gen_val(v.value)) - } - ret gen_var_init_expr(v, "") - } - ret gen_var_init_expr(v, get_init_expr(v.kind.kind)) -} - -fn gen_pkg_globals(mut &p: &Package, mut &global_initializers: str): str { - let mut obj = "" - for (_, mut f) in p.files { - for (_, mut v) in f.vars { - if v.token.id != TokenId.Na && !v.cpp_linked && !v.constant { - obj += gen_type_kind(v.kind.kind) - obj += " " - if v.reference { - obj += "&" - } - obj += var_out_ident(v) - obj += ";\n" - - global_initializers += indent() // Indentation for initializer scope. - global_initializers += var_out_ident(v) - if v.value != nil && v.value.expr != nil { - if v.value.data.model != nil { - global_initializers += " = " - global_initializers += gen_val(v.value) - } - } else { - global_initializers += " = " - global_initializers += get_init_expr(v.kind.kind) - } - global_initializers += ";\n" - } - } - } - ret obj -} - -// Generates C++ code of all globals. -fn gen_globals(mut ir: &IR, mut &global_initializers: str): str { - let mut obj = "" - - add_indent() // For global initializers's function indentation. - - for (_, mut u) in ir.used { - if !u.cpp_linked { - obj += gen_pkg_globals(u.package, global_initializers) - } - } - obj += gen_pkg_globals(ir.main, global_initializers) - - done_indent() - - ret obj -} - -// Generates C++ code of function. -fn gen_fn(mut &f: &Fn): str { - let mut obj = "" - for (_, mut ins) in f.instances { - obj += gen_fn_decl_head(ins, false) - obj += gen_params_ins(ins.params) - obj += " " - obj += gen_fn_scope(ins) - obj += "\n\n" - } - ret obj -} - -// Generates C++ code of all functions of package. -fn gen_pkg_fns(mut &p: &Package): str { - let mut obj = "" - for (_, mut f) in p.files { - for (_, mut f) in f.funcs { - if !env::TEST && has_directive(f.directives, Directive.Test) { - continue - } - if !f.cpp_linked && f.token.id != TokenId.Na { - obj += gen_fn(f) - obj += "\n\n" - } - } - } - ret obj -} - -// Generates C++ code of structure's methods. -fn gen_struct_method_defs(mut &s: &StructIns): str { - let mut obj = "" - for (_, mut f) in s.methods { - obj += indent() - obj += gen_fn(f) - obj += "\n\n" - } - ret obj -} - -// Reports method is reversed to_str method. -fn fn_is_to_str(mut &f: &Fn): bool { - if f == nil || - !f.public || - f.is_void() || - f.generics.len != 0 || - f.params.len != 1 || - f.params[0].mutable || - f.params[0].is_ref() { - ret false - } - - let mut ins = f.instances[0] - let prim = ins.result.prim() - if prim == nil { - ret false - } - ret prim.is_str() -} - -// Reports method is reversed dispose method. -fn fn_is_dispose(mut f: &Fn): bool { - ret f != nil && - f.public && - f.is_void() && - f.generics.len == 0 && - f.params.len == 1 && - f.params[0].mutable && - !f.params[0].is_ref() -} - -// Generates C++ code of structure's ostream. -fn gen_struct_ostream(mut &s: &StructIns): str { - let mut obj = "" - obj += indent() - obj += "std::ostream &operator<<(std::ostream &_Stream, const " - obj += struct_ins_out_ident(s) - obj += " &_Src) {\n" - add_indent() - obj += indent() - - let mut fts = s.find_method("to_str", false) - if fn_is_to_str(fts) { - obj += "_Stream << ((" - obj += struct_ins_out_ident(s) - obj += ")(_Src))." - obj += fn_out_ident(fts) - obj += "();\n" - } else { - obj += `_Stream << "` - obj += s.decl.ident - obj += "{\";\n" - - for (i, mut f) in s.fields { - obj += indent() - obj += `_Stream << "` - obj += f.decl.ident - obj += `:` - - // Skip C++-linked struct kinds. - let strct = f.kind.strct() - if strct != nil && strct.decl != nil && strct.decl.cpp_linked { - obj += ` cpp.` - obj += field_out_ident(f.decl) - obj += `"` - } else { - obj += `" << _Src.` - obj += field_out_ident(f.decl) - } - if i+1 < s.fields.len { - obj += " << \", \"" - } - obj += ";\n" - } - - obj += indent() - obj += "_Stream << \"}\";\n" - } - - obj += indent() - obj += "return _Stream;\n" - - done_indent() - obj += indent() - obj += "}" - ret obj -} - -fn gen_struct_derive_defs(mut &s: &StructIns): str { - let mut obj = "" - - if s.decl.is_derives(Derive.Clone) { - obj += indent() - obj += get_derive_fn_def_clone(s.decl) - obj += "{\n" - add_indent() - obj += indent() - obj += gen_struct_kind_ins(s) - obj += " clone;\n" - for _, f in s.fields { - let ident = field_out_ident(f.decl) - - obj += indent() - obj += "clone." - obj += ident - obj += " = jule::clone(this->" - obj += ident - obj += ");\n" - } - obj += indent() - obj += "return clone;\n" - done_indent() - obj += indent() - obj += "}" - } - - ret obj -} - -// Generates C++ code of structure instance definition. -fn gen_struct_ins(mut &s: &StructIns): str { - let mut obj = gen_struct_method_defs(s) - obj += "\n\n" - obj += gen_struct_derive_defs(s) - obj += "\n\n" - obj += gen_struct_ostream(s) - ret obj -} - -// Generates C++ code of structure definition. -fn gen_struct(mut &s: &Struct): str { - let mut obj = "" - for (_, mut ins) in s.instances { - obj += gen_struct_ins(ins) - obj += "\n\n" - } - ret obj -} - -// Generates C++ code of all structures. -fn gen_structs(mut &structs: []&Struct): str { - let mut obj = "" - for (_, mut s) in structs { - if !s.cpp_linked && s.token.id != TokenId.Na { - obj += gen_struct(s) - obj += "\n\n" - } - } - ret obj -} - -// Generates C++ code of all functions. -fn gen_fns(mut &ir: &IR): str { - let mut obj = "" - - for (_, mut u) in ir.used { - if !u.cpp_linked { - obj += gen_pkg_fns(u.package) - } - } - obj += gen_pkg_fns(ir.main) - - ret obj -} - -fn push_init(mut &pkg: &Package, mut &obj: str) { - const INDENTION = "\t" - - const CPP_LINKED = false - let f = pkg.find_fn(INIT_FN, CPP_LINKED) - if f == nil { - ret - } - - obj += "\n" + INDENTION - obj += fn_out_ident(f) - obj += "();" -} - -// Generated C++ code of all initializer functions. -fn gen_init_caller(mut &ir: &IR, &global_initializers: str): str { - let mut obj = "void " - obj += INIT_CALLER_IDENT - obj += "(void) {\n" - obj += global_initializers - - for (_, mut u) in ir.used { - if !u.cpp_linked { - push_init(u.package, obj) - } - } - push_init(ir.main, obj) - - obj += "\n}" - ret obj -} - -pub fn find_testing_package(mut &ir: &IR): &ImportInfo { - for (_, mut imp) in ir.used { - if imp.link_path == "std::testing" { - ret imp - } - } - ret nil -} - -pub fn append_test(mut &obj: str, mut f: &FnIns) { - obj += indent() - obj += "_t->_method_reset();\n" - obj += indent() - obj += "std::cout << \">>> TEST RUNNING: \";\n" - obj += indent() - obj += "jule::outln(" - obj += get_cstr_model([]byte(f.decl.ident)) - obj += ");\n" - obj += indent() - obj += fn_ins_out_ident(f) - obj += "(_t);\n" - obj += indent() - obj += "post_test();\n" -} - -pub fn append_package_tests(mut &obj: str, mut &p: &Package) { - for (_, mut file) in p.files { - for (_, mut f) in file.funcs { - if has_directive(f.directives, Directive.Test) { - append_test(obj, f.instances[0]) - } - } - } -} - -pub fn append_test_point(mut &obj: str, mut &ir: &IR) { - obj += "\nvoid test_point(void) {\n" - add_indent() - obj += indent() - - let mut p = find_testing_package(ir) - if p == nil { - // std::testing is not used. - // So, developers cannot write valid test function. - // Append empty test point and return. - obj += "}" - done_indent() - ret - } - - let mut t = p.find_struct("T", false).instances[0] - - obj += as_sptr_kind(gen_struct_kind_ins(t)) - obj += " _t = jule::new_struct<" - obj += gen_struct_kind_ins(t) - obj += ">(" - if !env::PRODUCTION { - obj += `"/jule/init", ` - } - obj += "new(std::nothrow) " - obj += gen_struct_kind_ins(t) - obj += ");\n" - - obj += indent() - obj += "jule::Uint total = 0, failed = 0, skipped = 0;\n" - obj += indent() - - obj += "auto post_test = [&](void) {\n" - add_indent() - obj += indent() - obj += "++total;\n" - obj += indent() - obj += "if (_t->_method_failed()) { ++failed; std::cout << \" [*] FAILED\" << std::endl; }\n" - obj += indent() - obj += "else if (_t->_method_skipped()) { ++skipped; std::cout << \" [*] SKIPPED\" << std::endl; }\n" - obj += indent() - obj += "else { std::cout << \" [*] PASSED\" << std::endl; }\n" - done_indent() - obj += indent() - obj += "};\n" - - append_package_tests(obj, ir.main) - - obj += "\n\n" - obj += indent() - obj += "std::cout << std::endl << std::endl << \"total tests: \" << total << \" skipped: \" << skipped << \" failed: \" << failed << \" pass: \" << total-failed-skipped << std::endl;\n" - - done_indent() - obj += indent() - obj += "}\n" -} - -pub fn append_standard(mut &obj_code: str, compiler: str, compiler_cmd: str) { - let time = Time.now().abs() - - let mut time_str = "" - time_str += conv::fmt_uint(time.day, 10) - time_str += "/" - time_str += conv::fmt_uint(time.month, 10) - time_str += "/" - time_str += conv::fmt_uint(time.year, 10) - time_str += " " - time_str += conv::fmt_uint(time.hour, 10) - time_str += "." - time_str += conv::fmt_uint(time.minute, 10) - time_str += " (DD/MM/YYYY) (HH.MM) UTC" - - let mut s = "" - s += "// Auto generated by JuleC.\n" - s += "// JuleC version: " - s += VERSION - s += "\n" - s += "// Date: " - s += time_str - s += ` -// -// Recommended Compile Command; -// ` - s += compiler - s += " " - s += compiler_cmd - s += "\n\n" - - if env::PRODUCTION { - s += "#define __JULE_ENABLE__PRODUCTION\n" - } - if !env::RC { - s += "#define __JULE_DISABLE__REFERENCE_COUNTING\n" - } - if !env::SAFETY { - s += "#define __JULE_DISABLE__SAFETY\n" - } - - s += "\n\n#include \"" - s += PATH_API - s += "\"\n\n" - s += obj_code - s += ` -int main(int argc, char *argv[], char *envp[]) { - jule::setup_argv(argc, argv); - jule::setup_envp(envp); - - __jule_call_initializers(); - ` - if env::TEST { - s += "test_point();" - } else { - s += "entry_point();" - } - - s += ` - - return EXIT_SUCCESS; -}` - obj_code = s -} - -// Generates C++ codes from SymbolTables. -pub fn gen(mut &ir: &IR): str { - let mut global_initializers = "" - let mut obj = "" - obj += gen_links(ir) - obj += "\n" - obj += gen_prototypes(ir) - obj += "\n\n" - obj += gen_globals(ir, global_initializers) - obj += "\n" - obj += gen_structs(ir.ordered.structs) - obj += gen_fns(ir) - obj += "\n" - obj += gen_init_caller(ir, global_initializers) - obj += "\n" - ret obj -} diff --git a/src/julec/obj/cxx/ident.jule b/src/julec/obj/cxx/ident.jule index e636309d7..afd3f23d2 100644 --- a/src/julec/obj/cxx/ident.jule +++ b/src/julec/obj/cxx/ident.jule @@ -14,202 +14,195 @@ use std::jule::sema::{ Field, Var, Param, - TypeKind, } // Identifier of initialize function caller function. const INIT_CALLER_IDENT = "__jule_call_initializers" -// Returns specified identifer as JuleC identifer. -// Equavalents: "JULEC_ID(" + ident + ")" of JuleC API. -fn as_ident(ident: str): str { ret "_" + ident } +struct IdentCoder {} -// Returns given identifier as Jule type identifier. -fn as_jt(mut id: str): str { - if 97 <= id[0] && id[0] <= 122 { - id[0] -= 32 // To upper. +impl IdentCoder { + // Returns specified identifer as JuleC identifer. + // Equavalents: "JULEC_ID(" + ident + ")" of JuleC API. + static fn to_ident(ident: str): str { + ret "_" + ident } - ret "jule::" + id -} - -// Returns cpp output identifier form of pointer address. -fn get_addr_as_ident(addr: uintptr): str { - ret "_" + conv::fmt_uint(u64(addr), 0xF) -} -// Returns cpp output identifier form of given identifier. -// -// Parameters: -// - ident: Identifier. -// - addr: Pointer address of package file handler. -fn as_out_ident(ident: str, addr: uintptr): str { - if addr != 0 { - let mut obj = get_addr_as_ident(addr) - obj += "_" - obj += ident - ret obj - } - ret as_ident(ident) -} - -// Returns cpp output local identifier form of fiven identifier. -// -// Parameters: -// - row: Row of definition. -// - col: Column of definition. -// - ident: Identifier of definition. -fn as_local_ident(row: int, col: int, ident: str): str { - let mut obj = conv::itoa(row) - obj += conv::itoa(col) - obj += "_" - obj += ident - ret as_ident(obj) -} - -// Returns output identifier of function. -fn fn_out_ident(&f: &Fn): str { - match { - | f.cpp_linked: - ret f.ident - - | f.ident == ENTRY_POINT: - ret "entry_point" - - | f.is_method(): - if f.statically { - ret "_static_method_" + f.ident + // Returns given identifier as Jule type identifier. + static fn to_type(mut id: str): str { + if 97 <= id[0] && id[0] <= 122 { + id[0] -= 32 // To upper first byte. } - ret "_method_" + f.ident - - |: - ret as_out_ident(f.ident, f.token.file.addr()) - } -} - -// Returns output identifier of function instance. -fn fn_ins_out_ident(mut &f: &FnIns): str { - if f.is_builtin() { - ret "jule::" + f.decl.ident + ret "jule::" + id } - if f.decl.cpp_linked || f.generics.len == 0 { - ret fn_out_ident(f.decl) + // Returns cpp output identifier form of pointer address. + static fn from_addr(addr: uintptr): str { + ret "_" + conv::fmt_uint(u64(addr), 0xF) } - for (i, mut ins) in f.decl.instances { - if ins.same(f) { - let mut obj = fn_out_ident(f.decl) + // Returns cpp output identifier form of given identifier. + // + // Parameters: + // - ident: Identifier. + // - addr: Pointer address of package file handler. + static fn to_out(ident: str, addr: uintptr): str { + if addr != 0 { + let mut obj = IdentCoder.from_addr(addr) obj += "_" - obj += conv::itoa(i) - ret obj + obj += ident + ret obj } + ret IdentCoder.to_ident(ident) } - ret "__?__" -} - -// Returns output identifier of trait. -fn trait_out_ident(t: &Trait): str { - if t.is_builtin() { - ret "jule::" + t.ident + // Returns cpp output local identifier form of fiven identifier. + // + // Parameters: + // - row: Row of definition. + // - col: Column of definition. + // - ident: Identifier of definition. + static fn to_local(row: int, col: int, ident: str): str { + let mut obj = conv::itoa(row) + obj += conv::itoa(col) + obj += "_" + obj += ident + ret IdentCoder.to_ident(obj) } - ret as_out_ident(t.ident, t.token.file.addr()) -} -// Returns output identifier of parameter. -fn param_out_ident(&p: &Param): str { - if is_anon_ident(p.ident) || is_ignore_ident(p.ident) { - ret "" + // Returns output identifier of function. + static fn from_fn(&f: &Fn): str { + match { + | f.cpp_linked: + ret f.ident + | f.ident == ENTRY_POINT: + ret "entry_point" + | f.is_method(): + if f.statically { + ret "_static_method_" + f.ident + } + ret "_method_" + f.ident + |: + ret IdentCoder.to_out(f.ident, f.token.file.addr()) + } } - ret as_local_ident(p.token.row, p.token.column, p.ident) -} -// Returns output identifier of structure. -fn struct_out_ident(&s: &Struct): str { - if s.cpp_linked { - if has_directive(s.directives, Directive.Typedef) { - ret s.ident + // Returns output identifier of function instance. + static fn from_fn_ins(mut &f: &FnIns): str { + if f.is_builtin() { + ret "jule::" + f.decl.ident } - ret "struct " + s.ident + if f.decl.cpp_linked || f.generics.len == 0 { + ret IdentCoder.from_fn(f.decl) + } + for (i, mut ins) in f.decl.instances { + if ins.same(f) { + let mut obj = IdentCoder.from_fn(f.decl) + obj += "_" + obj += conv::itoa(i) + ret obj + } + } + ret "__?__" } - ret as_out_ident(s.ident, s.token.file.addr()) -} -// Returns output identifier of structure instance. -fn struct_ins_out_ident(mut &s: &StructIns): str { - if s.decl.cpp_linked || s.generics.len == 0 { - ret struct_out_ident(s.decl) + // Returns output identifier of trait. + static fn from_trait(t: &Trait): str { + if t.is_builtin() { + ret "jule::" + t.ident + } + ret IdentCoder.to_out(t.ident, t.token.file.addr()) } - for (i, mut ins) in s.decl.instances { - if ins.same(s) { - let mut obj = struct_out_ident(s.decl) - obj += "_" - obj += conv::itoa(i) - ret obj + // Returns output identifier of parameter. + static fn from_param(&p: &Param): str { + if is_anon_ident(p.ident) || is_ignore_ident(p.ident) { + ret "" } + ret IdentCoder.to_local(p.token.row, p.token.column, p.ident) } - ret "__?__" -} - -// Returns output identifier of field. -fn field_out_ident(&f: &Field): str { - if f.owner.cpp_linked { - ret f.ident + // Returns output identifier of structure. + static fn from_struct(&s: &Struct): str { + if s.cpp_linked { + if has_directive(s.directives, Directive.Typedef) { + ret s.ident + } + ret "struct " + s.ident + } + ret IdentCoder.to_out(s.ident, s.token.file.addr()) } - ret "_field_" + f.ident -} - -// Returns output identifier of variable. -fn var_out_ident(mut v: &Var): str { - match { - | v.cpp_linked: - ret v.ident - | v.ident == TokenKind.Error: - ret "except.error" - - | v.ident == TokenKind.Self: - if v.kind.kind.sptr() != nil { - ret "this->self" + // Returns output identifier of structure instance. + static fn from_struct_ins(mut &s: &StructIns): str { + if s.decl.cpp_linked || s.generics.len == 0 { + ret IdentCoder.from_struct(s.decl) + } + for (i, mut ins) in s.decl.instances { + if ins.same(s) { + let mut obj = IdentCoder.from_struct(s.decl) + obj += "_" + obj += conv::itoa(i) + ret obj + } } - ret "(*this)" + ret "__?__" + } - | v.scope != nil: - ret as_local_ident(v.token.row, v.token.column, v.ident) + // Returns output identifier of field. + static fn from_field(&f: &Field): str { + if f.owner.cpp_linked { + ret f.ident + } + ret "_field_" + f.ident + } - |: - ret as_out_ident(v.ident, v.token.file.addr()) + // Returns output identifier of variable. + static fn from_var(mut v: &Var): str { + match { + | v.cpp_linked: + ret v.ident + | v.ident == TokenKind.Error: + ret "except.error" + | v.ident == TokenKind.Self: + if v.kind.kind.sptr() != nil { + ret "this->self" + } + ret "(*this)" + | v.scope != nil: + ret IdentCoder.to_local(v.token.row, v.token.column, v.ident) + |: + ret IdentCoder.to_out(v.ident, v.token.file.addr()) + } } -} -// Returns begin label identifier of iteration. -fn iter_begin_label_ident(it: uintptr): str { - ret "_iter_begin_" + conv::itoa(int(it)) -} + // Returns begin label identifier of iteration. + static fn to_iter_begin(it: uintptr): str { + ret "_iter_begin_" + conv::itoa(int(it)) + } -// Returns end label identifier of iteration. -fn iter_end_label_ident(it: uintptr): str { - ret "_iter_end_" + conv::itoa(int(it)) -} + // Returns end label identifier of iteration. + static fn to_iter_end(it: uintptr): str { + ret "_iter_end_" + conv::itoa(int(it)) + } -// Returns next label identifier of iteration. -fn iter_next_label_ident(it: uintptr): str { - ret "_iter_next_" + conv::itoa(int(it)) -} + // Returns next label identifier of iteration. + static fn to_iter_next(it: uintptr): str { + ret "_iter_next_" + conv::itoa(int(it)) + } -// Returns label identifier. -fn label_ident(ident: str): str { - ret "_julec_label_" + ident -} + // Returns label identifier. + static fn to_label(ident: str): str { + ret "_julec_label_" + ident + } -// Returns end label identifier of match-case. -fn match_end_label_ident(m: uintptr): str { - ret "_match_end_" + conv::itoa(int(m)) -} + // Returns end label identifier of match-case. + static fn to_match_end(m: uintptr): str { + ret "_match_end_" + conv::itoa(int(m)) + } -// Returns begin label identifier of case. -fn case_begin_label_ident(c: uintptr): str { - ret "_case_begin_" + conv::itoa(int(c)) + // Returns begin label identifier of case. + static fn to_case_begin(c: uintptr): str { + ret "_case_begin_" + conv::itoa(int(c)) + } } diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule new file mode 100644 index 000000000..06208b19b --- /dev/null +++ b/src/julec/obj/cxx/object.jule @@ -0,0 +1,1202 @@ +// Copyright 2023-2024 The Jule Programming Language. +// Use of this source code is governed by a BSD 3-Clause +// license that can be found in the LICENSE file. +/* +use env +use obj::{IR} + +use conv for std::conv +use std::jule::{VERSION} +use std::jule::build::{ + Directive, + Derive, + INIT_FN, + PATH_API, + is_std_header_path, + is_valid_cpp_ext, + is_valid_header_ext, +} +use std::jule::lex::{ + TokenId, + TokenKind, + is_ignore_ident, + is_anon_ident, +} +use std::jule::sema::{ + Package, + ImportInfo, + SymbolTable, + Var, + StructIns, + Struct, + FieldIns, + Fn, + FnIns, + Trait, + Param, + ParamIns, + BUILTIN_TRAIT_DISPOSE, +} +use std::time::{Time} +use strings for std::strings + +// The self keyword equavalent of generated cpp. +const CPP_SELF = "this" + +// C++ statement terminator. +const CPP_ST_TERM = ";" + +const INDENT_KIND = "\t" + +struct ObjectCoder { + ir: &IR + // Current indention. + indent_buffer: str +} + +impl ObjectCoder { + // Increase indentation. + fn add_indent(mut self) { + self.indent_buffer += INDENT_KIND + } + + // Decrase indentation. + fn done_indent(mut self) { + self.indent_buffer = self.indent_buffer[:self.indent_buffer.len - 1] + } + + // Returns indention string by indent_buffer. + fn indent(self): str { + ret self.indent_buffer + } + + // Generates all C/C++ include directives. + fn gen_links(mut self): str { + let mut obj = "" + for (_, mut pkg) in self.ir.used { + match { + | !pkg.cpp_linked: + continue + | is_std_header_path(pkg.path): + obj += "#include " + obj += pkg.path + obj += "\n" + } + } + + for (_, mut pkg) in self.ir.used { + match { + | !pkg.cpp_linked: + continue + | is_cpp_header_file(pkg.path): + obj += `#include "` + obj += pkg.path + obj += "\"\n" + } + } + ret obj + } + + // Generates C++ code of function's result type. + fn gen_fn_result(mut self, mut &f: &Fn): str { + if f.is_void() { + if f.exceptional { + ret "jule::VoidExceptional" + } + ret "void" + } + if f.exceptional { + ret "jule::Exceptional<" + gen_type_kind(f.result.kind.kind) + ">" + } + ret gen_type_kind(f.result.kind.kind) + } + + // Generates C++ code of function instance's result type. + fn gen_fn_ins_result(mut self, mut &f: &FnIns): str { + if f.decl.is_void() { + if f.decl.exceptional { + ret "jule::VoidExceptional" + } + ret "void" + } + if f.decl.exceptional { + ret "jule::Exceptional<" + gen_type_kind(f.result) + ">" + } + ret gen_type_kind(f.result) + } + + // Generates C++ prototype code of parameter. + fn gen_param_prototype(mut self, mut &p: &Param): str { + let mut obj = "" + if p.variadic { + obj += as_jt("slice") + obj += "<" + obj += gen_type_kind(p.kind.kind) + obj += ">" + } else { + obj += gen_type_kind(p.kind.kind) + } + ret obj + } + + // Generates C++ code of parameter instance. + fn gen_param_ins(mut self, mut &p: &ParamIns): str { + let mut obj = self.gen_param_ins_prototype(p) + obj += " " + obj += param_out_ident(p.decl) + ret obj + } + + // Generates C++ prototype code of parameter instance. + fn gen_param_ins_prototype(mut self, &p: &ParamIns): str { + let mut obj = "" + if p.decl.variadic { + obj += as_jt("slice") + obj += "<" + obj += gen_type_kind(p.kind) + obj += ">" + } else { + obj += gen_type_kind(p.kind) + } + if p.decl.reference { + obj += "&" + } + ret obj + } + + // Generates C++ code of parameter. + fn gen_param(mut self, mut &p: &Param): str { + let mut obj = gen_param_prototype(p) + if p.ident != "" && !is_ignore_ident(p.ident) && !is_anon_ident(p.ident) { + obj += " " + obj += param_out_ident(p) + } + ret obj + } + + // Generates C++ code of parameters. + fn gen_params(mut self, mut ¶ms: []&Param): str { + match { + | params.len == 0: + ret "(void)" + | params.len == 1 && params[0].is_self(): + ret "(void)" + } + let mut obj = "(" + for (_, mut p) in params { + if !p.is_self() { + obj += gen_param(p) + "," + } + } + + // Remove comma. + obj = obj[:obj.len-1] + ret obj + ")" + } + + fn gen_params_ins(mut self, mut ¶ms: []&ParamIns): str { + match { + | params.len == 0: + ret "(void)" + | params.len == 1 && params[0].decl.is_self(): + ret "(void)" + } + + let mut obj = "(" + for (_, mut p) in params { + if !p.decl.is_self() { + obj += gen_param_ins(p) + "," + } + } + + // Remove comma. + obj = obj[:obj.len-1] + ret obj + ")" + } + + // Generates C++ declaration code of parameters. + fn gen_params_prototypes(mut self, mut ¶ms: []&ParamIns): str { + match { + | params.len == 0: + ret "(void)" + | params.len == 1 && params[0].decl.is_self(): + ret "(void)" + } + + let mut obj = "(" + for (_, mut p) in params { + if !p.decl.is_self() { + obj += gen_param_ins_prototype(p) + obj += "," + } + } + + // Remove comma. + obj = obj[:obj.len-1] + ret obj + ")" + } + + // Generates C++ code of trait. + fn gen_trait(mut self, mut &t: &Trait): str { + const INDENTION = "\t" + let outid = trait_out_ident(t) + + let mut obj = "struct " + obj += outid + obj += " {\n" + obj += INDENTION + obj += "virtual ~" + obj += outid + obj += "(void) {}\n\n" + for (_, mut f) in t.methods { + obj += INDENTION + obj += "virtual " + obj += gen_fn_result(f) + obj += " _method_" + obj += f.ident + obj += gen_params(f.params) + obj += " {" + if !f.is_void() { + obj += " return {}; " + } + obj += "}\n" + } + obj += "};" + ret obj + } + + // Generates C++ code of SymbolTable's all traits. + fn gen_traits_tbl(mut self, mut &tbl: &SymbolTable): str { + let mut obj = "" + for (_, mut t) in tbl.traits { + obj += gen_trait(t) + obj += "\n\n" + } + ret obj + } + + // Generates C++ code of package's all traits. + fn gen_traits_pkg(mut self, mut &pkg: &Package): str { + let mut obj = "" + for (_, mut tbl) in pkg.files { + obj += gen_traits_tbl(tbl) + } + ret obj + } + + // Generates C++ code of all traits. + fn gen_traits(mut self, mut &ir: &IR): str { + let mut obj = "" + for (_, mut u) in ir.used { + if !u.cpp_linked { + obj += gen_traits_pkg(u.package) + } + } + obj += gen_traits_pkg(ir.main) + ret obj + } + + // Generates C++ declaration code of trait. + fn gen_trait_prototype(mut self, &t: &Trait): str { + let mut obj = "struct " + obj += trait_out_ident(t) + obj += CPP_ST_TERM + ret obj + } + + // Generates C++ declaration code of all traits. + fn gen_trait_prototypes(mut self, mut &p: &Package): str { + let mut obj = "" + for (_, mut f) in p.files { + for _, t in f.traits { + if t.token.id != TokenId.Na { + obj += gen_trait_prototype(t) + obj += "\n" + } + } + } + ret obj + } + + // Generates C++ plain-prototype code of structure. + fn gen_struct_plain_prototype(mut self, mut &s: &Struct): str { + let mut obj = "" + for (_, mut ins) in s.instances { + obj += "\nstruct " + obj += struct_ins_out_ident(ins) + obj += CPP_ST_TERM + obj += "\n" + } + ret obj + } + + // Generates C++ plain-prototype code of all structures. + fn gen_struct_plain_prototypes(mut self, mut &structs: []&Struct): str { + let mut obj = "" + for (_, mut s) in structs { + if !s.cpp_linked && s.token.id != TokenId.Na { + obj += gen_struct_plain_prototype(s) + obj += "\n" + } + } + ret obj + } + + // Generates C++ derive code of structure's implemented traits. + fn gen_struct_traits(mut self, &s: &Struct): str { + if s.implements.len == 0 { + ret "" + } + let mut obj = ": " + for _, i in s.implements { + obj += "public " + obj += trait_out_ident(i) + obj += "," + } + obj = obj[:obj.len-1] // Remove last comma. + ret obj + } + + // Generates C++ declaration code of field. + fn gen_field_decl(mut self, mut &f: &FieldIns): str { + let mut obj = gen_type_kind(f.kind) + obj += " " + obj += field_out_ident(f.decl) + obj += " = " + if f.default == nil { + // No default expression. + // Use default expression of data-type. + obj += get_init_expr(f.kind) + } else { + obj += gen_expr(f.default.model) + } + obj += CPP_ST_TERM + ret obj + } + + fn gen_struct_self_field_type_kind(mut self, mut &s: &StructIns): str { + ret as_sptr_kind(gen_struct_kind_ins(s)) + } + + // Generates C++ field declaration code of structure's self field. + fn gen_struct_self_field(mut self, mut &s: &StructIns): str { + let mut obj = gen_struct_self_field_type_kind(s) + obj += " self;" + ret obj + } + + fn gen_struct_self_field_init_st(mut self, mut &s: &StructIns): str { + let mut obj = "this->self = " + obj += gen_struct_self_field_type_kind(s) + obj += "::make(this, nullptr);" + ret obj + } + + fn gen_struct_constructor(mut self, mut &s: &StructIns): str { + let mut obj = struct_ins_out_ident(s) + + obj += "(" + if s.fields.len > 0 { + for (_, mut f) in s.fields { + obj += gen_type_kind(f.kind) + obj += " __param_" + obj += f.decl.ident + obj += ", " + } + obj = obj[:obj.len-2] // Remove last comma. + } else { + obj += "void" + } + + obj += ")" + if s.fields.len > 0 { + obj += ": " + for _, f in s.fields { + obj += field_out_ident(f.decl) + obj += "(" + obj += "__param_" + obj += f.decl.ident + obj += "), " + } + obj = obj[:obj.len-2] // Remove trailing comma. + } + + obj += " {" + if s.decl.has_ref_accessible() { + obj += "\n" + add_indent() + obj += indent() + obj += gen_struct_self_field_init_st(s) + obj += "\n" + done_indent() + obj += "\n" + obj += indent() + } + obj += "}" + ret obj + } + + fn gen_struct_destructor(mut self, mut &s: &StructIns): str { + const STATIC = false // Dispose method must be non-static + let mut disposed = fn_is_dispose(s.decl.find_method("dispose", STATIC)) + let ref_access = s.decl.has_ref_accessible() + // Call destructor if implemented. + if !ref_access && !disposed { + ret "" + } + + let mut obj = "~" + obj += struct_ins_out_ident(s) + obj += "(void) { " + + if disposed { + let dispose_method = s.find_method("dispose", false) + obj += "this->" + obj += fn_out_ident(dispose_method) + obj += "(); " + } + + if ref_access { + obj += "this->self.ref = nullptr; " + } + + obj += "}" + ret obj + } + + fn gen_struct_operators(mut self, mut &s: &StructIns): str { + let out_ident = struct_ins_out_ident(s) + let mut obj = "" + + obj += indent() + if env::OPT_INLINE { + obj += "inline " + } + obj += "bool operator==(const " + obj += out_ident + obj += " &_Src) const {" + if s.fields.len > 0 { + add_indent() + obj += "\n" + obj += indent() + obj += "return " + add_indent() + let mut n = 0 + for (_, mut f) in s.fields { + // Skip C++-linked struct kinds. + let strct = f.kind.strct() + if strct != nil && strct.decl != nil && strct.decl.cpp_linked { + continue + } + + n++ + obj += "\n" + obj += indent() + obj += "this->" + let f_ident = field_out_ident(f.decl) + obj += f_ident + obj += " == _Src." + obj += f_ident + obj += " &&" + } + done_indent() + if n > 0 { + obj = obj[:obj.len-3] // Remove last suffix " &&" + } else { + obj += "true" + } + obj += ";\n" + done_indent() + obj += indent() + obj += "}" + } else { + obj += " return true; }" + } + obj += "\n\n" + obj += indent() + if env::OPT_INLINE { + obj += "inline " + } + obj += "bool operator!=(const " + obj += out_ident + obj += " &_Src) const { return !this->operator==(_Src); }" + ret obj + } + + fn gen_struct_derive_defs_prototypes(mut self, &s: &StructIns): str { + let mut obj = "" + + if s.decl.is_derives(Derive.Clone) { + obj += indent() + obj += get_derive_fn_decl_clone(s.decl) + obj += ";\n\n" + } + + ret obj + } + + fn gen_struct_ins_prototype(mut self, mut &s: &StructIns): str { + let mut obj = "struct " + let out_ident = struct_ins_out_ident(s) + + obj += out_ident + obj += gen_struct_traits(s.decl) + obj += " {\n" + + let ref_access = s.decl.has_ref_accessible() + + add_indent() + if ref_access { + obj += indent() + obj += gen_struct_self_field(s) + obj += "\n\n" + } + if s.fields.len > 0 { + for (_, mut f) in s.fields { + obj += indent() + obj += gen_field_decl(f) + obj += "\n" + } + obj += "\n\n" + obj += indent() + obj += gen_struct_constructor(s) + obj += "\n\n" + } + + obj += indent() + obj += gen_struct_destructor(s) + obj += "\n\n" + + // Default constructor. + obj += indent() + obj += out_ident + if ref_access { + obj += "(void) { " + obj += gen_struct_self_field_init_st(s) + obj += " }\n\n" + } else { + obj += "(void) = default;\n\n" + } + + for (_, mut f) in s.methods { + obj += gen_fn_prototype(f, true) + obj += "\n\n" + } + + obj += gen_struct_derive_defs_prototypes(s) + + obj += gen_struct_operators(s) + obj += "\n" + + done_indent() + obj += indent() + "};" + ret obj + } + + // Generates C++ declaration code of structure. + fn gen_struct_prototype(mut self, mut &s: &Struct): str { + let mut obj = "" + for (_, mut ins) in s.instances { + obj += gen_struct_ins_prototype(ins) + obj += "\n\n" + } + ret obj + } + + // Generates C++ declaration code of all structures. + fn gen_struct_prototypes(mut self, mut &structs: []&Struct): str { + let mut obj = "" + for (_, mut s) in structs { + if !s.cpp_linked && s.token.id != TokenId.Na { + obj += gen_struct_prototype(s) + obj += "\n" + } + } + ret obj + } + + fn gen_fn_decl_head(mut self, mut &f: &FnIns, method: bool): str { + let mut obj = "" + + if method && f.decl.statically { + obj += "static " + } + + if env::OPT_INLINE && !f.decl.is_entry_point() { + obj += "inline " + } + + obj += gen_fn_ins_result(f) + " " + + if !method && f.decl.owner != nil { + obj += struct_ins_out_ident(f.owner) + obj += "::" + } + obj += fn_ins_out_ident(f) + ret obj + } + + // Generates C++ declaration code of function's combinations. + fn gen_fn_prototype(mut self, mut &f: &Fn, method: bool): str { + let mut obj = "" + for (_, mut c) in f.instances { + obj += indent() + obj += gen_fn_decl_head(c, method) + obj += gen_params_prototypes(c.params) + obj += CPP_ST_TERM + "\n" + } + ret obj + } + + // Generates C++ declaration code of all functions. + fn gen_fn_prototypes(mut self, mut &pkg: &Package): str { + let mut obj = "" + for (_, mut file) in pkg.files { + for (_, mut f) in file.funcs { + if !f.cpp_linked && f.token.id != TokenId.Na { + obj += gen_fn_prototype(f, false) + } + } + } + ret obj + } + + // Generates C++ code of all can-be-prototyped declarations. + fn gen_prototypes(mut self): str { + let mut obj = "" + + for (_, mut u) in self.ir.used { + if !u.cpp_linked { + obj += gen_trait_prototypes(u.package) + } + } + obj += gen_trait_prototypes(self.ir.main) + + + obj += gen_struct_plain_prototypes(self.ir.ordered.structs) + + obj += gen_traits(self.ir) + obj += "\n" + + obj += gen_struct_prototypes(self.ir.ordered.structs) + + for (_, mut u) in ir.used { + if !u.cpp_linked { + obj += gen_fn_prototypes(u.package) + } + } + obj += gen_fn_prototypes(self.ir.main) + + ret obj + } + + // Generats C++ code of variable with initialize expression. + fn gen_var_init_expr(mut self, mut &v: &Var, init: str): str { + let mut obj = "" + if v.statically { + obj += "static " + } + + obj += gen_type_kind(v.kind.kind) + obj += " " + if v.reference { + obj += "&" + } + obj += var_out_ident(v) + if init != "" { + obj += " = " + obj += init + } + obj += CPP_ST_TERM + ret obj + } + + // Generates C++ code of variable. + fn gen_var(mut self, mut v: &Var): str { + if is_ignore_ident(v.ident) { + ret "" + } + if v.value != nil && v.value.expr != nil { + if v.value.data.model != nil { + ret gen_var_init_expr(v, gen_val(v.value)) + } + ret gen_var_init_expr(v, "") + } + ret gen_var_init_expr(v, get_init_expr(v.kind.kind)) + } + + fn gen_pkg_globals(mut self, mut &p: &Package, mut &global_initializers: str): str { + let mut obj = "" + for (_, mut f) in p.files { + for (_, mut v) in f.vars { + if v.token.id != TokenId.Na && !v.cpp_linked && !v.constant { + obj += gen_type_kind(v.kind.kind) + obj += " " + if v.reference { + obj += "&" + } + obj += var_out_ident(v) + obj += ";\n" + + global_initializers += indent() // Indentation for initializer scope. + global_initializers += var_out_ident(v) + if v.value != nil && v.value.expr != nil { + if v.value.data.model != nil { + global_initializers += " = " + global_initializers += gen_val(v.value) + } + } else { + global_initializers += " = " + global_initializers += get_init_expr(v.kind.kind) + } + global_initializers += ";\n" + } + } + } + ret obj + } + + // Generates C++ code of all globals. + fn gen_globals(mut self, mut &global_initializers: str): str { + let mut obj = "" + + self.add_indent() // For global initializers's function indentation. + + for (_, mut u) in self.ir.used { + if !u.cpp_linked { + obj += gen_pkg_globals(u.package, global_initializers) + } + } + obj += gen_pkg_globals(self.ir.main, global_initializers) + + self.done_indent() + + ret obj + } + + // Generates C++ code of function. + fn gen_fn(mut self, mut &f: &Fn): str { + let mut obj = "" + for (_, mut ins) in f.instances { + obj += gen_fn_decl_head(ins, false) + obj += gen_params_ins(ins.params) + obj += " " + obj += gen_fn_scope(ins) + obj += "\n\n" + } + ret obj + } + + // Generates C++ code of all functions of package. + fn gen_pkg_fns(mut self, mut &p: &Package): str { + let mut obj = "" + for (_, mut f) in p.files { + for (_, mut f) in f.funcs { + if !env::TEST && has_directive(f.directives, Directive.Test) { + continue + } + if !f.cpp_linked && f.token.id != TokenId.Na { + obj += gen_fn(f) + obj += "\n\n" + } + } + } + ret obj + } + + // Generates C++ code of structure's methods. + fn gen_struct_method_defs(mut self, mut &s: &StructIns): str { + let mut obj = "" + for (_, mut f) in s.methods { + obj += indent() + obj += gen_fn(f) + obj += "\n\n" + } + ret obj + } + + // Reports method is reversed to_str method. + fn fn_is_to_str(mut self, mut &f: &Fn): bool { + if f == nil || + !f.public || + f.is_void() || + f.generics.len != 0 || + f.params.len != 1 || + f.params[0].mutable || + f.params[0].is_ref() { + ret false + } + + let mut ins = f.instances[0] + let prim = ins.result.prim() + if prim == nil { + ret false + } + ret prim.is_str() + } + + // Generates C++ code of structure's ostream. + fn gen_struct_ostream(mut self, mut &s: &StructIns): str { + let mut obj = "" + obj += indent() + obj += "std::ostream &operator<<(std::ostream &_Stream, const " + obj += struct_ins_out_ident(s) + obj += " &_Src) {\n" + add_indent() + obj += indent() + + let mut fts = s.find_method("to_str", false) + if fn_is_to_str(fts) { + obj += "_Stream << ((" + obj += struct_ins_out_ident(s) + obj += ")(_Src))." + obj += fn_out_ident(fts) + obj += "();\n" + } else { + obj += `_Stream << "` + obj += s.decl.ident + obj += "{\";\n" + + for (i, mut f) in s.fields { + obj += indent() + obj += `_Stream << "` + obj += f.decl.ident + obj += `:` + + // Skip C++-linked struct kinds. + let strct = f.kind.strct() + if strct != nil && strct.decl != nil && strct.decl.cpp_linked { + obj += ` cpp.` + obj += field_out_ident(f.decl) + obj += `"` + } else { + obj += `" << _Src.` + obj += field_out_ident(f.decl) + } + if i+1 < s.fields.len { + obj += " << \", \"" + } + obj += ";\n" + } + + obj += indent() + obj += "_Stream << \"}\";\n" + } + + obj += indent() + obj += "return _Stream;\n" + + done_indent() + obj += indent() + obj += "}" + ret obj + } + + fn gen_struct_derive_defs(mut self, mut &s: &StructIns): str { + let mut obj = "" + + if s.decl.is_derives(Derive.Clone) { + obj += indent() + obj += get_derive_fn_def_clone(s.decl) + obj += "{\n" + add_indent() + obj += indent() + obj += gen_struct_kind_ins(s) + obj += " clone;\n" + for _, f in s.fields { + let ident = field_out_ident(f.decl) + + obj += indent() + obj += "clone." + obj += ident + obj += " = jule::clone(this->" + obj += ident + obj += ");\n" + } + obj += indent() + obj += "return clone;\n" + done_indent() + obj += indent() + obj += "}" + } + + ret obj + } + + // Generates C++ code of structure instance definition. + fn gen_struct_ins(mut self, mut &s: &StructIns): str { + let mut obj = gen_struct_method_defs(s) + obj += "\n\n" + obj += gen_struct_derive_defs(s) + obj += "\n\n" + obj += gen_struct_ostream(s) + ret obj + } + + // Generates C++ code of structure definition. + fn gen_struct(mut self, mut &s: &Struct): str { + let mut obj = "" + for (_, mut ins) in s.instances { + obj += gen_struct_ins(ins) + obj += "\n\n" + } + ret obj + } + + // Generates C++ code of all structures. + fn gen_structs(mut self, mut &structs: []&Struct): str { + let mut obj = "" + for (_, mut s) in structs { + if !s.cpp_linked && s.token.id != TokenId.Na { + obj += gen_struct(s) + obj += "\n\n" + } + } + ret obj + } + + // Generates C++ code of all functions. + fn gen_fns(mut self): str { + let mut obj = "" + for (_, mut u) in self.ir.used { + if !u.cpp_linked { + obj += gen_pkg_fns(u.package) + } + } + obj += gen_pkg_fns(self.ir.main) + ret obj + } + + fn push_init(self, mut &pkg: &Package, mut &obj: str) { + const INDENTION = "\t" + const CPP_LINKED = false + let f = pkg.find_fn(INIT_FN, CPP_LINKED) + if f == nil { + ret + } + obj += "\n" + INDENTION + obj += fn_out_ident(f) + obj += "();" + } + + // Generated C++ code of all initializer functions. + fn gen_init_caller(mut self, &global_initializers: str): str { + let mut obj = "void " + obj += INIT_CALLER_IDENT + obj += "(void) {\n" + obj += global_initializers + + for (_, mut u) in self.ir.used { + if !u.cpp_linked { + push_init(u.package, obj) + } + } + push_init(self.ir.main, obj) + + obj += "\n}" + ret obj + } +} + + + + + + + + +/* +// Reports method is reversed dispose method. +fn fn_is_dispose(mut f: &Fn): bool { + ret f != nil && + f.public && + f.is_void() && + f.generics.len == 0 && + f.params.len == 1 && + f.params[0].mutable && + !f.params[0].is_ref() +} + +fn is_cpp_header_file(path: str): bool { + let offset = strings::find_last_byte(path, '.') + if offset == -1 { + ret false + } + ret is_valid_header_ext(path[offset:]) +} + +pub fn find_testing_package(mut &ir: &IR): &ImportInfo { + for (_, mut imp) in ir.used { + if imp.link_path == "std::testing" { + ret imp + } + } + ret nil +} + +pub fn append_test(mut &obj: str, mut f: &FnIns) { + obj += indent() + obj += "_t->_method_reset();\n" + obj += indent() + obj += "std::cout << \">>> TEST RUNNING: \";\n" + obj += indent() + obj += "jule::outln(" + obj += get_cstr_model([]byte(f.decl.ident)) + obj += ");\n" + obj += indent() + obj += fn_ins_out_ident(f) + obj += "(_t);\n" + obj += indent() + obj += "post_test();\n" +} + +pub fn append_package_tests(mut &obj: str, mut &p: &Package) { + for (_, mut file) in p.files { + for (_, mut f) in file.funcs { + if has_directive(f.directives, Directive.Test) { + append_test(obj, f.instances[0]) + } + } + } +} + +pub fn append_test_point(mut &obj: str, mut &ir: &IR) { + obj += "\nvoid test_point(void) {\n" + add_indent() + obj += indent() + + let mut p = find_testing_package(ir) + if p == nil { + // std::testing is not used. + // So, developers cannot write valid test function. + // Append empty test point and return. + obj += "}" + done_indent() + ret + } + + let mut t = p.find_struct("T", false).instances[0] + + obj += as_sptr_kind(gen_struct_kind_ins(t)) + obj += " _t = jule::new_struct<" + obj += gen_struct_kind_ins(t) + obj += ">(" + if !env::PRODUCTION { + obj += `"/jule/init", ` + } + obj += "new(std::nothrow) " + obj += gen_struct_kind_ins(t) + obj += ");\n" + + obj += indent() + obj += "jule::Uint total = 0, failed = 0, skipped = 0;\n" + obj += indent() + + obj += "auto post_test = [&](void) {\n" + add_indent() + obj += indent() + obj += "++total;\n" + obj += indent() + obj += "if (_t->_method_failed()) { ++failed; std::cout << \" [*] FAILED\" << std::endl; }\n" + obj += indent() + obj += "else if (_t->_method_skipped()) { ++skipped; std::cout << \" [*] SKIPPED\" << std::endl; }\n" + obj += indent() + obj += "else { std::cout << \" [*] PASSED\" << std::endl; }\n" + done_indent() + obj += indent() + obj += "};\n" + + append_package_tests(obj, ir.main) + + obj += "\n\n" + obj += indent() + obj += "std::cout << std::endl << std::endl << \"total tests: \" << total << \" skipped: \" << skipped << \" failed: \" << failed << \" pass: \" << total-failed-skipped << std::endl;\n" + + done_indent() + obj += indent() + obj += "}\n" +} + +pub fn append_standard(mut &obj_code: str, compiler: str, compiler_cmd: str) { + let time = Time.now().abs() + + let mut time_str = "" + time_str += conv::fmt_uint(time.day, 10) + time_str += "/" + time_str += conv::fmt_uint(time.month, 10) + time_str += "/" + time_str += conv::fmt_uint(time.year, 10) + time_str += " " + time_str += conv::fmt_uint(time.hour, 10) + time_str += "." + time_str += conv::fmt_uint(time.minute, 10) + time_str += " (DD/MM/YYYY) (HH.MM) UTC" + + let mut s = "" + s += "// Auto generated by JuleC.\n" + s += "// JuleC version: " + s += VERSION + s += "\n" + s += "// Date: " + s += time_str + s += ` +// +// Recommended Compile Command; +// ` + s += compiler + s += " " + s += compiler_cmd + s += "\n\n" + + if env::PRODUCTION { + s += "#define __JULE_ENABLE__PRODUCTION\n" + } + if !env::RC { + s += "#define __JULE_DISABLE__REFERENCE_COUNTING\n" + } + if !env::SAFETY { + s += "#define __JULE_DISABLE__SAFETY\n" + } + + s += "\n\n#include \"" + s += PATH_API + s += "\"\n\n" + s += obj_code + s += ` +int main(int argc, char *argv[], char *envp[]) { + jule::setup_argv(argc, argv); + jule::setup_envp(envp); + + __jule_call_initializers(); + ` + if env::TEST { + s += "test_point();" + } else { + s += "entry_point();" + } + + s += ` + + return EXIT_SUCCESS; +}` + obj_code = s +} + +// Generates C++ codes from SymbolTables. +pub fn gen(mut &ir: &IR): str { + let mut oc = ObjectCoder.new(ir) + let mut global_initializers = "" + let mut obj = "" + obj += oc.gen_links() + obj += "\n" + obj += oc.gen_prototypes() + obj += "\n\n" + obj += oc.gen_globals(global_initializers) + obj += "\n" + obj += oc.gen_structs() + obj += oc.gen_fns() + obj += "\n" + obj += oc.gen_init_caller(global_initializers) + obj += "\n" + ret obj +}*/ diff --git a/src/julec/obj/cxx/scope.jule b/src/julec/obj/cxx/scope.jule index 4f56ead8e..2777c451f 100644 --- a/src/julec/obj/cxx/scope.jule +++ b/src/julec/obj/cxx/scope.jule @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD 3-Clause // license that can be found in the LICENSE file. +/* + use env use std::jule::constant::{Const} @@ -760,3 +762,5 @@ fn gen_fn_scope(mut f: &FnIns): str { fn gen_method_scope(mut f: &FnIns): str { ret gen_scope(f.scope) } + +*/ diff --git a/src/julec/obj/cxx/type.jule b/src/julec/obj/cxx/type.jule index 1e39a4330..4d546469f 100644 --- a/src/julec/obj/cxx/type.jule +++ b/src/julec/obj/cxx/type.jule @@ -2,8 +2,26 @@ // Use of this source code is governed by a BSD 3-Clause // license that can be found in the LICENSE file. -use conv for std::conv use ast for std::jule::ast + +// Returns directive if exist. +fn find_directive(mut &directives: []&ast::Directive, tag: str): &ast::Directive { + for (_, mut dr) in directives { + if dr.tag.kind == tag { + ret dr + } + } + ret nil +} + +// Reports whether directive is exist. +fn has_directive(&directives: []&ast::Directive, tag: str): bool { + ret find_directive(unsafe { *(&directives) }, tag) != nil +} + +/* + +use conv for std::conv use std::jule::build::{Directive} use std::jule::sema::{ Prim, @@ -21,20 +39,7 @@ use std::jule::sema::{ Arr, } -// Returns directive if exist. -fn find_directive(mut &directives: []&ast::Directive, tag: str): &ast::Directive { - for (_, mut dr) in directives { - if dr.tag.kind == tag { - ret dr - } - } - ret nil -} -// Reports whether directive is exist. -fn has_directive(&directives: []&ast::Directive, tag: str): bool { - ret find_directive(unsafe { *(&directives) }, tag) != nil -} // Generates C++ code of Prim TypeKind. fn gen_prim_kind(p: &Prim): str { @@ -73,7 +78,6 @@ fn gen_ptr_kind(mut p: &Ptr): str { if p.is_unsafe() { ret "void" + CPP_POINTER_MARK } - let mut elem = gen_type_kind(p.elem) elem += CPP_POINTER_MARK ret elem @@ -128,7 +132,6 @@ fn gen_struct_kind(s: &Struct): str { if s.cpp_linked && !has_directive(s.directives, Directive.Typedef) { rep += "struct " } - rep += struct_out_ident(s) ret rep } @@ -175,7 +178,6 @@ fn gen_fn_anon_decl(mut f: &FnIns): str { if param.decl.is_self() { continue } - decl += gen_param_ins_prototype(param) decl += "," } @@ -184,7 +186,6 @@ fn gen_fn_anon_decl(mut f: &FnIns): str { decl += "void" } decl += ")" - ret decl } @@ -200,18 +201,32 @@ fn gen_fn_kind(mut f: &FnIns): str { // Generates C++ code of TypeKind. fn gen_type_kind(mut k: &TypeKind): str { match { - | k.cpp_linked(): ret k.cpp_ident - | k.prim() != nil: ret gen_prim_kind(k.prim()) - | k.tup() != nil: ret gen_tuple_kind(k.tup()) - | k.sptr() != nil: ret gen_sptr_kind(k.sptr()) - | k.ptr() != nil: ret gen_ptr_kind(k.ptr()) - | k.enm() != nil: ret gen_enum_kind(k.enm()) - | k.slc() != nil: ret gen_slice_kind(k.slc()) - | k.map() != nil: ret gen_map_kind(k.map()) - | k.trt() != nil: ret gen_trait_kind(k.trt()) - | k.strct() != nil: ret gen_struct_kind_ins(k.strct()) - | k.arr() != nil: ret gen_array_kind(k.arr()) - | k.fnc() != nil: ret gen_fn_kind(k.fnc()) - |: ret "[]" + | k.cpp_linked(): + ret k.cpp_ident + | k.prim() != nil: + ret gen_prim_kind(k.prim()) + | k.tup() != nil: + ret gen_tuple_kind(k.tup()) + | k.sptr() != nil: + ret gen_sptr_kind(k.sptr()) + | k.ptr() != nil: + ret gen_ptr_kind(k.ptr()) + | k.enm() != nil: + ret gen_enum_kind(k.enm()) + | k.slc() != nil: + ret gen_slice_kind(k.slc()) + | k.map() != nil: + ret gen_map_kind(k.map()) + | k.trt() != nil: + ret gen_trait_kind(k.trt()) + | k.strct() != nil: + ret gen_struct_kind_ins(k.strct()) + | k.arr() != nil: + ret gen_array_kind(k.arr()) + | k.fnc() != nil: + ret gen_fn_kind(k.fnc()) + |: + ret "[]" } } +*/ \ No newline at end of file