-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #67 from UCNot/processor-bootstrap
Schema processor bootstrap
- Loading branch information
Showing
86 changed files
with
1,227 additions
and
1,440 deletions.
There are no files selected for viewing
89 changes: 89 additions & 0 deletions
89
src/compiler/bootstrap/impl/ucc-processor.constraint-application.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { UcProcessorName, UcSchemaConstraint } from '../../../schema/uc-constraints.js'; | ||
import { ucModelName } from '../../../schema/uc-model-name.js'; | ||
import { UcPresentationName } from '../../../schema/uc-presentations.js'; | ||
import { UcSchema } from '../../../schema/uc-schema.js'; | ||
import { UccBootstrap } from '../ucc-bootstrap.js'; | ||
import { UccFeature } from '../ucc-feature.js'; | ||
import { UccProcessor$ConstraintIssue } from './ucc-processor.constraint-issue.js'; | ||
import { UccProcessor$FeatureSet } from './ucc-processor.feature-set.js'; | ||
|
||
export class UccProcessor$ConstraintApplication< | ||
in out TBoot extends UccBootstrap<TBoot>, | ||
in out TOptions, | ||
> implements UccFeature.ConstraintApplication<TBoot, TOptions> { | ||
|
||
readonly #featureSet: UccProcessor$FeatureSet<TBoot>; | ||
readonly #schema: UcSchema; | ||
readonly #issue: UccProcessor$ConstraintIssue<TOptions>; | ||
readonly #feature: UccFeature<TBoot, TOptions>; | ||
#applied = 0; | ||
|
||
constructor( | ||
featureSet: UccProcessor$FeatureSet<TBoot>, | ||
schema: UcSchema, | ||
issue: UccProcessor$ConstraintIssue<TOptions>, | ||
feature: UccFeature<TBoot, TOptions>, | ||
) { | ||
this.#featureSet = featureSet; | ||
this.#schema = schema; | ||
this.#issue = issue; | ||
this.#feature = feature; | ||
} | ||
|
||
get schema(): UcSchema { | ||
return this.#schema; | ||
} | ||
|
||
get processor(): UcProcessorName { | ||
return this.#issue.processor; | ||
} | ||
|
||
get within(): UcPresentationName | undefined { | ||
return this.#issue.within; | ||
} | ||
|
||
get constraint(): UcSchemaConstraint { | ||
return this.#issue.constraint; | ||
} | ||
|
||
get options(): TOptions { | ||
return this.#issue.options; | ||
} | ||
|
||
isApplied(): boolean { | ||
return this.#applied > 0; | ||
} | ||
|
||
isIgnored(): boolean { | ||
return this.#applied < 0; | ||
} | ||
|
||
apply(): void { | ||
if (this.isApplied()) { | ||
return; | ||
} | ||
|
||
this.#applied = 1; | ||
this.#constrain(); | ||
} | ||
|
||
#constrain(): void { | ||
const handle = this.#featureSet.enableFeature(this.#feature); | ||
const { options } = this; | ||
|
||
if (handle) { | ||
this.#featureSet.runWithCurrent(this, () => handle.constrain(this)); | ||
} else if (options !== undefined) { | ||
throw new TypeError( | ||
`Feature ${this.#issue} can not constrain schema "${ucModelName(this.schema)}"`, | ||
); | ||
} | ||
} | ||
|
||
ignore(): void { | ||
if (!this.isApplied()) { | ||
this.#applied = -1; | ||
} | ||
} | ||
|
||
} |
23 changes: 23 additions & 0 deletions
23
src/compiler/bootstrap/impl/ucc-processor.constraint-issue.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { esQuoteKey, esStringLiteral } from 'esgen'; | ||
import { UcProcessorName, UcSchemaConstraint } from '../../../schema/uc-constraints.js'; | ||
import { UcPresentationName } from '../../../schema/uc-presentations.js'; | ||
|
||
export class UccProcessor$ConstraintIssue<out TOptions> { | ||
|
||
constructor( | ||
readonly processor: UcProcessorName, | ||
readonly within: UcPresentationName | undefined, | ||
readonly constraint: UcSchemaConstraint, | ||
) {} | ||
|
||
get options(): TOptions { | ||
return this.constraint.with as TOptions; | ||
} | ||
|
||
toString(): string { | ||
const { use, from } = this.constraint; | ||
|
||
return `import(${esStringLiteral(from)}).${esQuoteKey(use)}`; | ||
} | ||
|
||
} |
52 changes: 52 additions & 0 deletions
52
src/compiler/bootstrap/impl/ucc-processor.constraint-mapper.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { UcProcessorName, UcSchemaConstraint } from '../../../schema/uc-constraints.js'; | ||
import { UcPresentationName } from '../../../schema/uc-presentations.js'; | ||
import { UccBootstrap } from '../ucc-bootstrap.js'; | ||
import { UccFeature } from '../ucc-feature.js'; | ||
|
||
export class UccProcessor$ConstraintMapper<in out TBoot extends UccBootstrap<TBoot>> { | ||
|
||
readonly #handlers = new Map<string, UccFeature.ConstraintHandler<TBoot>>(); | ||
|
||
onConstraint<TOptions>( | ||
{ processor, within, use, from }: UccFeature.ConstraintCriterion, | ||
handler: UccFeature.ConstraintHandler<TBoot, TOptions>, | ||
): void { | ||
const handlerId = this.#handlerId(processor, within, use, from); | ||
const prevHandler = this.#handlers.get(handlerId) as UccFeature.ConstraintHandler< | ||
TBoot, | ||
TOptions | ||
>; | ||
|
||
if (prevHandler) { | ||
this.#handlers.set( | ||
handlerId, | ||
(application: UccFeature.ConstraintApplication<TBoot, TOptions>) => { | ||
prevHandler(application); | ||
handler(application); | ||
}, | ||
); | ||
} else { | ||
this.#handlers.set(handlerId, handler); | ||
} | ||
} | ||
|
||
findHandler<TOptions>( | ||
processor: UcProcessorName, | ||
within: UcPresentationName | undefined, | ||
{ use, from }: UcSchemaConstraint, | ||
): UccFeature.ConstraintHandler<TBoot, TOptions> | undefined { | ||
return this.#handlers.get( | ||
this.#handlerId(processor, within, use, from), | ||
) as UccFeature.ConstraintHandler<TBoot, TOptions>; | ||
} | ||
|
||
#handlerId( | ||
processor: UcProcessorName, | ||
within: UcPresentationName | undefined, | ||
use: string, | ||
from: string, | ||
): string { | ||
return `${processor}(${within} ?? '*'):${use}@${from}`; | ||
} | ||
|
||
} |
56 changes: 56 additions & 0 deletions
56
src/compiler/bootstrap/impl/ucc-processor.constraint-usage.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { UcSchema } from '../../../schema/uc-schema.js'; | ||
import { UccBootstrap } from '../ucc-bootstrap.js'; | ||
import { UccProcessor$ConstraintApplication } from './ucc-processor.constraint-application.js'; | ||
import { UccProcessor$ConstraintIssue } from './ucc-processor.constraint-issue.js'; | ||
import { UccProcessor$FeatureSet } from './ucc-processor.feature-set.js'; | ||
|
||
export class UccProcessor$ConstraintUsage< | ||
in out TBoot extends UccBootstrap<TBoot>, | ||
out TOptions = unknown, | ||
> { | ||
|
||
readonly #featureSet: UccProcessor$FeatureSet<TBoot>; | ||
readonly #schema: UcSchema; | ||
readonly #issues: UccProcessor$ConstraintIssue<TOptions>[] = []; | ||
|
||
constructor(featureSet: UccProcessor$FeatureSet<TBoot>, schema: UcSchema) { | ||
this.#featureSet = featureSet; | ||
this.#schema = schema; | ||
} | ||
|
||
issue(issue: UccProcessor$ConstraintIssue<TOptions>): void { | ||
this.#issues.push(issue); | ||
} | ||
|
||
async apply(): Promise<void> { | ||
for (const issue of this.#issues) { | ||
await this.#applyConstraint(issue); | ||
} | ||
this.#issues.length = 0; | ||
} | ||
|
||
async #applyConstraint(issue: UccProcessor$ConstraintIssue<TOptions>): Promise<void> { | ||
const featureSet = this.#featureSet; | ||
const schema = this.#schema; | ||
const { processor, within, constraint } = issue; | ||
const { constraintMapper: profiler } = featureSet; | ||
const application = new UccProcessor$ConstraintApplication( | ||
featureSet, | ||
schema, | ||
issue, | ||
await featureSet.resolveFeature(issue), | ||
); | ||
|
||
featureSet.runWithCurrent({ processor, schema, within, constraint }, () => { | ||
profiler.findHandler(processor, within, constraint)?.(application); | ||
if (within) { | ||
// Apply any presentation handler. | ||
profiler.findHandler(processor, undefined, constraint)?.(application); | ||
} | ||
if (!application.isIgnored()) { | ||
application.apply(); | ||
} | ||
}); | ||
} | ||
|
||
} |
4 changes: 2 additions & 2 deletions
4
...r/processor/impl/ucc-processor.current.ts → ...r/bootstrap/impl/ucc-processor.current.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
import { UcFeatureConstraint, UcProcessorName } from '../../../schema/uc-constraints.js'; | ||
import { UcProcessorName, UcSchemaConstraint } from '../../../schema/uc-constraints.js'; | ||
import { UcPresentationName } from '../../../schema/uc-presentations.js'; | ||
import { UcSchema } from '../../../schema/uc-schema.js'; | ||
|
||
export interface UccProcessor$Current { | ||
readonly processor?: UcProcessorName | undefined; | ||
readonly schema?: UcSchema | undefined; | ||
readonly within?: UcPresentationName | undefined; | ||
readonly constraint?: UcFeatureConstraint | undefined; | ||
readonly constraint?: UcSchemaConstraint | undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { lazyValue, mayHaveProperties } from '@proc7ts/primitives'; | ||
import { UccBootstrap } from '../ucc-bootstrap.js'; | ||
import { UccFeature } from '../ucc-feature.js'; | ||
import { UccProcessor$ConstraintIssue } from './ucc-processor.constraint-issue.js'; | ||
import { UccProcessor$ConstraintMapper } from './ucc-processor.constraint-mapper.js'; | ||
import { UccProcessor$Current } from './ucc-processor.current.js'; | ||
|
||
export class UccProcessor$FeatureSet<in out TBoot extends UccBootstrap<TBoot>> { | ||
|
||
readonly #constraintMapper: UccProcessor$ConstraintMapper<TBoot>; | ||
readonly #resolutions = new Map<string, Promise<{ [key in string]: UccFeature<TBoot> }>>(); | ||
readonly #enable: <TOptions>( | ||
feature: UccFeature<TBoot, TOptions>, | ||
) => UccFeature.Handle<TOptions> | void; | ||
|
||
readonly #features = new Map<UccFeature<TBoot, never>, UccFeature$Entry>(); | ||
|
||
#current: UccProcessor$Current = {}; | ||
|
||
constructor( | ||
constraintMapper: UccProcessor$ConstraintMapper<TBoot>, | ||
enable: <TOptions>(feature: UccFeature<TBoot, TOptions>) => UccFeature.Handle<TOptions> | void, | ||
) { | ||
this.#constraintMapper = constraintMapper; | ||
this.#enable = enable; | ||
} | ||
|
||
get constraintMapper(): UccProcessor$ConstraintMapper<TBoot> { | ||
return this.#constraintMapper; | ||
} | ||
|
||
get current(): UccProcessor$Current { | ||
return this.#current; | ||
} | ||
|
||
async resolveFeature<TOptions>( | ||
issue: UccProcessor$ConstraintIssue<TOptions>, | ||
): Promise<UccFeature<TBoot, TOptions>> { | ||
const { | ||
constraint: { use, from }, | ||
} = issue; | ||
let resolveFeatures = this.#resolutions.get(from); | ||
|
||
if (!resolveFeatures) { | ||
resolveFeatures = import(from); | ||
this.#resolutions.set(from, resolveFeatures); | ||
} | ||
|
||
const { [use]: feature } = await resolveFeatures; | ||
|
||
if ((mayHaveProperties(feature) && 'uccEnable' in feature) || typeof feature === 'function') { | ||
return feature as UccFeature<TBoot, TOptions>; | ||
} | ||
if (feature === undefined) { | ||
throw new ReferenceError(`No such schema processing feature: ${issue}`); | ||
} else { | ||
throw new ReferenceError(`Not a schema processing feature: ${issue}`); | ||
} | ||
} | ||
|
||
enableFeature<TOptions>( | ||
feature: UccFeature<TBoot, TOptions>, | ||
): UccFeature.Handle<TOptions> | void { | ||
let entry = this.#features.get(feature) as UccFeature$Entry<TOptions> | undefined; | ||
|
||
if (!entry) { | ||
entry = { | ||
getHandle: lazyValue(() => this.runWithCurrent({}, () => this.#enable(feature))), | ||
}; | ||
this.#features.set(feature, entry); | ||
} | ||
|
||
return entry.getHandle(); | ||
} | ||
|
||
runWithCurrent<T>(current: UccProcessor$Current, action: () => T): T { | ||
const prev = this.#current; | ||
|
||
this.#current = current.processor ? current : { ...current, processor: prev.processor }; | ||
|
||
try { | ||
return action(); | ||
} finally { | ||
this.#current = prev; | ||
} | ||
} | ||
|
||
} | ||
|
||
interface UccFeature$Entry<in TOptions = never> { | ||
readonly getHandle: () => UccFeature.Handle<TOptions> | void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,4 @@ | ||
export * from './ucc-capability.js'; | ||
export * from './ucc-config.js'; | ||
export * from './ucc-bootstrap.js'; | ||
export * from './ucc-feature.js'; | ||
export * from './ucc-processor.js'; | ||
export * from './ucc-schema-index.js'; | ||
export * from './ucc-setup.js'; |
Oops, something went wrong.