-
-
Notifications
You must be signed in to change notification settings - Fork 187
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add validation for max line length in compiler
This commit adds validation logic in compiler to check for max allowed characters per line for scripts. This allows preventing bugs caused by limitation of terminal emulators. Other supporting changes: - Rename/refactor related code for clarity and better maintainability. - Drop `I` prefix from interfaces to align with latest convention. - Refactor CodeValidator to be functional rather than object-oriented for simplicity. - Refactor syntax definition construction to be functional and be part of rule for better separation of concerns. - Refactored validation logic to use an enum-based factory pattern for improved maintainability and scalability.
- Loading branch information
1 parent
db090f3
commit dc5c873
Showing
65 changed files
with
2,215 additions
and
1,348 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/application/Parser/Executable/CategoryCollectionContext.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import type { FunctionData } from '@/application/collections/'; | ||
import type { ScriptingLanguage } from '@/domain/ScriptingLanguage'; | ||
import { createScriptCompiler, type ScriptCompilerFactory } from './Script/Compiler/ScriptCompilerFactory'; | ||
import type { ScriptCompiler } from './Script/Compiler/ScriptCompiler'; | ||
|
||
export interface CategoryCollectionContext { | ||
readonly compiler: ScriptCompiler; | ||
readonly language: ScriptingLanguage; | ||
} | ||
|
||
export interface CategoryCollectionContextFactory { | ||
( | ||
functionsData: ReadonlyArray<FunctionData> | undefined, | ||
language: ScriptingLanguage, | ||
compilerFactory?: ScriptCompilerFactory, | ||
): CategoryCollectionContext; | ||
} | ||
|
||
export const createCategoryCollectionContext: CategoryCollectionContextFactory = ( | ||
functionsData: ReadonlyArray<FunctionData> | undefined, | ||
language: ScriptingLanguage, | ||
compilerFactory: ScriptCompilerFactory = createScriptCompiler, | ||
) => { | ||
return { | ||
compiler: compilerFactory({ | ||
categoryContext: { | ||
functions: functionsData ?? [], | ||
language, | ||
}, | ||
}), | ||
language, | ||
}; | ||
}; |
35 changes: 0 additions & 35 deletions
35
src/application/Parser/Executable/CategoryCollectionSpecificUtilities.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 0 additions & 7 deletions
7
src/application/Parser/Executable/Script/Compiler/IScriptCompiler.ts
This file was deleted.
Oops, something went wrong.
87 changes: 4 additions & 83 deletions
87
src/application/Parser/Executable/Script/Compiler/ScriptCompiler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,86 +1,7 @@ | ||
import type { FunctionData, ScriptData, CallInstruction } from '@/application/collections/'; | ||
import type { ScriptData } from '@/application/collections/'; | ||
import type { ScriptCode } from '@/domain/Executables/Script/Code/ScriptCode'; | ||
import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Validation/Syntax/ILanguageSyntax'; | ||
import { CodeValidator } from '@/application/Parser/Executable/Script/Validation/CodeValidator'; | ||
import { NoEmptyLines } from '@/application/Parser/Executable/Script/Validation/Rules/NoEmptyLines'; | ||
import type { ICodeValidator } from '@/application/Parser/Executable/Script/Validation/ICodeValidator'; | ||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/Common/ContextualError'; | ||
import { createScriptCode, type ScriptCodeFactory } from '@/domain/Executables/Script/Code/ScriptCodeFactory'; | ||
import { filterEmptyStrings } from '@/application/Common/Text/FilterEmptyStrings'; | ||
import { FunctionCallSequenceCompiler } from './Function/Call/Compiler/FunctionCallSequenceCompiler'; | ||
import { parseFunctionCalls } from './Function/Call/FunctionCallsParser'; | ||
import { parseSharedFunctions, type SharedFunctionsParser } from './Function/SharedFunctionsParser'; | ||
import type { CompiledCode } from './Function/Call/Compiler/CompiledCode'; | ||
import type { IScriptCompiler } from './IScriptCompiler'; | ||
import type { ISharedFunctionCollection } from './Function/ISharedFunctionCollection'; | ||
import type { FunctionCallCompiler } from './Function/Call/Compiler/FunctionCallCompiler'; | ||
|
||
interface ScriptCompilerUtilities { | ||
readonly sharedFunctionsParser: SharedFunctionsParser; | ||
readonly callCompiler: FunctionCallCompiler; | ||
readonly codeValidator: ICodeValidator; | ||
readonly wrapError: ErrorWithContextWrapper; | ||
readonly scriptCodeFactory: ScriptCodeFactory; | ||
} | ||
|
||
const DefaultUtilities: ScriptCompilerUtilities = { | ||
sharedFunctionsParser: parseSharedFunctions, | ||
callCompiler: FunctionCallSequenceCompiler.instance, | ||
codeValidator: CodeValidator.instance, | ||
wrapError: wrapErrorWithAdditionalContext, | ||
scriptCodeFactory: createScriptCode, | ||
}; | ||
|
||
interface CategoryCollectionDataContext { | ||
readonly functions: readonly FunctionData[]; | ||
readonly syntax: ILanguageSyntax; | ||
} | ||
|
||
export class ScriptCompiler implements IScriptCompiler { | ||
private readonly functions: ISharedFunctionCollection; | ||
|
||
constructor( | ||
categoryContext: CategoryCollectionDataContext, | ||
private readonly utilities: ScriptCompilerUtilities = DefaultUtilities, | ||
) { | ||
this.functions = this.utilities.sharedFunctionsParser( | ||
categoryContext.functions, | ||
categoryContext.syntax, | ||
); | ||
} | ||
|
||
public canCompile(script: ScriptData): boolean { | ||
return hasCall(script); | ||
} | ||
|
||
public compile(script: ScriptData): ScriptCode { | ||
try { | ||
if (!hasCall(script)) { | ||
throw new Error('Script does include any calls.'); | ||
} | ||
const calls = parseFunctionCalls(script.call); | ||
const compiledCode = this.utilities.callCompiler.compileFunctionCalls(calls, this.functions); | ||
validateCompiledCode(compiledCode, this.utilities.codeValidator); | ||
return this.utilities.scriptCodeFactory( | ||
compiledCode.code, | ||
compiledCode.revertCode, | ||
); | ||
} catch (error) { | ||
throw this.utilities.wrapError(error, `Failed to compile script: ${script.name}`); | ||
} | ||
} | ||
} | ||
|
||
function validateCompiledCode(compiledCode: CompiledCode, validator: ICodeValidator): void { | ||
filterEmptyStrings([compiledCode.code, compiledCode.revertCode]) | ||
.forEach( | ||
(code) => validator.throwIfInvalid( | ||
code, | ||
[new NoEmptyLines()], | ||
), | ||
); | ||
} | ||
|
||
function hasCall(data: ScriptData): data is ScriptData & CallInstruction { | ||
return (data as CallInstruction).call !== undefined; | ||
export interface ScriptCompiler { | ||
canCompile(script: ScriptData): boolean; | ||
compile(script: ScriptData): ScriptCode; | ||
} |
Oops, something went wrong.