@wmfs/j2119
Version:
A general-purpose validator generator that uses RFC2119-style assertions as input.
142 lines (117 loc) • 3.95 kB
JavaScript
const roleConstraints = require('./role_constraints')
const roleFinder = require('./role_finder')
const allowedFields = require('./allowed_fields')
const matcher = require('./line_matcher')
const assigner = require('./assigner')
const readlines = require('./readlines')
const ROOT = /This\s+document\s+specifies\s+a\s+JSON\s+object\s+called\s+an?\s+"([^"]+)"\./
const EXTENSION_ROOT = /This\s+document\s+specifies\s+an\s+extension\s+to\s+a\s+JSON\s+object\s+called\s+an?\s+"([^"]+)"\./
class Parser {
constructor (source) {
this.constraints = roleConstraints()
this.finder = roleFinder()
this.allowedFields = allowedFields()
this.errors = []
this.load(readlines(source))
if (this.errors.length) {
throw new Error(`Could not create parser: \n ${this.errors.join('\n ')}`)
}
} // constructor
extend (extensionSource) {
const reader = readlines(extensionSource)
const firstLine = reader.next().value
const extensionMatch = firstLine.match(EXTENSION_ROOT)
if (extensionMatch) {
this.procExtension(firstLine, extensionMatch[1])
} else {
this.error('Extension declaration must be first line')
}
this.load(reader, true)
if (this.errors.length) {
throw new Error(`Could not extend parser: \n ${this.errors.join('\n ')}`)
}
} // extend
get haveRoot () { return this.root !== undefined }
load (lineReader, isExtension = false) {
for (const line of lineReader) {
const rootMatch = line.match(ROOT)
if (rootMatch) {
this.procRoot(line, rootMatch[1])
} else if (isExtension && line.match(EXTENSION_ROOT)) {
this.error('Only one extension declaration permitted per file')
} else {
this.procLine(line)
}
} // for ...
}
procRoot (line, root) {
if (this.haveRoot) {
this.error('Only one root declaration permitted')
return
}
this.root = root
this.matcher = matcher(this.root)
this.assigner = assigner(
this.constraints,
this.finder,
this.matcher,
this.allowedFields
)
} // procRoot
procExtension (line, extension) {
if (extension !== this.root) {
this.error(`Extension does not extend "${this.root}"`)
}
} // procExtension
procLine (line) {
if (!this.haveRoot) {
this.error('Root declaration must be first line')
return
}
try {
if (this.matcher.isConstraintLine(line)) {
this.assigner.assignConstraints(this.matcher.buildConstraint(line))
} else if (this.matcher.isOnlyOneMatchLine(line)) {
this.assigner.assignOnlyOneOf(this.matcher.buildOnlyOne(line))
} else if (this.matcher.isEachOfLine(line)) {
this.procLines(this.matcher.buildEachOfs(line))
} else if (this.matcher.isRoleDefLine(line)) {
this.assigner.assignRoles(this.matcher.buildRoleDef(line))
} else {
this.error(`Unrecognized line: ${line}`)
}
} catch (err) {
this.error(`Unprocessable line: ${line}`)
}
} // procLine
procLines (lines) {
lines.forEach(l => this.procLine(l))
} // procLines
error (message) {
this.errors.push(message)
} // error
findMoreRoles (node, roles) {
return this.finder.findMoreRoles(node, roles)
}
findGrandchildRoles (roles, name) {
return this.finder.findGrandchildRoles(roles, name)
}
findChildRoles (roles, name) {
return this.finder.findChildRoles(roles, name)
}
getConstraints (role) {
return this.constraints.getConstraints(role)
}
isFieldAllowed (roles, child) {
return this.allowedFields.isAllowed(roles, child)
}
allowsAnyField (roles) {
return this.allowedFields.allowsAny(roles)
}
toString () {
return `${this.finder}\n${this.constraints}\n${this.allowedFields}`
}
} // class Parser
module.exports = source => new Parser(source)
module.exports.ROOT = ROOT
module.exports.EXTENSION_ROOT = EXTENSION_ROOT