save progress
This commit is contained in:
parent
1c54fc9873
commit
f8332b50f8
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
@ -231,10 +231,151 @@ final class FirstPass : ASTVisitor
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
symbol.acSymbol.protection = protection.current;
|
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)
|
override void visit(const VariableDeclaration dec)
|
||||||
{
|
{
|
||||||
assert (currentSymbol);
|
assert (currentSymbol);
|
||||||
|
|
||||||
|
warning("current: ", currentSymbol.acSymbol.name);
|
||||||
|
|
||||||
foreach (declarator; dec.declarators)
|
foreach (declarator; dec.declarators)
|
||||||
{
|
{
|
||||||
SemanticSymbol* symbol = allocateSemanticSymbol(
|
SemanticSymbol* symbol = allocateSemanticSymbol(
|
||||||
|
|
@ -255,9 +396,20 @@ final class FirstPass : ASTVisitor
|
||||||
// TODO: remove this cast. See the note on structFieldTypes
|
// TODO: remove this cast. See the note on structFieldTypes
|
||||||
structFieldTypes.insert(cast() dec.type);
|
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)
|
if (dec.autoDeclaration !is null)
|
||||||
{
|
{
|
||||||
|
warning("is auto!");
|
||||||
foreach (part; dec.autoDeclaration.parts)
|
foreach (part; dec.autoDeclaration.parts)
|
||||||
{
|
{
|
||||||
SemanticSymbol* symbol = allocateSemanticSymbol(
|
SemanticSymbol* symbol = allocateSemanticSymbol(
|
||||||
|
|
@ -270,6 +422,81 @@ final class FirstPass : ASTVisitor
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
currentScope.addSymbol(symbol.acSymbol, false);
|
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
|
if (currentSymbol.acSymbol.kind == CompletionKind.structName
|
||||||
|| currentSymbol.acSymbol.kind == CompletionKind.unionName)
|
|| currentSymbol.acSymbol.kind == CompletionKind.unionName)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import dsymbol.type_lookup;
|
||||||
import dsymbol.deferred;
|
import dsymbol.deferred;
|
||||||
import dsymbol.import_;
|
import dsymbol.import_;
|
||||||
import dsymbol.modulecache;
|
import dsymbol.modulecache;
|
||||||
|
import dsymbol.coloredlogger;
|
||||||
import std.experimental.allocator;
|
import std.experimental.allocator;
|
||||||
import std.experimental.allocator.gc_allocator : GCAllocator;
|
import std.experimental.allocator.gc_allocator : GCAllocator;
|
||||||
import std.experimental.logger;
|
import std.experimental.logger;
|
||||||
|
|
@ -55,6 +56,24 @@ void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCac
|
||||||
resolveType(currentSymbol.acSymbol, currentSymbol.typeLookups,
|
resolveType(currentSymbol.acSymbol, currentSymbol.typeLookups,
|
||||||
moduleScope, cache);
|
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;
|
break;
|
||||||
case importSymbol:
|
case importSymbol:
|
||||||
if (currentSymbol.acSymbol.type is null)
|
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,
|
void resolveImport(DSymbol* acSymbol, ref TypeLookups typeLookups,
|
||||||
ref ModuleCache cache)
|
ref ModuleCache cache)
|
||||||
in
|
in
|
||||||
|
|
|
||||||
|
|
@ -37,4 +37,19 @@ struct TypeLookup
|
||||||
UnrolledList!istring breadcrumbs;
|
UnrolledList!istring breadcrumbs;
|
||||||
/// The kind of type lookup
|
/// The kind of type lookup
|
||||||
TypeLookupKind kind;
|
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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
identifiers
|
||||||
|
inside_inner v int inside_inner stdin 0
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
identifiers
|
||||||
|
yo_T v A yo_T stdin 0
|
||||||
|
yo_U v B yo_U stdin 0
|
||||||
|
|
@ -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.
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
Loading…
Reference in New Issue