Skip to content

Commit

Permalink
feat: add command parsing
Browse files Browse the repository at this point in the history
Closes #105
  • Loading branch information
romanrostislavovich committed Nov 30, 2018
1 parent 5e32d64 commit dae583a
Show file tree
Hide file tree
Showing 23 changed files with 455 additions and 106 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"type": "node",
"request": "launch",
"name": "Debug Project",
"program": "${workspaceFolder}\\src\\index.ts",
"program": "${workspaceFolder}\\index.ts",
"preLaunchTask": "npm: build",
"sourceMaps": true,
"smartStep": true,
Expand Down
4 changes: 2 additions & 2 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Usage} from './models/usage';
import {config} from './config';
import {ClientFactory} from './factory/ClientFactory';
import {Usage} from './models/usage';
import {ClientFactory} from './factory/сlientFactory';

import './extensions/String';

Expand Down
7 changes: 4 additions & 3 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
export const config = {
settings: {
trimEnd: '.',
example: 'example',
example: ['example', 'usage'],
blackList: {
section: ['example'],
},
line: {
breaker: /\n|\r/gim,
breaker: /\r\n|\r|\n/gim,
join: ' ',
}
},
reg: {
arg: {
start: '-',
start: '(?:^|^ *)((-[^-]|--).*?)(?:\\s|=|$|,)',
long: '(--(?:\\w|-|$)*)',
short: '(?:\\s|^)(-[^-]*?)(?:\\s|=|$|,)',
},
enums: {
values: '(?:[<(\["])(([\\S]+([|,]|\\sor\\s|,\\s))+[\\S]{1,})(?:[>)\\]"]|,\\s|$)',
split: /\||<|>|\(|\)|\[|\]|\"|\'|,|\s|or/
},
command: '(?:^)(?:((?:\\w*\\s*){1,1})(?: |$))$',
section: '(?:^)(?:((?:\\w*\\s*){1,2})(?::| |$)|(?:\\w*\\s*){3,4}(?::|$))$',
delimiter: '[\\s]*[-]+[^ \t(\n|\r\n)]+(\\s|=)[^ :\t(\n|\r\n)-]',
tabulation: /\\s\\s|\\t| /,
Expand Down
93 changes: 60 additions & 33 deletions src/factory/ClientFactory.ts → src/factory/сlientFactory.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import {Usage} from './../models/usage';
import {Group} from './../models/group';
import {Section} from './../models/section';
import {Argument} from './../models/argument';
import {config} from '../config';
import {LineType} from '../types/lineType';
import {Usage} from '../models/usage';
import {Group} from '../models/group';
import {Section} from '../models/section';
import {Command} from '../models/command';
import {Argument} from '../models/argument';
import {LineParts} from '../models/lineParts';
import {UsageProperty} from '../interface/UsageProperty';

import {config} from './../config';
import './../interface/String';
import '../interface/String';

/**
* The client factory with describe how to work with main data in Usage Parser
Expand All @@ -13,12 +17,12 @@ class ClientFactory {

/**
* Examples of usage
* @type {?string[] | undefined}
* @type {string[] | undefined}
*/
examples?: string[] | undefined;
examples: string[];

constructor() {
this.examples = undefined;
this.examples = [];
}

/**
Expand All @@ -28,8 +32,7 @@ class ClientFactory {
*/
splitLine(input: string) {
const linesArray = input.split(config.settings.line.breaker);
const linesClear = linesArray.filter((x) => x !== '');
return linesClear;
return linesArray;
}

/**
Expand All @@ -51,23 +54,40 @@ class ClientFactory {
}

/**
* Create Arguments from the lines. Each of line is splitting to 2 parts before create Argument
* @param {string[]} lines - The lines which contains argument and description
* @return {Argument[]} - The array of arguments with properties
* Create Usage properties from the lines. Each of line is splitting to 2 parts before create Argument
* @param {string[]} lines - The lines which contains arguments, commands and description
* @return {UsageProperty[]} - The array of properties
*/
createArguments = (lines: string[]) : Argument[] => {
createProperties = (lines: LineParts[]) : UsageProperty[] => {
return lines.reduce((acum, line) => {
const cleanLine = line.trimEnd(config.settings.trimEnd);
const parts = cleanLine
.split(config.reg.tabulation)
.filter((x: string) => x !== '');
if (!parts.length) { return acum; }
const argsLine = parts[0].includes(config.reg.arg.start) ? parts.shift() : undefined;
const descLine = parts.join(config.settings.line.join).unify();
const arg = Argument.create(argsLine, descLine);
acum.push(arg);
const clearRight = line.right ? line.right.unify() : line.right;
switch(line.type) {
case LineType.command:
acum.push(Command.create(line.left, clearRight));
break;
case LineType.argument:
acum.push(Argument.create(line.left, clearRight));
break;
default:
break;
}
return acum;
}, new Array<Argument>());
}, new Array<UsageProperty>());
}


/**
* Create examples from group lines
* @param group - The group from which need to create examples
* @returns {void}
*/
createExamples(group: Group) : void {
this.examples = group.lines.reduce((acum, line) => {
const examplesArray = line.fully.split(config.reg.tabulation);
const examplesClear = examplesArray.filter((x: string) => x !== '');
acum = acum.concat(examplesClear);
return acum;
}, this.examples);
}

/**
Expand All @@ -76,17 +96,22 @@ class ClientFactory {
* @return {Section[]} - The array of sections
*/
createSections (groupsArray: Group[]) : Section[] {
const exampleRegExp = new RegExp(config.settings.example, 'i');
const exampleRegExp = new RegExp(config.settings.example.join('|'), 'i');
return groupsArray.reduce((acum, group) => {

if (group.name && group.name.search(exampleRegExp) !== -1) {
this.examples = group.lines;
this.createExamples(group);
return acum;
}
const argsArray = this.createArguments(group.lines);
const argsClear = argsArray.filter((arg) =>
!(arg.longName === undefined && arg.shortName === undefined));
if (argsClear.length) {
const section = Section.create(group.name, argsClear);

const propertiesArray = this.createProperties(group.lines);
const propertiesClear = propertiesArray.filter((property) => {
return property instanceof Argument ?
!(property.longName === undefined && property.shortName === undefined) : true;
});

if (propertiesClear.length) {
const section = Section.create(group.name, propertiesClear);
acum.push(section);
}
return acum;
Expand All @@ -105,7 +130,9 @@ class ClientFactory {
const sectionsClear = sectionsArray.filter((item) => {
return item.name ? item.name.search(ignoredSectionRegExp) === -1 : true;
});
const usage = sectionsClear.length ? Usage.create(sectionsClear, delimiter, this.examples) : undefined;

const usageExamples = this.examples.length ? this.examples : undefined;
const usage = sectionsClear.length ? Usage.create(sectionsClear, delimiter, usageExamples) : undefined;
return usage;
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/interface/UsageProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
interface UsageProperty {}

export {UsageProperty};
17 changes: 9 additions & 8 deletions src/models/argument.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {config} from './../config';
import { ArgumentType } from '../types/argumentType';
import { ArgumentTypes } from './argumentTypes';
import {config} from '../config';
import {ArgumentType} from '../types/argumentType';
import {ArgumentTypes} from './argumentTypes';
import {UsageProperty} from '../interface/UsageProperty';

import './../extensions/String';
import '../extensions/String';

/** Argument object of usage's doc */
export class Argument {
export class Argument implements UsageProperty {
/**
* The full name of the argument
* @type {?string | undefined}
Expand Down Expand Up @@ -71,7 +72,7 @@ export class Argument {
* @param {string | undefined} argsLine - The line which include anything but description
* @param {string } descLine - The line which include only description
*/
constructor(argsLine: string | undefined, descLine: string) {
constructor(argsLine: string | undefined, descLine: string | undefined) {
this.longName = config.reg.arg.long.firstMatch(argsLine);
this.shortName = config.reg.arg.short.firstMatch(argsLine);
this.description = descLine;
Expand Down Expand Up @@ -114,7 +115,7 @@ export class Argument {
* @param {string} descLine - The string which contains only description of argument
* @return {string[] | undefined} - The array strings or undefined. Undefined if values cannot identify
*/
_getValues(argsLine: string | undefined, descLine: string) : string[] | undefined {
_getValues(argsLine: string | undefined, descLine: string | undefined) : string[] | undefined {
const fullLine = (argsLine || '') + config.reg.tabulation + descLine;
const valuesLine = config.reg.enums.values.firstMatch(fullLine);
const valuesArray = valuesLine ? valuesLine
Expand All @@ -129,7 +130,7 @@ export class Argument {
* @param {string} descLine - The line which include only description
* @return {Argument} - The Argument
*/
static create(argsLine: string | undefined, descLine: string) {
static create(argsLine: string | undefined, descLine: string | undefined) {
return new Argument(argsLine, descLine);
}
}
5 changes: 3 additions & 2 deletions src/models/argumentTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ArgumentType } from '../types/argumentType';
import { config } from './../config';
import {config} from '../config';
import {ArgumentType} from '../types/argumentType';


/**
* The Argument Type class which include type and aliases, also method for indentify
Expand Down
59 changes: 59 additions & 0 deletions src/models/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {Usage} from "./usage";
import {UsageProperty} from '../interface/UsageProperty';

/**
* The command with name, description and usage, if they exist
*/
class Command implements UsageProperty {

/**
* The command name
* @type {string}
*/
command: string | undefined;

/**
* The command description
* @type {?string | undefined}
*/
description?: string | undefined;

/**
* The commands usage
* @type {?Usage | undefined}
*/
usage?: Usage | undefined;

/**
* @constructor
* @param {string} command - The command name
* @param {string | undefined } descripton - The command description
* @param {Usage | undefined} usage - The command usage
*/
constructor(
command: string | undefined,
descripton: string | undefined,
usage: Usage | undefined
) {
this.command = command;
this.usage = usage;
this.description = descripton;
}

/**
* Create Command object with name, description and usage
* @param {string} command - The command name
* @param {string | undefined } [descripton="undefined"] - The command description
* @param {Usage | undefined} [usage="undefined"] - The command usage
* @returns {Command} - The command object
*/
static create(
command: string | undefined,
description: string | undefined = undefined,
usage: Usage | undefined = undefined
) {
return new Command(command, description, usage);
}
}

export { Command };
31 changes: 23 additions & 8 deletions src/models/group.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {config} from './../config';
import {LineType} from '../types/lineType';
import {LineParts} from './lineParts';

/**
* The Group with name and array of lines
Expand All @@ -15,16 +16,23 @@ export class Group {
* The Array of lines
* @type {string[]}
*/
lines: string[];
lines: LineParts[];

/**
* If the next line needs to add alone
* @type {boolean}
*/
nextIsProperty: boolean;

/**
* @constructor
* @param {string | undefined } name - The name of group
* @param {string[]} lines - The array of lines
*/
constructor(name : string | undefined, lines: string[]){
constructor(name : string | undefined, lines: LineParts[]){
this.name = name;
this.lines = lines;
this.nextIsProperty = true;
}

/**
Expand All @@ -33,19 +41,26 @@ export class Group {
* @return {Group} - The current Group with added line
*/
addLine(line: string) : Group {
const readyLine = line.includes(config.reg.arg.start) ?
line : (this.lines.pop() || '') + config.settings.line.join + line;
this.lines.push(readyLine);

const linesParts = LineParts.create(line);
const isEmptyLine = line === '';

this.nextIsProperty = isEmptyLine || linesParts.type !== LineType.undefined || !this.lines.length;

const readyLineParts = this.nextIsProperty ? linesParts : this.lines.pop()!.update(linesParts);

this.lines.push(readyLineParts);
this.nextIsProperty = isEmptyLine;
return this;
}

/**
* Create Group with name and array of lines
* @param {string | undefined} [name="undefined"] - The name of group, default is `undefined`
* @param {string[]} [lines="[]"] - The array of lines, default is `[]`
* @param {LineParts[]} [lines="[]"] - The array of lines, default is `[]`
* @return {Group} - The Group
*/
static create(name : string | undefined = undefined, lines: string[] = []) : Group {
static create(name : string | undefined = undefined, lines: LineParts[] = []) : Group {
return new Group(name, lines);
}
}
Loading

0 comments on commit dae583a

Please sign in to comment.