add initial macOS support #40
This commit is contained in:
@@ -3,6 +3,7 @@ import { IProjectInformation } from '@/domain/IProjectInformation';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { parseCategoryCollection } from './CategoryCollectionParser';
|
||||
import WindowsData from 'js-yaml-loader!@/application/collections/windows.yaml';
|
||||
import MacOsData from 'js-yaml-loader!@/application/collections/macos.yaml';
|
||||
import { CollectionData } from 'js-yaml-loader!@/*';
|
||||
import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser';
|
||||
import { Application } from '@/domain/Application';
|
||||
@@ -10,10 +11,11 @@ import { Application } from '@/domain/Application';
|
||||
export function parseApplication(
|
||||
parser = CategoryCollectionParser,
|
||||
processEnv: NodeJS.ProcessEnv = process.env,
|
||||
collectionData = LoadedCollectionData): IApplication {
|
||||
collectionsData = PreParsedCollections): IApplication {
|
||||
validateCollectionsData(collectionsData);
|
||||
const information = parseProjectInformation(processEnv);
|
||||
const collection = parser(collectionData, information);
|
||||
const app = new Application(information, [ collection ]);
|
||||
const collections = collectionsData.map((collection) => parser(collection, information));
|
||||
const app = new Application(information, collections);
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -23,5 +25,14 @@ export type CategoryCollectionParserType
|
||||
const CategoryCollectionParser: CategoryCollectionParserType
|
||||
= (file, info) => parseCategoryCollection(file, info);
|
||||
|
||||
const LoadedCollectionData: CollectionData
|
||||
= WindowsData;
|
||||
const PreParsedCollections: readonly CollectionData []
|
||||
= [ WindowsData, MacOsData ];
|
||||
|
||||
function validateCollectionsData(collections: readonly CollectionData[]) {
|
||||
if (!collections.length) {
|
||||
throw new Error('no collection provided');
|
||||
}
|
||||
if (collections.some((collection) => !collection)) {
|
||||
throw new Error('undefined collection provided');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import { Category } from '@/domain/Category';
|
||||
import { CollectionData } from 'js-yaml-loader!@/*';
|
||||
import { parseCategory } from './CategoryParser';
|
||||
import { ScriptCompiler } from './Compiler/ScriptCompiler';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { parseScriptingDefinition } from './ScriptingDefinitionParser';
|
||||
import { createEnumParser } from '../Common/Enum';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { CategoryCollection } from '@/domain/CategoryCollection';
|
||||
import { IProjectInformation } from '@/domain/IProjectInformation';
|
||||
import { CategoryCollectionParseContext } from './Script/CategoryCollectionParseContext';
|
||||
|
||||
export function parseCategoryCollection(
|
||||
content: CollectionData,
|
||||
info: IProjectInformation,
|
||||
osParser = createEnumParser(OperatingSystem)): ICategoryCollection {
|
||||
validate(content);
|
||||
const compiler = new ScriptCompiler(content.functions);
|
||||
const scripting = parseScriptingDefinition(content.scripting, info);
|
||||
const context = new CategoryCollectionParseContext(content.functions, scripting);
|
||||
const categories = new Array<Category>();
|
||||
for (const action of content.actions) {
|
||||
const category = parseCategory(action, compiler);
|
||||
const category = parseCategory(action, context);
|
||||
categories.push(category);
|
||||
}
|
||||
const os = osParser.parseEnum(content.os, 'os');
|
||||
const scripting = parseScriptingDefinition(content.scripting, info);
|
||||
const collection = new CategoryCollection(
|
||||
os,
|
||||
categories,
|
||||
|
||||
@@ -2,8 +2,8 @@ import { CategoryData, ScriptData, CategoryOrScriptData } from 'js-yaml-loader!@
|
||||
import { Script } from '@/domain/Script';
|
||||
import { Category } from '@/domain/Category';
|
||||
import { parseDocUrls } from './DocumentationParser';
|
||||
import { parseScript } from './ScriptParser';
|
||||
import { IScriptCompiler } from './Compiler/IScriptCompiler';
|
||||
import { ICategoryCollectionParseContext } from './Script/ICategoryCollectionParseContext';
|
||||
import { parseScript } from './Script/ScriptParser';
|
||||
|
||||
let categoryIdCounter: number = 0;
|
||||
|
||||
@@ -12,17 +12,15 @@ interface ICategoryChildren {
|
||||
subScripts: Script[];
|
||||
}
|
||||
|
||||
export function parseCategory(category: CategoryData, compiler: IScriptCompiler): Category {
|
||||
if (!compiler) {
|
||||
throw new Error('undefined compiler');
|
||||
}
|
||||
export function parseCategory(category: CategoryData, context: ICategoryCollectionParseContext): Category {
|
||||
if (!context) { throw new Error('undefined context'); }
|
||||
ensureValid(category);
|
||||
const children: ICategoryChildren = {
|
||||
subCategories: new Array<Category>(),
|
||||
subScripts: new Array<Script>(),
|
||||
};
|
||||
for (const data of category.children) {
|
||||
parseCategoryChild(data, children, category, compiler);
|
||||
parseCategoryChild(data, children, category, context);
|
||||
}
|
||||
return new Category(
|
||||
/*id*/ categoryIdCounter++,
|
||||
@@ -49,13 +47,13 @@ function parseCategoryChild(
|
||||
data: CategoryOrScriptData,
|
||||
children: ICategoryChildren,
|
||||
parent: CategoryData,
|
||||
compiler: IScriptCompiler) {
|
||||
context: ICategoryCollectionParseContext) {
|
||||
if (isCategory(data)) {
|
||||
const subCategory = parseCategory(data as CategoryData, compiler);
|
||||
const subCategory = parseCategory(data as CategoryData, context);
|
||||
children.subCategories.push(subCategory);
|
||||
} else if (isScript(data)) {
|
||||
const scriptData = data as ScriptData;
|
||||
const script = parseScript(scriptData, compiler);
|
||||
const script = parseScript(scriptData, context);
|
||||
children.subScripts.push(script);
|
||||
} else {
|
||||
throw new Error(`Child element is neither a category or a script.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { FunctionData } from 'js-yaml-loader!*';
|
||||
import { IScriptCompiler } from './Compiler/IScriptCompiler';
|
||||
import { ScriptCompiler } from './Compiler/ScriptCompiler';
|
||||
import { ICategoryCollectionParseContext } from './ICategoryCollectionParseContext';
|
||||
import { SyntaxFactory } from './Syntax/SyntaxFactory';
|
||||
import { ISyntaxFactory } from './Syntax/ISyntaxFactory';
|
||||
|
||||
export class CategoryCollectionParseContext implements ICategoryCollectionParseContext {
|
||||
public readonly compiler: IScriptCompiler;
|
||||
public readonly syntax: ILanguageSyntax;
|
||||
|
||||
constructor(
|
||||
functionsData: ReadonlyArray<FunctionData> | undefined,
|
||||
scripting: IScriptingDefinition,
|
||||
syntaxFactory: ISyntaxFactory = new SyntaxFactory()) {
|
||||
if (!scripting) { throw new Error('undefined scripting'); }
|
||||
this.syntax = syntaxFactory.create(scripting.language);
|
||||
this.compiler = new ScriptCompiler(functionsData, this.syntax);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { IScriptCode } from '@/domain/IScriptCode';
|
||||
import { ScriptCode } from '@/domain/ScriptCode';
|
||||
import { ScriptData, FunctionData, FunctionCallData, ScriptFunctionCallData, FunctionCallParametersData } from 'js-yaml-loader!@/*';
|
||||
import { IScriptCompiler } from './IScriptCompiler';
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
|
||||
interface ICompiledCode {
|
||||
readonly code: string;
|
||||
@@ -10,8 +11,11 @@ interface ICompiledCode {
|
||||
}
|
||||
|
||||
export class ScriptCompiler implements IScriptCompiler {
|
||||
constructor(private readonly functions: readonly FunctionData[]) {
|
||||
constructor(
|
||||
private readonly functions: readonly FunctionData[] | undefined,
|
||||
private syntax: ILanguageSyntax) {
|
||||
ensureValidFunctions(functions);
|
||||
if (!syntax) { throw new Error('undefined syntax'); }
|
||||
}
|
||||
public canCompile(script: ScriptData): boolean {
|
||||
if (!script.call) {
|
||||
@@ -33,7 +37,7 @@ export class ScriptCompiler implements IScriptCompiler {
|
||||
compiledCodes.push(functionCode);
|
||||
});
|
||||
const scriptCode = merge(compiledCodes);
|
||||
return new ScriptCode(script.name, scriptCode.code, scriptCode.revertCode);
|
||||
return new ScriptCode(scriptCode.code, scriptCode.revertCode, script.name, this.syntax);
|
||||
}
|
||||
|
||||
private getFunctionByName(name: string): FunctionData {
|
||||
@@ -43,7 +47,6 @@ export class ScriptCompiler implements IScriptCompiler {
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
private ensureCompilable(call: ScriptFunctionCallData) {
|
||||
if (!this.functions || this.functions.length === 0) {
|
||||
throw new Error('cannot compile without shared functions');
|
||||
@@ -69,7 +72,11 @@ function ensureNoDuplicatesInFunctionNames(functions: readonly FunctionData[]) {
|
||||
throw new Error(`duplicate function name: ${printList(duplicateFunctionNames)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureNoUndefinedItem(functions: readonly FunctionData[]) {
|
||||
if (functions.some((func) => !func)) {
|
||||
throw new Error(`some functions are undefined`);
|
||||
}
|
||||
}
|
||||
function ensureNoDuplicatesInParameterNames(functions: readonly FunctionData[]) {
|
||||
const functionsWithParameters = functions
|
||||
.filter((func) => func.parameters && func.parameters.length > 0);
|
||||
@@ -95,9 +102,10 @@ function ensureNoDuplicateCode(functions: readonly FunctionData[]) {
|
||||
}
|
||||
|
||||
function ensureValidFunctions(functions: readonly FunctionData[]) {
|
||||
if (!functions) {
|
||||
if (!functions || functions.length === 0) {
|
||||
return;
|
||||
}
|
||||
ensureNoUndefinedItem(functions);
|
||||
ensureNoDuplicatesInFunctionNames(functions);
|
||||
ensureNoDuplicatesInParameterNames(functions);
|
||||
ensureNoDuplicateCode(functions);
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { IScriptCompiler } from './Compiler/IScriptCompiler';
|
||||
|
||||
export interface ICategoryCollectionParseContext {
|
||||
readonly compiler: IScriptCompiler;
|
||||
readonly syntax: ILanguageSyntax;
|
||||
}
|
||||
@@ -1,22 +1,20 @@
|
||||
import { Script } from '@/domain/Script';
|
||||
import { ScriptData } from 'js-yaml-loader!@/*';
|
||||
import { parseDocUrls } from './DocumentationParser';
|
||||
import { parseDocUrls } from '../DocumentationParser';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { IScriptCompiler } from './Compiler/IScriptCompiler';
|
||||
import { IScriptCode } from '@/domain/IScriptCode';
|
||||
import { ScriptCode } from '@/domain/ScriptCode';
|
||||
import { createEnumParser, IEnumParser } from '../Common/Enum';
|
||||
import { createEnumParser, IEnumParser } from '../../Common/Enum';
|
||||
import { ICategoryCollectionParseContext } from './ICategoryCollectionParseContext';
|
||||
|
||||
export function parseScript(
|
||||
data: ScriptData, compiler: IScriptCompiler,
|
||||
data: ScriptData, context: ICategoryCollectionParseContext,
|
||||
levelParser = createEnumParser(RecommendationLevel)): Script {
|
||||
validateScript(data);
|
||||
if (!compiler) {
|
||||
throw new Error('undefined compiler');
|
||||
}
|
||||
if (!context) { throw new Error('undefined context'); }
|
||||
const script = new Script(
|
||||
/* name */ data.name,
|
||||
/* code */ parseCode(data, compiler),
|
||||
/* code */ parseCode(data, context),
|
||||
/* docs */ parseDocUrls(data),
|
||||
/* level */ parseLevel(data.recommend, levelParser));
|
||||
return script;
|
||||
@@ -29,11 +27,11 @@ function parseLevel(level: string, parser: IEnumParser<RecommendationLevel>): Re
|
||||
return parser.parseEnum(level, 'level');
|
||||
}
|
||||
|
||||
function parseCode(script: ScriptData, compiler: IScriptCompiler): IScriptCode {
|
||||
if (compiler.canCompile(script)) {
|
||||
return compiler.compile(script);
|
||||
function parseCode(script: ScriptData, context: ICategoryCollectionParseContext): IScriptCode {
|
||||
if (context.compiler.canCompile(script)) {
|
||||
return context.compiler.compile(script);
|
||||
}
|
||||
return new ScriptCode(script.name, script.code, script.revertCode);
|
||||
return new ScriptCode(script.code, script.revertCode, script.name, context.syntax);
|
||||
}
|
||||
|
||||
function ensureNotBothCallAndCode(script: ScriptData) {
|
||||
6
src/application/Parser/Script/Syntax/BatchFileSyntax.ts
Normal file
6
src/application/Parser/Script/Syntax/BatchFileSyntax.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
|
||||
export class BatchFileSyntax implements ILanguageSyntax {
|
||||
public readonly commentDelimiters = [ 'REM', '::' ];
|
||||
public readonly commonCodeParts = [ '(', ')', 'else' ];
|
||||
}
|
||||
6
src/application/Parser/Script/Syntax/ISyntaxFactory.ts
Normal file
6
src/application/Parser/Script/Syntax/ISyntaxFactory.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
|
||||
export interface ISyntaxFactory {
|
||||
create(language: ScriptingLanguage): ILanguageSyntax;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
|
||||
export class ShellScriptSyntax implements ILanguageSyntax {
|
||||
public readonly commentDelimiters = [ '#' ];
|
||||
public readonly commonCodeParts = [ '(', ')', 'else' ];
|
||||
}
|
||||
15
src/application/Parser/Script/Syntax/SyntaxFactory.ts
Normal file
15
src/application/Parser/Script/Syntax/SyntaxFactory.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { ISyntaxFactory } from './ISyntaxFactory';
|
||||
import { BatchFileSyntax } from './BatchFileSyntax';
|
||||
import { ShellScriptSyntax } from './ShellScriptSyntax';
|
||||
|
||||
export class SyntaxFactory implements ISyntaxFactory {
|
||||
public create(language: ScriptingLanguage): ILanguageSyntax {
|
||||
switch (language) {
|
||||
case ScriptingLanguage.batchfile: return new BatchFileSyntax();
|
||||
case ScriptingLanguage.shellscript: return new ShellScriptSyntax();
|
||||
default: throw new RangeError(`unknown language: "${ScriptingLanguage[language]}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { ScriptingDefinition } from '@/domain/ScriptingDefinition';
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { IProjectInformation } from '@/domain/IProjectInformation';
|
||||
import { createEnumParser } from '../Common/Enum';
|
||||
import { generateIlCode } from './Compiler/ILCode';
|
||||
import { generateIlCode } from './Script/Compiler/ILCode';
|
||||
|
||||
export function parseScriptingDefinition(
|
||||
definition: ScriptingDefinitionData,
|
||||
|
||||
Reference in New Issue
Block a user