Skip to content

Commit

Permalink
feat: add argument types parsing
Browse files Browse the repository at this point in the history
- Add parsing of arguments types
- Flags now have type:null
- Add dictionary for argument types
- Add tests for argument types

Closes #12
  • Loading branch information
ifedchankau authored and itekaf committed Aug 16, 2018
1 parent b8ef2c8 commit 5d369a8
Show file tree
Hide file tree
Showing 12 changed files with 258 additions and 82 deletions.
1 change: 1 addition & 0 deletions .vscode/spellright.dict
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ enum
gim
npm
lodash
boolean
166 changes: 115 additions & 51 deletions src/arguments.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@
*/
const getArgumentObject = (object, context) => {
let argument = context.get.template.argument();
object.arg = removeExtraCharacters(object.arg);
argument = {
enum: getEnum(object.arg + object.description, context),
longName: getPropertyName(object.arg, context.regexp.argument.long),
shortName: getPropertyName(object.arg, context.regexp.argument.short),
defaultValue: getDefaultValue(
object.description, context.regexp.defaultValue),
description: unifyDescription(object.description),
description: unifyDescription(object.description.trim()),
};
argument.isFlag = !isPropertyTyped(object.arg)
&& isValueBoolean(argument.defaultValue)
&& (argument.enum === null);

argument.isFlag = identifyIsFlag(object.arg, argument);
argument.type = getPropertyType(object.arg, argument, context);
return argument;
};

Expand All @@ -30,9 +29,8 @@ const getArgumentObject = (object, context) => {
*/
const unifyDescription = (description) => {
if (typeof description !== 'string' && description.length === 0) return '';
let result = description.trim();
result = result.charAt(0).toUpperCase() + result.slice(1);
return removeExtraSpaces(deleteDotAtTheEnd(result));
const result = description.charAt(0).toUpperCase() + description.slice(1);
return removeExtraSpaces(removeDotAtTheEnd(result));
};

/**
Expand All @@ -43,7 +41,7 @@ const unifyDescription = (description) => {
*/
const getDefaultValue = (description, regexp) => {
const result = getValueByRegexp(description, regexp);
return result ? deleteDotAtTheEnd(result[5].trim()) : null;
return result ? removeDotAtTheEnd(result[5].trim()) : null;
};

/**
Expand All @@ -55,19 +53,8 @@ const getDefaultValue = (description, regexp) => {
const getEnum = (string, context) => {
const result = getValueByRegexp(string, context.regexp.enumValues.enum);
return result ? removeExtraEnumValues(
result[2].split(context.regexp.enumValues.split), context) : null;
};

/**
* Remove extra enum values (such as 'file', 'path', 'folder', etc.)
* @param {array} enumArray - array with enum values
* @param {object} context - internal config
* @return {array} enumArray - enum without extra values or null if it empty
*/
const removeExtraEnumValues = (enumArray, context) => {
const result = enumArray.filter((value) =>
value.toLowerCase().match(context.regexp.path) === null);
return result.length === 0 ? null : result;
result[2].split(context.regexp.enumValues.split), context.regexp.path)
: null;
};

/**
Expand All @@ -89,7 +76,7 @@ const getDelimiterValue = (string, regexp) => {
*/
const getPropertyName = (string, regexp) => {
const result = getValueByRegexp(string, regexp);
return result ? result[0].trim() : null;
return result ? removeComaAtTheEnd(result[0].trim()) : null;
};

/**
Expand All @@ -102,39 +89,70 @@ const isValueBoolean = (string) => {
};

/**
* Checks if argument string has any types by
* additional chars after property name
* Get type of an argument (string, number, null (for flags), etc.)
* @param {string} string - argument string without description
* @return {bool} - result of check
* @param {object} argument - argument object with description, names, etc.
* @param {object} context - internal parser config
* @return {string} argumentType - type of argument
*/
const isPropertyTyped = (string) => {
let isType = false;
string.split(/\s+/).map((argumentSubstring) => {
argumentSubstring.trim();
isType = argumentSubstring.indexOf('-') !== 0 ? true : isType;
});
return isType;
const getPropertyType = (string, argument, context) => {
if (argument.isFlag) return null;
const typesDictionary = context.get.template.typesDictionary();
const argumentAddition = removeExtraArgumentNames(string, argument);
const result = Object.keys(typesDictionary)
.find((type) => typesDictionary[type]
.find((alias) => {
return argumentAddition.toLowerCase().indexOf(alias) !== -1;
}));

return result ? result : context.get.template.option().type;
};

/**
* Identify if argument is flag
* @param {string} string - argument string without description
* @param {object} argument - argument object with description, names, etc.
* @return {boolean} isFlag - is argument flag
*/
const identifyIsFlag = (string, argument) => {
const argumentAddition = removeExtraArgumentNames(string, argument);
isFlag = !isValueBoolean(argument.defaultValue) ||
argument.enum !== null ||
argumentAddition ? false : true;
return isFlag;
};

/**
* General function for getting some values by regexp
* @param {string} string - searching string
* @param {string} regexp - regexp for finding
* @return {Array} - Array of result
* Remove extra enum values (such as 'file', 'path', 'folder', etc.)
* @param {array} enumArray - array with enum values
* @param {string} regexp - regexp for remove
* @return {array} enumArray - enum without extra values or null if it empty
*/
const getValueByRegexp = (string, regexp) => {
const regularExp = new RegExp(regexp, 'gim');
const result = regularExp.exec(string);
return result ? result : null;
const removeExtraEnumValues = (enumArray, regexp) => {
const result = enumArray.filter((value) => {
return value.toLowerCase().match(regexp) === null;
});
return result.length !== 0 ? result : null;
};

/**
* Remove coma and sing from string
* Remove extra coma from string
* @param {string} string - any string
* @return {string} - result string
*/
const removeExtraCharacters = (string) => {
return string.replace(/=/g, ' ').replace(/,/g, ' ');
const removeExtraComa = (string) => {
return removeChar(string, [',']).trim();
};
/**
* Remove argument names from string
* @param {string} string - any string
* @param {argument} argument - argument object with description, names, etc.
* @return {string} - result string
*/
const removeExtraArgumentNames = (string, argument) => {
return removeChar(
removeExtraComa(string),
[argument.longName, argument.shortName]).trim();
};

/**
Expand All @@ -143,19 +161,65 @@ const removeExtraCharacters = (string) => {
* @return {string} - result string
*/
const removeExtraSpaces = (string) => {
return string.replace(/\t/g, ' ').replace(/[\s]+/g, ' ');
return removeChar(string, [/\\t/g, /[\s]+/g]);
};

/**
* Delete dot at the end of string
* @param {string} string - source string
* @return {*} - string without dot at the end
*/
const deleteDotAtTheEnd = (string) => {
return string.charAt(string.length - 1) === '.' ?
* Remove extra coma from the end of line
* @param {string} string - any string
* @return {string} - result string
*/
const removeComaAtTheEnd = (string) => {
return removeCharAtTheEnd(string, ',');
};

/**
* Remove extra dot from the end of line
* @param {string} string - any string
* @return {string} - result string
*/
const removeDotAtTheEnd = (string) => {
return removeCharAtTheEnd(string, '.');
};


/**
* Delete char at the end of string
* @param {string} string - source string
* @param {char} char - char for remove
* @return {*} - string without dot at the end
*/
const removeCharAtTheEnd = (string, char) => {
return string.charAt(string.length - 1) === char ?
string.slice(0, string.length - 1) : string;
};

/**
* Remove all char from array in string
* @param {string} string - input string for removing
* @param {array} array - array of char which need to remove
* @return {string} - result string
*/
const removeChar = (string, array) => {
let result = string;
array.map((char) => {
result = result.replace(char, ' ');
});
return result;
};

/**
* General function for getting some values by regexp
* @param {string} string - searching string
* @param {string} regexp - regexp for finding
* @return {Array} - Array of result
*/
const getValueByRegexp = (string, regexp) => {
const regularExp = new RegExp(regexp, 'gim');
const result = regularExp.exec(string);
return result ? result : null;
};

// Export functions
exports = module.exports = {
getArgumentObject,
Expand Down
4 changes: 2 additions & 2 deletions src/template/configDefault.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
},
"delimiter": "=",
"prefixes": {
"custom": "linterhub:",
"nonFlag": "args:"
"custom": "linterhub",
"nonFlag": "args"
}
}
11 changes: 7 additions & 4 deletions src/template/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ const _ = require('lodash');
// Import function
const sections = require('./../sections.js');

// +// Import templates
// Import templates
const argument = require('./argument.json');
const option = require('./option.json');
const types = require('./types.dictionary.json');
const args = require('./args.json');


// Internal configuration with parsed arguments
const context = {
section: {
Expand All @@ -35,9 +37,9 @@ const context = {
path: 'file|path|folder|dir|directory',
delimiter: '-[^ \t(\n|\r\n)]+(\\s|=)[^ \t(\n|\r\n)-]',
enumValues: {
enum: '(<|\\(|\\")(([\\S]+(\\||\\",\\s\\"|\\sor\\s))+[\\S]+)' +
'(>|\\)|\\"|,\\s)',
split: /[\|]|\",\s\"|\"\sor\s\"/,
enum: '(<|\\(|\\")(([\\S]+(\\||\\",\\s\\"|\\"\\sor\\s\\"|,\\s))' +
'+[\\S]+[^<\\)\\"])(>|\\)|\\"|,\\s)',
split: /[\|]|\",\s\"|\"\sor\s\"|,\s/,
},
argument: {
short: '(\\s|^)-[^-]*?(\\s|=|$)',
Expand All @@ -51,6 +53,7 @@ const context = {
args: () => _.cloneDeep(args),
option: () => _.cloneDeep(option),
argument: () => _.cloneDeep(argument),
typesDictionary: () => _.cloneDeep(types),
},
},
};
Expand Down
25 changes: 25 additions & 0 deletions src/template/types.dictionary.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"string": [
"string",
"str",
"dir",
"directory",
"path",
"file"
],
"number": [
"number",
"integer",
"int"
],
"array": [
"array"
],
"object": [
"object"
],
"boolean": [
"bool",
"boolean"
]
}
24 changes: 12 additions & 12 deletions src/templatizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,36 @@ const templatizer = (context, config) => {
(option.shortName ? option.shortName : '');
switch (argumentName) {
case '--version':
optionSchema.id = customPrefix + 'version';
optionSchema.type = 'null';
optionSchema.id = `${customPrefix}:version`;
optionSchema.type = null;
break;
case '--help':
optionSchema.id = customPrefix + 'help';
optionSchema.type = 'null';
optionSchema.id = `${customPrefix}:help`;
optionSchema.type = null;
break;
case '--config':
optionSchema.id = customPrefix + 'config';
optionSchema.id = `${customPrefix}:config`;
break;
case '--stdin':
optionSchema.id = customPrefix + 'stdin';
optionSchema.id = `${customPrefix}:stdin`;
break;
case '--stdin-filename':
case '--stdin-filepath':
optionSchema.id = customPrefix + 'filename';
optionSchema.id = `${customPrefix}:filename`;
break;
case '':
optionSchema.id = customPrefix + 'path';
optionSchema.id = `${customPrefix}:path`;
option.description = 'Path to file or folder to analyze';
break;
default:
option.longName =
option.longName ? option.longName : option.shortName;
optionSchema.id = (!option.isFlag ? nonFlagPrefix : '')
+ option.longName;
const prefix = !option.isFlag ? `${nonFlagPrefix}:` : '';
optionSchema.id = prefix + argumentName;
optionSchema.type = option.type;
}
optionSchema.description = option.description;
if (option.defaultValue) optionSchema.default = option.defaultValue;
if (option.enum) optionSchema.enum = option.enum;

result.definitions.arguments.properties[argumentName] = optionSchema;
});

Expand Down
2 changes: 1 addition & 1 deletion test/parsing/files/delimiter.equal.valid.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"properties": {
"--ignore": {
"id": "--ignore",
"type": "string",
"type": null,
"description": "Additional paths to ignore [Can be set multiple times]"
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/parsing/files/delimiter.space.valid.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"properties": {
"--ignore": {
"id": "--ignore",
"type": "string",
"type": null,
"description": "Additional paths to ignore [Can be set multiple times]"
}
}
Expand Down
Loading

0 comments on commit 5d369a8

Please sign in to comment.