From 15b82f3954242c697bbe498fb964d9066500485f Mon Sep 17 00:00:00 2001 From: Rafed Muhammad Yasir Date: Tue, 15 Oct 2024 16:56:53 -0700 Subject: [PATCH] Massive refactoring --- codemetrica/SourceParser.ts | 22 +-- codemetrica/interface/IClass.ts | 2 +- codemetrica/interface/ICodeBlock.ts | 6 - codemetrica/interface/IFile.ts | 5 + codemetrica/interface/IFunction.ts | 2 +- codemetrica/language/java/JavaClass.ts | 22 +-- codemetrica/language/java/JavaCodeBlock.ts | 60 ++++--- .../language/java/JavaCodeBlockBase.ts | 138 ---------------- codemetrica/language/java/JavaFile.ts | 25 +++ codemetrica/language/java/JavaMethod.ts | 13 +- codemetrica/language/java/JavaParameter.ts | 4 +- codemetrica/language/java/index.ts | 2 +- codemetrica/language/python/PyClass.ts | 32 ++-- codemetrica/language/python/PyCodeBlock.ts | 150 +++++++++++------- .../language/python/PyCodeBlockBase.ts | 141 ---------------- codemetrica/language/python/PyFile.ts | 46 ++++++ codemetrica/language/python/PyFunction.ts | 8 +- .../python/{parser.ts => PyParser.ts} | 12 -- codemetrica/language/python/index.ts | 4 +- .../language/python/metric/MethodLength.ts | 8 +- ...x_conditional.ts => ComplexConditional.ts} | 13 +- .../smell/{long_method.ts => LongMethod.ts} | 2 +- codemetrica/language/python/smell/index.ts | 2 +- codemetrica/utils/visitor.ts | 7 - examples/index.ts | 18 ++- 25 files changed, 271 insertions(+), 473 deletions(-) delete mode 100644 codemetrica/interface/ICodeBlock.ts create mode 100644 codemetrica/interface/IFile.ts delete mode 100644 codemetrica/language/java/JavaCodeBlockBase.ts create mode 100644 codemetrica/language/java/JavaFile.ts delete mode 100644 codemetrica/language/python/PyCodeBlockBase.ts create mode 100644 codemetrica/language/python/PyFile.ts rename codemetrica/language/python/{parser.ts => PyParser.ts} (58%) rename codemetrica/language/python/smell/{complex_conditional.ts => ComplexConditional.ts} (68%) rename codemetrica/language/python/smell/{long_method.ts => LongMethod.ts} (90%) delete mode 100644 codemetrica/utils/visitor.ts diff --git a/codemetrica/SourceParser.ts b/codemetrica/SourceParser.ts index 76c65ef..5c95433 100644 --- a/codemetrica/SourceParser.ts +++ b/codemetrica/SourceParser.ts @@ -1,14 +1,14 @@ -import { ICodeBlock } from "./interface/ICodeBlock"; - -import { parsePythonSource } from './language/python/parser'; +import { parsePythonSource } from './language/python/PyParser'; import { parseJavaSource } from './language/java/parser'; import * as fs from 'fs'; -import { JavaCodeBlock } from './language/java'; -import { PyCodeBlock } from './language/python'; +import { JavaFile } from './language/java'; +import { PyFile } from './language/python'; + +type CodeFile = PyFile | JavaFile; export class ParseSource { - static fromFile(filePath: string): ICodeBlock { + static fromFileSync(filePath: string): CodeFile { const extension = filePath.split('.').pop(); if(!extension) { @@ -16,15 +16,17 @@ export class ParseSource { } const text = fs.readFileSync(filePath, 'utf-8'); - return ParseSource.fromText(text, extension); + const file = ParseSource.fromText(text, extension); + file.filePath = filePath; + return file; } - static fromText(text: string, extension: string): JavaCodeBlock | PyCodeBlock { + static fromText(text: string, extension: string): CodeFile { switch(extension) { case 'java': - return new JavaCodeBlock(parseJavaSource(text)); + return new JavaFile(parseJavaSource(text)); case 'py': - return new PyCodeBlock(parsePythonSource(text)); + return new PyFile(parsePythonSource(text)); default: throw new Error('Unsupported language'); } diff --git a/codemetrica/interface/IClass.ts b/codemetrica/interface/IClass.ts index 8eed7c8..d6e68ab 100644 --- a/codemetrica/interface/IClass.ts +++ b/codemetrica/interface/IClass.ts @@ -1,4 +1,4 @@ -import { ICodeBlock } from './ICodeBlock'; +import { ICodeBlock } from './IFile'; import { IMethod } from './IMethod'; export interface IClass extends ICodeBlock { diff --git a/codemetrica/interface/ICodeBlock.ts b/codemetrica/interface/ICodeBlock.ts deleted file mode 100644 index 8d37738..0000000 --- a/codemetrica/interface/ICodeBlock.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Language } from "../language"; - -export abstract class ICodeBlock { - ctx: any; - language?: Language; -} \ No newline at end of file diff --git a/codemetrica/interface/IFile.ts b/codemetrica/interface/IFile.ts new file mode 100644 index 0000000..62a3fad --- /dev/null +++ b/codemetrica/interface/IFile.ts @@ -0,0 +1,5 @@ +import { Language } from "../language"; + +export interface IFile { + filePath: string | undefined; +} \ No newline at end of file diff --git a/codemetrica/interface/IFunction.ts b/codemetrica/interface/IFunction.ts index 1616824..5766302 100644 --- a/codemetrica/interface/IFunction.ts +++ b/codemetrica/interface/IFunction.ts @@ -1,4 +1,4 @@ -import { ICodeBlock } from './ICodeBlock'; +import { ICodeBlock } from './IFile'; import { IParameter } from './IParameter'; export interface IFunction extends ICodeBlock{ diff --git a/codemetrica/language/java/JavaClass.ts b/codemetrica/language/java/JavaClass.ts index 46631eb..e889e77 100644 --- a/codemetrica/language/java/JavaClass.ts +++ b/codemetrica/language/java/JavaClass.ts @@ -1,9 +1,9 @@ -import { JavaCodeBlockBase } from "./JavaCodeBlockBase"; +import { JavaCodeBlock } from "./JavaCodeBlock"; import { JavaMethod } from "./JavaMethod"; import { NormalClassDeclarationContext, MethodDeclarationContext } from "../../../grammars-v4/java/java20/Java20Parser"; import JavaParserVisitor from "../../../grammars-v4/java/java20/Java20ParserVisitor"; -export class JavaClass extends JavaCodeBlockBase { +export class JavaClass extends JavaCodeBlock { constructor(ctx: NormalClassDeclarationContext) { super(ctx); } @@ -13,19 +13,13 @@ export class JavaClass extends JavaCodeBlockBase } getMethods(): JavaMethod[] { - class MethodVisitor extends JavaParserVisitor { - methods: JavaMethod[]; - constructor() { - super(); - this.methods = []; + const visitor = new (class extends JavaParserVisitor { + methods: JavaMethod[] = []; + visitMethodDeclaration = (ctx: MethodDeclarationContext): void => { + this.methods.push(new JavaMethod(ctx)); } - - visitMethodDeclaration = (ctx: MethodDeclarationContext) => { - this.methods.push(new JavaMethod(ctx)); - } - } - - const visitor = new MethodVisitor(); + })(); + visitor.visit(this.ctx); return visitor.methods; } diff --git a/codemetrica/language/java/JavaCodeBlock.ts b/codemetrica/language/java/JavaCodeBlock.ts index ee9daf0..f29190b 100644 --- a/codemetrica/language/java/JavaCodeBlock.ts +++ b/codemetrica/language/java/JavaCodeBlock.ts @@ -1,29 +1,39 @@ -import JavaParserVisitor from "../../../grammars-v4/java/java20/Java20ParserVisitor"; -import { JavaClass } from "./JavaClass"; -import { JavaMethod } from "./JavaMethod"; -import { CompilationUnitContext, NormalClassDeclarationContext, MethodDeclarationContext } from "../../../grammars-v4/java/java20/Java20Parser"; -import { JavaCodeBlockBase } from "./JavaCodeBlockBase"; +import { ParserRuleContext } from 'antlr4'; +import { StatementContext } from '../../../grammars-v4/java/java20/Java20Parser'; +import JavaParserVisitor from '../../../grammars-v4/java/java20/Java20ParserVisitor'; +import { Language } from '../../language'; -export class JavaCodeBlock extends JavaCodeBlockBase { - constructor(ctx: CompilationUnitContext) { - super(ctx); - } +export abstract class JavaCodeBlock { + ctx: T + language: Language - getClasses(): JavaClass[] { - class ClassVisitor extends JavaParserVisitor { - classes: JavaClass[]; - constructor() { - super(); - this.classes = []; - } + constructor(ctx: T) { + this.ctx = ctx; + this.language = Language.JAVA; + } - visitNormalClassDeclaration = (ctx: NormalClassDeclarationContext) => { - this.classes.push(new JavaClass(ctx)); - } - } + getClasses() { + throw new Error("Method getClasses() must be implemented"); + } - const visitor = new ClassVisitor(); - visitor.visit(this.ctx); - return visitor.classes; - } -} \ No newline at end of file + getFunctions() { + throw new Error("Method getFunctions() must be implemented"); + } + + getMethods() { + throw new Error("Method getMethods() must be implemented"); + } + + getSimpleStatements(): StatementContext[] { + const visitor = new (class extends JavaParserVisitor { + statements: StatementContext[] = []; + + visitStatement = (ctx: StatementContext): void => { + this.statements.push(ctx); + } + })(); + + visitor.visit(this.ctx); + return visitor.statements; + } +} diff --git a/codemetrica/language/java/JavaCodeBlockBase.ts b/codemetrica/language/java/JavaCodeBlockBase.ts deleted file mode 100644 index e38d35d..0000000 --- a/codemetrica/language/java/JavaCodeBlockBase.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { ParserRuleContext } from 'antlr4'; -import { StatementContext } from '../../../grammars-v4/java/java20/Java20Parser'; -import JavaParserVisitor from '../../../grammars-v4/java/java20/Java20ParserVisitor'; -import { Language } from '../../language'; - -export abstract class JavaCodeBlockBase { - ctx: T - language: Language - filePath?: string - - constructor(ctx: T) { - this.ctx = ctx; - this.language = Language.JAVA; - } - - getClasses() { - throw new Error("Method getClasses() must be implemented"); - } - - getFunctions() { - throw new Error("Method getFunctions() must be implemented"); - } - - getMethods() { - throw new Error("Method getMethods() must be implemented"); - } - - getSimpleStatements(): StatementContext[] { - class SimpleStmtVisitor extends JavaParserVisitor { - statements: StatementContext[]; - - constructor() { - super(); - this.statements = []; - } - - visitSimple_stmt = (ctx: StatementContext): void => { - this.statements.push(ctx); - } - } - - const visitor = new SimpleStmtVisitor(); - visitor.visit(this.ctx); - return visitor.statements; - } - - // getExpressions(): ExpressionContext[] { - // class ExpressionVisitor extends PythonParserVisitor { - // expressions: ExpressionContext[]; - // constructor() { - // super(); - // this.expressions = []; - // } - - // visitExpression = (ctx: ExpressionContext): void => { - // this.expressions.push(ctx); - // } - // } - - // const visitor = new ExpressionVisitor(); - // visitor.visit(this.ctx); - // return visitor.expressions; - // } - - // getMatches() { - // class MatchVisitor extends PythonParserVisitor { - // matches: Match_stmtContext[]; - - // constructor() { - // super(); - // this.matches = []; - // } - - // visitMatch_stmt = (ctx: Match_stmtContext): void => { - // this.matches.push(ctx); - // } - // } - - // const visitor = new MatchVisitor(); - // visitor.visit(this.ctx); - // return visitor.matches; - // } - - // getNumberLiterals() { - // class NumberLiteralVisitor extends PythonParserVisitor { - // numberLiterals: Star_expressionContext[] = []; - // constructor() { - // super(); - // this.numberLiterals = []; - // } - - // visitStar_expression = (ctx: Star_expressionContext) => { - // if (this.anyTerminalsAreNumber(ctx)) { - // this.numberLiterals.push(ctx); - // } - // } - - // anyTerminalsAreNumber = (ctx: any) => { - // if (ctx instanceof AtomContext && ctx.NUMBER()) { - // return true; - // } - // for (let i = 0; i < ctx.getChildCount(); i++) { - // if (this.anyTerminalsAreNumber(ctx.getChild(i))) { - // return true; - // } - // } - // return false; - // } - // } - - // const visitor = new NumberLiteralVisitor(); - // visitor.visit(this.ctx); - // return visitor.numberLiterals; - // } - - // getExcepts() { - // class ExceptVisitor extends PythonParserVisitor { - // excepts: (Except_blockContext | Except_star_blockContext)[]; - - // constructor() { - // super(); - // this.excepts = []; - // } - - // visitExcept_block = (ctx:Except_blockContext):void => { - // this.excepts.push(ctx); - // } - - // visitExcept_star_block = (ctx:Except_star_blockContext):void => { - // this.excepts.push(ctx); - // } - // } - - // const visitor = new ExceptVisitor(); - // visitor.visit(this.ctx); - // return visitor.excepts; - // } -} diff --git a/codemetrica/language/java/JavaFile.ts b/codemetrica/language/java/JavaFile.ts new file mode 100644 index 0000000..8e49c13 --- /dev/null +++ b/codemetrica/language/java/JavaFile.ts @@ -0,0 +1,25 @@ +import JavaParserVisitor from "../../../grammars-v4/java/java20/Java20ParserVisitor"; +import { JavaClass } from "./JavaClass"; +import { CompilationUnitContext, NormalClassDeclarationContext } from "../../../grammars-v4/java/java20/Java20Parser"; +import { JavaCodeBlock } from "./JavaCodeBlock"; +import { IFile } from "../../interface/IFile"; + +export class JavaFile extends JavaCodeBlock implements IFile{ + filePath: string | undefined; + + constructor(ctx: CompilationUnitContext) { + super(ctx); + } + + getClasses(): JavaClass[] { + const visitor = new (class extends JavaParserVisitor { + classes: JavaClass[] = []; + visitNormalClassDeclaration = (ctx: NormalClassDeclarationContext): void => { + this.classes.push(new JavaClass(ctx)); + } + })(); + + visitor.visit(this.ctx); + return visitor.classes; + } +} \ No newline at end of file diff --git a/codemetrica/language/java/JavaMethod.ts b/codemetrica/language/java/JavaMethod.ts index 1f75a34..fb29729 100644 --- a/codemetrica/language/java/JavaMethod.ts +++ b/codemetrica/language/java/JavaMethod.ts @@ -1,10 +1,9 @@ import { IMethod } from '../../interface/IMethod'; -import { JavaCodeBlockBase } from "./JavaCodeBlockBase"; +import { JavaCodeBlock } from "./JavaCodeBlock"; import { MethodDeclarationContext } from "../../../grammars-v4/java/java20/Java20Parser"; -import { IParameter } from '../../interface/IParameter'; - -export class JavaMethod extends JavaCodeBlockBase implements IMethod { +import { JavaParameter } from './JavaParameter'; +export class JavaMethod extends JavaCodeBlock implements IMethod { constructor(ctx: MethodDeclarationContext) { super(ctx); } @@ -14,12 +13,12 @@ export class JavaMethod extends JavaCodeBlockBase impl } getParameters() { - const parameters: IParameter[] = []; + const parameters: JavaParameter[] = []; - const paramCtx = this.ctx.MethodDeclaration()?.params()?.parameters()?.children || [] + const paramCtx = this.ctx?.methodHeader()?.typeParameters()?.children || [] paramCtx.forEach((param: any) => { - parameters.push(new Parameter(param.getText(), "lol")); + parameters.push(new JavaParameter(param.getText(), "lol")); }); return parameters; diff --git a/codemetrica/language/java/JavaParameter.ts b/codemetrica/language/java/JavaParameter.ts index 138a69c..58aefcb 100644 --- a/codemetrica/language/java/JavaParameter.ts +++ b/codemetrica/language/java/JavaParameter.ts @@ -1,12 +1,10 @@ import { IParameter } from "../../interface/IParameter"; -import { JavaCodeBlockBase } from "./JavaCodeBlockBase"; -export class Parameter extends JavaCodeBlockBase<> implements IParameter { +export class JavaParameter implements IParameter { name: string; type: string; constructor(name: string, type: string) { - super(); this.name = name; this.type = type; } diff --git a/codemetrica/language/java/index.ts b/codemetrica/language/java/index.ts index b28ca3d..bfde785 100644 --- a/codemetrica/language/java/index.ts +++ b/codemetrica/language/java/index.ts @@ -3,7 +3,7 @@ */ export * from "./JavaClass"; -export * from "./JavaCodeBlockBase"; export * from "./JavaCodeBlock"; +export * from "./JavaFile"; export * from "./JavaMethod"; export * from "./parser"; \ No newline at end of file diff --git a/codemetrica/language/python/PyClass.ts b/codemetrica/language/python/PyClass.ts index ac9fe15..f97a6bd 100644 --- a/codemetrica/language/python/PyClass.ts +++ b/codemetrica/language/python/PyClass.ts @@ -1,11 +1,10 @@ -import { PyCodeBlockBase } from "./PyCodeBlockBase"; +import { PyCodeBlock } from "./PyCodeBlock"; import { PyFunction } from "./PyFunction"; import { Class_defContext, Function_defContext } from "../../../grammars-v4/python/python3_12_1/PythonParser.js"; import PythonParserVisitor from "../../../grammars-v4/python/python3_12_1/PythonParserVisitor"; import { IClass } from "../../interface/IClass"; -import { visitWith } from "../../utils/visitor"; -export class PyClass extends PyCodeBlockBase implements IClass { +export class PyClass extends PyCodeBlock implements IClass { constructor(ctx: Class_defContext) { super(ctx); } @@ -14,25 +13,14 @@ export class PyClass extends PyCodeBlockBase implements IClass return this.ctx.class_def_raw().NAME().getText(); } - // getMethods(): PyFunction[] { - // class FunctionVisitor extends PythonParserVisitor { - // methods: PyFunction[]; - // constructor() { - // super(); - // this.methods = []; - // } - - // visitFunction_def = (ctx:Function_defContext) => { - // this.methods.push(new PyFunction(ctx)); - // } - // } - - // const visitor = new FunctionVisitor(); - // visitor.visit(this.ctx); - // return visitor.methods; - // } - getMethods(): PyFunction[] { - return visitWith(Function_defContext); + const visitor = new (class extends PythonParserVisitor { + methods: PyFunction[] = []; + visitFunction_def = (ctx: Function_defContext): void => { + this.methods.push(new PyFunction(ctx)); + }; + })(); + visitor.visit(this.ctx); + return visitor.methods; } } \ No newline at end of file diff --git a/codemetrica/language/python/PyCodeBlock.ts b/codemetrica/language/python/PyCodeBlock.ts index 3b27d59..91608d3 100644 --- a/codemetrica/language/python/PyCodeBlock.ts +++ b/codemetrica/language/python/PyCodeBlock.ts @@ -1,60 +1,104 @@ +import { AtomContext, Simple_stmtContext, Except_blockContext, Except_star_blockContext, ExpressionContext, Match_stmtContext, Star_expressionContext } from "../../../grammars-v4/python/python3_12_1/PythonParser"; import PythonParserVisitor from "../../../grammars-v4/python/python3_12_1/PythonParserVisitor"; -import { Class_defContext, File_inputContext, Function_defContext } from "../../../grammars-v4/python/python3_12_1/PythonParser.js"; -import { PyCodeBlockBase } from "./PyCodeBlockBase"; -import { PyClass } from "./PyClass"; -import { PyFunction } from "./PyFunction"; - -export class PyCodeBlock extends PyCodeBlockBase { - constructor(ctx: File_inputContext) { - super(ctx); - } - - get fileName() { - return this.filePath?.split('/').pop(); - } - - get extension() { - return this.filePath?.includes('.') ? this.filePath.split('.').pop() : null; - } - - // get LOC() { - // return this.ctx.stop.line - this.ctx.start.line + 1; - // } - - getClasses(): PyClass[] { - class ClassVisitor extends PythonParserVisitor { - classes: PyClass[]; - constructor() { - super(); - this.classes = []; - } - - visitClass_def = (ctx: Class_defContext) => { - this.classes.push(new PyClass(ctx)); - } - } +import { ParserRuleContext } from 'antlr4'; +import { Language } from "../../language"; + +export abstract class PyCodeBlock{ + ctx: T + language: Language + + constructor(ctx: T) { + this.ctx = ctx; + this.language = Language.PYTHON; + } + + getClasses() { + throw new Error("Method getClasses() must be implemented"); + } + + getFunctions() { + throw new Error("Method getFunctions() must be implemented"); + } + + getMethods() { + throw new Error("Method getMethods() must be implemented"); + } + + getSimpleStatements(): Simple_stmtContext[] { + const visitor = new (class extends PythonParserVisitor { + simpleStatements: Simple_stmtContext[] = []; + visitSimple_stmt = (ctx: Simple_stmtContext): void => { + this.simpleStatements.push(ctx); + }; + })(); + + visitor.visit(this.ctx); + return visitor.simpleStatements + } - const visitor = new ClassVisitor(); - visitor.visit(this.ctx); - return visitor.classes; - } + getExpressions(): ExpressionContext[] { + const visitor = new (class extends PythonParserVisitor { + expressions: ExpressionContext[] = []; + visitExpression = (ctx: ExpressionContext): void => { + this.expressions.push(ctx); + }; + })(); - getFunctions() { - class FunctionVisitor extends PythonParserVisitor { - functions: PyFunction[]; + visitor.visit(this.ctx); + return visitor.expressions; + } - constructor() { - super(); - this.functions = []; - } + getMatches() { + const visitor = new (class extends PythonParserVisitor { + matches: Match_stmtContext[] = []; + visitMatch_stmt = (ctx: Match_stmtContext): void => { + this.matches.push(ctx); + }; + })(); - visitFunction_def = (ctx: Function_defContext) => { - this.functions.push(new PyFunction(ctx)); - } + visitor.visit(this.ctx); + return visitor.matches; + } + + getNumberLiterals() { + const visitor = new (class extends PythonParserVisitor { + numberLiterals: Star_expressionContext[] = []; + visitStar_expression = (ctx: Star_expressionContext): void => { + if (this.anyTerminalsAreNumber(ctx)) { + this.numberLiterals.push(ctx); + } + }; + + anyTerminalsAreNumber = (ctx: any): boolean => { + if (ctx instanceof AtomContext && ctx.NUMBER()) { + return true; + } + for (let i = 0; i < ctx.getChildCount(); i++) { + if (this.anyTerminalsAreNumber(ctx.getChild(i))) { + return true; + } } + return false; + }; + })(); + + visitor.visit(this.ctx); + return visitor.numberLiterals; + } + + getExcepts() { + const visitor = new (class extends PythonParserVisitor { + excepts: (Except_blockContext | Except_star_blockContext)[] = []; + visitExcept_block = (ctx: Except_blockContext): void => { + this.excepts.push(ctx); + }; + + visitExcept_star_block = (ctx: Except_star_blockContext): void => { + this.excepts.push(ctx); + }; + })(); - const visitor = new FunctionVisitor(); - visitor.visit(this.ctx); - return visitor.functions; - } -} \ No newline at end of file + visitor.visit(this.ctx); + return visitor.excepts; + } +} diff --git a/codemetrica/language/python/PyCodeBlockBase.ts b/codemetrica/language/python/PyCodeBlockBase.ts deleted file mode 100644 index 1e87f92..0000000 --- a/codemetrica/language/python/PyCodeBlockBase.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { AtomContext, Simple_stmtContext, Except_blockContext, Except_star_blockContext, ExpressionContext, Match_stmtContext, Star_expressionContext } from "../../../grammars-v4/python/python3_12_1/PythonParser"; -import PythonParserVisitor from "../../../grammars-v4/python/python3_12_1/PythonParserVisitor"; -import { ParserRuleContext } from 'antlr4'; -import { ICodeBlock } from "../../interface/ICodeBlock"; -import { Language } from "../../language"; -import { visitWith } from "../../utils/visitor"; - -export abstract class PyCodeBlockBase implements ICodeBlock{ - ctx: T - language: Language - filePath: string | null = null; - - constructor(ctx: T) { - this.ctx = ctx; - this.language = Language.PYTHON; - } - - getClasses() { - throw new Error("Method getClasses() must be implemented"); - } - - getFunctions() { - throw new Error("Method getFunctions() must be implemented"); - } - - getMethods() { - throw new Error("Method getMethods() must be implemented"); - } - - getSimpleStatements(): Simple_stmtContext[] { - return visitWith(Simple_stmtContext); - // class SimpleStmtVisitor extends PythonParserVisitor { - // statements: Simple_stmtContext[]; - - // constructor() { - // super(); - // this.statements = []; - // } - - // visitSimple_stmt = (ctx: Simple_stmtContext): void => { - // this.statements.push(ctx); - // } - // } - - // const visitor = new SimpleStmtVisitor(); - // visitor.visit(this.ctx); - // return visitor.statements; - } - - getExpressions(): ExpressionContext[] { - class ExpressionVisitor extends PythonParserVisitor { - expressions: ExpressionContext[]; - constructor() { - super(); - this.expressions = []; - } - - visitExpression = (ctx: ExpressionContext): void => { - this.expressions.push(ctx); - } - } - - const visitor = new ExpressionVisitor(); - visitor.visit(this.ctx); - return visitor.expressions; - } - - getMatches() { - class MatchVisitor extends PythonParserVisitor { - matches: Match_stmtContext[]; - - constructor() { - super(); - this.matches = []; - } - - visitMatch_stmt = (ctx: Match_stmtContext): void => { - this.matches.push(ctx); - } - } - - const visitor = new MatchVisitor(); - visitor.visit(this.ctx); - return visitor.matches; - } - - getNumberLiterals() { - class NumberLiteralVisitor extends PythonParserVisitor { - numberLiterals: Star_expressionContext[] = []; - constructor() { - super(); - this.numberLiterals = []; - } - - visitStar_expression = (ctx: Star_expressionContext) => { - if (this.anyTerminalsAreNumber(ctx)) { - this.numberLiterals.push(ctx); - } - } - - anyTerminalsAreNumber = (ctx: any) => { - if (ctx instanceof AtomContext && ctx.NUMBER()) { - return true; - } - for (let i = 0; i < ctx.getChildCount(); i++) { - if (this.anyTerminalsAreNumber(ctx.getChild(i))) { - return true; - } - } - return false; - } - } - - const visitor = new NumberLiteralVisitor(); - visitor.visit(this.ctx); - return visitor.numberLiterals; - } - - getExcepts() { - class ExceptVisitor extends PythonParserVisitor { - excepts: (Except_blockContext | Except_star_blockContext)[]; - - constructor() { - super(); - this.excepts = []; - } - - visitExcept_block = (ctx:Except_blockContext):void => { - this.excepts.push(ctx); - } - - visitExcept_star_block = (ctx:Except_star_blockContext):void => { - this.excepts.push(ctx); - } - } - - const visitor = new ExceptVisitor(); - visitor.visit(this.ctx); - return visitor.excepts; - } -} diff --git a/codemetrica/language/python/PyFile.ts b/codemetrica/language/python/PyFile.ts new file mode 100644 index 0000000..9e140c4 --- /dev/null +++ b/codemetrica/language/python/PyFile.ts @@ -0,0 +1,46 @@ +import PythonParserVisitor from "../../../grammars-v4/python/python3_12_1/PythonParserVisitor"; +import { Class_defContext, File_inputContext, Function_defContext } from "../../../grammars-v4/python/python3_12_1/PythonParser.js"; +import { PyCodeBlock } from "./PyCodeBlock"; +import { PyClass } from "./PyClass"; +import { PyFunction } from "./PyFunction"; +import { IFile } from "../../interface/IFile"; + +export class PyFile extends PyCodeBlock implements IFile { + filePath: string | undefined = undefined; + + constructor(ctx: File_inputContext) { + super(ctx); + } + + get fileName(): string | undefined { + return this.filePath?.split('/').pop(); + } + + get extension(): string | undefined { + return this.filePath?.includes('.') ? this.filePath.split('.').pop() : undefined; + } + + getClasses(): PyClass[] { + const visitor = new (class extends PythonParserVisitor { + classes: PyClass[] = []; + visitClass_def = (ctx: Class_defContext): void => { + this.classes.push(new PyClass(ctx)); + }; + })(); + + visitor.visit(this.ctx); + return visitor.classes; + } + + getFunctions() { + const visitor = new (class extends PythonParserVisitor { + functions: PyFunction[] = []; + visitFunction_def = (ctx: Function_defContext): void => { + this.functions.push(new PyFunction(ctx)); + }; + })(); + + visitor.visit(this.ctx); + return visitor.functions; + } +} \ No newline at end of file diff --git a/codemetrica/language/python/PyFunction.ts b/codemetrica/language/python/PyFunction.ts index 659fe7c..d5f42b1 100644 --- a/codemetrica/language/python/PyFunction.ts +++ b/codemetrica/language/python/PyFunction.ts @@ -1,10 +1,11 @@ -import { PyCodeBlockBase } from "./PyCodeBlockBase"; +import { PyCodeBlock } from "./PyCodeBlock"; import { Function_defContext } from "../../../grammars-v4/python/python3_12_1/PythonParser"; import { IParameter } from "../../interface/IParameter"; import { IFunction } from "../../interface/IFunction"; +import { IMethod } from "../../interface/IMethod"; import { PyParameter } from "./PyParameter"; -export class PyFunction extends PyCodeBlockBase implements IFunction { +export class PyFunction extends PyCodeBlock implements IFunction, IMethod { constructor(ctx: Function_defContext) { super(ctx); } @@ -14,8 +15,7 @@ export class PyFunction extends PyCodeBlockBase implements } getParameters(): PyParameter[] { - const parameters: IParameter[] = []; - + const parameters: PyParameter[] = []; const parameters_ctx = this.ctx.function_def_raw()?.params()?.parameters()?.children || [] parameters_ctx.forEach((param: any) => { diff --git a/codemetrica/language/python/parser.ts b/codemetrica/language/python/PyParser.ts similarity index 58% rename from codemetrica/language/python/parser.ts rename to codemetrica/language/python/PyParser.ts index 34767d4..7426921 100644 --- a/codemetrica/language/python/parser.ts +++ b/codemetrica/language/python/PyParser.ts @@ -2,18 +2,6 @@ import antlr4 from 'antlr4'; import PythonLexer from '../../../grammars-v4/python/python3_12_1/PythonLexer'; import PythonParser from '../../../grammars-v4/python/python3_12_1/PythonParser'; -export class Parser { - static getANTLRContext(fileName: string) { - const inputStream = new antlr4.FileStream(fileName); - const lexer = new PythonLexer(inputStream); - const tokenStream = new antlr4.CommonTokenStream(lexer); - - const parser = new PythonParser(tokenStream); - const tree = parser.file_input(); - return tree; - } -} - export function parsePythonSource(sourceCode: string) { const inputStream = new antlr4.InputStream(sourceCode) as unknown as antlr4.CharStream;; const lexer = new PythonLexer(inputStream) ; diff --git a/codemetrica/language/python/index.ts b/codemetrica/language/python/index.ts index ca0a79f..1fc05ec 100644 --- a/codemetrica/language/python/index.ts +++ b/codemetrica/language/python/index.ts @@ -3,7 +3,7 @@ */ export * from "./PyClass"; -export * from "./PyCodeBlockBase"; export * from "./PyCodeBlock"; +export * from "./PyFile"; export * from "./PyFunction"; -export * from "./parser"; \ No newline at end of file +export * from "./PyParser"; \ No newline at end of file diff --git a/codemetrica/language/python/metric/MethodLength.ts b/codemetrica/language/python/metric/MethodLength.ts index e15036c..7732914 100644 --- a/codemetrica/language/python/metric/MethodLength.ts +++ b/codemetrica/language/python/metric/MethodLength.ts @@ -1,14 +1,8 @@ -import { IFunction } from '../../../interface/IFunction'; import { PyFunction } from '../PyFunction'; -export function calculateMethodLength(method: IFunction): number { - const method_length = method.ctx.stop.line - method.ctx.start.line + 1 - return method_length; -} - export class MethodLength{ static calculate(method: PyFunction): number { - const method_length = method.ctx.stop.line - method.ctx.start.line + 1 + const method_length = method.ctx.stop!.line - method.ctx.start.line + 1 return method_length; } } \ No newline at end of file diff --git a/codemetrica/language/python/smell/complex_conditional.ts b/codemetrica/language/python/smell/ComplexConditional.ts similarity index 68% rename from codemetrica/language/python/smell/complex_conditional.ts rename to codemetrica/language/python/smell/ComplexConditional.ts index 16186f2..82f5031 100644 --- a/codemetrica/language/python/smell/complex_conditional.ts +++ b/codemetrica/language/python/smell/ComplexConditional.ts @@ -4,22 +4,15 @@ import { ExpressionContext } from "../../../../grammars-v4/python/python3_12_1/P export class ComplexConditional { static detect(ctx: ExpressionContext): boolean { - class LogicalOperatorCounterVisitor extends PythonParserVisitor { - count: number - - constructor() { - super(); - this.count = 0; - } - + const visitor = new (class extends PythonParserVisitor { + count: number = 0; visitTerminal(node: any) { if (node.symbol.text === 'and' || node.symbol.text === 'or') { this.count++; } } - } + })(); - const visitor = new LogicalOperatorCounterVisitor(); visitor.visit(ctx); return visitor.count > Thresholds.COMPLEX_CONDITIONAL; } diff --git a/codemetrica/language/python/smell/long_method.ts b/codemetrica/language/python/smell/LongMethod.ts similarity index 90% rename from codemetrica/language/python/smell/long_method.ts rename to codemetrica/language/python/smell/LongMethod.ts index 6262732..ab855db 100644 --- a/codemetrica/language/python/smell/long_method.ts +++ b/codemetrica/language/python/smell/LongMethod.ts @@ -2,7 +2,7 @@ import { Thresholds } from '../../../Thresholds'; import { PyFunction } from '../PyFunction'; import { MethodLength } from '../metric/MethodLength'; -export class LongMethod { +export class LongMethod{ static detect(func: PyFunction): boolean { const method_length = MethodLength.calculate(func) return method_length > Thresholds.LONG_METHOD diff --git a/codemetrica/language/python/smell/index.ts b/codemetrica/language/python/smell/index.ts index e50b176..d2799a9 100644 --- a/codemetrica/language/python/smell/index.ts +++ b/codemetrica/language/python/smell/index.ts @@ -1,4 +1,4 @@ /** * @module codemetrica/python/smells */ -export * from "./complex_conditional"; \ No newline at end of file +export * from "./ComplexConditional"; \ No newline at end of file diff --git a/codemetrica/utils/visitor.ts b/codemetrica/utils/visitor.ts deleted file mode 100644 index ca1f2e2..0000000 --- a/codemetrica/utils/visitor.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ParserRuleContext } from "antlr4"; - -export function visitWith(this: any, VisitorClass: new () => { visit: (ctx: ParserRuleContext) => void }): T[] { - const visitor = new VisitorClass(); - visitor.visit(this.ctx); - return visitor as unknown as T[]; -} \ No newline at end of file diff --git a/examples/index.ts b/examples/index.ts index b57c767..5677438 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -2,19 +2,23 @@ import { ParseSource } from "../codemetrica/SourceParser"; import { LongMethod } from "../codemetrica/smell/longMethod"; -const pyck = ParseSource.fromFile("test.py"); +const pyFile = ParseSource.fromFileSync("test.py"); -for (const c of pyck.getClasses()) { +for (const c of pyFile.getClasses()) { console.log(c.name); + for(const m of c.getExpressions()) { + console.log(m.getText()); + // console.log(LongMethod.detect(m)); + } } -for (const f of pyck.getSimpleStatements()) { - console.log(f); -} +// for (const f of pyck.getSimpleStatements()) { +// console.log(f); +// } -const javack = ParseSource.fromFile("test.java"); +const javaFile = ParseSource.fromFileSync("test.java"); -for (const c of javack.getClasses()) { +for (const c of javaFile.getClasses()) { for(const m of c.getMethods()) { console.log(m.name); console.log(LongMethod.detect(m));