mirror of https://gitlab.com/basile.b/dexed.git
#104, start Halstead metrics
This commit is contained in:
parent
e91831c3b7
commit
af7d5d6c34
|
@ -41,6 +41,7 @@ object CurrentProject: TCENativeProject
|
||||||
'src/imports.d'
|
'src/imports.d'
|
||||||
'src/mainfun.d'
|
'src/mainfun.d'
|
||||||
'src/common.d'
|
'src/common.d'
|
||||||
|
'src/halstead.d'
|
||||||
)
|
)
|
||||||
ConfigurationIndex = 1
|
ConfigurationIndex = 1
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,389 @@
|
||||||
|
module halstead;
|
||||||
|
|
||||||
|
import
|
||||||
|
std.meta, std.traits, std.algorithm.iteration, std.json;
|
||||||
|
import
|
||||||
|
dparse.lexer, dparse.parser, dparse.ast, dparse.rollback_allocator;
|
||||||
|
import
|
||||||
|
iz.memory, iz.containers;
|
||||||
|
version(unittest){} else import
|
||||||
|
common;
|
||||||
|
|
||||||
|
void performHalsteadMetrics(const(Module) mod)
|
||||||
|
{
|
||||||
|
HalsteadMetric hm = construct!(HalsteadMetric);
|
||||||
|
hm.visit(mod);
|
||||||
|
hm.serialize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Function
|
||||||
|
{
|
||||||
|
size_t line;
|
||||||
|
string name;
|
||||||
|
size_t N1, n1;
|
||||||
|
size_t N2, n2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class HalsteadMetric: ASTVisitor
|
||||||
|
{
|
||||||
|
alias visit = ASTVisitor.visit;
|
||||||
|
|
||||||
|
Function[] functions;
|
||||||
|
size_t[string] operators;
|
||||||
|
size_t[string] operands;
|
||||||
|
size_t functionNesting;
|
||||||
|
bool functionCall;
|
||||||
|
bool ifStatement;
|
||||||
|
JSONValue fs;
|
||||||
|
|
||||||
|
this()
|
||||||
|
{
|
||||||
|
fs = parseJSON("[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
void serialize()
|
||||||
|
{
|
||||||
|
import std.stdio: write;
|
||||||
|
JSONValue js;
|
||||||
|
js["functions"] = fs;
|
||||||
|
js.toString.write;
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(PragmaExpression)){}
|
||||||
|
|
||||||
|
//TODO: add share/static/__ctor & __dtor
|
||||||
|
|
||||||
|
override void visit(const(FunctionDeclaration) decl)
|
||||||
|
{
|
||||||
|
if (!decl.functionBody)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (functionNesting++ == 0)
|
||||||
|
functions.length = functions.length + 1;
|
||||||
|
|
||||||
|
decl.accept(this);
|
||||||
|
|
||||||
|
functions[$-1].name = decl.name.text;
|
||||||
|
functions[$-1].line = decl.name.line;
|
||||||
|
|
||||||
|
if (operators.length)
|
||||||
|
{
|
||||||
|
functions[$-1].N1 = operators.byValue.fold!((a,b) => b = a + b);
|
||||||
|
functions[$-1].n1 = operators.length;
|
||||||
|
}
|
||||||
|
if (operands.length)
|
||||||
|
{
|
||||||
|
functions[$-1].N2 = operands.byValue.fold!((a,b) => b = a + b);
|
||||||
|
functions[$-1].n2 = operands.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONValue f;
|
||||||
|
f["name"] = functions[$-1].name;
|
||||||
|
f["line"] = functions[$-1].line;
|
||||||
|
f["n1Sum"] = functions[$-1].N1;
|
||||||
|
f["n1Count"] = functions[$-1].n1;
|
||||||
|
f["n2Sum"] = functions[$-1].N2;
|
||||||
|
f["n2Count"] = functions[$-1].n2;
|
||||||
|
fs ~= [f];
|
||||||
|
|
||||||
|
version(unittest)
|
||||||
|
{
|
||||||
|
import std.stdio;
|
||||||
|
writeln(functions[$-1]);
|
||||||
|
writeln('\t',operators);
|
||||||
|
writeln('\t',operands);
|
||||||
|
}
|
||||||
|
|
||||||
|
operators.clear;
|
||||||
|
operands.clear;
|
||||||
|
|
||||||
|
functionNesting--;
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(PrimaryExpression) primary)
|
||||||
|
{
|
||||||
|
if (primary.identifierOrTemplateInstance !is null
|
||||||
|
&& primary.identifierOrTemplateInstance.identifier != tok!"")
|
||||||
|
{
|
||||||
|
if (!functionCall)
|
||||||
|
++operands[primary.identifierOrTemplateInstance.identifier.text];
|
||||||
|
else
|
||||||
|
++operators[primary.identifierOrTemplateInstance.identifier.text];
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (primary.primary.type.isLiteral)
|
||||||
|
{
|
||||||
|
import std.digest.crc: crc32Of, toHexString;
|
||||||
|
++operands["literal" ~ primary.primary.text.crc32Of.toHexString.idup];
|
||||||
|
}
|
||||||
|
|
||||||
|
functionCall = false;
|
||||||
|
|
||||||
|
primary.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(UnaryExpression) expr)
|
||||||
|
{
|
||||||
|
if (expr.prefix.type)
|
||||||
|
++operators[str(expr.prefix.type)];
|
||||||
|
if (expr.suffix.type)
|
||||||
|
++operators[str(expr.suffix.type)];
|
||||||
|
|
||||||
|
// TODO: detect function name here
|
||||||
|
if (expr.functionCallExpression)
|
||||||
|
functionCall = true;
|
||||||
|
|
||||||
|
// TODO: detect function call w/o parens
|
||||||
|
//else if (expr.prefix.type == tok!"" && expr.suffix.type == tok!"")
|
||||||
|
// functionCall = true;
|
||||||
|
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(AndAndExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["&&"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(OrOrExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["||"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(AndExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["&"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(AsmAndExp) expr)
|
||||||
|
{
|
||||||
|
++operators["&"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(OrExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["|"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(InExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["in"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(PowExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["^"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(XorExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["^^"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(IndexExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["[]"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(NewExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["new"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(NewAnonClassExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["new"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(CastExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["cast"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(IsExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["is"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(TypeidExpression) expr)
|
||||||
|
{
|
||||||
|
++operators["typeid"];
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(IfStatement) st)
|
||||||
|
{
|
||||||
|
++operators["if"];
|
||||||
|
ifStatement = true;
|
||||||
|
st.accept(this);
|
||||||
|
ifStatement = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(DeclarationOrStatement) st)
|
||||||
|
{
|
||||||
|
if (ifStatement && st.statement)
|
||||||
|
++operators["thenOrElse"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(WhileStatement) st)
|
||||||
|
{
|
||||||
|
++operators["while"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(ForStatement) st)
|
||||||
|
{
|
||||||
|
++operators["for"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(ForeachStatement) st)
|
||||||
|
{
|
||||||
|
++operators["foreach"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(ReturnStatement) st)
|
||||||
|
{
|
||||||
|
++operators["return"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(BreakStatement) st)
|
||||||
|
{
|
||||||
|
++operators["break"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(ContinueStatement) st)
|
||||||
|
{
|
||||||
|
++operators["continue"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(GotoStatement) st)
|
||||||
|
{
|
||||||
|
++operators["goto"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(SwitchStatement) st)
|
||||||
|
{
|
||||||
|
++operators["switch"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(CaseStatement) st)
|
||||||
|
{
|
||||||
|
++operators["case"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(CaseRangeStatement) st)
|
||||||
|
{
|
||||||
|
++operators["case"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(DefaultStatement) st)
|
||||||
|
{
|
||||||
|
++operators["case"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(ThrowStatement) st)
|
||||||
|
{
|
||||||
|
++operators["throw"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const(TryStatement) st)
|
||||||
|
{
|
||||||
|
++operators["try"];
|
||||||
|
st.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static string exprAliases()
|
||||||
|
{
|
||||||
|
import std.range: iota;
|
||||||
|
|
||||||
|
alias ExprWithOp = AliasSeq!(
|
||||||
|
AddExpression,
|
||||||
|
AsmAddExp,
|
||||||
|
AsmEqualExp,
|
||||||
|
AsmMulExp,
|
||||||
|
AsmRelExp,
|
||||||
|
AsmShiftExp,
|
||||||
|
AssignExpression,
|
||||||
|
EqualExpression,
|
||||||
|
MulExpression,
|
||||||
|
RelExpression,
|
||||||
|
ShiftExpression,
|
||||||
|
);
|
||||||
|
|
||||||
|
enum exprOverride(T) = "
|
||||||
|
override void visit(const(" ~ T.stringof ~ ") expr)
|
||||||
|
{
|
||||||
|
static assert(__traits(hasMember," ~ T.stringof ~ ", \"operator\"));
|
||||||
|
++operators[str(expr.operator)];
|
||||||
|
expr.accept(this);
|
||||||
|
}";
|
||||||
|
|
||||||
|
string result;
|
||||||
|
foreach(i; aliasSeqOf!(iota(0, ExprWithOp.length)))
|
||||||
|
result ~= exprOverride!(ExprWithOp[i]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin(exprAliases);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
version(unittest)
|
||||||
|
{
|
||||||
|
T parseAndVisit(T : ASTVisitor)(const(char)[] source)
|
||||||
|
{
|
||||||
|
RollbackAllocator allocator;
|
||||||
|
LexerConfig config = LexerConfig("", StringBehavior.source, WhitespaceBehavior.skip);
|
||||||
|
StringCache cache = StringCache(StringCache.defaultBucketCount);
|
||||||
|
const(Token)[] tokens = getTokensForParser(cast(ubyte[]) source, config, &cache);
|
||||||
|
Module mod = parseModule(tokens, "", &allocator);
|
||||||
|
T result = construct!(T);
|
||||||
|
result.visit(mod);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test(T)(T t)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto a = 1;
|
||||||
|
auto b = a++;
|
||||||
|
auto c = a || b;
|
||||||
|
auto d = a << c;
|
||||||
|
auto e = a >>> c;
|
||||||
|
test(test());
|
||||||
|
test;
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
import std.file;
|
||||||
|
char[] source = cast(char[]) __FILE__.read;
|
||||||
|
auto r = source.parseAndVisit!HalsteadMetric;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import
|
||||||
import
|
import
|
||||||
dparse.lexer, dparse.parser, dparse.ast, dparse.rollback_allocator;
|
dparse.lexer, dparse.parser, dparse.ast, dparse.rollback_allocator;
|
||||||
import
|
import
|
||||||
common, todos, symlist, imports, mainfun;
|
common, todos, symlist, imports, mainfun, halstead;
|
||||||
|
|
||||||
|
|
||||||
private __gshared bool deepSymList;
|
private __gshared bool deepSymList;
|
||||||
|
@ -59,6 +59,7 @@ void main(string[] args)
|
||||||
"m", &handleMainfunOption,
|
"m", &handleMainfunOption,
|
||||||
"s", &handleSymListOption,
|
"s", &handleSymListOption,
|
||||||
"t", &handleTodosOption,
|
"t", &handleTodosOption,
|
||||||
|
"H", &handleHalsteadOption,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +121,21 @@ void handleMainfunOption()
|
||||||
.detectMainFun();
|
.detectMainFun();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles the "-H" option: write the halstead metrics
|
||||||
|
void handleHalsteadOption()
|
||||||
|
{
|
||||||
|
mixin(logCall);
|
||||||
|
|
||||||
|
RollbackAllocator alloc;
|
||||||
|
StringCache cache = StringCache(StringCache.defaultBucketCount);
|
||||||
|
LexerConfig config = LexerConfig("", StringBehavior.source);
|
||||||
|
|
||||||
|
source.data
|
||||||
|
.getTokensForParser(config, &cache)
|
||||||
|
.parseModule("", &alloc, &ignoreErrors)
|
||||||
|
.performHalsteadMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
private void handleErrors(string fname, size_t line, size_t col, string message,
|
private void handleErrors(string fname, size_t line, size_t col, string message,
|
||||||
bool err)
|
bool err)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,7 +4,7 @@ unit ce_dastworx;
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, process, ce_common;
|
Classes, SysUtils, process, xjsonscanner, xfpjson, xjsonparser, ce_common;
|
||||||
|
|
||||||
(**
|
(**
|
||||||
* Gets the module name and the imports of the source code located in
|
* Gets the module name and the imports of the source code located in
|
||||||
|
@ -20,6 +20,8 @@ procedure getModuleImports(source, imports: TStrings);
|
||||||
*)
|
*)
|
||||||
procedure getModulesImports(const files: string; results: TStrings);
|
procedure getModulesImports(const files: string; results: TStrings);
|
||||||
|
|
||||||
|
procedure getHalsteadMetrics(source: TStrings; out jsn: TJSONObject);
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
var
|
var
|
||||||
|
@ -85,5 +87,39 @@ begin
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure getHalsteadMetrics(source: TStrings; out jsn: TJSONObject);
|
||||||
|
var
|
||||||
|
prc: TProcess;
|
||||||
|
prs: TJSONParser;
|
||||||
|
jps: TJSONData;
|
||||||
|
str: string;
|
||||||
|
begin
|
||||||
|
str := getToolName;
|
||||||
|
if str.isEmpty then
|
||||||
|
exit;
|
||||||
|
prc := TProcess.Create(nil);
|
||||||
|
try
|
||||||
|
prc.Executable := str;
|
||||||
|
prc.Parameters.Add('-H');
|
||||||
|
prc.Options := [poUsePipes {$IFDEF WINDOWS}, poNewConsole{$ENDIF}];
|
||||||
|
prc.ShowWindow := swoHIDE;
|
||||||
|
prc.Execute;
|
||||||
|
str := source.Text;
|
||||||
|
prc.Input.Write(str[1], str.length);
|
||||||
|
prc.CloseInput;
|
||||||
|
prs := TJSONParser.Create(prc.Output, [joIgnoreTrailingComma, joUTF8]);
|
||||||
|
jps := prs.Parse;
|
||||||
|
if jps.isNotNil and (jps.JSONType = jtObject) then
|
||||||
|
jsn := TJSONObject(jps.Clone);
|
||||||
|
jps.Free;
|
||||||
|
while prc.Running do ;
|
||||||
|
// TODO-cmaintenance: remove this from version 3 gold
|
||||||
|
tryRaiseFromStdErr(prc);
|
||||||
|
finally
|
||||||
|
prs.Free;
|
||||||
|
prc.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
|
@ -2387,6 +2387,9 @@ object CEMainForm: TCEMainForm
|
||||||
AC002178AD002178AD002178AD002178AD002178AD002178AD00
|
AC002178AD002178AD002178AD002178AD002178AD002178AD00
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
object MenuItem77: TMenuItem
|
||||||
|
Action = actFileMetricsHalstead
|
||||||
|
end
|
||||||
object MenuItem60: TMenuItem
|
object MenuItem60: TMenuItem
|
||||||
Action = actFileOpenContFold
|
Action = actFileOpenContFold
|
||||||
Bitmap.Data = {
|
Bitmap.Data = {
|
||||||
|
@ -5190,6 +5193,12 @@ object CEMainForm: TCEMainForm
|
||||||
ImageIndex = 27
|
ImageIndex = 27
|
||||||
OnExecute = actProjNewGroupExecute
|
OnExecute = actProjNewGroupExecute
|
||||||
end
|
end
|
||||||
|
object actFileMetricsHalstead: TAction
|
||||||
|
Category = 'File'
|
||||||
|
Caption = 'View Halstead metrics'
|
||||||
|
ImageIndex = 35
|
||||||
|
OnExecute = actFileMetricsHalsteadExecute
|
||||||
|
end
|
||||||
end
|
end
|
||||||
object imgList: TImageList
|
object imgList: TImageList
|
||||||
left = 64
|
left = 64
|
||||||
|
|
|
@ -7,7 +7,7 @@ interface
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, LazFileUtils, SynEditKeyCmds, SynHighlighterLFM, Forms,
|
Classes, SysUtils, LazFileUtils, SynEditKeyCmds, SynHighlighterLFM, Forms,
|
||||||
StdCtrls, AnchorDocking, AnchorDockStorage, AnchorDockOptionsDlg, Controls,
|
StdCtrls, AnchorDocking, AnchorDockStorage, AnchorDockOptionsDlg, Controls,
|
||||||
Graphics, strutils, Dialogs, Menus, ActnList, ExtCtrls, process,
|
Graphics, strutils, Dialogs, Menus, ActnList, ExtCtrls, process, math,
|
||||||
{$IFDEF WINDOWS}Windows, {$ENDIF} XMLPropStorage, SynExportHTML, fphttpclient,
|
{$IFDEF WINDOWS}Windows, {$ENDIF} XMLPropStorage, SynExportHTML, fphttpclient,
|
||||||
xfpjson, xjsonparser, xjsonscanner,
|
xfpjson, xjsonparser, xjsonscanner,
|
||||||
ce_common, ce_dmdwrap, ce_ceproject, ce_synmemo, ce_writableComponent,
|
ce_common, ce_dmdwrap, ce_ceproject, ce_synmemo, ce_writableComponent,
|
||||||
|
@ -15,7 +15,7 @@ uses
|
||||||
ce_search, ce_miniexplorer, ce_libman, ce_libmaneditor, ce_todolist, ce_observer,
|
ce_search, ce_miniexplorer, ce_libman, ce_libmaneditor, ce_todolist, ce_observer,
|
||||||
ce_toolseditor, ce_procinput, ce_optionseditor, ce_symlist, ce_mru, ce_processes,
|
ce_toolseditor, ce_procinput, ce_optionseditor, ce_symlist, ce_mru, ce_processes,
|
||||||
ce_infos, ce_dubproject, ce_dialogs, ce_dubprojeditor,{$IFDEF UNIX} ce_gdb,{$ENDIF}
|
ce_infos, ce_dubproject, ce_dialogs, ce_dubprojeditor,{$IFDEF UNIX} ce_gdb,{$ENDIF}
|
||||||
ce_dfmt, ce_lcldragdrop, ce_projgroup, ce_projutils, ce_stringrange;
|
ce_dfmt, ce_lcldragdrop, ce_projgroup, ce_projutils, ce_stringrange, ce_dastworx;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
|
@ -101,6 +101,7 @@ type
|
||||||
actFileRunDub: TAction;
|
actFileRunDub: TAction;
|
||||||
actFileRunDubOut: TAction;
|
actFileRunDubOut: TAction;
|
||||||
actFileNewDubScript: TAction;
|
actFileNewDubScript: TAction;
|
||||||
|
actFileMetricsHalstead: TAction;
|
||||||
actProjGroupCompileCustomSync: TAction;
|
actProjGroupCompileCustomSync: TAction;
|
||||||
actProjGroupClose: TAction;
|
actProjGroupClose: TAction;
|
||||||
actProjGroupCompileSync: TAction;
|
actProjGroupCompileSync: TAction;
|
||||||
|
@ -149,6 +150,7 @@ type
|
||||||
MenuItem103: TMenuItem;
|
MenuItem103: TMenuItem;
|
||||||
MenuItem104: TMenuItem;
|
MenuItem104: TMenuItem;
|
||||||
MenuItem105: TMenuItem;
|
MenuItem105: TMenuItem;
|
||||||
|
MenuItem77: TMenuItem;
|
||||||
mnuOpts: TMenuItem;
|
mnuOpts: TMenuItem;
|
||||||
mnuItemMruGroup: TMenuItem;
|
mnuItemMruGroup: TMenuItem;
|
||||||
MenuItem11: TMenuItem;
|
MenuItem11: TMenuItem;
|
||||||
|
@ -253,6 +255,7 @@ type
|
||||||
MenuItem9: TMenuItem;
|
MenuItem9: TMenuItem;
|
||||||
procedure actFileCompileExecute(Sender: TObject);
|
procedure actFileCompileExecute(Sender: TObject);
|
||||||
procedure actFileDscannerExecute(Sender: TObject);
|
procedure actFileDscannerExecute(Sender: TObject);
|
||||||
|
procedure actFileMetricsHalsteadExecute(Sender: TObject);
|
||||||
procedure actFileNewDubScriptExecute(Sender: TObject);
|
procedure actFileNewDubScriptExecute(Sender: TObject);
|
||||||
procedure actFileRunDubExecute(Sender: TObject);
|
procedure actFileRunDubExecute(Sender: TObject);
|
||||||
procedure actFileRunDubOutExecute(Sender: TObject);
|
procedure actFileRunDubOutExecute(Sender: TObject);
|
||||||
|
@ -2872,6 +2875,84 @@ begin
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TCEMainForm.actFileMetricsHalsteadExecute(Sender: TObject);
|
||||||
|
procedure computeMetrics(const obj: TJSONObject);
|
||||||
|
var
|
||||||
|
n1, sn1, n2, sn2: integer;
|
||||||
|
val: TJSONData;
|
||||||
|
voc, len, line: integer;
|
||||||
|
vol, dif, eff: single;
|
||||||
|
begin
|
||||||
|
val := obj.Find('n1Count');
|
||||||
|
if val.isNil then
|
||||||
|
exit;
|
||||||
|
n1 := val.AsInteger;
|
||||||
|
|
||||||
|
val := obj.Find('n1Sum');
|
||||||
|
if val.isNil then
|
||||||
|
exit;
|
||||||
|
sn1 := val.AsInteger;
|
||||||
|
|
||||||
|
val := obj.Find('n2Count');
|
||||||
|
if val.isNil then
|
||||||
|
exit;
|
||||||
|
n2 := val.AsInteger;
|
||||||
|
|
||||||
|
val := obj.Find('n2Sum');
|
||||||
|
if val.isNil then
|
||||||
|
exit;
|
||||||
|
sn2 := val.AsInteger;
|
||||||
|
|
||||||
|
val := obj.Find('line');
|
||||||
|
if val.isNil then
|
||||||
|
exit;
|
||||||
|
line := val.AsInteger;
|
||||||
|
val := obj.Find('name');
|
||||||
|
if val.isNil then
|
||||||
|
exit;
|
||||||
|
fMsgs.message(format('%s(%d): Halstead metrics for "%s"',
|
||||||
|
[fDoc.fileName, line, val.AsString]), fDoc, amcEdit, amkInf);
|
||||||
|
|
||||||
|
voc := n1 + n2;
|
||||||
|
len := sn1 + sn2;
|
||||||
|
vol := len * log2(voc);
|
||||||
|
dif := n1 * 0.5 * (sn2 / n2);
|
||||||
|
eff := dif * vol;
|
||||||
|
|
||||||
|
fMsgs.message(format(' Vocabulary: %d', [voc]), fDoc, amcEdit, amkInf);
|
||||||
|
fMsgs.message(format(' Length: %d', [len]), fDoc, amcEdit, amkInf);
|
||||||
|
fMsgs.message(format(' Volume: %.2f', [vol]), fDoc, amcEdit, amkInf);
|
||||||
|
fMsgs.message(format(' Difficulty: %.2f', [dif]), fDoc, amcEdit, amkInf);
|
||||||
|
fMsgs.message(format(' Effort: %.2f', [eff]), fDoc, amcEdit, amkInf);
|
||||||
|
fMsgs.message(format(' Time required: %.2f secs.', [eff / 18]), fDoc, amcEdit, amkInf);
|
||||||
|
|
||||||
|
end;
|
||||||
|
var
|
||||||
|
jsn: TJSONObject = nil;
|
||||||
|
fnc: TJSONObject = nil;
|
||||||
|
val: TJSONData;
|
||||||
|
arr: TJSONArray;
|
||||||
|
i: integer;
|
||||||
|
begin
|
||||||
|
if fDoc.isNil then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
getHalsteadMetrics(fDoc.Lines, jsn);
|
||||||
|
if jsn.isNil then
|
||||||
|
exit;
|
||||||
|
val := jsn.Find('functions');
|
||||||
|
if val.isNil or (val.JSONType <> jtArray) then
|
||||||
|
exit;
|
||||||
|
arr := TJSONArray(val);
|
||||||
|
for i := 0 to arr.Count-1 do
|
||||||
|
begin
|
||||||
|
fnc := TJSONObject(arr.Objects[i]);
|
||||||
|
if fnc.isNotNil then
|
||||||
|
computeMetrics(fnc);
|
||||||
|
end;
|
||||||
|
jsn.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TCEMainForm.actFileNewDubScriptExecute(Sender: TObject);
|
procedure TCEMainForm.actFileNewDubScriptExecute(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
newFile;
|
newFile;
|
||||||
|
|
Loading…
Reference in New Issue