Skip to content

Commit

Permalink
refactor: simplify code generation by using different number of open …
Browse files Browse the repository at this point in the history
…blocks
  • Loading branch information
epoberezkin committed Aug 25, 2020
1 parent 0aad959 commit f5a46db
Show file tree
Hide file tree
Showing 16 changed files with 85 additions and 153 deletions.
26 changes: 9 additions & 17 deletions lib/compile/validate/keyword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default function keywordCode(
def: KeywordDefinition
): void {
// TODO "code" keyword
// TODO refactor
if (cxt.$data && "validate" in def) {
funcKeywordCode(cxt, def as FuncKeywordDefinition)
} else if ("macro" in def) {
Expand All @@ -32,7 +33,7 @@ export default function keywordCode(
}

function macroKeywordCode(cxt: KeywordContext, def: MacroKeywordDefinition) {
const {gen, keyword, schema, parentSchema, it} = cxt
const {gen, fail, keyword, schema, parentSchema, it} = cxt
const macroSchema = def.macro.call(it.self, schema, parentSchema, it)
const schemaRef = addCustomRule(it, keyword, macroSchema)
if (it.opts.validateSchema !== false) it.self.validateSchema(macroSchema, true)
Expand All @@ -50,15 +51,11 @@ function macroKeywordCode(cxt: KeywordContext, def: MacroKeywordDefinition) {
valid
)

// TODO refactor ifs
gen.if(`!${valid}`)
reportExtraError(cxt, keywordError)
if (it.allErrors) gen.endIf()
else gen.else()
fail(`!${valid}`, () => reportExtraError(cxt, keywordError))
}

function funcKeywordCode(cxt: KeywordContext, def: FuncKeywordDefinition) {
const {gen, ok, fail, keyword, schema, schemaCode, parentSchema, data, $data, it} = cxt
const {gen, fail, keyword, schema, schemaCode, parentSchema, data, $data, it} = cxt
checkAsync(it, def)
const validate =
"compile" in def && !$data ? def.compile.call(it.self, schema, parentSchema, it) : def.validate
Expand Down Expand Up @@ -99,8 +96,7 @@ function funcKeywordCode(cxt: KeywordContext, def: FuncKeywordDefinition) {
gen.code(`${valid} = ${def.async ? "await " : ""}${callValidate(validateRef)};`)
if (def.modifying) modifyData(cxt)
gen.endBlock()
if (def.valid) return ok()
fail(`!${valid}`)
if (!def.valid) fail(`!${valid}`)
}

function validateAsyncRule() {
Expand Down Expand Up @@ -159,19 +155,15 @@ function reportKeywordErrors(
ruleErrs: string,
errsCount: string
): void {
const {gen, ok, it} = cxt
const {ok, fail} = cxt
switch (def.valid) {
case true:
return ok()
return
case false:
addKeywordErrors(cxt, ruleErrs, errsCount)
return ok("false")
return ok("false") // TODO maybe add gen.skip() to remove code till the end of the block?
default:
// TODO refactor ifs
gen.if(`!${valid}`)
addKeywordErrors(cxt, ruleErrs, errsCount)
if (it.allErrors) gen.endIf()
else gen.else()
fail(`!${valid}`, () => addKeywordErrors(cxt, ruleErrs, errsCount))
}
}

Expand Down
21 changes: 6 additions & 15 deletions lib/keyword.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
KeywordDefinition,
CodeKeywordDefinition,
KeywordErrorDefinition,
Vocabulary,
ErrorObject,
ValidateFunction,
Expand Down Expand Up @@ -177,14 +176,15 @@ function ruleCode(it: CompilationContext, keyword: string, ruleType?: string): v
;(def.code || keywordCode)(cxt, ruleType, this.definition)

// TODO replace with fail_ below
function fail(condition?: string, context?: KeywordContext): void {
function fail(condition?: string, failAction?: () => void, context?: KeywordContext): void {
const action = failAction || _reportError
if (condition) {
gen.if(condition)
_reportError()
action()
if (allErrors) gen.endIf()
else gen.else()
} else {
_reportError()
action()
if (!allErrors) gen.if("false")
}

Expand All @@ -193,8 +193,8 @@ function ruleCode(it: CompilationContext, keyword: string, ruleType?: string): v
}
}

function ok(condition?: string): void {
if (!allErrors) gen.if(condition || "true")
function ok(condition: string): void {
if (!allErrors) gen.if(condition)
}

function errorParams(obj: KeywordContextParams, assign?: true) {
Expand All @@ -203,15 +203,6 @@ function ruleCode(it: CompilationContext, keyword: string, ruleType?: string): v
}
}

// TODO remove when "fail" replaced
export function fail_(condition: string, cxt: KeywordContext, error: KeywordErrorDefinition): void {
const {gen, allErrors} = cxt.it
gen.if(condition)
reportError(cxt, error)
if (allErrors) gen.endIf()
else gen.else()
}

function validSchemaType(schema: any, schemaType: string | string[]): boolean {
// TODO add tests
if (Array.isArray(schemaType)) {
Expand Down
4 changes: 2 additions & 2 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ export type Vocabulary = KeywordDefinition[]

export interface KeywordContext {
gen: CodeGen
fail: (condition?: string, context?: KeywordContext) => void
ok: (condition?: string) => void
fail: (condition?: string, failAction?: () => void, context?: KeywordContext) => void
ok: (condition: string) => void
errorParams: (obj: KeywordContextParams, assing?: true) => void
keyword: string
data: string
Expand Down
4 changes: 2 additions & 2 deletions lib/vocabularies/applicator/additionalProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ const def: CodeKeywordDefinition = {
schemaType: ["object", "boolean"],
error,
code(cxt) {
const {gen, ok, errorParams, schema, parentSchema, data, it} = cxt
const {gen, errorParams, schema, parentSchema, data, it} = cxt
const {
allErrors,
usePattern,
opts: {removeAdditional},
} = it

if ((schema === undefined || alwaysValidSchema(it, schema)) && removeAdditional !== "all") {
return ok()
return
}

const props = allSchemaProperties(parentSchema.properties)
Expand Down
18 changes: 5 additions & 13 deletions lib/vocabularies/applicator/allOf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,12 @@ const def: CodeKeywordDefinition = {
keyword: "allOf",
schemaType: "array",
code({gen, ok, schema, it}) {
let emptySchemas = true
const valid = gen.name("valid")
gen.block(() =>
schema.forEach((sch: object | boolean, i: number) => {
if (alwaysValidSchema(it, sch)) return
emptySchemas = false
applySubschema(it, {keyword: "allOf", schemaProp: i}, valid)
if (!it.allErrors) gen.if(valid)
})
)

if (emptySchemas) ok()
else ok(valid)

schema.forEach((sch: object | boolean, i: number) => {
if (alwaysValidSchema(it, sch)) return
applySubschema(it, {keyword: "allOf", schemaProp: i}, valid)
ok(valid)
})
// TODO possibly add allOf error
},
}
Expand Down
4 changes: 2 additions & 2 deletions lib/vocabularies/applicator/anyOf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ const def: CodeKeywordDefinition = {
keyword: "anyOf",
schemaType: "array",
code(cxt) {
const {gen, ok, schema, it} = cxt
const {gen, schema, it} = cxt
const alwaysValid = schema.some((sch: object | boolean) => alwaysValidSchema(it, sch))
if (alwaysValid) return ok()
if (alwaysValid) return

const valid = gen.name("valid")
const schValid = gen.name("_valid")
Expand Down
15 changes: 3 additions & 12 deletions lib/vocabularies/applicator/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,10 @@ const def: CodeKeywordDefinition = {
schemaType: "object",
code(cxt) {
const {gen, ok, errorParams, schema, data, it} = cxt

const [propDeps, schDeps] = splitDependencies()

const valid = gen.name("valid")
const errsCount = gen.name("_errs")
gen.code(`const ${errsCount} = errors;`)

gen.block(() => {
validatePropertyDeps(propDeps)
validateSchemaDeps(schDeps)
})

ok(`${errsCount} === errors`)
validatePropertyDeps(propDeps)
validateSchemaDeps(schDeps)

function splitDependencies(): [PropertyDependencies, SchemaDependencies] {
const propertyDeps: PropertyDependencies = {}
Expand Down Expand Up @@ -78,7 +69,7 @@ const def: CodeKeywordDefinition = {
() => applySubschema(it, {keyword: "dependencies", schemaProp: prop}, valid),
`var ${valid} = true;` // TODO refactor var
)
if (!it.allErrors) gen.if(valid)
ok(valid)
}
}
},
Expand Down
20 changes: 8 additions & 12 deletions lib/vocabularies/applicator/if.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ import {alwaysValidSchema} from "../util"
import {applySubschema} from "../../compile/subschema"
import {reportExtraError, resetErrorsCount} from "../../compile/errors"

const error: KeywordErrorDefinition = {
message: ({params}) => `'should match "' + ${params.ifClause} + '" schema'`,
params: ({params}) => `{failingKeyword: ${params.ifClause}}`,
}

const def: CodeKeywordDefinition = {
keyword: "if",
schemaType: ["object", "boolean"],
// TODO
// implements: ["then", "else"],
code(cxt) {
const {gen, ok, errorParams, it} = cxt
const {gen, fail, errorParams, it} = cxt
const hasThen = hasSchema(it, "then")
const hasElse = hasSchema(it, "else")
if (!hasThen && !hasElse) {
// TODO strict mode: fail or warning if both "then" and "else" are not present
ok()
return
}

Expand All @@ -41,12 +45,7 @@ const def: CodeKeywordDefinition = {
gen.if(`!${schValid}`, validateClause("else"))
}

// // TODO refactor failCompoundOrReset?
// // TODO refactor ifs
gen.if(`!${valid}`)
reportExtraError(cxt, def.error as KeywordErrorDefinition)
if (it.allErrors) gen.endIf()
else gen.else()
fail(`!${valid}`, () => reportExtraError(cxt, error))

function validateIf(): void {
applySubschema(
Expand All @@ -70,10 +69,7 @@ const def: CodeKeywordDefinition = {
}
}
},
error: {
message: ({params}) => `'should match "' + ${params.ifClause} + '" schema'`,
params: ({params}) => `{failingKeyword: ${params.ifClause}}`,
},
error,
}

module.exports = def
Expand Down
56 changes: 23 additions & 33 deletions lib/vocabularies/applicator/items.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types"
import {CodeKeywordDefinition} from "../../types"
import {alwaysValidSchema} from "../util"
import {applySubschema, Expr} from "../../compile/subschema"
import {fail_} from "../../keyword"

const def: CodeKeywordDefinition = {
keyword: "items",
Expand All @@ -12,41 +11,31 @@ const def: CodeKeywordDefinition = {
// implements: ["additionalItems"],
code(cxt) {
// TODO strict mode: fail or warning if "additionalItems" is present without "items"

const {gen, ok, schema, parentSchema, data, it} = cxt
const errsCount = gen.name("_errs")
const {gen, ok, fail, schema, parentSchema, data, it} = cxt
const len = gen.name("len")
gen.code(
`const ${errsCount} = errors;
const ${len} = ${data}.length;`
)
gen.block(validateItemsKeyword)
ok(`${errsCount} === errors`)
gen.code(`const ${len} = ${data}.length;`)

if (Array.isArray(schema)) {
validateItemsArray(schema)
} else if (!alwaysValidSchema(it, schema)) {
validateItems("items", 0)
}

function validateItemsKeyword(): void {
if (Array.isArray(schema)) {
const addIts = parentSchema.additionalItems
if (addIts === false) validateDataLength(schema)
validateDefinedItems()
if (typeof addIts == "object" && !alwaysValidSchema(it, addIts)) {
gen.if(`${len} > ${schema.length}`, () => validateItems("additionalItems", schema.length))
}
} else if (!alwaysValidSchema(it, schema)) {
validateItems("items", 0)
function validateItemsArray(sch: (object | boolean)[]) {
const addIts = parentSchema.additionalItems
if (addIts === false) validateDataLength(sch)
validateDefinedItems()
if (typeof addIts == "object" && !alwaysValidSchema(it, addIts)) {
gen.if(`${len} > ${sch.length}`, () => validateItems("additionalItems", sch.length))
}
}

function validateDataLength(sch: any[]): void {
// TODO replace with "fail"
fail_(
`${len} > ${sch.length}`,
{
...cxt,
keyword: "additionalItems",
schemaValue: false,
},
def.error as KeywordErrorDefinition
)
function validateDataLength(sch: (object | boolean)[]): void {
fail(`${len} > ${sch.length}`, undefined, {
...cxt,
keyword: "additionalItems",
schemaValue: false,
})
}

function validateDefinedItems(): void {
Expand All @@ -65,7 +54,7 @@ const def: CodeKeywordDefinition = {
valid
)
)
if (!it.allErrors) gen.if(valid)
ok(valid)
})
}

Expand All @@ -76,6 +65,7 @@ const def: CodeKeywordDefinition = {
applySubschema(it, {keyword, dataProp: i, expr: Expr.Num}, valid)
if (!it.allErrors) gen.if(`!${valid}`, "break")
})
ok(valid)
}
},
error: {
Expand Down
11 changes: 3 additions & 8 deletions lib/vocabularies/applicator/patternProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,11 @@ const def: CodeKeywordDefinition = {
type: "object",
schemaType: "object",
code(cxt) {
const {gen, ok, schema, it} = cxt
const {gen, schema, it} = cxt
const patterns = schemaProperties(it, schema)
if (patterns.length === 0) return ok()

if (patterns.length === 0) return
const valid = gen.name("valid")
const errsCount = gen.name("_errs")
gen.code(`const ${errsCount} = errors;`)

gen.block(validatePatternProperties)
ok(`${errsCount} === errors`)
validatePatternProperties()

function validatePatternProperties() {
for (const pat of patterns) {
Expand Down
Loading

0 comments on commit f5a46db

Please sign in to comment.