cucumber-expressions
Version:
Cucumber Expressions - a simpler alternative to Regular Expressions
152 lines (141 loc) • 4.56 kB
text/typescript
import ParameterType from './ParameterType'
import CucumberExpressionGenerator from './CucumberExpressionGenerator'
import { AmbiguousParameterTypeError, CucumberExpressionError } from './Errors'
export default class ParameterTypeRegistry {
public static readonly INTEGER_REGEXPS = [/-?\d+/, /\d+/]
public static readonly FLOAT_REGEXP = /(?=.*\d.*)[-+]?\d*(?:\.(?=\d.*))?\d*(?:\d+[E][+\-]?\d+)?/
public static readonly WORD_REGEXP = /[^\s]+/
public static readonly STRING_REGEXP = /"([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'/
public static readonly ANONYMOUS_REGEXP = /.*/
private readonly parameterTypeByName = new Map<string, ParameterType<any>>()
private readonly parameterTypesByRegexp = new Map<
string,
Array<ParameterType<any>>
>()
constructor() {
this.defineParameterType(
new ParameterType(
'int',
ParameterTypeRegistry.INTEGER_REGEXPS,
Number,
s => (s === undefined ? null : Number(s)),
true,
true
)
)
this.defineParameterType(
new ParameterType(
'float',
ParameterTypeRegistry.FLOAT_REGEXP,
Number,
s => (s === undefined ? null : parseFloat(s)),
true,
false
)
)
this.defineParameterType(
new ParameterType(
'word',
ParameterTypeRegistry.WORD_REGEXP,
String,
s => s,
false,
false
)
)
this.defineParameterType(
new ParameterType(
'string',
ParameterTypeRegistry.STRING_REGEXP,
String,
s => (s || '').replace(/\\"/g, '"').replace(/\\'/g, "'"),
true,
false
)
)
this.defineParameterType(
new ParameterType(
'',
ParameterTypeRegistry.ANONYMOUS_REGEXP,
String,
s => s,
false,
true
)
)
}
get parameterTypes() {
return this.parameterTypeByName.values()
}
public lookupByTypeName(typeName: string) {
return this.parameterTypeByName.get(typeName)
}
public lookupByRegexp(
parameterTypeRegexp: string,
expressionRegexp: RegExp,
text: string
): ParameterType<any> {
const parameterTypes = this.parameterTypesByRegexp.get(parameterTypeRegexp)
if (!parameterTypes) {
return null
}
if (parameterTypes.length > 1 && !parameterTypes[0].preferForRegexpMatch) {
// We don't do this check on insertion because we only want to restrict
// ambiguiuty when we look up by Regexp. Users of CucumberExpression should
// not be restricted.
const generatedExpressions = new CucumberExpressionGenerator(
this
).generateExpressions(text)
throw AmbiguousParameterTypeError.forRegExp(
parameterTypeRegexp,
expressionRegexp,
parameterTypes,
generatedExpressions
)
}
return parameterTypes[0]
}
public defineParameterType(parameterType: ParameterType<any>) {
if (parameterType.name !== undefined) {
if (this.parameterTypeByName.has(parameterType.name)) {
if (parameterType.name.length === 0) {
throw new Error(
`The anonymous parameter type has already been defined`
)
} else {
throw new Error(
`There is already a parameter type with name ${parameterType.name}`
)
}
}
this.parameterTypeByName.set(parameterType.name, parameterType)
}
for (const parameterTypeRegexp of parameterType.regexpStrings) {
if (!this.parameterTypesByRegexp.has(parameterTypeRegexp)) {
this.parameterTypesByRegexp.set(parameterTypeRegexp, [])
}
const parameterTypes = this.parameterTypesByRegexp.get(
parameterTypeRegexp
)
const existingParameterType = parameterTypes[0]
if (
parameterTypes.length > 0 &&
existingParameterType.preferForRegexpMatch &&
parameterType.preferForRegexpMatch
) {
throw new CucumberExpressionError(
'There can only be one preferential parameter type per regexp. ' +
`The regexp /${parameterTypeRegexp}/ is used for two preferential parameter types, {${existingParameterType.name}} and {${parameterType.name}}`
)
}
if (parameterTypes.indexOf(parameterType) === -1) {
parameterTypes.push(parameterType)
this.parameterTypesByRegexp.set(
parameterTypeRegexp,
parameterTypes.sort(ParameterType.compare)
)
}
}
}
}
module.exports = ParameterTypeRegistry