Skip to content

Commit

Permalink
Removed _formatTag from ParsedHedTag as this is handled in tokenizer
Browse files Browse the repository at this point in the history
  • Loading branch information
VisLab committed Nov 8, 2024
1 parent fb23337 commit bbf27de
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 86 deletions.
86 changes: 62 additions & 24 deletions parser/parsedHedTag.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IssueError } from '../common/issues/issues'
import { getTagLevels } from '../utils/hedStrings'
import { getParentTag, getTagLevels, getTagName } from '../utils/hedStrings'
import ParsedHedSubstring from './parsedHedSubstring'
import { SchemaValueTag } from '../schema/entries'
import TagConverter from './tagConverter'
Expand Down Expand Up @@ -48,15 +48,15 @@ export default class ParsedHedTag extends ParsedHedSubstring {
* @throws {IssueError} If tag conversion or parsing fails.
*/
constructor(tagSpec, hedSchemas, hedString) {
super(tagSpec.tag, tagSpec.bounds)

this._convertTag(hedSchemas, hedString, tagSpec)

this.formattedTag = this._formatTag()
super(tagSpec.tag, tagSpec.bounds) // Sets originalTag and originalBounds
this._convertTag(hedSchemas, hedString, tagSpec) // Sets various parameters
//this._checkTagAttributes() // Checks various aspects like requireChild or extensionAllowed.
//this.formattedTag = this._formatTag()
//this.formattedTag = this.canonicalTag.toLowerCase()
}

/**
* Convert this tag to long form.
* Convert this tag to its various forms
*
* @param {Schemas} hedSchemas The collection of HED schemas.
* @param {string} hedString The original HED string.
Expand All @@ -83,6 +83,7 @@ export default class ParsedHedTag extends ParsedHedSubstring {
this._schemaTag = schemaTag
this._remainder = remainder
this.canonicalTag = this._schemaTag.longExtend(remainder)
this.formattedTag = this.canonicalTag.toLowerCase()
}

/**
Expand Down Expand Up @@ -121,23 +122,6 @@ export default class ParsedHedTag extends ParsedHedSubstring {
}
}

/**
* Format this HED tag by removing newlines and double quotes.
*
* @returns {string} The formatted version of this tag.
*/
_formatTag() {
this.originalTag = this.originalTag.replace('\n', ' ')
let hedTagString = this.canonicalTag.trim()
if (hedTagString.startsWith('"')) {
hedTagString = hedTagString.slice(1)
}
if (hedTagString.endsWith('"')) {
hedTagString = hedTagString.slice(0, -1)
}
return hedTagString.toLowerCase()
}

/**
* Determine whether this tag has a given attribute.
*
Expand Down Expand Up @@ -440,4 +424,58 @@ export default class ParsedHedTag extends ParsedHedSubstring {
return units
})
}

/**
* Validate a unit and strip it from the value.
*
* @param {ParsedHedTag} tag A HED tag.
* @returns {[boolean, boolean, string]} Whether a unit was found, whether it was valid, and the stripped value.
*/
validateUnits(tag) {
const originalTagUnitValue = tag.originalTagName
const tagUnitClassUnits = tag.validUnits
const validUnits = tag.schema.entries.allUnits
const unitStrings = Array.from(validUnits.keys())
unitStrings.sort((first, second) => {
return second.length - first.length
})
let actualUnit = getTagName(originalTagUnitValue, ' ')
let noUnitFound = false
if (actualUnit === originalTagUnitValue) {
actualUnit = ''
noUnitFound = true
}
let foundUnit, foundWrongCaseUnit, strippedValue
for (const unitName of unitStrings) {
const unit = validUnits.get(unitName)
const isPrefixUnit = unit.isPrefixUnit
const isUnitSymbol = unit.isUnitSymbol
for (const derivativeUnit of unit.derivativeUnits()) {
if (isPrefixUnit && originalTagUnitValue.startsWith(derivativeUnit)) {
foundUnit = true
noUnitFound = false
strippedValue = originalTagUnitValue.substring(derivativeUnit.length).trim()
}
if (actualUnit === derivativeUnit) {
foundUnit = true
strippedValue = getParentTag(originalTagUnitValue, ' ')
} else if (actualUnit.toLowerCase() === derivativeUnit.toLowerCase()) {
if (isUnitSymbol) {
foundWrongCaseUnit = true
} else {
foundUnit = true
}
strippedValue = getParentTag(originalTagUnitValue, ' ')
}
if (foundUnit) {
const unitIsValid = tagUnitClassUnits.has(unit)
return [true, unitIsValid, strippedValue]
}
}
if (foundWrongCaseUnit) {
return [true, false, strippedValue]
}
}
return [!noUnitFound, false, originalTagUnitValue]
}
}
12 changes: 6 additions & 6 deletions parser/tagConverter.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ export default class TagConverter {
_getSchemaTag(tagLevelIndex) {
let tagLevel = this.tagLevels[tagLevelIndex].toLowerCase()
// TODO: These two checks should probably be removed as the tokenizer handles this.
if (tagLevelIndex === 0) {
tagLevel = tagLevel.trimLeft()
}
if (tagLevel === '' || tagLevel !== tagLevel.trim()) {
IssueError.generateAndThrow('invalidTag', { tag: this.tagString })
}
// if (tagLevelIndex === 0) {
// tagLevel = tagLevel.trimLeft()
// }
// if (tagLevel === '' || tagLevel !== tagLevel.trim()) {
// IssueError.generateAndThrow('invalidTag', { tag: this.tagString })
// }
return this.tagMapping.getEntry(tagLevel)
}

Expand Down
2 changes: 1 addition & 1 deletion tests/stringParserTests.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('Parse HED string tests', () => {
const status = test.errors.length === 0 ? 'Expect pass' : 'Expect fail'
const header = `[${test.testname} (${status})]`
const thisSchema = schemaMap.get(test.schemaVersion)
assert.isDefined(thisSchema, `header: ${test.schemaVersion} is not available in test ${test.name}`)
assert.isDefined(thisSchema, `header: ${test.schemaVersion} is not available in test ${test.testname}`)

// Parse the string before converting
const [parsedString, errorIssues, warningIssues] = getHedString(test.stringIn, thisSchema)
Expand Down
27 changes: 17 additions & 10 deletions tests/parsedHedTagTests.spec.js → tests/tagParserTests.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { beforeAll, describe, afterAll } from '@jest/globals'

import ParsedHedTag from '../parser/parsedHedTag'
import { shouldRun } from './testUtilities'
import { parsedHedTagTests } from './testData/parsedHedTagTests.data'
import { parsedHedTagTests } from './testData/tagParserTests.data'
import { SchemaSpec, SchemasSpec } from '../schema/specs'
import path from 'path'
import { buildSchemas } from '../schema/init'
Expand All @@ -13,7 +13,7 @@ import { SchemaTag } from '../schema/entries'
// Ability to select individual tests to run
const skipMap = new Map()
const runAll = true
const runMap = new Map([[]])
const runMap = new Map([['valid-tags', ['valid-tag-with-extension']]])

describe('TagSpec converter tests using JSON tests', () => {
const schemaMap = new Map([
Expand All @@ -36,25 +36,32 @@ describe('TagSpec converter tests using JSON tests', () => {

describe.each(parsedHedTagTests)('$name : $description', ({ name, tests }) => {
const hedTagTest = function (test) {
const status = test.errors.length > 0 ? 'Expect fail' : 'Expect pass'
const status = test.error !== null ? 'Expect fail' : 'Expect pass'
const header = `\n[${test.testname}](${status}): ${test.explanation}`

const thisSchema = schemaMap.get(test.schemaVersion)
assert.isDefined(thisSchema, `header: ${test.schemaVersion} is not available in test ${test.name}`)
assert.isDefined(thisSchema, `${header}: ${test.schemaVersion} is not available in test ${test.name}`)

const tag = new ParsedHedTag(test.tagSpec, thisSchema)

assert.strictEqual(tag.formattedTag, test.formattedTag)
assert.strictEqual(tag.format(false), test.tagShort)
assert.strictEqual(tag.format(true), test.tagLong)
let issue = null
let tag = null
try {
tag = new ParsedHedTag(test.tagSpec, thisSchema, test.fullString)
} catch (error) {
issue = error.issue
}
assert.deepEqual(issue, issue)
assert.strictEqual(tag?.format(false), test.tagShort, `${header}: wrong short version`)
assert.strictEqual(tag?.format(true), test.tagLong, `${header}: wrong long version`)
assert.strictEqual(tag?.formattedTag, test.formattedTag, `${header}: wrong formatted version`)
assert.strictEqual(tag?.canonicalTag, test.canonicalTag, `${header}: wrong canonical version`)
}

beforeAll(async () => {})

afterAll(() => {})

if (tests && tests.length > 0) {
test.each(tests)('$testname: $explanation for "$string"', (test) => {
test.each(tests)('$testname: $explanation', (test) => {
if (shouldRun(name, test.testname, runAll, runMap, skipMap)) {
hedTagTest(test)
} else {
Expand Down
45 changes: 0 additions & 45 deletions tests/testData/parsedHedTagTests.data.js

This file was deleted.

120 changes: 120 additions & 0 deletions tests/testData/tagParserTests.data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { generateIssue } from '../../common/issues/issues'
import { ColumnSpliceSpec, GroupSpec, TagSpec } from '../../parser/tokenizer'

export const parsedHedTagTests = [
{
name: 'valid-tags',
description: 'Valid tags with extensions',
warning: false,
tests: [
{
testname: 'valid-tag-one-level',
explanation: '"Item" is a top-level-tag.',
schemaVersion: '8.3.0',
fullString: 'Item',
tagSpec: new TagSpec('Item', 0, 4, ''),
tagLong: 'Item',
tagShort: 'Item',
formattedTag: 'item',
canonicalTag: 'Item',
error: null,
},
{
testname: 'valid-tag-with-blanks',
explanation: '" Item " has surrounding blanks.',
schemaVersion: '8.3.0',
fullString: ' Item ',
tagSpec: new TagSpec('Item', 1, 5, ''),
tagLong: 'Item',
tagShort: 'Item',
formattedTag: 'item',
canonicalTag: 'Item',
takesValue: false,
error: null,
},
{
testname: 'valid-Two-level-tag',
explanation: '" Item/object " is two-level and mixed case.',
schemaVersion: '8.3.0',
fullString: ' Item/object ',
tagSpec: new TagSpec('Item/object', 1, 12, ''),
tagLong: 'Item/Object',
tagShort: 'Object',
formattedTag: 'item/object',
canonicalTag: 'Item/Object',
takesValue: false,
error: null,
},
{
testname: 'valid-tag-with-extension',
explanation: '" Object/blech " is two-level and mixed case.',
schemaVersion: '8.3.0',
fullString: ' Object/blech ',
tagSpec: new TagSpec('object/Blech', 1, 13, ''),
tagLong: 'Item/Object/Blech',
tagShort: 'Object/Blech',
formattedTag: 'item/object/blech',
canonicalTag: 'Item/Object/Blech',
takesValue: false,
error: null,
},
{
testname: 'valid-tag-with-value-no-units',
explanation: '" Age/5 " has a value but no units.',
schemaVersion: '8.3.0',
fullString: ' Age/5 ',
tagSpec: new TagSpec(' Age/5 ', 1, 6, ''),
tagLong: 'Property/Agent-property/Agent-trait/Age/5',
tagShort: 'Age/5',
formattedTag: 'property/agent-property/agent-trait/age/5',
canonicalTag: 'Property/Agent-property/Agent-trait/Age/5',
takesValue: true,
error: null,
},
{
testname: 'valid-tag-with-value-and-units',
explanation: '" Length/3 m " has a value and valid units.',
schemaVersion: '8.3.0',
fullString: ' Length/3 m ',
tagSpec: new TagSpec(' Length/3 m ', 1, 11, ''),
tagLong: 'Property/Data-property/Data-value/Spatiotemporal-value/Spatial-value/Size/Length/3 m',
tagShort: 'Length/3 m',
formattedTag: 'property/data-property/data-value/spatiotemporal-value/spatial-value/size/length/3 m',
canonicalTag: 'Property/Data-property/Data-value/Spatiotemporal-value/Spatial-value/Size/Length/3 m',
takesValue: true,
error: null,
},
],
},
{
name: 'invalid-tags',
description: 'Various invalid tags',
warning: false,
tests: [
{
testname: 'invalid-tag-requires-child',
explanation: '"Duration" should have a child.',
schemaVersion: '8.3.0',
fullString: 'Duration',
tagSpec: new TagSpec('Duration', 0, 8, ''),
tagLong: undefined,
tagShort: undefined,
formattedTag: undefined,
canonicalTag: undefined,
error: generateIssue('childRequired', { tag: 'Duration' }),
},
{
testname: 'invalid-tag-should-not-have-a-placeholder',
explanation: '"object/#" should not have a placeholder.',
schemaVersion: '8.3.0',
fullString: 'object/#',
tagSpec: new TagSpec('object/#', 0, 8, ''),
tagLong: undefined,
tagShort: undefined,
formattedTag: undefined,
canonicalTag: undefined,
error: null,
},
],
},
]

0 comments on commit bbf27de

Please sign in to comment.