diff --git a/bids/validator/sidecarValidator.js b/bids/validator/sidecarValidator.js index 0fbfc3b6..790c23fb 100644 --- a/bids/validator/sidecarValidator.js +++ b/bids/validator/sidecarValidator.js @@ -48,12 +48,11 @@ export class BidsHedSidecarValidator extends BidsValidator { for (const [sidecarKeyName, hedData] of this.bidsFile.parsedHedData) { if (hedData instanceof ParsedHedString) { // Value options have HED as string. - issues.push(...this._checkDetails(sidecarKeyName, hedData, true)) + issues.push(...this._checkDetails(sidecarKeyName, hedData)) } else if (hedData instanceof Map) { // Categorical options have HED as a Map. for (const valueString of hedData.values()) { - const placeholdersAllowed = this.bidsFile.sidecarKeys.get(sidecarKeyName).hasDefinitions - issues.push(...this._checkDetails(sidecarKeyName, valueString, placeholdersAllowed)) + issues.push(...this._checkDetails(sidecarKeyName, valueString)) } } else { IssueError.generateAndThrow('internalConsistencyError', { diff --git a/common/issues/data.js b/common/issues/data.js index 083250e3..4457edb6 100644 --- a/common/issues/data.js +++ b/common/issues/data.js @@ -277,7 +277,7 @@ export default { tooManyGroupTopTags: { hedCode: 'TAG_GROUP_ERROR', level: 'error', - message: stringTemplate`Group "${'string'}" has too many or too few tags at the top level.`, + message: stringTemplate`Group "${'string'}" has too many or too few tags or Def-expand groups at the top level.`, }, multipleTopLevelTagGroupTags: { hedCode: 'TAG_GROUP_ERROR', diff --git a/data/json/reservedTags.json b/data/json/reservedTags.json index 614c68d5..f4d95d07 100644 --- a/data/json/reservedTags.json +++ b/data/json/reservedTags.json @@ -141,7 +141,7 @@ "forbiddenSubgroupTags": [], "isTemporalTag": true, "requiresDef": true, - "otherAllowedNonDefTags": ["Def", "Delay"] + "otherAllowedNonDefTags": ["Delay"] }, "Onset": { "name": "Onset", diff --git a/parser/definitionManager.js b/parser/definitionManager.js index ce2328a4..83d4b934 100644 --- a/parser/definitionManager.js +++ b/parser/definitionManager.js @@ -1,6 +1,5 @@ import { generateIssue, IssueError } from '../common/issues/issues' import { parseHedString } from './parser' -//import { filterNonEqualDuplicates } from './parseUtils' import { filterByTagName } from './parseUtils' export class Definition { @@ -219,7 +218,7 @@ export class DefinitionManager { } /** - * Evaluate the definition based on a parsed HED tag + * Evaluate the definition based on a parsed HED tag. * @param {ParsedHedTag} tag - The tag to evaluate against the definitions. * @param {Schemas} hedSchemas - The schemas to be used to assist in the evaluation. * @param {boolean} placeholderAllowed - If true then placeholder is allowed in the def tag. diff --git a/parser/parseUtils.js b/parser/parseUtils.js index ea9e4a0d..8b70896c 100644 --- a/parser/parseUtils.js +++ b/parser/parseUtils.js @@ -44,20 +44,6 @@ export function filterTagMapByNames(tagMap, tagNames) { return keys.flatMap((key) => tagMap.get(key)) } -/*/!** - * Extract the ParsedHedTag tags that have a name from a specified list of names - * @param {ParsedHedTag[]} tags - to be filtered by name - * @param {[string]} tagList - List of tag names to filter by. - * @returns {ParsedHedTag[]} - A list of tags whose - *!/ - -export function filterByTagNames(tags, tagList) { - if (!tags || !tagList) { - return [] - } - return tags.filter((tag) => tagList.includes(tag.schemaTag.name)) -}*/ - /** * Convert a list of ParsedHedTag objects into a comma-separated string of their string representations. * @param {ParsedHedTag []} tagList - The HED tags whose string representations should be put in a comma-separated list. @@ -68,10 +54,10 @@ export function getTagListString(tagList) { } /** - * Create a map of the ParsedHedTags by type - * @param { ParsedHedTag[] } tagList - * @param {Set} tagNames - * @returns {Map} + * Create a map of the ParsedHedTags by type. + * @param { ParsedHedTag[] } tagList - The HED tags to be categorized. + * @param {Set} tagNames - The tag names to use as categories. + * @returns {Map} - A map of tag name to a list of tags with that name. */ export function categorizeTagsByName(tagList, tagNames = null) { // Initialize the map with keys from tagNames and an "other" key @@ -89,9 +75,9 @@ export function categorizeTagsByName(tagList, tagNames = null) { } /** - * Return a list of duplicate strings - * @param { string[] } itemList - A list of strings to look for duplicates in - * @returns {string []} - a list of unique duplicate strings (multiple copies not repeated + * Return a list of duplicate strings. + * @param { string[] } itemList - A list of strings to look for duplicates in. + * @returns {string []} - a list of unique duplicate strings (multiple copies not repeated. */ export function getDuplicates(itemList) { const checkSet = new Set() diff --git a/parser/parsedHedGroup.js b/parser/parsedHedGroup.js index 35590c55..0212b4e9 100644 --- a/parser/parsedHedGroup.js +++ b/parser/parsedHedGroup.js @@ -19,23 +19,41 @@ import { */ export default class ParsedHedGroup extends ParsedHedSubstring { /** - * The parsed HED tags or parsedHedGroups or parsedColumnSplices in the HED tag group at the top level + * The parsed HED tags, groups, or splices in the HED tag group at the top level. * @type {ParsedHedSubstring[]} */ tags + /** + * The top-level parsed HED tags in this string. + * @type {ParsedHedTag[]} + */ topTags + /** + * The top-level parsed HED groups in this string. + * @type {ParsedHedGroup[]} + */ topGroups + /** + * The top-level column splices in this string + * @type {ParsedHedColumnSplice[]} + */ topSplices + /** + * All the parsed HED tags in this string. + * @type {ParsedHedTag[]} + */ allTags + /** - * Any HED tags with special handling. This only covers top-level tags in the group + * Any HED tags with special handling. This only covers top-level tags in the group. * @type {Map} */ specialTags + /** * Whether this HED tag group has child groups with a Def-expand tag. * @type {boolean} @@ -48,12 +66,28 @@ export default class ParsedHedGroup extends ParsedHedSubstring { */ defExpandChildren + /** + * True if this group has a Def-expand tag at the top level. + * @type {boolean} + */ isDefExpandGroup + /** + * True if this group has a Definition tag at the top level. + * @type {boolean} + */ isDefinitionGroup + /** + * The total number of top-level Def tags and top-level Def-expand groups. + * @type {Number} + */ defCount + /** + * The unique top-level tag requiring a Def or Def-expand group, if any. + * @type {ParsedHedTag | null} + */ requiresDefTag /** @@ -74,11 +108,20 @@ export default class ParsedHedGroup extends ParsedHedSubstring { this._initializeGroups() } + /** + * Recursively create a list of all the tags in this group. + * @returns {ParsedHedTag[]} + * @private + */ _getAllTags() { const subgroupTags = this.topGroups.flatMap((tagGroup) => tagGroup.allTags) return this.topTags.concat(subgroupTags) } + /** + * Sets information about the special tags, particularly definition-related tags in this group. + * @private + */ _initializeGroups() { const special = ReservedChecker.getInstance() this.specialTags = categorizeTagsByName(this.topTags, special.specialNames) @@ -91,10 +134,11 @@ export default class ParsedHedGroup extends ParsedHedSubstring { } /** - * Filter top subgroups that include a special at the top-level of the group + * Filter top subgroups that include a special at the top-level of the group. * * @param {string} tagName - The schemaTag name to filter by. * @returns {Array} - Array of subgroups containing the specified tag. + * @private */ _filterSubgroupsByTagName(tagName) { return Array.from(this.topLevelGroupIterator()).filter((subgroup) => subgroup.specialTags.has(tagName)) @@ -147,18 +191,6 @@ export default class ParsedHedGroup extends ParsedHedSubstring { return this.specialTags.get(tagName) ?? [] } - isSpecialGroup(tagName) { - return this.specialTags.has(tagName) - } - - /** - * Whether this HED tag group is an onset, offset, or inset group. - * @returns {boolean} - */ - get isTemporalGroup() { - return this.isSpecialGroup('Onset') || this.isSpecialGroup('Offset') || this.isSpecialGroup('Inset') - } - get defTags() { const tags = this.getSpecial('Def') for (const group of this.defExpandChildren) { @@ -178,22 +210,6 @@ export default class ParsedHedGroup extends ParsedHedSubstring { ) } - /** - * The deeply nested array of parsed tags. - * @returns {ParsedHedTag[]} - */ - nestedGroups() { - const groups = [] - for (const innerTag of this.tags) { - if (innerTag instanceof ParsedHedTag) { - groups.push(innerTag) - } else if (innerTag instanceof ParsedHedGroup) { - groups.push(innerTag.nestedGroups()) - } - } - return groups - } - /** * Return a normalized string representation * @returns {string} @@ -220,20 +236,6 @@ export default class ParsedHedGroup extends ParsedHedSubstring { return `(${sortedNormalizedItems.join(',')})` // Using curly braces to indicate unordered group } - /** - * Iterator over the full HED groups and subgroups in this HED tag group. - * - * @yields {ParsedHedTag[]} The subgroups of this tag group. - */ - *subGroupArrayIterator() { - for (const innerTag of this.tags) { - if (innerTag instanceof ParsedHedGroup) { - yield* innerTag.subGroupArrayIterator() - } - } - yield this.tags - } - /** * Iterator over the ParsedHedGroup objects in this HED tag group. * @param {string | null} tagName - The name of the tag whose groups are to be iterated over or null if all tags. diff --git a/parser/parsedHedString.js b/parser/parsedHedString.js index 3e4a5f7a..274a3d97 100644 --- a/parser/parsedHedString.js +++ b/parser/parsedHedString.js @@ -39,7 +39,7 @@ export class ParsedHedString { */ columnSplices /** - * The top-level tag groups in the string, split into arrays. + * The tags in the top-level tag groups in the string, split into arrays. * @type {ParsedHedTag[][]} */ topLevelGroupTags diff --git a/parser/parsedHedTag.js b/parser/parsedHedTag.js index c4f3ad20..8ad463ee 100644 --- a/parser/parsedHedTag.js +++ b/parser/parsedHedTag.js @@ -77,7 +77,6 @@ export default class ParsedHedTag extends ParsedHedSubstring { super(tagSpec.tag, tagSpec.bounds) // Sets originalTag and originalBounds this._convertTag(hedSchemas, hedString, tagSpec) this._normalized = this.format(false) // Sets various forms of the tag. - this._validUnits = null } /** @@ -113,11 +112,12 @@ export default class ParsedHedTag extends ParsedHedSubstring { } /** - * Handle the remainder portion for value tag (converter handles others) + * Handle the remainder portion for value tag (converter handles others). * - * @param {SchemaTag} schemaTag - The part of the tag that is in the schema - * @param {string} remainder - the leftover part + * @param {SchemaTag} schemaTag - The part of the tag that is in the schema. + * @param {string} remainder - the leftover part. * @throws {IssueError} If parsing the remainder section fails. + * @private */ _handleRemainder(schemaTag, remainder) { if (!(schemaTag instanceof SchemaValueTag)) { @@ -149,10 +149,10 @@ export default class ParsedHedTag extends ParsedHedSubstring { } /** - * Separate the remainder of the tag into three parts: + * Separate the remainder of the tag into three parts. * - * @param {SchemaTag} schemaTag - The part of the tag that is in the schema - * @param {string} remainder - the leftover part + * @param {SchemaTag} schemaTag - The part of the tag that is in the schema. + * @param {string} remainder - The leftover part. * @returns {[SchemaUnit, string, string]} - The actual Unit, the unit string and the value string. * @throws {IssueError} If parsing the remainder section fails. */ @@ -206,6 +206,10 @@ export default class ParsedHedTag extends ParsedHedSubstring { } } + /** + * Return the normalized version of this tag. + * @returns {string} - The normalized version of this tag. + */ get normalized() { return this._normalized } @@ -233,82 +237,6 @@ export default class ParsedHedTag extends ParsedHedSubstring { return this.schema?.tagHasAttribute(this.formattedTag, attribute) } - /** - * Get the last part of a HED tag. - * - * @param {string} tagString A HED tag. - * @returns {string} The last part of the tag using the given separator. - */ - static getTagName(tagString) { - const lastSlashIndex = tagString.lastIndexOf('/') - if (lastSlashIndex === -1) { - return tagString - } else { - return tagString.substring(lastSlashIndex + 1) - } - } - - /** - * The trailing portion of {@link originalTag}. - * - * @returns {string} The "name" portion of the original tag. - */ - get originalTagName() { - return ParsedHedTag.getTagName(this.originalTag) - } - - /** - * Get the HED tag prefix (up to the last slash). - * - * @param {string} tagString A HED tag. - * @returns {string} The portion of the tag up to the last slash. - */ - static getParentTag(tagString) { - const lastSlashIndex = tagString.lastIndexOf('/') - if (lastSlashIndex === -1) { - return tagString - } else { - return tagString.substring(0, lastSlashIndex) - } - } - - /** - * Iterate through a tag's ancestor tag strings. - * - * @param {string} tagString - A tag string. - * @yields {string} - The tag's ancestor tags. - */ - static *ancestorIterator(tagString) { - while (tagString.lastIndexOf('/') >= 0) { - yield tagString - tagString = ParsedHedTag.getParentTag(tagString) - } - yield tagString - } - /* - - /!** - * Determine whether this tag is a descendant of another tag. - * - * @param {ParsedHedTag|string} parent The possible parent tag. - * @returns {boolean} Whether {@link parent} is the parent tag of this tag. - *!/ - isDescendantOf(parent) { - if (parent instanceof ParsedHedTag) { - if (this.schema !== parent.schema) { - return false - } - parent = parent.formattedTag - } - for (const ancestor of ParsedHedTag.ancestorIterator(this.formattedTag)) { - if (ancestor === parent) { - return true - } - } - return false - } -*/ - /** * Determine if this HED tag is equivalent to another HED tag. * @@ -379,26 +307,6 @@ export default class ParsedHedTag extends ParsedHedSubstring { return [] } - /** - * Get the legal units for this HED tag. - * - * @returns {Set} The legal units for this HED tag. - */ - get validUnits() { - if (this._validUnits) { - return this._validUnits - } - const tagUnitClasses = this.unitClasses - this._validUnits = new Set() - for (const unitClass of tagUnitClasses) { - const unitClassUnits = this.schema?.entries.unitClasses.getEntry(unitClass.name).units - for (const unit of unitClassUnits.values()) { - this._validUnits.add(unit) - } - } - return this._validUnits - } - /** * Check if value is a valid value for this tag. * diff --git a/parser/parser.js b/parser/parser.js index c80a3174..4bc8bd9f 100644 --- a/parser/parser.js +++ b/parser/parser.js @@ -19,15 +19,23 @@ class HedStringParser { */ hedSchemas + /** + * True if definitions are allowed in this string. + * @type {boolean} + */ definitionsAllowed + /** + * True if placeholders are allowedare 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 {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 */ @@ -48,9 +56,7 @@ class HedStringParser { if (this.hedString === null || this.hedString === undefined) { return [null, [generateIssue('invalidTagString', {})]] } - // if (!this.hedString) { - // return [null, []] - // } + const placeholderIssues = this._getPlaceholderCountIssues() if (placeholderIssues.length > 0) { return [null, placeholderIssues] @@ -157,7 +163,7 @@ export function parseHedString(hedString, hedSchemas, fullCheck, definitionsAllo /** * Parse a list of HED strings. * - * @param {string[]|ParsedHedString[]} hedStrings A list of HED strings. + * @param {string[]|ParsedHedString[]} hedStrings - A list of HED strings. * @param {Schemas} hedSchemas - The collection of HED schemas. * @param {boolean} fullCheck - If the strings is in final form -- can be fully parsed * @param {boolean} definitionsAllowed - True if definitions are allowed diff --git a/parser/reservedChecker.js b/parser/reservedChecker.js index 606aa084..d77326ca 100644 --- a/parser/reservedChecker.js +++ b/parser/reservedChecker.js @@ -120,7 +120,7 @@ export class ReservedChecker { */ checkTagGroupLevels(hedString, fullCheck) { const issues = [] - const topGroupTags = hedString.topLevelGroupTags + const topGroupTags = hedString.topLevelGroupTags.flat() hedString.tags.forEach((tag) => { // Check for top-level violations because tag is deep if (ReservedChecker.hasTopLevelTagGroupAttribute(tag)) { @@ -230,7 +230,7 @@ export class ReservedChecker { */ _checkGroupRequirements(group, specialTag, fullCheck) { const specialRequirements = ReservedChecker.reservedMap.get(specialTag.schemaTag.name) - const issues = this._checkAllowedTags(group, specialTag, specialRequirements.otherAllowedNonDefTags) + const issues = this._checkAllowedTags(group, specialTag, specialRequirements) if (issues.length > 0) { return issues } @@ -239,22 +239,30 @@ export class ReservedChecker { } /** - * Verify that the tags in the group are allowed with the special tag + * Verify that the tags in the group are allowed with the special tag. * - * @param {ParsedHedGroup} group - The enclosing tag group - * @param {ParsedHedTag} specialTag - The special tag whose tag requirements are to be checked - * @param { string[]} otherAllowed - The list of tags that are allowed with this tag - * @returns {Issue[]|[]} + * @param {ParsedHedGroup} group - The enclosing tag group. + * @param {ParsedHedTag} specialTag - The special tag whose tag requirements are to be checked. + * @param { Object } requirements - The requirements for this special tag. + * @returns {Issue[]} - Issues because * @private */ - _checkAllowedTags(group, specialTag, otherAllowed) { - if (otherAllowed === null || otherAllowed === undefined) { + _checkAllowedTags(group, specialTag, requirements) { + // The allowed tag requirement isn't applicable + if (requirements.otherAllowedNonDefTags === null || requirements.otherAllowedNonDefTags === undefined) { return [] } + // Check for def or def-expand tags for a special tag that does not need them. + if (!requirements.requiresDef && !group.requiresDefTag && group.defCount > 0) { + return [generateIssue('tooManyGroupTopTags', { string: group.originalTag })] + } + + // Isolate the other top tags. const otherTopTags = group.topTags.filter((tag) => tag !== specialTag) if (otherTopTags.length === 0) { return [] } + const encountered = new Set() for (const tag of otherTopTags) { if (encountered.has(tag.schemaTag.name)) { @@ -265,7 +273,7 @@ export class ReservedChecker { continue } // This tag is not allowed with the special tag - if (!otherAllowed.includes(tag.schemaTag.name)) { + if (!requirements.otherAllowedNonDefTags.includes(tag.schemaTag.name)) { return [ generateIssue('invalidGroupTopTags', { tags: getTagListString(group.topTags), string: group.originalTag }), ] @@ -285,10 +293,12 @@ export class ReservedChecker { * @private */ _checkAllowedGroups(group, specialTag, requirements, fullCheck) { - // Group checks are not applicable to this special tag + // Group checks are not applicable to this special tag. if (!requirements.tagGroup) { return [] } + + // Proper Def and Def-expand count for a tag that requires a def is checked when group is created. let subgroupCount = group.topGroups.length if (group.hasDefExpandChildren && group.requiresDefTag !== null) { subgroupCount = subgroupCount - 1 diff --git a/parser/tagConverter.js b/parser/tagConverter.js index 837a6ac4..fcbae2f1 100644 --- a/parser/tagConverter.js +++ b/parser/tagConverter.js @@ -137,23 +137,6 @@ export default class TagConverter { } } - /* /!** - * Handle the case where it does not allow an extension or if it requires a child and doesn't have one. - * @param {int} tagLevelIndex index of the tag - * @throws {IssueError} If the tag has an extension that is not allowed. - *!/ - _checkExtensionRequirements(tagLevelIndex) { - // Check allow extension or requires a child - const schemaTag = this.getSchemaTag(tagLevelIndex - 1) - const remainder = this.tagLevels.slice(tagLevelIndex).join('/') - if (this.special.noExtension.includes(schemaTag.name) && remainder !== '') { - IssueError.generateAndThrow('invalidExtension',{tag: remainder, parentTag: schemaTag.name}) - } - if (remainder === '' && schemaTag.hasAttributeName('requireChild')) ( - IssueError.generateAndThrow('childRequired', {tag:schemaTag.name}) - ) - }*/ - _getSchemaTag(tagLevelIndex) { const tagLevel = this.tagLevels[tagLevelIndex].toLowerCase() return this.tagMapping.getEntry(tagLevel) diff --git a/spec_tests/javascriptTests.json b/spec_tests/javascriptTests.json index e63f341b..0e4f0786 100644 --- a/spec_tests/javascriptTests.json +++ b/spec_tests/javascriptTests.json @@ -2728,7 +2728,7 @@ "name": "sidecar-braces-circular-reference", "description": "The item in curly braces has a HED annotation that contains curly braces.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -2836,7 +2836,7 @@ "name": "sidecar-braces-self-reference", "description": "The item in curly braces has a HED annotation that contains itself.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -2948,7 +2948,7 @@ "name": "sidecar-braces-appear-as-value-rather-than-tag", "description": "The curly braces are in a value rather than as a separate tag substitute.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -4186,7 +4186,7 @@ "name": "tag-group-error-missing", "description": "A tag has tagGroup or topLevelTagGroup attribute, but is not enclosed in parentheses.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -4286,7 +4286,7 @@ "name": "tag-group-error-not-top-level", "description": "A tag with the topLevelTagGroup does not appear at a HED tag group at the top level in an assembled HED annotation.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -4357,7 +4357,7 @@ "name": "multiple-top-level-tags-in-same-group", "description": "Multiple tags with the topLevelTagGroup attribute appear in the same top-level tag group. (Delay and Duration are allowed to be in the same topLevelTagGroup).", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -4842,19 +4842,19 @@ "name": "tag-requires-child-no-child", "description": "A tag has the requireChild schema attribute but does not have a child.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { - "fails": ["(Blue, Description)", "Label, Red"], - "passes": ["(Blue, Description/Yes this is a description., (Red))", "Label/Redish, Red"] + "fails": ["(Blue, Def)", "Def, Red"], + "passes": ["(Blue, Def/MyColor, (Red))", "Label/Redish, Def/MyColor, Red"] }, "sidecar_tests": { "fails": [ { "event_code": { "HED": { - "face": "(Blue, Description)", + "face": "(Blue, Def)", "ball": "Label, Red" } } @@ -4864,7 +4864,7 @@ { "event_code": { "HED": { - "face": "(Blue, Description/Yes this is a description., (Red))", + "face": "(Blue, Def/MyColor, (Red))", "ball": "Label/Redish, Red" } } @@ -4875,14 +4875,13 @@ "fails": [ [ ["onset", "duration", "HED"], - [4.5, 0, "(Blue, Description)"], - [5.0, 0, "Label, Red"] + [4.5, 0, "(Blue, Def)"] ] ], "passes": [ [ ["onset", "duration", "HED"], - [4.5, 0, "(Blue, Description/Yes this is a description., (Red))"], + [4.5, 0, "(Blue, Def/MyColor, (Red))"], [5.0, 0, "Label/Redish, Red"] ] ] @@ -4893,7 +4892,7 @@ "sidecar": { "event_code": { "HED": { - "face": "(Blue, Description)", + "face": "(Blue, Def)", "ball": "Label, Red" } } @@ -4931,7 +4930,7 @@ "name": "temporal-tag-error-not-tag-group", "description": "An Onset or Offset tag does not appear in a tag group.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5011,7 +5010,7 @@ "name": "temporal-tag-error-nested-group", "description": "An Onset or Offset tag appears in a nested tag group (not a top-level tag group).", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5094,7 +5093,7 @@ "name": "temporal-tag-error-wrong-number-of-defs", "description": "An Onset or Offset tag is not grouped with exactly one Def-expand tag group or a Def tag.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5181,7 +5180,7 @@ "name": "temporal-tag-error-onset-has-more-groups", "description": "An Onset group has more than one additional tag group.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5271,7 +5270,7 @@ "name": "temporal-tag-error-offset-has-groups", "description": "An Offset appears with one or more tags or additional tag groups.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5376,7 +5375,7 @@ "name": "temporal-tag-error-offset-with-no-onset", "description": "An Offset tag associated with a given definition appears after a previous Offset tag without the appearance of an intervening Onset of the same name.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5450,7 +5449,7 @@ "name": "temporal-tag-error-extra tags", "description": "An Onset tag group with has tags besides the anchor Def or Def-expand that are not in a tag group.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5535,7 +5534,7 @@ "name": "temporal-tag-error-duplicated-onset-or-offset", "description": "An Onset or an Offset with a given Def or Def-expand anchor appears in the same event marker with another Onset or Offset that uses the same anchor.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5624,7 +5623,7 @@ "name": "temporal-tag-error-inset-outside-its-event", "description": "An Inset tag is not grouped with a Def or Def-expand of an ongoing Onset.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5701,7 +5700,7 @@ "name": "temporal-tag-error-inset-group-has-extras", "description": "An Inset group has tags or groups in addition to its defining Def or Def-expand.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5781,7 +5780,7 @@ "name": "temporal-tag-error-duration-group", "description": "A Duration or Delay has extra tags or groups.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5845,7 +5844,7 @@ "name": "temporal-tag-error-tag-appears-where-not-allowed", "description": "A temporal tag appears appears in a tsv with no onset column", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -5941,11 +5940,15 @@ "name": "temporal-tag-error-not-tag-group-delay", "description": "A Delay is not in the tag group.", "warning": false, - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { - "fails": ["(Delay, Red)", "(Delay, Def/Acc/5.4)"], + "fails": [ + "(Delay/5, Red)", + "(Delay/5, Def/Acc/5.4)", + "(Delay/5, (Def-expand/Acc/5.4, (Acceleration/5.4 m-per-s^2, Red)))" + ], "passes": ["(Delay/5 s, (Def/Acc/5.4))"] }, "sidecar_tests": { @@ -6030,7 +6033,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-nested-group-delay", "description": "A delay appears in a group not in the top level.", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6112,7 +6115,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-wrong-number-of-defs-delay", "description": "An Onset or Offset tag is not grouped with exactly one Def-expand tag group or a Def tag.", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6201,7 +6204,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-onset-has-more-groups-delay", "description": "An Onset group has more than one additional tag group.", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6219,7 +6222,13 @@ { "event_code": { "HED": { - "face": "(Delay/5.0 s, Onset, Def/MyColor, (Red), (Blue))", + "face": "(Delay/5.0 s, Onset, Def/MyColor, (Red), (Blue))" + } + } + }, + { + "event_code": { + "HED": { "ball": "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), (Green), (Yellow), Onset)" } } @@ -6240,7 +6249,10 @@ "fails": [ [ ["onset", "duration", "HED"], - [4.5, 0, "(Delay/5.0 s, Onset, Def/MyColor, (Red), (Blue))"], + [4.5, 0, "(Delay/5.0 s, Onset, Def/MyColor, (Red), (Blue))"] + ], + [ + ["onset", "duration", "HED"], [5.5, 0, "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), (Green), ((Yellow)), Onset)"] ] ], @@ -6258,15 +6270,38 @@ "sidecar": { "event_code": { "HED": { - "face": "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), (Green), ((Yellow)), Onset)", + "face": "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), (Green), ((Yellow)), Onset)" + } + } + }, + "events": [ + ["onset", "duration", "event_code", "HED"], + [4.5, 0, "face", "Red, Def/MyColor"] + ] + }, + { + "sidecar": { + "event_code": { + "HED": { "ball": "(Delay/5.0 s, Onset, Def/MyColor, (Red), (Blue))" } } }, "events": [ ["onset", "duration", "event_code", "HED"], - [4.5, 0, "face", "Red, Def/MyColor"], - [5.0, 0, "ball", "Green"], + [5.0, 0, "ball", "Green"] + ] + }, + { + "sidecar": { + "event_code": { + "HED": { + "ball": "(Delay/5.0 s, Onset, Def/MyColor, (Blue))" + } + } + }, + "events": [ + ["onset", "duration", "event_code", "HED"], [6.0, 0, "n/a", "(Delay/5.0 s, Def/MyColor, (Red), (Blue), Onset)"] ] } @@ -6296,7 +6331,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-offset-has-groups-delay", "description": "An Offset appears with one or more tags or additional tag groups.", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6310,7 +6345,15 @@ "HED": { "face": "(Delay/5.0 s, Onset, Def/MyColor)", "ball": "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), Onset)", - "square": "(Delay/5.0 s, Offset, Def/MyColor, (Red))", + "square": "(Delay/5.0 s, Offset, Def/MyColor, (Red))" + } + } + }, + { + "event_code": { + "HED": { + "face": "(Delay/5.0 s, Onset, Def/MyColor)", + "ball": "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), Onset)", "circle": "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), Offset, Blue)" } } @@ -6351,6 +6394,24 @@ }, "combo_tests": { "fails": [ + { + "sidecar": { + "event_code": { + "HED": { + "face": "(Delay/5.0 s, Onset, Def/MyColor)", + "ball": "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), Onset)", + "square": "(Delay/5.0 s, Offset, Def/MyColor)" + } + } + }, + "events": [ + ["onset", "duration", "event_code", "HED"], + [4.5, 0, "face", "n/a"], + [4.8, 0, "square", "n/a"], + [4.9, 0, "ball", "Green"], + [5.5, 0, "n/a", "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), Offset, Blue), Orange"] + ] + }, { "sidecar": { "event_code": { @@ -6366,8 +6427,7 @@ ["onset", "duration", "event_code", "HED"], [4.5, 0, "face", "n/a"], [4.8, 0, "square", "n/a"], - [4.9, 0, "ball", "Green"], - [5.5, 0, "n/a", "(Delay/5.0 s, (Def-expand/MyColor, (Label/Pie)), Offset, Blue)", "Orange"] + [4.9, 0, "ball", "Green"] ] } ], @@ -6400,7 +6460,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-mismatch-delay", "description": "An Offset tag associated with a given definition appears after a previous Offset tag without the appearance of an intervening Onset of the same name.", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6473,7 +6533,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-extra tags-delay", "description": "An Onset tag group with has tags besides the anchor Def or Def-expand that are not in a tag group.", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6557,7 +6617,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-duplicated-onset-or-offset-delay", "description": "An Onset or an Offset with a given Def or Def-expand anchor appears in the same event marker with another Onset or Offset that uses the same anchor.", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6645,7 +6705,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-inset-outside-its-event-delay", "description": "An Inset tag is not grouped with a Def or Def-expand of an ongoing Onset.", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6721,7 +6781,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-inset-group-has-extras-delay", "description": "An Inset group has tags or groups in addition to its defining Def or Def-expand.", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6800,7 +6860,7 @@ "alt_codes": ["TAG_GROUP_ERROR"], "name": "temporal-tag-error-tag-appears-where-not-allowed-delay", "description": "An Inset, Offset, or Onset tag appears in a tsv with no onset column", - "schema": "8.2.0", + "schema": "8.3.0", "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], "tests": { "string_tests": { @@ -6855,7 +6915,7 @@ "event_code": { "HED": { "face": "(Delay/5.0 s, Def/MyColor, Duration)", - "ball": "(Delay/5.0 s, Def/MyColor, Delay)" + "ball": "(Delay/5.0 s, Def/MyColor, Delay/4.2)" } } }, diff --git a/tests/stringParserTests.spec.js b/tests/stringParserTests.spec.js index fbadf4cb..f3f23b15 100644 --- a/tests/stringParserTests.spec.js +++ b/tests/stringParserTests.spec.js @@ -12,7 +12,7 @@ import { shouldRun, getHedString } from './testUtilities' const skipMap = new Map() const runAll = true -const runMap = new Map([['tag-strings', ['multiple-complex-duplicates']]]) +const runMap = new Map([['special-tag-group-tests', ['inset-delay-def']]]) describe('Null schema objects should cause parsing to bail', () => { it('Should not proceed if no schema and valid string', () => { diff --git a/tests/testData/stringParserTests.data.js b/tests/testData/stringParserTests.data.js index f5264461..48e8bda4 100644 --- a/tests/testData/stringParserTests.data.js +++ b/tests/testData/stringParserTests.data.js @@ -852,6 +852,19 @@ export const parseTestData = [ errors: [], warnings: [], }, + { + testname: 'delay-with-unnested-def-expand-full-check', + explanation: '"(Delay/5, (Def-expand/MyColor, (Label/Pie)))" needs an extra group for Def-expand.', + schemaVersion: '8.3.0', + stringIn: '(Delay/5, (Def-expand/MyColor, (Label/Pie)))', + stringLong: null, + stringShort: null, + fullCheck: true, + placeholdersAllowed: false, + definitionsAllowed: false, + errors: [generateIssue('tooManyGroupTopTags', { string: '(Delay/5, (Def-expand/MyColor, (Label/Pie)))' })], + warnings: [], + }, { testname: 'offset-in-group-with-other-tags', explanation: '"(Offset, Item)" has an extra tag and needs a def tag.',