diff --git a/package.json b/package.json index 7770e83..d14d170 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "daScript language support", "author": "Dmitri Granetchi", "license": "MIT", - "version": "1.3.5", + "version": "1.3.6", "repository": { "type": "git", "url": "https://github.com/profelis/daScript-plugin" diff --git a/server/0.6.3/completion_boost.das b/server/0.6.3/completion_boost.das new file mode 100644 index 0000000..f894e42 --- /dev/null +++ b/server/0.6.3/completion_boost.das @@ -0,0 +1,503 @@ +options gen2 +options rtti +options indenting = 4 +options gen2 = false +options no_global_variables +options no_unused_function_arguments +options no_unused_block_arguments +options strict_smart_pointers +options no_aot + +require daslib/ast +require daslib/rtti +require daslib/math +require daslib/strings +require daslib/strings_boost + + +struct CompletionAt { + @optional file : string + @optional line : int + @optional column : int + @optional lineEnd : int + @optional columnEnd : int +} + + +struct CompletionResult { + enums : array + structs : array + typeDecls : array + typeDefs : array // aliases, typedef Foo = int + globals : array + functions : array +} + + +struct CompletionContext { + typeDecls : table + result : CompletionResult +} + +def is_builtin(mod : rtti::Module?) : bool { + return mod.moduleFlags.builtIn && !mod.moduleFlags.promoted +} + +def to_json(val) { + return val |> sprint_json(/*readable*/true) +} + // return JV(val) |> write_json() + +def fill_at(var res; at : LineInfo?) { + if (at != null) { + fill_at(res, at) + } +} + +def fill_at(var res : CompletionAt&; at : LineInfo) { + res.file = at.fileInfo != null ? "{at.fileInfo.name}" : "" + res.line = at.line |> int() + res.column = at.column |> int() + res.lineEnd = at.last_line |> int() + res.columnEnd = at.last_column |> int() +} + + +def BaseTypeString(t : rtti::Type) : string { + let res = "{t}" + if (length(res) > 1) { + let secondChar = res.slice(1, 2) + let lowSecondChar = to_lower(secondChar) + if (lowSecondChar != secondChar) { + return "{lowSecondChar}{res.slice(2)}" + } + } + return res +} + +/// enum + +struct CompletionEnumValue : CompletionAt { + name : string + @optional value : string +} + +struct CompletionEnum : CompletionAt { + name : string + @optional mod : string + @optional cpp : string + @optional tdk : string + @optional baseType : string + @optional values : array +} + +def parse_enum(var res : CompletionContext; e : Enumeration?) { + var en <- CompletionEnum(name = "{e.name}", cpp = "{e.cppName}", baseType = BaseTypeString(e.baseType)) + en |> fill_at(e.at) + if (e._module != null && length(e._module.name) > 0) { + en.mod = e._module.name |> string() + en.tdk = "{en.mod}::{en.name}" + } else { + en.tdk = "{en.name}" + } + for (ef in e.list) { + var ev <- CompletionEnumValue(name = "{ef.name}") + ev |> fill_at(ef.at) + ev.value = ef.value |> describe() + en.values |> emplace(ev) + } + res.result.enums |> emplace(en) +} + +/// global + +struct CompletionGlobal : CompletionAt { + name : string + @optional tdk : string + @optional value : string + @optional mod : string + @optional gen : bool + @optional isUnused : bool +} + +def parse_global(var res : CompletionContext; g : Variable?) { + var gl <- CompletionGlobal(name = "{g.name}") + gl |> fill_at(g.at) + if (g._module != null) { + gl.mod = g._module.name |> string() + } + gl.tdk = TypeDeclKey(g._type) + res |> parse_typedecl(gl.tdk, g._type, /*is_generic*/false) + gl.gen = g.flags.generated + if (g.init != null) { + gl.value = g.init |> describe() + } + gl.isUnused = !g.flags.marked_used && g.isAccessUnused + res.result.globals |> emplace(gl) +} + +/// struct + +struct CompletionStructField : CompletionAt { + name : string + // mn : string + tdk : string + offset : int + @optional isPrivate : bool + @optional gen : bool +} + +struct CompletionStruct : CompletionAt { + name : string + @optional parentName : string + @optional parentMod : string + @optional mod : string + @optional fields : array + @optional isClass : bool + @optional isLambda : bool + @optional isMacro : bool + @optional isGenerator : bool + @optional gen : bool +} + +def parse_struct(var res : CompletionContext; s : Structure?) { + var st <- CompletionStruct(name = "{s.name}") + st |> fill_at(s.at) + if (s._module != null) { + st.mod = s._module.name |> string() + } + st.isClass = s.flags.isClass + st.isLambda = s.flags.isLambda + st.isMacro = s.flags.macroInterface + st.isGenerator = s.flags._generator + st.gen = s.flags.generated + if (s.parent != null) { + parse_struct(res, s.parent) + st.parentName = s.parent.name |> string() + st.parentMod = s.parent._module != null ? "{s.parent._module.name}" : "" + } + for (f in s.fields) { + var field <- CompletionStructField(name = "{f.name}") + field |> fill_at(f.at) + field.isPrivate = f.flags.privateField + field.gen = f.flags.generated + field.offset = f.offset + field.tdk = TypeDeclKey(f._type) + res |> parse_typedecl(field.tdk, f._type, /*is_generic*/false) + // field.mn = f._type |> get_mangled_name() + st.fields |> emplace(field) + } + res.result.structs |> emplace(st) +} + +/// type decl +// TODO: support optional type (split by | ) +// TODO: keep full type name as separate field (for preview) +def private cleanupTDK(str : string) : string { + var parts <- str |> split("const?") + + for (res in parts) { + res = replace_multiple(res, [ (text = " ==const", replacement = ""), (text = " const", replacement = ""), (text = " implicit", replacement = ""), (text = ";", replacement = ",") ]) + } + + return parts |> join("const?") +} + +def TypeDeclKey(t : TypeDecl?) : string { + return t |> describe_typedecl(false, false, true) |> cleanupTDK() +} + +struct CompletionTypeDeclField { + name : string + tdk : string +} + +struct CompletionTypeDecl : CompletionAt { + baseType : string + tdk : string + @optional fields : array + @optional dim : array + @optional alias : string + sizeOf : int + alignOf : int + @optional enumName : string + @optional structName : string + @optional mod : string // enum name or struct name + @optional tdk1 : string + @optional tdk2 : string + @optional canCopy : bool + @optional canMove : bool + @optional canClone : bool +} + +let skipTypeDescSize = fixed_array(Type.none, Type.autoinfer, Type.alias, Type.fakeLineInfo, + Type.fakeContext, Type.alias, Type.typeDecl, Type.typeMacro, Type.option) + +def parse_typedecl(var res : CompletionContext; tdk : string; t : TypeDecl?; is_generic : bool) : void { + let tdkHash = hash(tdk) + if (res.typeDecls.key_exists(tdkHash)) { + return + } + res.typeDecls.insert(tdkHash) + let resIdx = res.result.typeDecls |> length() + res.result.typeDecls |> emplace(CompletionTypeDecl()) + // a fixed-array chain (Type tFixedArray, daScript 0.6.4+) flattens into the client model: + // dim list + element-type fields on this node, tdk1 = one-level peel for index completion + var leaf = t + var td <- CompletionTypeDecl(tdk = tdk) + while (leaf.baseType == Type.tFixedArray && leaf.firstType != null) { + td.dim |> push(leaf.fixedDim) + leaf = leaf.firstType + } + td.baseType = BaseTypeString(leaf.baseType) + td.canCopy = t.canCopy + td.canMove = t.canMove + td.canClone = t.canClone + let totalArgs = min(length(leaf.argNames), length(leaf.argTypes)) + td.fields |> reserve(max(length(leaf.argNames), length(leaf.argTypes))) + for (idx, argN, argT in count(), leaf.argNames, leaf.argTypes) { + var tf <- CompletionTypeDeclField(name = length(argN) > 0 ? "{argN}" : "_{idx}") + tf.tdk = TypeDeclKey(argT) + res |> parse_typedecl(tf.tdk, argT, is_generic) + td.fields |> emplace(tf) + } + for (idx in totalArgs..length(leaf.argNames)) { + assume argN = leaf.argNames[idx] + var tf <- CompletionTypeDeclField(name = length(argN) > 0 ? "{argN}" : "_{idx}") + td.fields |> emplace(tf) + } + for (idx in totalArgs..length(leaf.argTypes)) { + assume argT = leaf.argTypes[idx] + var tf <- CompletionTypeDeclField(name = "_{idx}") + tf.tdk = TypeDeclKey(argT) + res |> parse_typedecl(tf.tdk, argT, is_generic) + td.fields |> emplace(tf) + } + td |> fill_at(t.at) + td.alias = t.alias |> string() + if (leaf._module != null) { + td.mod = leaf._module.name |> string() + } + + if (!is_generic && skipTypeDescSize |> find_index(leaf.baseType) == -1) { + td.sizeOf = t.sizeOf + td.alignOf = t.alignOf + } + if (leaf.enumType != null) { + td.enumName = leaf.enumType.name |> string() + td.mod = leaf.enumType._module != null ? leaf.enumType._module.name |> string() : "" + } + if (leaf.structType != null) { + td.structName = leaf.structType.name |> string() + td.mod = leaf.structType._module != null ? leaf.structType._module.name |> string() : "" + } + if (leaf.annotation != null) { + td.structName = leaf.annotation.name |> string() + td.mod = leaf.annotation._module != null ? leaf.annotation._module.name |> string() : "" + } + if (t.baseType == Type.tFixedArray && t.firstType != null) { + td.tdk1 = TypeDeclKey(t.firstType) + res |> parse_typedecl(td.tdk1, t.firstType, is_generic) + } elif (leaf.firstType != null) { + td.tdk1 = TypeDeclKey(leaf.firstType) + res |> parse_typedecl(td.tdk1, leaf.firstType, is_generic) + } + if (leaf.secondType != null) { + td.tdk2 = TypeDeclKey(leaf.secondType) + res |> parse_typedecl(td.tdk2, leaf.secondType, is_generic) + } + res.result.typeDecls[resIdx] <- td +} + // return foundIdx + +/// type def + +struct CompletionTypeDef : CompletionAt { + name : string + mod : string + tdk : string +} + +def parse_typedef(var res : CompletionContext; mod : Module?; n : string#; t : TypeDecl?) { + var td <- CompletionTypeDef(name = "{n}") + td |> fill_at(t.at) + td.mod = mod.name |> string() + td.tdk = TypeDeclKey(t) + res |> parse_typedecl(td.tdk, t, /*is_generic*/false) + res.result.typeDefs |> emplace(td) +} + +/// function + +struct CompletionFuncArg : CompletionAt { + name : string + @optional alias : string + @optional variable : bool + tdk : array + @optional value : string +} + +struct CompletionFunction : CompletionAt { + name : string + @optional mod : string + @optional origMod : string + @optional cpp : string + tdk : string + decl : CompletionAt + @optional args : array + @optional gen : bool + @optional isClassMethod : bool + @optional isGeneric : bool +} + + +def remove_fn_generation(name : string) : string { + var lastAt = -1 + for (ch, idx in name, count()) { + if (ch == '`') { + lastAt = idx + } + } + if (lastAt == -1) { + return name + } + for (i in lastAt + 1..length(name)) { + if (!is_number(unsafe(character_uat(name, i)))) { + return name + } + } + if (name.starts_with("`")) { + return name.slice(1, lastAt) + } + return name.slice(0, lastAt) +} + +[nodiscard] +def fix_fn_name(name : string) : string { + var res = name + if (res |> starts_with("__::")) { + res = slice(name, 4) + } + if (res |> starts_with("builtin`")) { + res = slice(res, length("builtin`")) + } + return remove_fn_generation(res) +} + + +def parse_function(var res : CompletionContext; f : Function?; cpp : string; is_generic : bool) { + var fn <- CompletionFunction(name = "{f.name}", cpp = "{cpp}") + fn.name = fix_fn_name(fn.name) + fn.isGeneric = is_generic + fn |> fill_at(f.at) + if (f._module != null) { + fn.mod = f._module.name |> string() + } + var origin = f.origin + if (origin != null && origin._module != null) { + fn.origMod = origin._module.name |> string() + } + if (f.result != null) { + fn.tdk = TypeDeclKey(f.result) + res |> parse_typedecl(fn.tdk, f.result, is_generic) + } + fn.decl |> fill_at(f.atDecl) + fn.gen = f.flags.generated + fn.isClassMethod = f.flags.isClassMethod + for (arg in f.arguments) { + var fa <- CompletionFuncArg(name = "{arg.name}") + fa.variable = arg._type.flags.removeConstant + fa |> fill_at(arg.at) + if (arg._type.baseType == Type.option) { + for (argType in arg._type.argTypes) { + fa.tdk.push(TypeDeclKey(argType)) + res |> parse_typedecl(fa.tdk.back(), argType, is_generic) + } + } else { + fa.tdk.push(TypeDeclKey(arg._type)) + res |> parse_typedecl(fa.tdk[0], arg._type, is_generic) + } + fa.alias = arg._aka |> string() + if (arg.init != null) { + fa.value = arg.init |> describe() + } + fn.args |> emplace(fa) + } + res.result.functions |> emplace(fn) +} + + +def parse_annotation(var res : CompletionContext; ann : Annotation&) { + if (ann.isBasicStructureAnnotation) { + let sann & = unsafe(reinterpret(ann)) + var st <- CompletionStruct(name = "{sann.name}") + if (ann._module != null) { + st.mod = ann._module.name |> string() + } + for_each_field(sann) $ [unused_argument(cppName)] (name, cppName, xtype, offset) { + var field <- CompletionStructField(name = "{name}") + field.tdk = TypeDeclKey(xtype) + res |> parse_typedecl(field.tdk, xtype, /*is_generic*/false) + field.offset = int(offset) + st.fields |> emplace(field) + } + res.result.structs |> emplace(st) + } elif (ann.isTypeAnnotation) { + let tann & = unsafe(reinterpret(ann)) + var td <- CompletionStruct(name = "{tann.name}") + if (ann._module != null) { + td.mod = "{ann._module.name}" + } + res.result.structs |> emplace(td) + } else { + var td <- CompletionStruct(name = "{ann.name}") + if (ann._module != null) { + td.mod = "{ann._module.name}" + } + res.result.structs |> emplace(td) + } +} + +/// module + +def parse_module(var res : CompletionContext; mod : rtti::Module?) { + // debug(mod) + + mod |> for_each_enumeration() $(e) { + // debug(e) + res |> parse_enum(e) + } + + mod |> for_each_global() $(g) { + // debug(g) + res |> parse_global(g) + } + + mod |> for_each_structure() $(s) { + res |> parse_struct(s) + } + + mod |> for_each_typedef() $(n, t) { + // debug(t) + res |> parse_typedef(mod, n, t) + } + + var inscope funcCppNames : table + mod |> module_for_each_function() $(f) { + funcCppNames |> insert(f.name, clone_string(f.cppName)) + } + + mod |> for_each_generic() $(f) { + // debug(f) + res |> parse_function(f, funcCppNames?[string(f.name)] ?? "", true) + } + + mod |> for_each_function("") $(f) { + // debug(f) + res |> parse_function(f, funcCppNames?[string(f.name)] ?? "", false) + } + + mod |> module_for_each_annotation() $(a) { + res |> parse_annotation(a) + } +} diff --git a/server/0.6.3/validate_file.das b/server/0.6.3/validate_file.das new file mode 100644 index 0000000..1db1cb3 --- /dev/null +++ b/server/0.6.3/validate_file.das @@ -0,0 +1,931 @@ +options gen2 +options rtti +options indenting = 4 +options gen2 = false +options no_global_variables +options no_unused_function_arguments +options no_unused_block_arguments +options strict_smart_pointers +options no_aot + +require daslib/ast +require daslib/rtti +require daslib/strings +require daslib/fio +require daslib/ast_boost +require daslib/strings_boost +require daslib/das_source_formatter +require completion_boost + +def arg_value(args : array; name : string; default_value : string) : string { + let idx = args |> find_index(name) + return idx >= 0 && idx + 1 < length(args) ? args[idx + 1] : default_value +} + +def arg_value(args : array; name : string; default_value : int) : int { + let idx = args |> find_index(name) + return idx >= 0 && idx + 1 < length(args) ? to_int(args[idx + 1]) : default_value +} + +struct ValidateConfig { + compiler : string + file : string + originalFile : string + projectFile : string + resultFile : string + noGlobalVariables : bool + noUnusedBlockArguments : bool + noUnusedFunctionArguments : bool + failOnLackOfAotExport : bool + version2syntax : bool + strictProperties : bool + gen2MakeSyntax : bool + alwaysReportCandidatesThreshold : int + maxInferPasses : int + globalCompletion : bool + autoFormat : bool + + fileAccessRoots : table +} + +let WRITE_RESULT = true +let WRITE_RESULT_TO_FILE = false + +[export] +def main() { + var args <- get_command_line_arguments() + var config : ValidateConfig + + for (arg, idx in args, count()) { + if (arg == "--file-access-root") { + if (idx + 1 < length(args)) { + var inscope parts <- args[idx + 1] |> split(":") + if (length(parts) == 2) { + config.fileAccessRoots |> insert(parts[0], parts[1]) + } + } + } + } + + config.compiler = args[0] + config.file = args |> arg_value("--file", "") + config.originalFile = args |> arg_value("--original-file", config.file) + config.projectFile = args |> arg_value("--project-file", "") + config.resultFile = args |> arg_value("--result", "") + + config.noGlobalVariables = args |> has_value("--no-global-variables") + config.noUnusedBlockArguments = args |> has_value("--no-unused-block-arguments") + config.noUnusedFunctionArguments = args |> has_value("--no-unused-function-arguments") + config.failOnLackOfAotExport = args |> has_value("--fail-on-lack-of-aot-export") + config.version2syntax = args |> has_value("--version-2-syntax") + config.strictProperties = args |> has_value("--strict-properties") + config.gen2MakeSyntax = args |> has_value("--gen2-make-syntax") + config.alwaysReportCandidatesThreshold = args |> arg_value("--always-report-candidates-threshold", 6) + config.maxInferPasses = args |> arg_value("--max-infer-passes", 50) + config.globalCompletion = args |> has_value("--global-completion") + config.autoFormat = args |> has_value("--auto-format") + + var res : ValidationResult + res |> compile(config) + res.dasRoot = get_das_root() + + let textRes = config.autoFormat ? res.autoFormat : res |> to_json() + + if (WRITE_RESULT_TO_FILE) { + mkdir("results") + let fileName = file_name_to_uri(config.originalFile) |> split("/") |> back() + fopen("results/{fileName}.json", "w") $(f) { + if (f == null) { + error("unable to open file 'results/{fileName}.json'") + unsafe(fio::exit(1)) + } + f |> fwrite(textRes) + } + } + + if (WRITE_RESULT) { + fopen(config.resultFile, "w") $(f) { + if (f == null) { + error("unable to open file '{config.resultFile}'") + unsafe(fio::exit(1)) + } + f |> fwrite(textRes) + } + } +} + + +struct DasError : CompletionAt { + what : string + extra : string + fixme : string + cerr : int + level : int // 0 error, 1 warning +} + +struct ModDeps { + mod : string + depth : int +} + +struct ModuleRequirement : CompletionAt { + mod : string + req : string + isPublic : bool + dependencies : array +} + + +struct ValidationResult { + tokens : array + errors : array + builtinMods : array + // processedMods : array + // allMods : array + completion : CompletionResult + dasRoot : string + autoFormat : string + requirements : array +} + + +def init_file_access(var access; fileAccessorRoots : table) { + for (rootName, rootPath in keys(fileAccessorRoots), values(fileAccessorRoots)) { + access |> add_file_access_root(rootName, rootPath) + } +} + +struct ErrorData { + message : string +} + +def compile(var res : ValidationResult; config : ValidateConfig) { + + fopen(config.file, "rb") $(fr) { + if (fr == null) { + res.errors |> emplace(DasError(what = "unable to open file '{config.file}'")) + return + } + fmap(fr) $(fileCont) { + + if (config.autoFormat) { + res.autoFormat = das_source_formatter::format_source(fileCont) + return + } + + var inscope access <- make_file_access(config.projectFile) + access |> set_file_source(config.originalFile, string(fileCont)) + access |> init_file_access(config.fileAccessRoots) + + using() $(var mg : ModuleGroup) { + var cp = CodeOfPolicies() + cp.no_global_variables = config.noGlobalVariables + cp.no_unused_block_arguments = config.noUnusedBlockArguments + cp.no_unused_function_arguments = config.noUnusedFunctionArguments + cp.fail_on_lack_of_aot_export = config.failOnLackOfAotExport + cp.version_2_syntax = config.version2syntax + cp.strict_properties = config.strictProperties + cp.gen2_make_syntax = config.gen2MakeSyntax + cp.always_report_candidates_threshold = config.alwaysReportCandidatesThreshold + cp.max_infer_passes = config.maxInferPasses + if (typeinfo has_field(cp)) { + cp.temp_one_liner_warning = false + } + cp.export_all = true + cp.aot_module = true + cp.completion = true + cp.no_optimizations = true + try { + compile_file(config.originalFile, access, unsafe(addr(mg)), cp) $(ok, program, error) { + + + if (ok && program != null) { + var inscope completion <- CompletionContext() + + var allDeps : table + + program |> for_each_require_declaration() $(mod, name, file, isPublic, at) { + var modReq <- ModuleRequirement( + mod = mod != null ? "{mod.name}" : "", + req = "{name}", + isPublic = isPublic + ) + allDeps |> insert(intptr(mod)) + modReq |> fill_at(at) + modReq.file = "{file}" + if (mod != null) { + var allMods : table + var mods : array> + allMods |> insert(intptr(mod)) + mods |> push((mod = mod, depth = 0)) // root dep is public for local module + while (length(mods) > 0) { + var mod2 = mods |> back() + mods |> pop() + mod2.mod |> module_for_each_dependency() $(dep, isPublic2) { + return if (!isPublic2 || dep == null) + + modReq.dependencies |> emplace(ModDeps(mod = "{dep.name}", depth = mod2.depth + 1)) + allDeps |> insert(intptr(dep)) + if (!allMods |> key_exists(intptr(dep))) { + mods |> push((mod = dep, depth = mod2.depth + 1)) + allMods |> insert(intptr(dep)) + } + } + } + } + + res.requirements |> emplace(modReq) + } + + allDeps |> insert(intptr(program |> get_this_module())) + + // if config.globalCompletion + // if true + // program_for_each_registered_module() <| $(mod) + // res.allMods |> push("{mod.name}") + // if !is_builtin(mod) + // return + // if key_exists(processed, "{mod.name}") + // return + // completion |> parse_module(mod) + // processed |> insert("{mod.name}") + + // else + + program_for_each_registered_module() $(mod) { + if (mod |> is_builtin()) { + res.builtinMods |> push("{mod.name}") + } + } + + var processed : table + if (true) { + program |> get_ptr() |> for_each_module() $(mod) { + // res.progMods |> push("{mod.name}") + // global completion now contains only $(builtin) module + if (config.globalCompletion && mod.name != "$") { + return + } + if (!config.globalCompletion && (!key_exists(allDeps, intptr(mod)) || mod.name == "$")) { + return + } + if (key_exists(processed, "{mod.name}")) { + return + } + completion |> parse_module(mod) + processed |> insert("{mod.name}") + } + } + + // res.processedMods <- [for it in keys(processed); it] + + var visitor <- new AstData() + visitor.res = unsafe(addr(res)) + visitor.completion = unsafe(addr(completion)) + make_visitor(*visitor) $(adapter) { + visit_modules(program, adapter) + } + unsafe { + delete visitor + } + + res.completion <- completion.result + } + + if (program == null) { + res.errors |> emplace(DasError(what = "{error}", file = config.file)) + return + } + + for (err in program.errors) { + res.errors |> emplace(DasError( + what = "{err.what}", + extra = "{err.extra}", + fixme = "{err.fixme}", + file = err.at.fileInfo != null ? "{err.at.fileInfo.name}" : "", + line = err.at.line |> int(), + column = err.at.column |> int(), + lineEnd = err.at.last_line |> int(), + columnEnd = err.at.last_column |> int(), + cerr = int(err.cerr))) + } + } + } recover { + let msg = empty(this_context().exception) ? "failed to compile" : "failed to compile: {this_context().exception}" + res.errors |> emplace(DasError(what = msg, file = config.file)) + } + } + } + } +} + + +struct DasToken : CompletionAt { + declAt : CompletionAt = CompletionAt(line = 0, column = 0) + kind : string + @optional mod : string + @optional name : string + @optional value : string + @optional alias : string + tdk : string + @optional parentTdk : string + @optional isUnused : bool + @optional isConst : bool +} + // ptr : string + + +[unused_argument(completion)] def DasToken(expr; var completion : CompletionContext ?) : DasToken { + var token = DasToken() + token.kind = "{expr.__rtti}" + if (expr._type != null) { + token.tdk = TypeDeclKey(expr._type) + token.alias = expr._type.alias |> string() + token.isConst = expr._type.flags.constant + *completion |> parse_typedecl(token.tdk, expr._type, false) + } + token |> fill_at(expr.at) + // token.ptr = intptr(expr |> get_ptr()) + return token +} + +class AstData : AstVisitor { + @do_not_delete res : ValidationResult? + @do_not_delete completion : CompletionContext? + skipExprs : array + visitedExprs : table + visitedTypedecls : table + // def override preVisitFunction(fun:FunctionPtr) : void + // debug(fun) + // def override preVisitExprTypeDecl(expr:smart_ptr) : void + // if expr == null + // return + // let tdk = TypeDeclKey(expr._type) + // let foundNew = *completion |> parse_typedecl(tdk, expr._type) + // print("{tdk} new? {foundNew}\n") + + // TODO: enum + // TODO: variant + // TODO: tuple + // TODO: typedef + + def annotation_to_tokens(annotations : AnnotationList) { + for (ann in annotations) { + let annType = (ann.annotation.isBasicStructureAnnotation ? "BasicStructureAnnotation" : + ann.annotation.isFunctionAnnotation ? "FunctionAnnotation" : + ann.annotation.isStructureTypeAnnotation ? "StructureTypeAnnotation" : + ann.annotation.isStructureAnnotation ? "StructureAnnotation" : + ann.annotation.isTypeAnnotation ? "TypeAnnotation" : + "Annotation") + var token <- DasToken() + token.kind = "annotation" + token.name = annType + token.mod = ann.annotation._module != null ? "{ann.annotation._module.name}" : "" + token |> fill_at(ann.at) + res.tokens |> emplace(token) + } + } + + // visitedStructures : table + + def override preVisitStructure(str : StructurePtr) : void { + if (str == null) { + return + } + str.annotations |> annotation_to_tokens() + let mod = str._module != null ? "{str._module.name}" : "" + var token <- DasToken() + token.kind = "struct" + token.name = "{str.name}" + token.mod = mod + token |> fill_at(str.at) + res.tokens |> emplace(token) + + var inscope fields : table + var parent = str.parent + while (parent != null) { + for (fld in parent.fields) { + fields |> insert("{fld.name}") + } + parent = parent.parent + } + + for (fld in str.fields) { + if (fields.key_exists("{fld.name}")) { + continue + } + var token2 <- DasToken() + token2.kind = "field" + token2.name = "{fld.name}" + token2.tdk = TypeDeclKey(fld._type) + token2.isConst = fld._type.flags.constant + *completion |> parse_typedecl(token2.tdk, fld._type, false) + token2.mod = mod + token2 |> fill_at(fld.at) + res.tokens |> emplace(token2) + } + } + + + [unused_argument(index, last)] def override preVisitExprMakeStructField(expr : ExprMakeStruct ?; index : int; decl : MakeFieldDeclPtr; last : bool) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + var token <- DasToken() + token.kind = "fieldDecl" + token.name = "{decl.name}" + token.value = decl.value |> describe() + token.tdk = TypeDeclKey(decl.value._type) + token.parentTdk = TypeDeclKey(expr._type) + // token.isConst = decl.flags. + *completion |> parse_typedecl(token.tdk, decl.value._type, false) + token |> fill_at(expr.at) + // token.declAt |> fill_at(decl.at) + res.tokens |> emplace(token) + } + + + def preVisitTypeDecl_(typ : TypeDeclPtr) : int { + if (typ == null) { + return -1 + } + let tdk = TypeDeclKey(typ) + let tdkHash = hash((tdk, hash(typ.at))) + if (key_exists(visitedTypedecls, tdkHash)) { + return -1 + } + visitedTypedecls |> insert(tdkHash) + *completion |> parse_typedecl(tdk, typ, false) + var token <- DasToken() + token.kind = "typedecl" + // token.name = tdk + token.tdk = tdk + token.alias = typ.alias |> string() + token.mod = typ._module != null ? "{typ._module.name}" : "" + token.isConst = typ.flags.constant + token |> fill_at(typ.at) + res.tokens |> emplace(token) + return length(res.tokens) - 1 + } + + ignoreNextTypeDecl : bool + + def override preVisitTypeDecl(typ : TypeDeclPtr) : void { + if (ignoreNextTypeDecl) { + return + } + preVisitTypeDecl_(typ) + } + + [unused_argument(name)] def override preVisitAlias(typ : TypeDeclPtr; name : das_string) : void { + ignoreNextTypeDecl = true + let idx = preVisitTypeDecl_(typ) + if (idx >= 0) { + // res.tokens[idx].name = "{name}" + res.tokens[idx].alias = "{name}" + } + } + + [unused_argument(name)] def override visitAlias(var typ : TypeDeclPtr; name : das_string) : TypeDeclPtr { + ignoreNextTypeDecl = false + return typ + } + + + def override preVisitExpression(expr : ExpressionPtr) : void { + let ptr = intptr(expr) + if (key_exists(visitedExprs, ptr)) { + return + } + visitedExprs |> insert(ptr) + + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + if (expr is ExprBlock || expr is ExprMakeBlock) {// ignore blocks, huge amount of text, no value + return + } + if (expr is ExprFor || expr is ExprIfThenElse) {// ignore too + return + } + if (expr is ExprFakeLineInfo || expr is ExprFakeContext) {// generated exprs + return + } + if (expr is ExprCall || expr is ExprLet || expr is ExprVar || expr is ExprOp1) {// will be parsed separately + return + } + if (expr is ExprRef2Value) {// ignore, we need sub exprs + return + } + if (expr is ExprAssume) {// ignore, manually check + return + } + if (expr is ExprMakeStruct) {// fields are parsed separately + return + } + if (expr is ExprConstBitfield) {// ignore, parsed separately + return + } + var token <- DasToken(expr, completion) + if (expr is ExprField) { + let field = expr as ExprField + token.name = "{field.name}" + if (field.field != null) { + token.declAt |> fill_at(field.field.at) + if (field.field._type != null) { + token.tdk = TypeDeclKey(field.field._type) + token.isConst = field.field._type.flags.constant + *completion |> parse_typedecl(token.tdk, field.field._type, false) + } + } + } elif (expr is ExprAddr) { + let exprAddr = expr as ExprAddr + token.name = "{exprAddr.target}" + if (exprAddr.func != null) { + token.declAt |> fill_at(exprAddr.func.at) + if (exprAddr.func.result != null) { + let resTdk = TypeDeclKey(exprAddr.func.result) + // token.isConst = exprAddr.func.result.flags.constant + *completion |> parse_typedecl(resTdk, exprAddr.func.result, false) + } + } + } elif (expr is ExprGoto) { + let gt = expr as ExprGoto + token.name = "{gt.labelName}" + } elif (expr is ExprLabel) { + let lbl = expr as ExprLabel + token.name = "{lbl.labelName}" + } else { + token.name = expr |> describe() + } + res.tokens |> emplace(token) + } + + // def preVisitExprLooksLikeCall_(expr:ExprLooksLikeCall?): void + // if length(skipExprs) > 0 || expr == null || expr.genFlags.generated + // return + // var token <- DasToken(expr, completion) + // token.name = "{expr.name}" + // token.value = "def {expr.name}" + // res.tokens |> emplace(token) + + // def override preVisitExprLooksLikeCall(expr : smart_ptr) : void + // if length(skipExprs) > 0 || expr == null || expr.genFlags.generated + // return + // preVisitExprLooksLikeCall_(expr |> get_ptr()) + + def preVisitExprCallFunc(expr : ExprCallFunc?) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + var token <- DasToken(expr, completion) + token.name = "{expr.name}" |> fix_fn_name() + token.value = "def {token.name}(" + if (expr.func != null) { + token.mod = expr.func._module != null ? "{expr.func._module.name}" : "" + token.declAt |> fill_at(expr.func.at) + var first = true + for (a in expr.func.arguments) { + token.value += first ? "" : ", " + first = false + token.value += "{a.name}" + if (a._aka != "") { + token.value += " aka {a._aka}" + } + if (a._type != null) { + let atdk = TypeDeclKey(a._type) + token.value += ": {atdk}" + *completion |> parse_typedecl(atdk, a._type, false) + } + if (a.init != null) { + token.value += " = {a.init |> describe()}" + } + } + token.value += ")" + if (expr.func.result != null) { + let restdk = TypeDeclKey(expr.func.result) + token.value += ": {restdk}" + *completion |> parse_typedecl(restdk, expr.func.result, false) + } + } + res.tokens |> emplace(token) + } + + def override preVisitExprCall(expr : ExprCall?) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + preVisitExprCallFunc(expr) + } + + def override preVisitExprLet(expr : ExprLet?) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + for (v in expr.variables) { + var token <- DasToken(expr, completion) + token.name = "{v.name}" + if (v._type != null) { + token.tdk = TypeDeclKey(v._type) + token.alias = v._type.alias |> string() + token.isConst = v._type.flags.constant + *completion |> parse_typedecl(token.tdk, v._type, false) + } + token |> fill_at(v.at) + if (v.init != null) { + token.value = v.init |> describe() + } + token.mod = v._module != null ? "{v._module.name}" : "" + token.isUnused = !v.flags.marked_used && v.isAccessUnused + res.tokens |> emplace(token) + } + } + + [unused_argument(last)] def override preVisitExprForVariable(expr : ExprFor ?; svar : VariablePtr; last : bool) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated || svar.flags.generated) { + return + } + var token <- DasToken(expr, completion) + token.name = "{svar.name}" + if (svar._type != null) { + token.tdk = TypeDeclKey(svar._type) + token.alias = svar._type.alias |> string() + token.isConst = svar._type.flags.constant + } + *completion |> parse_typedecl(token.tdk, svar._type, false) + token |> fill_at(svar.at) + if (svar.init != null) { + token.value = svar.init |> describe() + } + token.mod = svar._module != null ? "{svar._module.name}" : "" + token.isUnused = !svar.flags.marked_used && svar.isAccessUnused + res.tokens |> emplace(token) + } + + + def override preVisitExprVar(expr : ExprVar?) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + if (expr.variable == null || expr.variable.flags.generated) { + return + } + var token <- DasToken(expr, completion) + token.name = "{expr.variable.name}" + token.declAt |> fill_at(expr.variable.at) + token.mod = expr.variable._module != null ? "{expr.variable._module.name}" : "" + if (token.mod == "") { + token.mod = funcModule + } + if (expr.variable.init != null) { + token.value = expr.variable.init |> describe() + } + if (expr.variable._type != null) { + token.tdk = TypeDeclKey(expr.variable._type) + token.isConst = expr.variable._type.flags.constant + token.alias = expr.variable._type.alias |> string() + *completion |> parse_typedecl(token.tdk, expr.variable._type, false) + } + // if expr._type != null + // token.isConst = expr._type.flags.constant + // token.tdk = TypeDeclKey(expr._type) + // *completion |> parse_typedecl(token.tdk, expr._type) + res.tokens |> emplace(token) + } + + // this code breaks array comprehension hints, see generateComprehension(...) for more info + // def override preVisitExprBlock(blk : smart_ptr) : void + // if blk.genFlags.generated + // skipExprs |> push(true) + // return + // if length(skipExprs) > 0 + // return + + // def override visitExprBlock(var blk:smart_ptr) : ExpressionPtr + // if blk.genFlags.generated + // skipExprs |> pop() + // return <- blk + + funcModule : string + + def override preVisitFunction(fun : FunctionPtr) : void { + if (fun.flags.generated && !fun.flags._generator && !fun.flags._lambda) { + skipExprs |> push(true) + return + } + if (length(skipExprs) > 0) { + return + } + if (fun.flags._lambda) { + return + } + let genericFunc = fun.isGeneric + + fun.annotations |> annotation_to_tokens() + + var token <- DasToken() + token.kind = "func" + token |> fill_at(fun.at) + token.name = "{fun.name}" |> fix_fn_name() + if (fun.result != null) { + let tdk = TypeDeclKey(fun.result) + token.tdk = tdk + token.alias = fun.result.alias |> string() + token.isConst = fun.result.flags.constant + *completion |> parse_typedecl(tdk, fun.result, genericFunc) + } + token.mod = fun._module != null ? "{fun._module.name}" : "" + funcModule = token.mod + token.declAt |> fill_at(fun.atDecl) + token.value = "def {token.name}(" + var first = true + for (a in fun.arguments) { + token.value += first ? "" : ", " + first = false + token.value += "{a.name}" + if (a._aka != "") { + token.value += " aka {a._aka}" + } + if (a._type != null) { + let atdk = TypeDeclKey(a._type) + token.value += ": {atdk}" + *completion |> parse_typedecl(atdk, a._type, genericFunc) + } + if (a.init != null) { + token.value += " = {a.init |> describe()}" + } + } + token.value += ")" + if (fun.result != null) { + let restdk = TypeDeclKey(fun.result) + token.value += ": {restdk}" + *completion |> parse_typedecl(restdk, fun.result, genericFunc) + } + res.tokens |> emplace(token) + } + + + [unused_argument(lastArg)] def override preVisitFunctionArgument(fun : FunctionPtr; arg : VariablePtr; lastArg : bool) : void { + if (length(skipExprs) > 0 || fun == null || fun.flags.generated || arg.flags.generated) { + return + } + var token <- DasToken() + token.kind = "func_arg" + token.name = "{arg.name}" + if (arg.init != null) { + token.value = "{arg.init |> describe()}" + } + token.tdk = TypeDeclKey(arg._type) + token.alias = arg._type.alias |> string() + token.isConst = arg._type.flags.constant + *completion |> parse_typedecl(token.tdk, arg._type, fun.isGeneric) + token.mod = fun._module != null ? "{fun._module.name}" : "" + token |> fill_at(arg.at) + res.tokens |> emplace(token) + } + + def override visitFunction(var fun : FunctionPtr) : FunctionPtr { + if (fun.flags.generated && !fun.flags._generator && !fun.flags._lambda) { + if (skipExprs.length() > 0) { + skipExprs |> pop() + } + } + return fun + } + + [unused_argument(lastArg)] def override preVisitExprBlockArgument(blk : ExprBlock ?; arg : VariablePtr; lastArg : bool) : void { + if (length(skipExprs) > 0 || blk == null || blk.genFlags.generated || arg.flags.generated) { + return + } + blk.annotations |> annotation_to_tokens() + var token <- DasToken() + token.kind = "block_arg" + token.name = "{arg.name}" + if (arg.init != null) { + token.value = "{arg.init |> describe()}" + } + token.tdk = TypeDeclKey(arg._type) + token.alias = arg._type.alias |> string() + token.isConst = arg._type.flags.constant + *completion |> parse_typedecl(token.tdk, arg._type, false) + token.mod = arg._module != null ? "{arg._module.name}" : "" + if (token.mod == "") { + token.mod = funcModule + } + token |> fill_at(arg.at) + res.tokens |> emplace(token) + } + + def override preVisitExprOp1(expr : ExprOp1?) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + preVisitExprCallFunc(expr) + } + + def override preVisitExprOp2(expr : ExprOp2?) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + preVisitExprCallFunc(expr) + } + + // def override preVisitExprTypeInfo(expr : smart_ptr) : void + // if length(skipExprs) > 0 || expr == null || expr.genFlags.generated + // return + // if length(skipExprs) > 0 || expr == null || expr.genFlags.generated + // return + // var name = "typeinfo" + // var desc = "{name}(" + // if !empty(expr.trait) + // desc += "{expr.trait}" + // if !empty(expr.subtrait) + // desc += "<{expr.subtrait}" + // if !empty(expr.extratrait) + // desc += "; {expr.extratrait}" + // desc += "> " + // else + // desc += " " + // var typ = expr._type != null ? describe(expr._type) : "" + // if expr.subexpr != null && expr.subexpr._type != null + // desc += "arg1 : {describe(expr.subexpr._type)}" + // elif expr.typeexpr != null + // desc += "type<{describe(expr.typeexpr)}>" + // desc += ")" + // if typ != "" + // desc += " : {typ}" + // var token <- DasToken() + // token.kind = "typeinfo" + // token.name = name + // token.value = desc + // token.tdk = TypeDeclKey(expr._type) + // token.isConst = expr._type.flags.constant + // *completion |> parse_typedecl(token.tdk, expr._type) + // token |> fill_at(expr.at) + // res.tokens |> emplace(token) + + + // def override preVisitTypeDecl(typ:TypeDeclPtr) : void + // debug(typ) + // TODO: + // - variables + // - functions + // - operators (Op1, Op2) + + def override preVisitExprAssume(expr : ExprAssume?) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + var token <- DasToken(expr, completion) + token.name = "{expr.alias}" + if (expr.subexpr != null) { + token.value = "{expr.subexpr |> describe()}" + token |> fill_at(expr.subexpr.at) + if (expr.subexpr._type != null) { + token.tdk = TypeDeclKey(expr.subexpr._type) + token.isConst = expr.subexpr._type.flags.constant + token.alias = expr.subexpr._type.alias |> string() + *completion |> parse_typedecl(token.tdk, expr.subexpr._type, false) + } + } + token |> fill_at(expr.at) + res.tokens |> emplace(token) + } + + def override preVisitExprMakeStruct(expr : ExprMakeStruct?) { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + var token <- DasToken(expr, completion) + token.name = expr.describe() + if (expr.constructor != null) { + fill_at(token.declAt, expr.constructor.atDecl) + } + res.tokens |> emplace(token) + } + + def override preVisitExprConstBitfield(expr : ExprConstBitfield?) : void { + if (length(skipExprs) > 0 || expr == null || expr.genFlags.generated) { + return + } + var token <- DasToken(expr, completion) + token.name = "{expr.describe()}" + token.value = "{expr.value}" + // if (expr.bitfieldType != null) { + // token.tdk = TypeDeclKey(expr.bitfieldType) + // token.alias = "{expr.bitfieldType.alias}" + // *completion |> parse_typedecl(token.tdk, expr.bitfieldType, false) + // } else { + token.tdk = TypeDeclKey(expr._type) + *completion |> parse_typedecl(token.tdk, expr._type, false) + token.alias = expr._type.alias.string() + // if (expr.bitfieldType != null) { + // token.tdk = "{token.tdk} --- {TypeDeclKey(expr.bitfieldType)} {expr.bitfieldType.flags.constant}" + // token.alias = "{token.alias} --- {expr.bitfieldType.alias}" + // *completion |> parse_typedecl(token.tdk, expr.bitfieldType, false) + // } + token.isConst = expr._type.flags.constant + token |> fill_at(expr.at) + res.tokens |> emplace(token) + } +} diff --git a/server/src/server.ts b/server/src/server.ts index bb59d78..104d998 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1588,6 +1588,7 @@ interface VersionScriptDir { // (up to the next entry's minVersion). The lowest minVersion is also the // fallback for unparseable --version output (old binaries without --version). const VERSION_SCRIPT_DIRS: VersionScriptDir[] = [ + { minVersion: [0, 6, 3], dir: '0.6.3' }, { minVersion: [0, 6, 1], dir: '0.6.1' }, { minVersion: [0, 0, 0], dir: '0.6.0' }, ]