From f8332b50f8203d5cdff9345c9c28ba537fc48910 Mon Sep 17 00:00:00 2001 From: ryuukk Date: Mon, 13 Feb 2023 15:43:55 +0100 Subject: [PATCH] save progress --- dsymbol/src/dsymbol/coloredlogger.d | 533 ++++++++++++++++++++++ dsymbol/src/dsymbol/conversion/first.d | 227 +++++++++ dsymbol/src/dsymbol/conversion/second.d | 188 ++++++++ dsymbol/src/dsymbol/type_lookup.d | 15 + tests/tc_templates_resolve/expected_1.txt | 2 + tests/tc_templates_resolve/expected_2.txt | 3 + tests/tc_templates_resolve/expected_3.txt | 0 tests/tc_templates_resolve/file1.d | 96 ++++ tests/tc_templates_resolve/file2.d | 47 ++ tests/tc_templates_resolve/file3.d | 105 +++++ tests/tc_templates_resolve/run.sh | 14 + 11 files changed, 1230 insertions(+) create mode 100644 dsymbol/src/dsymbol/coloredlogger.d create mode 100644 tests/tc_templates_resolve/expected_1.txt create mode 100644 tests/tc_templates_resolve/expected_2.txt create mode 100644 tests/tc_templates_resolve/expected_3.txt create mode 100644 tests/tc_templates_resolve/file1.d create mode 100644 tests/tc_templates_resolve/file2.d create mode 100644 tests/tc_templates_resolve/file3.d create mode 100644 tests/tc_templates_resolve/run.sh diff --git a/dsymbol/src/dsymbol/coloredlogger.d b/dsymbol/src/dsymbol/coloredlogger.d new file mode 100644 index 0000000..f4148f5 --- /dev/null +++ b/dsymbol/src/dsymbol/coloredlogger.d @@ -0,0 +1,533 @@ +module dsymbol.coloredlogger; + +import std.logger; +import std.stdio; +import std.concurrency; +import std.datetime; +import std.stdio; + + +import std.algorithm : map, filter, joiner; +import std.array : join, split; +import std.conv : to; +import std.format : format; +import std.functional : not; +import std.range : ElementType, empty, front, popFront; +import std.regex : ctRegex, Captures, replaceAll; +import std.string : toUpper; +import std.traits : EnumMembers; + +version (unittest) +{ + import core.thread : Thread; + import core.time : msecs; + import std.conv : to; + import std.process : environment; + import std.stdio : writeln, write; + import unit_threaded; +} +/// Available Colors +enum AnsiColor +{ + black = 30, + red = 31, + green = 32, + yellow = 33, + blue = 34, + magenta = 35, + cyan = 36, + lightGray = 37, + defaultColor = 39, + darkGray = 90, + lightRed = 91, + lightGreen = 92, + lightYellow = 93, + lightBlue = 94, + lightMagenta = 95, + lightCyan = 96, + white = 97 +} + +/// Available Styles +enum Style +{ + bold = 1, + dim = 2, + underlined = 4, + blink = 5, + reverse = 7, + hidden = 8 +} + +/// Internal structure to style a string +struct StyledString +{ + private string unformatted; + private int[] befores; + private int[] afters; + /// Create a styled string + public this(string unformatted) + { + this.unformatted = unformatted; + } + + private StyledString addPair(int before, int after) + { + befores ~= before; + afters ~= after; + return this; + } + + StyledString setForeground(int color) + { + return addPair(color, 0); + } + + StyledString setBackground(int color) + { + return addPair(color + 10, 0); + } + + /// Add styling to a string + StyledString addStyle(int style) + { + return addPair(style, 0); + } + + string toString() const @safe + { + auto prefix = befores.map!(a => "\033[%dm".format(a)).join(""); + auto suffix = afters.map!(a => "\033[%dm".format(a)).join(""); + return "%s%s%s".format(prefix, unformatted, suffix); + } + + /// Concatenate with another string + string opBinary(string op : "~")(string rhs) @safe + { + return toString ~ rhs; + } +} + +/// Truecolor string +struct RGBString +{ + private string unformatted; + /// Colorinformation + struct RGB + { + /// Red component 0..256 + ubyte r; + /// Green component 0..256 + ubyte g; + /// Blue component 0..256 + ubyte b; + } + + private RGB* foreground; + private RGB* background; + /// Create RGB String + this(string unformatted) + { + this.unformatted = unformatted; + } + + /// Set color + auto rgb(ubyte r, ubyte g, ubyte b) + { + this.foreground = new RGB(r, g, b); + return this; + } + + /// Set background color + auto onRgb(ubyte r, ubyte g, ubyte b) + { + this.background = new RGB(r, g, b); + return this; + } + + string toString() @safe + { + auto res = ""; + if (foreground != null) + { + res = "\033[38;2;%s;%s;%sm".format(foreground.r, foreground.g, foreground.b) ~ res; + } + if (background != null) + { + res = "\033[48;2;%s;%s;%sm".format(background.r, background.g, background.b) ~ res; + } + res ~= unformatted; + if (foreground != null || background != null) + { + res ~= "\033[0m"; + } + return res; + } +} + +/// Convinient helper function +string rgb(string s, ubyte r, ubyte g, ubyte b) +{ + return RGBString(s).rgb(r, g, b).toString; +} + +/// Convinient helper function +string onRgb(string s, ubyte r, ubyte g, ubyte b) +{ + return RGBString(s).onRgb(r, g, b).toString; +} + +@system @("rgb") unittest +{ + import std.experimental.color : RGBA8, convertColor; + import std.experimental.color.hsx : HSV; + + writeln("red: ", "r".rgb(255, 0, 0).onRgb(0, 255, 0)); + writeln("green: ", "g".rgb(0, 255, 0).onRgb(0, 0, 255)); + writeln("blue: ", "b".rgb(0, 0, 255).onRgb(255, 0, 0)); + writeln("mixed: ", ("withoutColor" ~ "red".red.to!string ~ "withoutColor").bold); + for (int r = 0; r <= 255; r += 10) + { + for (int g = 0; g <= 255; g += 3) + { + write(" ".onRgb(cast(ubyte) r, cast(ubyte) g, cast(ubyte)(255 - r))); + } + writeln; + } + + int delay = environment.get("DELAY", "0").to!int; + for (int j = 0; j < 255; j += 1) + { + for (int i = 0; i < 255; i += 3) + { + auto c = HSV!ubyte(cast(ubyte)(i - j), 0xff, 0xff); + auto rgb = convertColor!RGBA8(c).tristimulus; + write(" ".onRgb(rgb[0].value, rgb[1].value, rgb[2].value)); + } + Thread.sleep(delay.msecs); + write("\r"); + } + writeln; +} + +@system @("styledstring") unittest +{ + foreach (immutable color; [EnumMembers!AnsiColor]) + { + auto colorName = "%s".format(color); + writeln(StyledString(colorName).setForeground(color)); + } + foreach (immutable color; [EnumMembers!AnsiColor]) + { + auto colorName = "bg%s".format(color); + writeln(StyledString(colorName).setBackground(color)); + } + foreach (immutable style; [EnumMembers!Style]) + { + auto styleName = "%s".format(style); + writeln(StyledString(styleName).addStyle(style)); + } + + writeln("boldUnderlined".bold.underlined); + writeln("redOnGreenReverse".red.onGreen.reverse); +} + +@system @("styledstring ~") unittest +{ + ("test".red ~ "blub").should == "\033[31mtest\033[0mblub"; +} + +/// Create `color` and `onColor` functions for all enum members. e.g. "abc".green.onRed +auto colorMixin(T)() +{ + string res = ""; + foreach (immutable color; [EnumMembers!T]) + { + auto t = typeof(T.init).stringof; + auto c = "%s".format(color); + res ~= "auto %1$s(string s) { return StyledString(s).setForeground(%2$s.%1$s); }\n".format(c, + t); + res ~= "auto %1$s(StyledString s) { return s.setForeground(%2$s.%1$s); }\n".format(c, t); + string name = c[0 .. 1].toUpper ~ c[1 .. $]; + res ~= "auto on%3$s(string s) { return StyledString(s).setBackground(%2$s.%1$s); }\n".format(c, + t, name); + res ~= "auto on%3$s(StyledString s) { return s.setBackground(%2$s.%1$s); }\n".format(c, + t, name); + } + return res; +} + +/// Create `style` functions for all enum mebers, e.g. "abc".bold +auto styleMixin(T)() +{ + string res = ""; + foreach (immutable style; [EnumMembers!T]) + { + auto t = typeof(T.init).stringof; + auto s = "%s".format(style); + res ~= "auto %1$s(string s) { return StyledString(s).addStyle(%2$s.%1$s); }\n".format(s, t); + res ~= "auto %1$s(StyledString s) { return s.addStyle(%2$s.%1$s); }\n".format(s, t); + } + return res; +} + +mixin(colorMixin!AnsiColor); +mixin(styleMixin!Style); + +@system @("api") unittest +{ + "redOnGreen".red.onGreen.writeln; + "redOnYellowBoldUnderlined".red.onYellow.bold.underlined.writeln; + "bold".bold.writeln; + "test".writeln; +} + +/// Calculate length of string excluding all formatting escapes +ulong unformattedLength(string s) +{ + enum State + { + NORMAL, + ESCAPED, + } + + auto state = State.NORMAL; + ulong count = 0; + foreach (c; s) + { + switch (state) + { + case State.NORMAL: + if (c == 0x1b) + { + state = State.ESCAPED; + } + else + { + count++; + } + break; + case State.ESCAPED: + if (c == 'm') + { + state = State.NORMAL; + } + break; + default: + throw new Exception("Illegal state"); + } + } + return count; +} + +/++ Range to work with ansi escapes. The ESC[ parts and m must be + + already removed and the numbers need to be converted to uints. + + See https://en.wikipedia.org/wiki/ANSI_escape_code + +/ +auto tokenize(Range)(Range parts) +{ + struct TokenizeResult(Range) + { + Range parts; + ElementType!(Range)[] next; + this(Range parts) + { + this.parts = parts; + tokenizeNext(); + } + + private void tokenizeNext() + { + next = []; + if (parts.empty) + { + return; + } + switch (parts.front) + { + case 38: + case 48: + next ~= 38; + parts.popFront; + switch (parts.front) + { + case 2: + next ~= 2; + parts.popFront; + next ~= parts.front; + parts.popFront; + next ~= parts.front; + parts.popFront; + next ~= parts.front; + parts.popFront; + break; + case 5: + next ~= 5; + parts.popFront; + next ~= parts.front; + parts.popFront; + break; + default: + throw new Exception("Only [38,48];[2,5] are supported but got %s;%s".format(next[0], + parts.front)); + } + break; + case 0: .. case 37: + case 39: .. case 47: + case 49: + case 51: + .. case 55: + case 60: .. case 65: + case 90: .. case 97: + case 100: .. case 107: + next ~= parts.front; + parts.popFront; + break; + default: + throw new Exception("Only colors are supported"); + } + } + + auto front() + { + return next; + } + + bool empty() + { + return next == null; + } + + void popFront() + { + tokenizeNext(); + } + } + + return TokenizeResult!(Range)(parts); +} + +@system @("ansi tokenizer") unittest +{ + [38, 5, 2, 38, 2, 1, 2, 3, 36, 1, 2, 3, 4].tokenize.should == ([ + [38, 5, 2], [38, 2, 1, 2, 3], [36], [1], [2], [3], [4] + ]); +} + +/++ Remove classes of ansi escapes from a styled string. + +/ +string filterAnsiEscapes(alias predicate)(string s) +{ + string withFilters(Captures!string c) + { + auto parts = c[1].split(";").map!(a => a.to!uint) + .tokenize + .filter!(p => predicate(p)); + if (parts.empty) + { + return ""; + } + else + { + return "\033[" ~ parts.joiner.map!(a => "%d".format(a)).join(";") ~ "m"; + } + } + + alias r = ctRegex!"\033\\[(.*?)m"; + return s.replaceAll!(withFilters)(r); +} + +/// Predicate to select foreground color ansi escapes +bool foregroundColor(uint[] token) +{ + return token[0] >= 30 && token[0] <= 38; +} + +/// Predicate to select background color ansi escapes +bool backgroundColor(uint[] token) +{ + return token[0] >= 40 && token[0] <= 48; +} + +/// Predicate to select style ansi escapes +bool style(uint[] token) +{ + return token[0] >= 1 && token[0] <= 29; +} + +/// Predicate select nothing +bool none(uint[]) +{ + return false; +} + +/// Predicate to select all +bool all(uint[]) +{ + return true; +} + +@system @("configurable strip") unittest +{ + import unit_threaded; + import std.functional : not; + + "test".red.onGreen.bold.toString.filterAnsiEscapes!(foregroundColor).should == "\033[31mtest"; + "test".red.onGreen.bold.toString.filterAnsiEscapes!(not!foregroundColor) + .should == "\033[42m\033[1mtest\033[0m\033[0m\033[0m"; + "test".red.onGreen.bold.toString.filterAnsiEscapes!(style).should == "\033[1mtest"; + "test".red.onGreen.bold.toString.filterAnsiEscapes!(none).should == "test"; + "test".red.onGreen.bold.toString.filterAnsiEscapes!(all) + .should == "\033[31m\033[42m\033[1mtest\033[0m\033[0m\033[0m"; + "test".red.onGreen.bold.toString.filterAnsiEscapes!(backgroundColor).should == "\033[42mtest"; +} + +/// Add fillChar to the right of the string until width is reached +auto leftJustifyFormattedString(string s, ulong width, dchar fillChar = ' ') +{ + auto res = s; + const currentWidth = s.unformattedLength; + for (long i = currentWidth; i < width; ++i) + { + res ~= fillChar; + } + return res; +} + +@system @("leftJustifyFormattedString") unittest +{ + import unit_threaded; + + "test".red.toString.leftJustifyFormattedString(10).should == "\033[31mtest\033[0m "; +} + +/// Add fillChar to the left of the string until width is reached +auto rightJustifyFormattedString(string s, ulong width, char fillChar = ' ') +{ + auto res = s; + const currentWidth = s.unformattedLength; + for (long i = currentWidth; i < width; ++i) + { + res = fillChar ~ res; + } + return res; +} + +@system @("rightJustifyFormattedString") unittest +{ + "test".red.toString.rightJustifyFormattedString(10).should == (" \033[31mtest\033[0m"); +} + +/// Force a style on possible preformatted text +auto forceStyle(string text, Style style) { + return "\033[%d".format(style.to!int) ~ "m" ~ text.split("\033[0m").join("\033[0;%d".format(style.to!int) ~"m") ~ "\033[0m"; +} + +@("forceStyle") unittest +{ + auto splitt = "1es2eses3".split("es").filter!(not!(empty)); + splitt.should == ["1", "2", "3"]; + string s = "noformatting%snoformatting".format("red".red).forceStyle(Style.reverse); + writeln(s); + s.should == "\033[7mnoformatting\033[31mred\033[0;7mnoformatting\033[0m"; +} diff --git a/dsymbol/src/dsymbol/conversion/first.d b/dsymbol/src/dsymbol/conversion/first.d index e488566..8006aae 100644 --- a/dsymbol/src/dsymbol/conversion/first.d +++ b/dsymbol/src/dsymbol/conversion/first.d @@ -231,10 +231,151 @@ final class FirstPass : ASTVisitor currentSymbol.addChild(symbol, true); symbol.acSymbol.protection = protection.current; } + + + void processIdentifierOrTemplate(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, VariableContext.TypeInstance* current, IdentifierOrTemplateInstance ioti) + { + if (ioti.identifier != tok!"") + { + current.chain ~= ioti.identifier.text; + } + else if (ioti.templateInstance) + { + processTemplateInstance(symbol, lookup, ctx, current, ioti.templateInstance); + } + } + + void processTypeIdentifierPart(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, VariableContext.TypeInstance* current, TypeIdentifierPart tip) + { + + auto newArg = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + newArg.parent = current; + current.args ~= newArg; + + if (tip.identifierOrTemplateInstance) + { + processIdentifierOrTemplate(symbol, lookup, ctx, newArg, tip.identifierOrTemplateInstance); + } + + if (tip.typeIdentifierPart) + { + error("i should probably handle this"); + } + } + + void processTemplateInstance(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, VariableContext.TypeInstance* current, TemplateInstance ti) + { + if (ti.identifier != tok!"") + { + current.chain ~= ti.identifier.text; + warning("type: ", ti.identifier.text); + } + else assert(0, "templateinstance type missing"); + + if (ti.templateArguments) + { + if (ti.templateArguments.templateArgumentList) + { + foreach(i, targ; ti.templateArguments.templateArgumentList.items) + { + if (targ.type is null) continue; + if (targ.type.type2 is null) continue; + + auto part = targ.type.type2.typeIdentifierPart; + if (part is null) continue; + + auto newArg = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + newArg.parent = current; + current.args ~= newArg; + + if (part.identifierOrTemplateInstance) + { + processIdentifierOrTemplate(symbol, lookup, ctx, newArg, part.identifierOrTemplateInstance); + } + if (part.typeIdentifierPart) + { + if (part.typeIdentifierPart.identifierOrTemplateInstance) + { + processIdentifierOrTemplate(symbol, lookup, ctx, newArg, part.typeIdentifierPart.identifierOrTemplateInstance); + } + + if (part.typeIdentifierPart) + { + error("i should probably handle this"); + } + } + } + } + else if (ti.templateArguments.templateSingleArgument) + { + auto singleArg = ti.templateArguments.templateSingleArgument; + auto arg = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + arg.parent = current; + arg.name = singleArg.token.text; + arg.chain ~= arg.name; + current.args ~= arg; + } + } + } + + void buildChain(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, TypeIdentifierPart tip) + { + if (tip.identifierOrTemplateInstance) + { + buildChainTemplateOrIdentifier(symbol, lookup, ctx, tip.identifierOrTemplateInstance); + } + if (tip.typeIdentifierPart) + buildChain(symbol, lookup, ctx, tip.typeIdentifierPart); + } + + void buildChainTemplateOrIdentifier(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, IdentifierOrTemplateInstance iot) + { + auto crumb = iot.identifier; + if (crumb != tok!"") + { + warning(" c: ", crumb.text); + lookup.breadcrumbs.insert(istring(crumb.text)); + } + + if (iot.templateInstance) + { + if (iot.templateInstance.identifier != tok!"") + lookup.breadcrumbs.insert(istring(iot.templateInstance.identifier.text)); + } + } + + void traverseUnaryExpression( SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, UnaryExpression ue) + { + warning("traverse unary"); + if (PrimaryExpression pe = ue.primaryExpression) + { + warning("primary expression"); + if (pe.identifierOrTemplateInstance) + { + buildChainTemplateOrIdentifier(symbol, lookup, ctx, pe.identifierOrTemplateInstance); + } + } + if (IdentifierOrTemplateInstance iot = ue.identifierOrTemplateInstance) + { + warning("has iot"); + + auto crumb = iot.identifier; + if (crumb != tok!"") + { + warning(": ", crumb.text); + lookup.breadcrumbs.insert(istring(crumb.text)); + } + } + + if(ue.unaryExpression) traverseUnaryExpression(symbol, lookup, ctx, ue.unaryExpression); + } override void visit(const VariableDeclaration dec) { assert (currentSymbol); + + warning("current: ", currentSymbol.acSymbol.name); + foreach (declarator; dec.declarators) { SemanticSymbol* symbol = allocateSemanticSymbol( @@ -255,9 +396,20 @@ final class FirstPass : ASTVisitor // TODO: remove this cast. See the note on structFieldTypes structFieldTypes.insert(cast() dec.type); } + + auto lookup = symbol.typeLookups.front; + + if (dec.type && dec.type.type2 && dec.type.type2.typeIdentifierPart) + { + TypeIdentifierPart typeIdentifierPart = cast(TypeIdentifierPart) dec.type.type2.typeIdentifierPart; + + lookup.ctx.root = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + processTypeIdentifierPart(symbol, lookup, &lookup.ctx, lookup.ctx.root, typeIdentifierPart); + } } if (dec.autoDeclaration !is null) { + warning("is auto!"); foreach (part; dec.autoDeclaration.parts) { SemanticSymbol* symbol = allocateSemanticSymbol( @@ -270,6 +422,81 @@ final class FirstPass : ASTVisitor currentSymbol.addChild(symbol, true); currentScope.addSymbol(symbol.acSymbol, false); + warning(" part: ", symbol.acSymbol.name); + scope(exit) warning("crumbs: ", symbol.typeLookups.front.breadcrumbs[]); + + // for auto declaration, we'll properly traverse the initializer + // and set the proper crumbs instead of using just the first one + // so we can handle things like cast/templates + auto lookup = symbol.typeLookups.front; + lookup.breadcrumbs.clear(); + + auto initializer = part.initializer.nonVoidInitializer; + if (initializer && initializer.assignExpression) + { + UnaryExpression unary = cast(UnaryExpression) initializer.assignExpression; + if (unary) + { + if (CastExpression castExpression = unary.castExpression) + { + warning("cast expression"); + if (castExpression.type && castExpression.type.type2) + { + Type2 t2 = castExpression.type.type2; + if (t2 && t2.typeIdentifierPart) + { + buildChain(symbol, lookup, &lookup.ctx, t2.typeIdentifierPart); + } + } + } + else if (FunctionCallExpression fc = unary.functionCallExpression) + { + warning("functioncall expression ", fc.type, " ", fc.unaryExpression," ", fc.templateArguments); + //if (fc.unaryExpression) + // traverseUnaryExpression(symbol, lookup, &lookup.ctx, unary); + unary = fc.unaryExpression; + } + } + + if (unary) + { + // build chain + traverseUnaryExpression(symbol, lookup, &lookup.ctx, unary); + // needs to be reversed because it got added in order (right->left) + auto crumbs = &lookup.breadcrumbs; + istring[] result; + foreach(c; *crumbs) + { + result ~= c; + } + crumbs.clear(); + foreach_reverse(c; result) + { + lookup.breadcrumbs.insert(c); + } + + // check template + if (IdentifierOrTemplateInstance iot = unary.identifierOrTemplateInstance) + { + + auto crumb = iot.identifier; + if (crumb != tok!"") + { + lookup.breadcrumbs.insert(istring(crumb.text)); + } + else if (iot.templateInstance) + { + auto tic = iot.templateInstance.identifier; + warning("template! ", tic.text); + lookup.breadcrumbs.insert(istring(tic.text)); + + lookup.ctx.root = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + processTemplateInstance(symbol, lookup, &lookup.ctx, lookup.ctx.root, iot.templateInstance); + } + } + } + } + if (currentSymbol.acSymbol.kind == CompletionKind.structName || currentSymbol.acSymbol.kind == CompletionKind.unionName) { diff --git a/dsymbol/src/dsymbol/conversion/second.d b/dsymbol/src/dsymbol/conversion/second.d index 433cd66..d655c1c 100644 --- a/dsymbol/src/dsymbol/conversion/second.d +++ b/dsymbol/src/dsymbol/conversion/second.d @@ -28,6 +28,7 @@ import dsymbol.type_lookup; import dsymbol.deferred; import dsymbol.import_; import dsymbol.modulecache; +import dsymbol.coloredlogger; import std.experimental.allocator; import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.logger; @@ -55,6 +56,24 @@ void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCac resolveType(currentSymbol.acSymbol, currentSymbol.typeLookups, moduleScope, cache); } + if (currentSymbol.acSymbol.type && currentSymbol.typeLookups.length > 0) + { + TypeLookup* lookup = currentSymbol.typeLookups.front; + if (lookup.ctx.root) + { + auto type = currentSymbol.acSymbol.type; + if (type.kind == structName || type.kind == className && lookup.ctx.root.args.length > 0) + { + DSymbol*[string] mapping; + int depth; + resolveTemplate(currentSymbol.acSymbol, type, lookup, lookup.ctx.root, moduleScope, cache, depth, mapping); + } + } + } + else + { + warning("no type: ", currentSymbol.acSymbol.name," ", currentSymbol.acSymbol.kind); + } break; case importSymbol: if (currentSymbol.acSymbol.type is null) @@ -99,6 +118,175 @@ void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCac } } +DSymbol* createTypeWithTemplateArgs(DSymbol* type, TypeLookup* lookup, VariableContext.TypeInstance* ti, ref ModuleCache cache, Scope* moduleScope, ref int depth, DSymbol*[string] m) +{ + assert(type); + warning("processing type: ", type.name, " ", ti.chain, " ", ti.args); + DSymbol* newType = GCAllocator.instance.make!DSymbol("dummy", CompletionKind.dummy, null); + newType.name = type.name; + newType.kind = type.kind; + newType.qualifier = type.qualifier; + newType.protection = type.protection; + newType.symbolFile = type.symbolFile; + newType.doc = type.doc; + newType.callTip = type.callTip; + newType.type = type.type; + DSymbol*[string] mapping; + + if (m) + foreach(k,v; m) + { + warning("store mapping: ".yellow, k, " ", v.name); + mapping[k] = v; + } + + int[string] mapping_index; + int count = 0; + if (ti.args.length > 0) + { + warning("hard args, build mapping"); + foreach(part; type.opSlice()) + { + if (part.kind == CompletionKind.typeTmpParam) + { + scope(exit) count++; + + warning("building mapping for: ", part.name, " chain: ", ti.args[count].chain); + auto key = part.name; + + DSymbol* first; + foreach(i, crumb; ti.args[count].chain) + { + auto argName = crumb; + if (i == 0) + { + + if (key in mapping) + { + //first = mapping[argName]; + //continue; + argName = mapping[key].name; + } + + auto result = moduleScope.getSymbolsAtGlobalScope(istring(argName)); + if (result.length == 0) + { + error("can't find symbol: ".red, argName); + + foreach(k, v; mapping) + { + warning("k: ", k, " v: ", v); + } + + break; + } + first = result[0]; + } + else { + first = first.getFirstPartNamed(istring(argName)); + } + } + + mapping_index[key] = count; + if (first is null) + { + error("can't find type for mapping: ".red, part.name); + continue; + } + warning(" map: ", key ,"->", first.name); + + warning(" creating type: ".blue, first.name); + + auto ca = ti.args[count]; + if (ca.chain.length > 0) + mapping[key] = createTypeWithTemplateArgs(first, lookup, ca, cache, moduleScope, depth, mapping); + } + } + } + + + assert(newType); + warning("process parts.."); + string[] T_names; + foreach(part; type.opSlice()) + { + if (part.kind == CompletionKind.typeTmpParam) + { + warning(" #", count, " ", part.name); + T_names ~= part.name; + } + else if (part.type && part.type.kind == CompletionKind.typeTmpParam) + { + DSymbol* newPart = GCAllocator.instance.make!DSymbol(part.name, part.kind, null); + newPart.qualifier = part.qualifier; + newPart.protection = part.protection; + newPart.symbolFile = part.symbolFile; + newPart.doc = part.doc; + newPart.callTip = part.callTip; + newPart.ownType = false; + + if (part.type.name in mapping) + { + newPart.type = mapping[part.type.name]; + warning(" mapping found: ", part.type.name," -> ", newPart.type.name); + } + else + if (m && part.type.name in m) + { + newPart.type = m[part.type.name]; + warning(" mapping in m found: ", part.type.name," -> ", newPart.type.name); + } + else + error(" mapping not found: ".red, part.type.name," type: ", type.name, " cur: ", ti.chain, "args: ", ti.args); + + newType.addChild(newPart, true); + } + else + { + //if (depth < 50) + //if (part.type && part.kind == CompletionKind.variableName) + //foreach(partPart; part.type.opSlice()) + //{ + // if (partPart.kind == CompletionKind.typeTmpParam) + // { + // foreach(arg; ti.args) + // { + // warning(" >", arg.chain, " ", arg.args); + // } + // warning("go agane ".blue, part.name, " ", part.type.name, " with arg: ", ti.chain," Ts: ", T_names); + // //resolveTemplate(part, part.type, lookup, ti, moduleScope, cache, depth, mapping); + // break; + // } + //} + warning("adding untouched: ", part.name, "into: ", newType); + newType.addChild(part, false); + } + } + return newType; +} + +/** + * Resolve template arguments + */ +void resolveTemplate(DSymbol* variableSym, DSymbol* type, TypeLookup* lookup, VariableContext.TypeInstance* current, Scope* moduleScope, ref ModuleCache cache, ref int depth, DSymbol*[string] mapping = null) +{ + depth += 1; + + if (variableSym is null || type is null) return; + + warning("resolving template for var: ", variableSym.name, " type: ", type.name, "depth: ", depth); + warning("current args: "); + foreach(i, arg; current.args) + warning(" i: ", i, " ", arg.chain); + warning("current chain: ", current.chain, " name: ", current.name); + if (current.chain.length == 0) return; // TODO: should not be empty, happens for simple stuff Inner inner; + + DSymbol* newType = createTypeWithTemplateArgs(type, lookup, current, cache, moduleScope, depth, mapping); + + variableSym.type = newType; + variableSym.ownType = true; +} + void resolveImport(DSymbol* acSymbol, ref TypeLookups typeLookups, ref ModuleCache cache) in diff --git a/dsymbol/src/dsymbol/type_lookup.d b/dsymbol/src/dsymbol/type_lookup.d index 2260e57..f7f79de 100644 --- a/dsymbol/src/dsymbol/type_lookup.d +++ b/dsymbol/src/dsymbol/type_lookup.d @@ -37,4 +37,19 @@ struct TypeLookup UnrolledList!istring breadcrumbs; /// The kind of type lookup TypeLookupKind kind; + /// To store information about template instances + VariableContext ctx; } + +struct VariableContext +{ + struct TypeInstance + { + string[] chain; + TypeInstance*[] args; + string name; + TypeInstance* parent; + } + TypeInstance* root; + int num; +} \ No newline at end of file diff --git a/tests/tc_templates_resolve/expected_1.txt b/tests/tc_templates_resolve/expected_1.txt new file mode 100644 index 0000000..75a72ff --- /dev/null +++ b/tests/tc_templates_resolve/expected_1.txt @@ -0,0 +1,2 @@ +identifiers +inside_inner v int inside_inner stdin 0 diff --git a/tests/tc_templates_resolve/expected_2.txt b/tests/tc_templates_resolve/expected_2.txt new file mode 100644 index 0000000..d37899d --- /dev/null +++ b/tests/tc_templates_resolve/expected_2.txt @@ -0,0 +1,3 @@ +identifiers +yo_T v A yo_T stdin 0 +yo_U v B yo_U stdin 0 diff --git a/tests/tc_templates_resolve/expected_3.txt b/tests/tc_templates_resolve/expected_3.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/tc_templates_resolve/file1.d b/tests/tc_templates_resolve/file1.d new file mode 100644 index 0000000..981a773 --- /dev/null +++ b/tests/tc_templates_resolve/file1.d @@ -0,0 +1,96 @@ +struct Data +{ + int inside_data; + Inner inner; +} + +struct Inner +{ + int inside_inner; +} + +struct AganeOne(T) +{ + int inside_aganeone; + T yo; +} + +struct AganeTwo(T, U) +{ + int inside_aganetwo; + T yo_T; + U yo_U; +} + +struct Other(T) +{ + int inside_other; + T what; + AganeOne!(T) agane_T; + AganeOne!(Inner) agane_inner; +} + +struct One(T){ T inside_one; } + +struct Outter { + struct Two(T, U){ int inside_two; T agane_one; U agane_two; One!(T) one_agane_one; T get_T(T)(){return T.init;} U get_U(){return U.init;} } +} + +struct A{ int inside_a;} +struct B{ int inside_b;} + + +void main() +{ + auto from_auto = Outter.Two!( + AganeOne!(Other!(Data)), + AganeTwo!(A, B) + )(); + + + auto check = from_auto; + + + + import std; + + // should be of type Inner, completion: inside_inner + + + + + + + + + writeln(typeid(from_auto.agane_one)); //file1.AganeOne!(file1.Other!(file1.Data).Other).AganeOne + writeln(typeid(from_auto.agane_one.yo)); // file1.Other!(file1.Data).Other + writeln(typeid(from_auto.agane_one.yo.agane_inner)); // file1.AganeOne!(file1.Inner).AganeOne + writeln(typeid(from_auto.agane_one.yo.agane_inner.yo)); // file1.Inner +} + + +// struct S { int x; int y; } + +// S doStuff(int x) { return S(); } + +// void main(string[] args) +// { +// auto alpha = 10; +// auto bravo = S(1, 2); +// int charlie = 4; +// auto delta = doStuff(); +// { +// alpha +// } +// { +// bravo. +// } +// { +// charlie. +// } +// { +// delta. +// } +// } + diff --git a/tests/tc_templates_resolve/file2.d b/tests/tc_templates_resolve/file2.d new file mode 100644 index 0000000..f1fbf9d --- /dev/null +++ b/tests/tc_templates_resolve/file2.d @@ -0,0 +1,47 @@ +struct Data +{ + float inside_data; + Inner inner; +} + +struct Inner +{ + float inside_inner; +} + +struct AganeOne(T) +{ + T yo; +} + +struct AganeTwo(T, U) +{ + T yo_T; + U yo_U; +} + +struct Other(T) +{ + T what; + AganeOne!(T) agane_T; + AganeOne!(Inner) agane_inner; +} + +struct One(T){ T inside_one; } + +struct Outter { + struct Two(T, U){ T agane_one; U agane_two; One!(T) one_agane_one; } +} + +struct A{ int inside_a;} +struct B{ int inside_b;} + + +void main() +{ + auto from_auto = Outter.Two!( + AganeOne!(Other!Data), + AganeTwo!(A, B) + ); + from_auto.agane_two.yo +} diff --git a/tests/tc_templates_resolve/file3.d b/tests/tc_templates_resolve/file3.d new file mode 100644 index 0000000..12f59fa --- /dev/null +++ b/tests/tc_templates_resolve/file3.d @@ -0,0 +1,105 @@ +struct Data +{ + float inside_data; + Inner inner; +} + +struct Inner +{ + float inside_inner; +} + + +struct AganeOne(T) +{ + T yo; +} + +struct AganeTwo(T, U) +{ + T yo_T; + U yo_U; +} + +struct Other(T) +{ + T what; + AganeOne!(T) agane_T; + AganeOne!(Inner) agane_inner; +} + + +struct MyTemplate(T) +{ + T T_value; + Other!(T) other; + + T get_this_value(T)() + { + return T_value; + } +} +struct Fat +{ + struct Outter + { + struct Inner(T) + { + T from_inner_T; + } + int from_outter; + } + struct Other + { + int from_other; + } + struct Agane + { + int from_agane; + } + int from_fat; +} + +struct One(T){ T inside_one; } + +struct Outter { + struct Two(T, U){ T agane_one; U agane_two; One!(T) one_agane_one; } +} + +struct A{ int inside_a;} +struct B{ int inside_b;} + + +void main() +{ + auto from_auto = Outter.Two!( + AganeOne!(Other!Data), + AganeTwo!(A, B) + ); + + // import std; + from_auto.agane_one.yo.agane_inner.y; + + //writeln(typeid(from_auto.agane_one.yo.agane_inner)); + //writeln(typeid(from_auto.agane_one.yo.agane_T)); + //writeln(typeid(from_auto.agane_one.yo.what)); +} + + + + + + + + + +/** + Inner(IdentifierOrTemplateInstance) + + [One, Two] + [Fat, Outter] [A, B] + + + + + */ \ No newline at end of file diff --git a/tests/tc_templates_resolve/run.sh b/tests/tc_templates_resolve/run.sh new file mode 100644 index 0000000..f8d4e10 --- /dev/null +++ b/tests/tc_templates_resolve/run.sh @@ -0,0 +1,14 @@ +set -e +set -u + +../../bin/dcd-client $1 file1.d --extended -c 831 + +#echo "test1" +#../../bin/dcd-client $1 file1.d --extended -c 751 > actual_1.txt +#diff actual_1.txt expected_1.txt --strip-trailing-cr + + +#echo "test2" +#../../bin/dcd-client $1 file2.d --extended -c 674 > actual_2.txt +#diff actual_2.txt expected_2.txt --strip-trailing-cr +