dmd/druntime/benchmark/gcbench/vdparser.extra/vdc/ast/node.d
2024-07-21 10:43:40 +08:00

821 lines
18 KiB
D

// This file is part of Visual D
//
// Visual D integrates the D programming language into Visual Studio
// Copyright (c) 2010-2011 by Rainer Schuetze, All Rights Reserved
//
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
module vdc.ast.node;
import vdc.util;
import vdc.semantic;
import vdc.lexer;
import vdc.ast.expr;
import vdc.ast.type;
import vdc.ast.mod;
import vdc.ast.tmpl;
import vdc.ast.decl;
import vdc.ast.misc;
import vdc.ast.writer;
import vdc.logger;
import vdc.interpret;
import std.exception;
import std.stdio;
import std.string;
import std.conv;
import std.algorithm;
import stdext.util;
//version = COUNT;
//version = NODE_ALLOC;
version(COUNT) import visuald.windows;
version(NODE_ALLOC)
class NodeAllocData
{
enum kSize = 0x4000;
byte* pos;
private byte** data;
private int numdata;
byte* base() { return data[numdata-1]; }
void moreData()
{
byte* arr = cast(byte*) gc_calloc(kSize, 0);
// when appending to the array, ensure that no old reference is dangling
byte** ndata = cast(byte**) gc_malloc((numdata + 1) * data[0].sizeof, 0);
ndata[0..numdata] = data[0..numdata];
data[0..numdata] = null;
ndata[numdata] = arr;
gc_free(data);
data = ndata;
numdata++;
pos = arr;
}
~this()
{
destroy(false); // must not call back into GC
}
void destroy(bool free)
{
while(numdata > 0)
{
size_t sz;
byte* beg = data[--numdata];
byte* end = beg + kSize;
for(byte* p = beg; p < end && *cast(size_t*)p != 0; p += sz)
{
Node n = cast(Node) p;
sz = typeid(n).initializer.length;
sz = (sz + 15) & ~15;
assert(sz > 0);
clear(n); // calls rt_finalize
}
if(free)
gc_free(beg);
data[numdata] = null;
}
if(data && free)
gc_free(data);
data = null;
pos = null;
}
static NodeAllocData current;
static NodeAllocData detachCurrent()
{
auto cur = current;
current = null;
return cur;
}
static void checkAlloc(size_t sz)
{
if(!current)
current = new NodeAllocData;
if(!current.pos)
current.moreData();
if(current.pos + sz > current.base() + kSize)
current.moreData();
}
static void* alloc(size_t sz)
{
sz = (sz + 15) & ~15;
checkAlloc(sz);
void* p = current.pos;
current.pos += sz;
//if(current.pos < current.base() + kSize)
// *cast(size_t*)current.pos = 0;
return p;
}
}
// moved out of Node due to regression http://d.puremagic.com/issues/show_bug.cgi?id=9101
mixin template ForwardCtor()
{
this()
{
// default constructor needed for clone()
}
this(ref const(TextSpan) _span)
{
super(_span);
}
this(Token tok)
{
super(tok);
}
this(TokenId _id, ref const(TextSpan) _span)
{
super(_id, _span);
}
}
mixin template ForwardCtorTok()
{
this() {} // default constructor needed for clone()
this(Token tok)
{
super(tok);
}
}
mixin template ForwardCtorNoId()
{
this() {} // default constructor needed for clone()
this(ref const(TextSpan) _span)
{
super(_span);
}
this(Token tok)
{
super(tok.span);
}
}
class Node
{
TokenId id;
Attribute attr;
Annotation annotation;
TextSpan span; // file extracted from parent module
TextSpan fulspan;
Node parent;
Node[] members;
// semantic data
int semanticSearches;
Scope scop;
version(COUNT) __gshared int countNodes;
this()
{
version(COUNT) InterlockedIncrement(&countNodes);
// default constructor needed for clone()
}
this(ref const(TextSpan) _span)
{
version(COUNT) InterlockedIncrement(&countNodes);
fulspan = span = _span;
}
this(Token tok)
{
version(COUNT) InterlockedIncrement(&countNodes);
id = tok.id;
span = tok.span;
fulspan = tok.span;
}
this(TokenId _id, ref const(TextSpan) _span)
{
version(COUNT) InterlockedIncrement(&countNodes);
id = _id;
fulspan = span = _span;
}
version(COUNT) ~this()
{
version(COUNT) InterlockedDecrement(&countNodes);
}
void reinit()
{
id = 0;
attr = 0;
annotation = 0;
members.length = 0;
clearSpan();
}
final Node _cloneShallow()
{
Node n = static_cast!Node(typeid(this).create());
n.id = id;
n.attr = attr;
n.annotation = annotation;
n.span = span;
n.fulspan = fulspan;
return n;
}
Node clone()
{
Node n = _cloneShallow();
foreach(m; members)
n.addMember(m.clone());
return n;
}
bool compare(const(Node) n) const
{
if (typeid(this) !is typeid(n))
return false;
if(n.id != id || n.attr != attr || n.annotation != annotation)
return false;
// ignore span
if(members.length != n.members.length)
return false;
for(int m = 0; m < members.length; m++)
if(!members[m].compare(n.members[m]))
return false;
return true;
}
////////////////////////////////////////////////////////////
Node visit(DG)(DG dg)
{
if(!dg(this))
return this;
for(int m = 0; m < members.length; m++)
if(auto n = members[m].visit(dg))
return n;
return null;
}
bool detachFromModule(Module mod)
{
return true;
}
void disconnect()
{
for(int m = 0; m < members.length; m++)
members[m].disconnect();
for(int m = 0; m < members.length; m++)
members[m].parent = null;
members = members.init;
}
void free()
{
for(int m = 0; m < members.length; m++)
members[m].free();
for(int m = 0; m < members.length; m++)
members[m].parent = null;
import core.memory;
for(int m = 0; m < members.length; m++)
GC.free(cast(void*) (members[m]));
GC.free(cast(void*) (members.ptr));
members = members.init;
}
////////////////////////////////////////////////////////////
abstract void toD(CodeWriter writer)
{
writer(typeid(this).name);
writer.nl();
auto indent = CodeIndenter(writer);
foreach(c; members)
writer(c);
}
void toC(CodeWriter writer)
{
toD(writer);
}
////////////////////////////////////////////////////////////
static string genCheckState(string state)
{
return "
if(" ~ state ~ "!= 0)
return;
" ~ state ~ " = 1;
scope(exit) " ~ state ~ " = 2;
";
}
enum SemanticState
{
None,
ExpandingNonScopeMembers,
ExpandedNonScopeMembers,
AddingSymbols,
AddedSymbols,
ResolvingSymbols,
ResolvedSymbols,
SemanticDone,
}
int semanticState;
void expandNonScopeSimple(Scope sc, size_t i, size_t j)
{
Node[1] narray = void;
for(size_t m = i; m < j; )
{
Node n = members[m];
narray[0] = n;
size_t mlen = members.length;
Node[] nm = n.expandNonScopeBlock(sc, narray);
assert(members.length == mlen);
if(nm.length == 1 && nm[0] == n)
{
n.addSymbols(sc);
assert(members.length == mlen);
m++;
}
else
{
replaceMember(m, nm);
assert(members.length == mlen + nm.length - 1);
j += nm.length - 1;
}
}
}
void expandNonScopeBlocks(Scope sc)
{
if(semanticState >= SemanticState.ExpandingNonScopeMembers)
return;
// simple expansions
semanticState = SemanticState.ExpandingNonScopeMembers;
expandNonScopeSimple(sc, 0, members.length);
// expansions with interpretation
Node[1] narray;
for(int m = 0; m < members.length; )
{
Node n = members[m];
narray[0] = n;
Node[] nm = n.expandNonScopeInterpret(sc, narray);
if(nm.length == 1 && nm[0] == n)
m++;
else
{
replaceMember(m, nm);
expandNonScopeSimple(sc, m, m + nm.length);
}
}
semanticState = SemanticState.ExpandedNonScopeMembers;
}
Node[] expandNonScopeBlock(Scope sc, Node[] athis)
{
return athis;
}
Node[] expandNonScopeInterpret(Scope sc, Node[] athis)
{
return athis;
}
void addMemberSymbols(Scope sc)
{
if(semanticState >= SemanticState.AddingSymbols)
return;
scop = sc;
expandNonScopeBlocks(scop);
semanticState = SemanticState.AddedSymbols;
}
void addSymbols(Scope sc)
{
}
bool createsScope() const { return false; }
Scope enterScope(ref Scope nscope, Scope sc)
{
if(!nscope)
{
nscope = sc.pushClone();
nscope.node = this;
addMemberSymbols(nscope);
return nscope;
}
return sc.push(nscope);
}
Scope enterScope(Scope sc)
{
return enterScope(scop, sc);
}
final void semantic(Scope sc)
{
assert(sc);
if(semanticState < SemanticState.SemanticDone)
{
logInfo("Scope(%s):semantic(%s=%s)", cast(void*)sc, this, cast(void*)this);
LogIndent indent = LogIndent(1);
_semantic(sc);
semanticState = SemanticState.SemanticDone;
}
}
void _semantic(Scope sc)
{
// throw new SemanticException(text(this, ".semantic not implemented"));
foreach(m; members)
m.semantic(sc);
}
Scope getScope()
{
if(scop)
return scop;
if(parent)
{
Scope sc = parent.getScope();
assert(sc);
if(sc && createsScope())
sc = enterScope(sc);
return sc;
}
return null;
}
Node resolve()
{
return null;
}
Type calcType()
{
return semanticErrorType(this, ".calcType not implemented");
}
Value interpret(Context sc)
{
return semanticErrorValue(this, ".interpret not implemented");
}
Value interpretCatch(Context sc)
{
try
{
return interpret(sc);
}
catch(InterpretException)
{
}
return semanticErrorValue(this, ": interpretation stopped");
}
ParameterList getParameterList()
{
return null;
}
ArgumentList getFunctionArguments()
{
return null;
}
bool isTemplate()
{
return false;
}
Node expandTemplate(Scope sc, TemplateArgumentList args)
{
return this;
}
////////////////////////////////////////////////////////////
version(COUNT) {} else // invariant does not work with destructor
invariant
{
if(!__ctfe)
foreach(m; members)
assert(m.parent is this);
}
void addMember(Node m)
{
assert(m.parent is null);
members ~= m;
m.parent = this;
extendSpan(m.fulspan);
}
Node removeMember(Node m)
{
auto n = std.algorithm.countUntil(members, m);
assert(n >= 0);
return removeMember(n);
}
Node removeMember(size_t m)
{
Node n = members[m];
removeMember(m, 1);
return n;
}
void removeMember(size_t m, size_t cnt)
{
assert(m >= 0 && m + cnt <= members.length);
for (size_t i = 0; i < cnt; i++)
members[m + i].parent = null;
for (size_t n = m + cnt; n < members.length; n++)
members[n - cnt] = members[n];
members.length = members.length - cnt;
}
Node[] removeAll()
{
for (size_t m = 0; m < members.length; m++)
members[m].parent = null;
Node[] nm = members;
members = members.init;
return nm;
}
void replaceMember(Node m, Node[] nm)
{
auto n = std.algorithm.countUntil(members, m);
assert(n >= 0);
replaceMember(n, nm);
}
void replaceMember(size_t m, Node[] nm)
{
if(m < members.length)
members[m].parent = null;
if(nm.length == 1 && m < members.length)
members[m] = nm[0];
else
members = members[0..m] ~ nm ~ members[m+1..$];
foreach(n; nm)
n.parent = this;
}
T getMember(T = Node)(size_t idx)
{
if (idx < 0 || idx >= members.length)
return null;
return static_cast!T(members[idx]);
}
Module getModule()
{
Node n = this;
while(n)
{
if(n.scop)
return n.scop.mod;
n = n.parent;
}
return null;
}
string getModuleFilename()
{
Module mod = getModule();
if(!mod)
return null;
return mod.filename;
}
void semanticError(T...)(T args)
{
semanticErrorLoc(getModuleFilename(), span.start, args);
}
ErrorValue semanticErrorValue(T...)(T args)
{
semanticErrorLoc(getModuleFilename(), span.start, args);
return Singleton!(ErrorValue).get();
}
ErrorType semanticErrorType(T...)(T args)
{
semanticErrorLoc(getModuleFilename(), span.start, args);
return Singleton!(ErrorType).get();
}
////////////////////////////////////////////////////////////
void extendSpan(ref const(TextSpan) _span)
{
if(_span.start < fulspan.start)
fulspan.start = _span.start;
if(_span.end > fulspan.end)
fulspan.end = _span.end;
}
void limitSpan(ref const(TextSpan) _span)
{
if(_span.start > fulspan.start)
fulspan.start = _span.start;
if(_span.end < fulspan.end)
fulspan.end = _span.end;
}
void clearSpan()
{
span.end.line = span.start.line;
span.end.index = span.start.index;
fulspan = span;
}
}
class ParseRecoverNode : Node
{
mixin ForwardCtor!();
override void toD(CodeWriter writer)
{
string start = to!string(fulspan.start.line) ~ "," ~ to!string(fulspan.start.index);
string end = to!string(fulspan.end.line) ~ "," ~ to!string(fulspan.end.index);
writer("/+ syntax error: span = ", start, " - ", end, " +/");
writer.nl();
}
override void _semantic(Scope sc)
{
}
}
interface CallableNode
{
Value interpretCall(Context sc);
ParameterList getParameterList();
FunctionBody getFunctionBody();
}
TextPos minimumTextPos(Node node)
{
version(all)
return node.fulspan.start;
else
{
TextPos start = node.span.start;
while(node.members.length > 0)
{
if(compareTextSpanAddress(node.members[0].span.start.line, node.members[0].span.start.index,
start.line, start.index) < 0)
start = node.members[0].span.start;
node = node.members[0];
}
return start;
}
}
TextPos maximumTextPos(Node node)
{
version(all)
return node.fulspan.end;
else
{
TextPos end = node.span.end;
while(node.members.length > 0)
{
if(compareTextSpanAddress(node.members[$-1].span.end.line, node.members[$-1].span.start.index,
end.line, end.index) > 0)
end = node.members[$-1].span.end;
node = node.members[$-1];
}
return end;
}
}
// prefer start
bool nodeContains(Node node, in TextPos pos)
{
TextPos start = minimumTextPos(node);
if(start > pos)
return false;
TextPos end = maximumTextPos(node);
if(end <= pos)
return false;
return true;
}
bool nodeContains(Node node, in TextSpan* span)
{
TextPos start = minimumTextPos(node);
if(start > span.start)
return false;
TextPos end = maximumTextPos(node);
if(end < span.end)
return false;
return true;
}
// prefer end
bool nodeContainsEnd(Node node, in TextPos* pos)
{
TextPos start = minimumTextPos(node);
if(start >= *pos)
return false;
TextPos end = maximumTextPos(node);
if(end < *pos)
return false;
return true;
}
// figure out whether the given range is between the children of a binary expression
bool isBinaryOperator(Node root, int startLine, int startIndex, int endLine, int endIndex)
{
TextPos pos = TextPos(startIndex, startLine);
if(!nodeContains(root, pos))
return false;
L_loop:
if(root.members.length == 2)
{
if(cast(BinaryExpression) root)
if(maximumTextPos(root.members[0]) <= pos && minimumTextPos(root.members[1]) > pos)
return true;
}
foreach(m; root.members)
if(nodeContains(m, pos))
{
root = m;
goto L_loop;
}
return false;
}
Node getTextPosNode(Node root, in TextSpan* span, bool *inDotExpr)
{
if(!nodeContains(root, span))
return null;
L_loop:
foreach(m; root.members)
if(nodeContains(m, span))
{
root = m;
goto L_loop;
}
if(inDotExpr)
*inDotExpr = false;
if(auto dotexpr = cast(DotExpression)root)
{
if(inDotExpr)
{
root = dotexpr.getExpression();
*inDotExpr = true;
}
}
else if(auto id = cast(Identifier)root)
{
if(auto dotexpr = cast(DotExpression)id.parent)
{
if(dotexpr.getIdentifier() == id)
{
if(inDotExpr)
{
root = dotexpr.getExpression();
*inDotExpr = true;
}
else
root = dotexpr;
}
}
}
return root;
}