, process properly identifier chains in FunctionCallExpressions

operand.operand.operator()
This commit is contained in:
Basile Burg 2017-01-19 01:44:14 +01:00
parent 84219d44fb
commit 52f9eb4324
No known key found for this signature in database
GPG Key ID: 1868039F415CB8CF
1 changed files with 119 additions and 42 deletions
dastworx/src

View File

@ -3,7 +3,7 @@ module halstead;
import import
std.algorithm.iteration, std.conv, std.json, std.meta; std.algorithm.iteration, std.conv, std.json, std.meta;
import import
std.range: iota; std.stdio, std.range: iota;
import import
dparse.ast, dparse.lexer, dparse.parser, dparse.rollback_allocator; dparse.ast, dparse.lexer, dparse.parser, dparse.rollback_allocator;
import import
@ -50,10 +50,38 @@ private final class HalsteadMetric: ASTVisitor
size_t[string] operands; size_t[string] operands;
BinaryExprFlags[] binExprFlag; BinaryExprFlags[] binExprFlag;
size_t functionNesting; size_t functionNesting;
bool functionCall; bool[] inFunctionCallChain;
const(IdentifierOrTemplateInstance)[] chain;
bool ifStatement; bool ifStatement;
JSONValue fs; JSONValue fs;
void processCallChain()
{
version(none)
{
import std.array : join;
writeln("chain: ", chain.map!(a => a.identifier.text).join("."));
}
if (chain.length)
{
static Token getIdent(const(IdentifierOrTemplateInstance) i)
{
if (i.identifier != tok!"")
return i.identifier;
else
return i.templateInstance.identifier;
}
foreach(i, ident; chain)
{
if (i == chain.length-1)
++operators[getIdent(ident).text];
else
++operands[getIdent(ident).text];
}
chain.length = 0;
}
}
void pushExprFlags(bool leftFlag = false, bool rightFlag = false) void pushExprFlags(bool leftFlag = false, bool rightFlag = false)
{ {
binExprFlag.length += 1; binExprFlag.length += 1;
@ -74,6 +102,7 @@ private final class HalsteadMetric: ASTVisitor
{ {
fs = parseJSON("[]"); fs = parseJSON("[]");
pushExprFlags; pushExprFlags;
inFunctionCallChain.length++;
} }
void serialize() void serialize()
@ -124,8 +153,8 @@ private final class HalsteadMetric: ASTVisitor
{ {
import std.stdio: writeln; import std.stdio: writeln;
writeln(functions[$-1]); writeln(functions[$-1]);
writeln('\t',operators); writeln("\toperators: ",operators);
writeln('\t',operands); writeln("\toperands : ",operands);
} }
functionNesting--; functionNesting--;
@ -133,30 +162,24 @@ private final class HalsteadMetric: ASTVisitor
override void visit(const(FunctionCallExpression) expr) override void visit(const(FunctionCallExpression) expr)
{ {
if (expr.unaryExpression.primaryExpression)
{ inFunctionCallChain.length++;
const(PrimaryExpression) p = expr.unaryExpression.primaryExpression; inFunctionCallChain[$-1] = true;
if (p.identifierOrTemplateInstance)
{
if (p.identifierOrTemplateInstance.templateInstance)
++operators[p.identifierOrTemplateInstance.templateInstance.identifier.text];
else
++operators[p.identifierOrTemplateInstance.identifier.text];
}
}
else if (expr.unaryExpression.identifierOrTemplateInstance)
{
if (expr.unaryExpression.identifierOrTemplateInstance.templateInstance)
++operators[expr.unaryExpression.identifierOrTemplateInstance.templateInstance.identifier.text];
else
++operators[expr.unaryExpression.identifierOrTemplateInstance.identifier.text];
}
if (expr.templateArguments) if (expr.templateArguments)
{ {
if (expr.templateArguments.templateSingleArgument) if (expr.templateArguments.templateSingleArgument)
++operands[expr.templateArguments.templateSingleArgument.token.text]; ++operands[expr.templateArguments.templateSingleArgument.token.text];
} }
expr.accept(this); expr.accept(this);
if (inFunctionCallChain[$-1])
{
processCallChain;
}
inFunctionCallChain.length--;
} }
override void visit(const(FunctionDeclaration) decl) override void visit(const(FunctionDeclaration) decl)
@ -207,41 +230,57 @@ private final class HalsteadMetric: ASTVisitor
override void visit(const(PrimaryExpression) primary) override void visit(const(PrimaryExpression) primary)
{ {
if (primary.identifierOrTemplateInstance !is null if (primary.identifierOrTemplateInstance !is null)
&& primary.identifierOrTemplateInstance.identifier != tok!"")
{ {
if (!functionCall || (functionCall & exprLeftIsFunction) || (functionCall & exprRightIsFunction)) if (inFunctionCallChain[$-1])
chain ~= primary.identifierOrTemplateInstance;
if ((!inFunctionCallChain[$-1]) ||
(inFunctionCallChain[$-1] & exprLeftIsFunction) ||
(inFunctionCallChain[$-1] & exprRightIsFunction))
{
++operands[primary.identifierOrTemplateInstance.identifier.text]; ++operands[primary.identifierOrTemplateInstance.identifier.text];
}
} }
else if (primary.primary.type.isLiteral) else if (primary.primary.type.isLiteral)
{ {
import std.digest.crc: crc32Of, toHexString; import std.digest.crc: crc32Of, toHexString;
++operands["literal" ~ primary.primary.text.crc32Of.toHexString.idup]; ++operands["literal" ~ primary.primary.text.crc32Of.toHexString.idup];
} }
functionCall = false;
primary.accept(this); primary.accept(this);
} }
override void visit(const(ArgumentList) al)
{
if (inFunctionCallChain[$-1])
processCallChain;
inFunctionCallChain[$-1] = false;
al.accept(this);
}
override void visit(const(UnaryExpression) expr) override void visit(const(UnaryExpression) expr)
{ {
expr.accept(this);
if (expr.identifierOrTemplateInstance && !expr.primaryExpression) if (expr.identifierOrTemplateInstance)
{ {
++operators["."]; ++operators["."];
++operands[expr.identifierOrTemplateInstance.identifier.text];
if (inFunctionCallChain[$-1])
chain ~= expr.identifierOrTemplateInstance;
else
{
if (expr.identifierOrTemplateInstance.identifier != tok!"")
++operands[expr.identifierOrTemplateInstance.identifier.text];
else
++operands[expr.identifierOrTemplateInstance.templateInstance.identifier.text];
}
} }
if (expr.prefix.type) if (expr.prefix.type)
++operators[str(expr.prefix.type)]; ++operators[str(expr.prefix.type)];
if (expr.suffix.type) if (expr.suffix.type)
++operators[str(expr.suffix.type)]; ++operators[str(expr.suffix.type)];
if (expr.functionCallExpression)
functionCall = true;
expr.accept(this);
} }
override void visit(const(IndexExpression) expr) override void visit(const(IndexExpression) expr)
@ -415,7 +454,7 @@ private final class HalsteadMetric: ASTVisitor
decl.accept(this); decl.accept(this);
} }
final override void visit(const AutoDeclarationPart decl) override void visit(const AutoDeclarationPart decl)
{ {
++operands[decl.identifier.text]; ++operands[decl.identifier.text];
++operators["="]; ++operators["="];
@ -599,6 +638,19 @@ unittest
assert(r.operatorsKinds == 3); assert(r.operatorsKinds == 3);
} }
unittest
{
Function r =
q{
void foo()
{
bar!("lit")(a);
}
}.test;
assert(r.operandsKinds == 2);
assert(r.operatorsKinds == 1);
}
unittest unittest
{ {
Function r = Function r =
@ -714,11 +766,11 @@ unittest
q{ q{
void foo() void foo()
{ {
i += a << b; i += a << b.c;
} }
}.test; }.test;
assert(r.operandsKinds == 3); assert(r.operandsKinds == 4);
assert(r.operatorsKinds == 2); assert(r.operatorsKinds == 3);
} }
unittest unittest
@ -1092,7 +1144,6 @@ unittest
unittest unittest
{ {
//FIXME: operands 'c' instead of 'a'
Function r = Function r =
q{ q{
void foo() void foo()
@ -1107,15 +1158,41 @@ unittest
unittest unittest
{ {
//FIXME: operands 'c' instead of 'a'
Function r = Function r =
q{ q{
void foo() void foo()
{ {
a.b.c(d(e.f)); a.b.c.d(e.f());
} }
}.test; }.test;
assert(r.operandsKinds == 4); assert(r.operandsKinds == 4);
assert(r.operatorsKinds == 3); assert(r.operatorsKinds == 3);
} }
unittest
{
Function r =
q{
void foo()
{
a.b!(8,9).c = f;
}
}.test;
assert(r.operandsKinds == 6);
assert(r.operatorsKinds == 2);
}
unittest
{
//FIXME: single template param without parens not detected
Function r =
q{
void foo()
{
a.b!8.c = f;
}
}.test;
//assert(r.operandsKinds == 5);
assert(r.operatorsKinds == 2);
}