Skip to content

Commit

Permalink
Initial implementation of curly braces
Browse files Browse the repository at this point in the history
Also fix typo in unbalanced parentheses issue names.
  • Loading branch information
happy5214 committed Sep 15, 2023
1 parent 852bcba commit a66b105
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 12 deletions.
30 changes: 28 additions & 2 deletions common/issues/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ export default {
level: 'error',
message: stringTemplate`Number of opening and closing parentheses are unequal. ${'opening'} opening parentheses. ${'closing'} closing parentheses.`,
},
unopenedParentheses: {
unopenedParenthesis: {
hedCode: 'PARENTHESES_MISMATCH',
level: 'error',
message: stringTemplate`Closing parenthesis at index ${'index'} of string "${'string'}" does not have a corresponding opening parenthesis.`,
},
unclosedParentheses: {
unclosedParenthesis: {
hedCode: 'PARENTHESES_MISMATCH',
level: 'error',
message: stringTemplate`Opening parenthesis at index ${'index'} of string "${'string'}" does not have a corresponding closing parenthesis.`,
Expand Down Expand Up @@ -195,6 +195,32 @@ export default {
level: 'error',
message: stringTemplate`Source HED schema is invalid as it contains duplicate tags.`,
},
// Curly brace issues
unopenedCurlyBrace: {
hedCode: 'SIDECAR_BRACES_INVALID',
level: 'error',
message: stringTemplate`Closing curly brace at index ${'index'} of string "${'string'}" does not have a corresponding opening curly brace.`,
},
unclosedCurlyBrace: {
hedCode: 'SIDECAR_BRACES_INVALID',
level: 'error',
message: stringTemplate`Opening curly brace at index ${'index'} of string "${'string'}" does not have a corresponding closing curly brace.`,
},
nestedCurlyBrace: {
hedCode: 'SIDECAR_BRACES_INVALID',
level: 'error',
message: stringTemplate`Opening curly brace at index ${'index'} of string "${'string'}" when curly brace expression is already open.`,
},
emptyCurlyBrace: {
hedCode: 'SIDECAR_BRACES_INVALID',
level: 'error',
message: stringTemplate`Curly brace expression at indices (${0}, ${1}) is empty.`,
},
curlyBracesInDefinition: {
hedCode: 'DEFINITION_INVALID',
level: 'error',
message: stringTemplate`Curly brace expression "${'column'}" found in definition "${'definition'}". Indices (${0}, ${1}).`,
},
// Schema issues
invalidSchemaNickname: {
hedCode: 'SCHEMA_LOAD_FAILED',
Expand Down
6 changes: 2 additions & 4 deletions tests/event.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,13 @@ describe('HED string and event validation', () => {
}
const expectedIssues = {
openingBrace: [
generateIssue('invalidCharacter', {
character: '{',
generateIssue('unclosedCurlyBrace', {
index: 47,
string: testStrings.openingBrace,
}),
],
closingBrace: [
generateIssue('invalidCharacter', {
character: '}',
generateIssue('unopenedCurlyBrace', {
index: 47,
string: testStrings.closingBrace,
}),
Expand Down
9 changes: 9 additions & 0 deletions validator/event/hed3.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getParsedParentTags } from '../../utils/hedData'
import { getParentTag, getTagName, hedStringIsAGroup, replaceTagNameWithPound } from '../../utils/hedStrings'
import { getCharacterCount, isNumber } from '../../utils/string'
import { HedValidator } from './validator'
import ParsedHedColumnSplice from '../parser/parsedHedColumnSplice'

const tagGroupType = 'tagGroup'
const topLevelTagGroupType = 'topLevelTagGroup'
Expand Down Expand Up @@ -409,6 +410,14 @@ export class Hed3Validator extends HedValidator {
...defExpandParentTags.values(),
...defParentTags.values(),
]
if (innerTag instanceof ParsedHedColumnSplice) {
this.pushIssue('curlyBracesInDefinition', {
definition: definitionName,
bounds: innerTag.originalBounds,
column: innerTag.originalTag,
})
continue
}
if (
nestedDefinitionParentTags.some((parentTag) => {
return innerTag.isDescendantOf(parentTag)
Expand Down
6 changes: 6 additions & 0 deletions validator/parser/parsedHedColumnSplice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import ParsedHedSubstring from './parsedHedSubstring'

/**
* A parsed HED tag.
*/
export default class ParsedHedColumnSplice extends ParsedHedSubstring {}
3 changes: 2 additions & 1 deletion validator/parser/parsedHedGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getParsedParentTags } from '../../utils/hedData'
import { getTagName } from '../../utils/hedStrings'
import ParsedHedSubstring from './parsedHedSubstring'
import { ParsedHedTag } from './parsedHedTag'
import ParsedHedColumnSplice from './parsedHedColumnSplice'

/**
* A parsed HED tag group.
Expand Down Expand Up @@ -497,7 +498,7 @@ export default class ParsedHedGroup extends ParsedHedSubstring {
*/
*tagIterator() {
for (const innerTag of this.tags) {
if (innerTag instanceof ParsedHedTag) {
if (innerTag instanceof ParsedHedTag || innerTag instanceof ParsedHedColumnSplice) {
yield innerTag
} else if (innerTag instanceof ParsedHedGroup) {
yield* innerTag.tagIterator()
Expand Down
75 changes: 70 additions & 5 deletions validator/parser/splitHedString.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import flattenDeep from 'lodash/flattenDeep'

import { ParsedHedTag, ParsedHed3Tag } from './parsedHedTag'
import ParsedHedColumnSplice from './parsedHedColumnSplice'
import ParsedHedGroup from './parsedHedGroup'
import { Schema, Schemas } from '../../common/schema/types'
import { generateIssue } from '../../common/issues/issues'
Expand All @@ -12,10 +13,12 @@ import { ParsedHed2Tag } from '../hed2/parser/parsedHed2Tag'

const openingGroupCharacter = '('
const closingGroupCharacter = ')'
const openingColumnCharacter = '{'
const closingColumnCharacter = '}'
const commaCharacter = ','
const colonCharacter = ':'
const slashCharacter = '/'
const invalidCharacters = new Set(['{', '}', '[', ']', '~', '"'])
const invalidCharacters = new Set(['[', ']', '~', '"'])
const invalidCharactersOutsideOfValues = new Set([':'])

const generationToClass = [
Expand Down Expand Up @@ -51,6 +54,13 @@ class GroupSpec {
}
}

class ColumnSpliceSpec {
constructor(name, start, end) {
this.tag = name.trim()
this.bounds = [start, end]
}
}

class HedStringTokenizer {
hedString
syntaxIssues
Expand Down Expand Up @@ -86,6 +96,15 @@ class HedStringTokenizer {
}
this.pushTag(this.hedString.length)

if (this.columnSpliceIndex >= 0) {
this.syntaxIssues.push(
generateIssue('unclosedCurlyBrace', {
index: this.columnSpliceIndex,
string: this.hedString,
}),
)
}

this.unwindGroupStack()

const tagSpecs = this.currentGroupStack.pop()
Expand All @@ -106,6 +125,7 @@ class HedStringTokenizer {
this.resetStartingIndex = false
this.slashFound = false
this.librarySchema = ''
this.columnSpliceIndex = -1
this.currentGroupStack = [[]]
this.parenthesesStack = [new GroupSpec(0, this.hedString.length)]
}
Expand All @@ -114,6 +134,8 @@ class HedStringTokenizer {
const dispatchTable = {
[openingGroupCharacter]: (i, character) => this.openingGroupCharacter(i),
[closingGroupCharacter]: (i, character) => this.closingGroupCharacter(i),
[openingColumnCharacter]: (i, character) => this.openingColumnCharacter(i),
[closingColumnCharacter]: (i, character) => this.closingColumnCharacter(i),
[commaCharacter]: (i, character) => this.pushTag(i),
[colonCharacter]: (i, character) => this.colonCharacter(character),
[slashCharacter]: (i, character) => this.slashCharacter(character),
Expand Down Expand Up @@ -147,6 +169,44 @@ class HedStringTokenizer {
this.closeGroup(i)
}

openingColumnCharacter(i) {
if (this.columnSpliceIndex >= 0) {
this.syntaxIssues.push(
generateIssue('nestedCurlyBrace', {
index: i,
string: this.hedString,
}),
)
return
}
this.columnSpliceIndex = i
}

closingColumnCharacter(i) {
if (this.columnSpliceIndex < 0) {
this.syntaxIssues.push(
generateIssue('unopenedCurlyBrace', {
index: i,
string: this.hedString,
}),
)
return
}
if (!stringIsEmpty(this.currentTag)) {
this.currentGroupStack[this.groupDepth].push(new ColumnSpliceSpec(this.currentTag, this.startingIndex, i))
} else {
this.syntaxIssues.push(
generateIssue('emptyCurlyBrace', {
bounds: [this.startingIndex, i],
string: this.hedString,
}),
)
}
this.columnSpliceIndex = -1
this.resetStartingIndex = true
this.slashFound = false
}

colonCharacter(character) {
if (!this.slashFound && !this.librarySchema) {
this.librarySchema = this.currentTag
Expand Down Expand Up @@ -267,10 +327,15 @@ const createParsedTags = function (hedString, hedSchemas, tagSpecs, groupSpecs)
const syntaxIssues = []
const ParsedHedTagClass = generationToClass[hedSchemas.generation]

const createParsedTag = ({ library: librarySchema, tag: originalTag, bounds: originalBounds }) => {
const parsedTag = new ParsedHedTagClass(originalTag, hedString, originalBounds, hedSchemas, librarySchema)
conversionIssues.push(...parsedTag.conversionIssues)
return parsedTag
const createParsedTag = (tagSpec) => {
if (tagSpec instanceof TagSpec) {
const parsedTag = new ParsedHedTagClass(tagSpec.tag, hedString, tagSpec.bounds, hedSchemas, tagSpec.library)
conversionIssues.push(...parsedTag.conversionIssues)
return parsedTag
} else if (tagSpec instanceof ColumnSpliceSpec) {
const parsedTag = new ParsedHedColumnSplice(tagSpec.tag, tagSpec.bounds)
return parsedTag
}
}
const createParsedGroups = (tags, groupSpecs) => {
const tagGroups = []
Expand Down

0 comments on commit a66b105

Please sign in to comment.