import ParsedHedString from './parsedHedString'
import HedStringSplitter from './splitter'
import { generateIssue } from '../issues/issues'
import { ReservedChecker } from './reservedChecker'
import { DefinitionChecker } from './definitionChecker'
/**
* A parser for HED strings.
*/
class HedStringParser {
/**
* The HED string being parsed.
* @type {string|ParsedHedString}
*/
hedString
/**
* The collection of HED schemas.
* @type {Schemas}
*/
hedSchemas
/**
* True if definitions are allowed in this string.
* @type {boolean}
*/
definitionsAllowed
/**
* True if placeholders are allowed in this string.
* @type {boolean}
*/
placeholdersAllowed
/**
* Constructor.
*
* @param {string|ParsedHedString} hedString - The HED string to be parsed.
* @param {Schemas} hedSchemas - The collection of HED schemas.
* @param {boolean} definitionsAllowed - True if definitions are allowed
* @param {boolean} placeholdersAllowed - True if placeholders are allowed
*/
constructor(hedString, hedSchemas, definitionsAllowed, placeholdersAllowed) {
this.hedString = hedString
this.hedSchemas = hedSchemas
this.definitionsAllowed = definitionsAllowed
this.placeholdersAllowed = placeholdersAllowed
}
/**
* Parse a full HED string.
*
* @param {boolean} fullValidation - True if full full validation should be performed -- with assemploy
* ###Note: now separates errors and warnings for easier handling.
*
* @returns {Array} - [ParsedHedString|null, Issue[], Issue[]] representing the parsed HED string and any parsing issues.
*/
parse(fullValidation) {
if (this.hedString === null || this.hedString === undefined) {
return [null, [generateIssue('invalidTagString', {})], []]
}
const placeholderIssues = this._getPlaceholderCountIssues()
if (placeholderIssues.length > 0) {
return [null, placeholderIssues, []]
}
if (this.hedString instanceof ParsedHedString) {
return [this.hedString, [], []]
}
if (!this.hedSchemas) {
return [null, [generateIssue('missingSchemaSpecification', {})], []]
}
// This assumes that splitter errors are only errors and not warnings
const [parsedTags, parsingIssues] = new HedStringSplitter(this.hedString, this.hedSchemas).splitHedString()
if (parsedTags === null || parsingIssues.length > 0) {
return [null, parsingIssues, []]
}
// Returns a parsed HED string unless empty
const parsedString = new ParsedHedString(this.hedString, parsedTags)
if (!parsedString) {
return [null, null, []]
}
// Check the definition syntax issues
const definitionIssues = new DefinitionChecker(parsedString).check(this.definitionsAllowed)
if (definitionIssues.length > 0) {
return [null, definitionIssues, []]
}
// Check the other reserved tags requirements
const checkIssues = ReservedChecker.getInstance().checkHedString(parsedString, fullValidation)
if (checkIssues.length > 0) {
return [null, checkIssues, []]
}
// Warnings are only checked when there are no fatal errors
return [parsedString, [], this._getWarnings(parsedString)]
}
/**
* Get warnings applicable for a parsed HED string.
* @param {ParsedHedString} parsedString - HED string object to check for warnings.
* @returns {Issue[]} - Warnings for the parsed HED string
* @private
*/
_getWarnings(parsedString) {
const warnings = []
// Check for deprecated
const deprecatedTags = parsedString.tags.filter((tag) => tag.isDeprecated === true)
if (deprecatedTags.length > 0) {
const deprecated = deprecatedTags.map((tag) => tag.toString())
warnings.push(
generateIssue('deprecatedTag', { tags: '[' + deprecated.join(', ') + ']', string: parsedString.hedString }),
)
}
// Check for tag extensions
const extendedTags = parsedString.tags.filter((tag) => tag.isExtended === true)
if (extendedTags.length > 0) {
const extended = extendedTags.map((tag) => tag.toString())
warnings.push(
generateIssue('extendedTag', { tags: '[' + extended.join(', ') + ']', string: parsedString.hedString }),
)
}
return warnings
}
/**
* If placeholders are not allowed and the string has placeholders, return an issue.
* @returns {Issue[]} = Issues due to unwanted placeholders.
* @private
*/
_getPlaceholderCountIssues() {
if (this.placeholdersAllowed) {
return []
}
const checkString = this.hedString instanceof ParsedHedString ? this.hedString.hedString : this.hedString
if (checkString.split('#').length > 1) {
return [generateIssue('invalidPlaceholderContext', { string: checkString })]
}
return []
}
/**
* Parse a list of HED strings.
*
* @param {string[]|ParsedHedString[]} hedStrings A list of HED strings.
* @param {Schemas} hedSchemas The collection of HED schemas.
* @param {boolean} definitionsAllowed - True if definitions are allowed
* @param {boolean} placeholdersAllowed - True if placeholders are allowed
* @returns {Array} - [ParsedHedString[], Issue[], Issue[]] representing the parsed HED strings and any errors and warnings.
*/
static parseHedStrings(hedStrings, hedSchemas, definitionsAllowed, placeholdersAllowed) {
if (!hedSchemas) {
return [null, [generateIssue('missingSchemaSpecification', {})], []]
}
const parsedStrings = []
const errors = []
const warnings = []
for (const hedString of hedStrings) {
const [parsedString, errorIssues, warningIssues] = new HedStringParser(
hedString,
hedSchemas,
definitionsAllowed,
placeholdersAllowed,
).parse()
parsedStrings.push(parsedString)
errors.push(...errorIssues)
warnings.push(...warningIssues)
}
return [parsedStrings, errors, warnings]
}
}
/**
* Parse a HED string.
*
* ###Note: now separates errors and warnings for easier handling.
*
* @param {string|ParsedHedString} hedString A (possibly already parsed) HED string.
* @param {Schemas} hedSchemas - The collection of HED schemas.
* @param {boolean} definitionsAllowed - True if definitions are allowed.
* @param {boolean} placeholdersAllowed - True if placeholders are allowed.
* @param {boolean} fullValidation - True if full validation is required.
* @returns {Array} - [ParsedHedString, Issue[], Issue[]] representing the parsed HED string and any issues found.
*/
export function parseHedString(hedString, hedSchemas, definitionsAllowed, placeholdersAllowed, fullValidation) {
return new HedStringParser(hedString, hedSchemas, definitionsAllowed, placeholdersAllowed).parse(fullValidation)
}
/**
* Parse a list of HED strings.
*
* ###Note: now separates errors and warnings for easier handling.
*
* @param {string[]|ParsedHedString[]} hedStrings - A list of HED strings.
* @param {Schemas} hedSchemas - The collection of HED schemas.
* @param {boolean} definitionsAllowed - True if definitions are allowed
* @param {boolean} placeholdersAllowed - True if placeholders are allowed
* @returns {Array} - [ParsedHedString[], Issue[], Issue[]] representing the parsed HED strings and any issues found.
*/
export function parseHedStrings(hedStrings, hedSchemas, definitionsAllowed, placeholdersAllowed, fullValidation) {
return HedStringParser.parseHedStrings(
hedStrings,
hedSchemas,
definitionsAllowed,
placeholdersAllowed,
fullValidation,
)
}