Skip to content

Commit

Permalink
Merge pull request google#176 from Eyas/native-graph
Browse files Browse the repository at this point in the history
Refactor: natively operate on RDF graph
  • Loading branch information
Eyas authored Mar 11, 2022
2 parents b7a91f3 + c6e3e0e commit a134410
Show file tree
Hide file tree
Showing 34 changed files with 825 additions and 1,254 deletions.
4 changes: 2 additions & 2 deletions packages/schema-dts-gen/src/cli/internal/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import {Triple} from '../..';
import {Store} from 'n3';
import {Log, SetOptions} from '../../logging/index.js';
import {WriteDeclarations} from '../../transform/transform.js';
import {load, loadFile} from '../../triples/reader.js';
Expand All @@ -28,7 +28,7 @@ export async function main(write: (s: string) => void, args?: string[]) {

const ontologyUrl = options.ontology;
const filePath = options.file;
let result: Triple[];
let result: Store;

if (filePath) {
Log(`Loading Ontology from path: ${filePath}`);
Expand Down
15 changes: 7 additions & 8 deletions packages/schema-dts-gen/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@
*/

// Public Declarations only:
export {WriteDeclarations} from './transform/transform';
export {WriteDeclarations} from './transform/transform.js';

export {load as loadTriples} from './triples/reader';
export * from './triples/triple';
export * from './triples/types';
export * from './triples/wellKnown';
export {load as loadTriples} from './triples/reader.js';
export * from './triples/term_utils.js';
export * from './triples/wellKnown.js';

export * from './ts/class';
export * from './ts/enum';
export * from './ts/property';
export * from './ts/class.js';
export * from './ts/enum.js';
export * from './ts/property.js';
124 changes: 68 additions & 56 deletions packages/schema-dts-gen/src/transform/toClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
*/

import {Log} from '../logging/index.js';
import {ObjectPredicate, Topic, TypedTopic} from '../triples/triple.js';
import {UrlNode} from '../triples/types.js';

import {
IsDirectlyNamedClass,
IsDataType,
ClassIsDataType,
IsNamedUrl,
IsSubclass,
TypedTopic,
} from '../triples/wellKnown.js';
import {
AliasBuiltin,
Expand All @@ -31,104 +30,117 @@ import {
DataTypeUnion,
RoleBuiltin,
} from '../ts/class.js';
import {assert, asserted, assertIs} from '../util/assert.js';
import {assert, assertIs} from '../util/assert.js';

import type {Quad} from 'n3';
import {NamedNode} from 'n3';
import {shortStr} from '../index.js';

function toClass(cls: Class, topic: Topic, map: ClassMap): Class {
const rest: ObjectPredicate[] = [];
for (const value of topic.values) {
function toClass(cls: Class, topic: TypedTopic, map: ClassMap): Class {
const rest: Quad[] = [];

for (const value of topic.quads) {
const added = cls.add(value, map);
if (!added) rest.push(value);
}

if (rest.length > 0) {
Log(
`Class ${cls.subject.name}: Did not add [${rest
.map(r => `(${r.Predicate.name} ${r.Object.toString()})`)
`Class ${shortStr(cls.subject)}: Did not add [${rest
.map(r => `(${shortStr(r.predicate)} ${shortStr(r.object)})`)
.join(',')}]`
);
}
return cls;
}

function buildAlias(name: string, alias: string): AliasBuiltin[] {
return [
new AliasBuiltin(
new NamedNode(`http://schema.org/${name}`),
AliasBuiltin.Alias(alias)
),
new AliasBuiltin(
new NamedNode(`https://schema.org/${name}`),
AliasBuiltin.Alias(alias)
),
];
}
const wellKnownTypes = [
new AliasBuiltin('http://schema.org/Text', AliasBuiltin.Alias('string')),
...buildAlias('Text', 'string'),
// IMPORTANT: In the future, if possible, we should have: `${number}` in Float only,
// an integer string literal in Integer only, and Number becomes simply Float|Integer.
new AliasBuiltin(
'http://schema.org/Number',
new NamedNode('http://schema.org/Number'),
AliasBuiltin.Alias('number'),
AliasBuiltin.NumberStringLiteral()
),
new AliasBuiltin('http://schema.org/Time', AliasBuiltin.Alias('string')),
new AliasBuiltin('http://schema.org/Date', AliasBuiltin.Alias('string')),
new AliasBuiltin('http://schema.org/DateTime', AliasBuiltin.Alias('string')),
new AliasBuiltin('http://schema.org/Boolean', AliasBuiltin.Alias('boolean')),
new RoleBuiltin(
asserted(UrlNode.Parse('http://schema.org/Role'), IsNamedUrl)
),
new RoleBuiltin(
asserted(UrlNode.Parse('http://schema.org/OrganizationRole'), IsNamedUrl)
),
new RoleBuiltin(
asserted(UrlNode.Parse('http://schema.org/EmployeeRole'), IsNamedUrl)
),
new RoleBuiltin(
asserted(UrlNode.Parse('http://schema.org/LinkRole'), IsNamedUrl)
),
new RoleBuiltin(
asserted(UrlNode.Parse('http://schema.org/PerformanceRole'), IsNamedUrl)
new AliasBuiltin(
new NamedNode('https://schema.org/Number'),
AliasBuiltin.Alias('number'),
AliasBuiltin.NumberStringLiteral()
),
...buildAlias('Time', 'string'),
...buildAlias('Date', 'string'),
...buildAlias('DateTime', 'string'),
...buildAlias('Boolean', 'boolean'),
new RoleBuiltin(new NamedNode('http://schema.org/Role')),
new RoleBuiltin(new NamedNode('http://schema.org/OrganizationRole')),
new RoleBuiltin(new NamedNode('http://schema.org/EmployeeRole')),
new RoleBuiltin(new NamedNode('http://schema.org/LinkRole')),
new RoleBuiltin(new NamedNode('http://schema.org/PerformanceRole')),
new RoleBuiltin(new NamedNode('https://schema.org/Role')),
new RoleBuiltin(new NamedNode('https://schema.org/OrganizationRole')),
new RoleBuiltin(new NamedNode('https://schema.org/EmployeeRole')),
new RoleBuiltin(new NamedNode('https://schema.org/LinkRole')),
new RoleBuiltin(new NamedNode('https://schema.org/PerformanceRole')),
];

// Should we allow 'string' to be a valid type for all values of this type?
const wellKnownStrings = [
UrlNode.Parse('http://schema.org/Quantity'),
UrlNode.Parse('http://schema.org/EntryPoint'),
UrlNode.Parse('http://schema.org/Organization'),
UrlNode.Parse('http://schema.org/Person'),
UrlNode.Parse('http://schema.org/Place'),
UrlNode.Parse('https://schema.org/Quantity'),
UrlNode.Parse('https://schema.org/EntryPoint'),
UrlNode.Parse('https://schema.org/Organization'),
UrlNode.Parse('https://schema.org/Person'),
UrlNode.Parse('https://schema.org/Place'),
new NamedNode('http://schema.org/Quantity'),
new NamedNode('http://schema.org/EntryPoint'),
new NamedNode('http://schema.org/Organization'),
new NamedNode('http://schema.org/Person'),
new NamedNode('http://schema.org/Place'),
new NamedNode('https://schema.org/Quantity'),
new NamedNode('https://schema.org/EntryPoint'),
new NamedNode('https://schema.org/Organization'),
new NamedNode('https://schema.org/Person'),
new NamedNode('https://schema.org/Place'),
];

function ForwardDeclareClasses(topics: readonly TypedTopic[]): ClassMap {
const classes = new Map<string, Class>();
const dataType = new DataTypeUnion('http://schema.org/DataType', []);
const dataType = new DataTypeUnion(
new NamedNode('http://schema.org/DataType'),
[]
);

for (const topic of topics) {
if (IsDataType(topic.Subject)) {
classes.set(topic.Subject.toString(), dataType);
if (IsDataType(topic.subject)) {
classes.set(topic.subject.value, dataType);
continue;
} else if (!IsDirectlyNamedClass(topic) && !IsSubclass(topic)) continue;

if (!IsNamedUrl(topic.Subject)) {
throw new Error(
`Unexpected unnamed URL ${topic.Subject.toString()} as a class.`
);
}

const wk = wellKnownTypes.find(wk => wk.subject.equivTo(topic.Subject));
const wk = wellKnownTypes.find(wk => wk.subject.equals(topic.subject));
if (ClassIsDataType(topic)) {
assert(
wk,
`${topic.Subject.toString()} must have corresponding well-known type.`
`${topic.subject.value} must have corresponding well-known type.`
);
dataType.wk.push(wk);

wk['_isDataType'] = true;
}

const cls = wk || new Class(topic.Subject);
const allowString = wellKnownStrings.some(wks =>
wks.equivTo(topic.Subject)
);
assertIs(topic.subject, (s): s is NamedNode => s.termType === 'NamedNode');
const cls = wk || new Class(topic.subject);
const allowString = wellKnownStrings.some(wks => wks.equals(topic.subject));
if (allowString) cls.addTypedef(AliasBuiltin.Alias('string'));
if (IsDirectlyNamedClass(topic)) cls.markAsExplicitClass();

classes.set(topic.Subject.toString(), cls);
classes.set(topic.subject.value, cls);
}

return classes;
Expand All @@ -138,7 +150,7 @@ function BuildClasses(topics: readonly TypedTopic[], classes: ClassMap) {
for (const topic of topics) {
if (!IsDirectlyNamedClass(topic) && !IsSubclass(topic)) continue;

const cls = classes.get(topic.Subject.toString());
const cls = classes.get(topic.subject.value);
assert(cls);
toClass(cls, topic, classes);
}
Expand Down
18 changes: 11 additions & 7 deletions packages/schema-dts-gen/src/transform/toEnum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
* limitations under the License.
*/

import {NamedNode, Quad} from 'n3';

import {Log} from '../logging/index.js';
import {Format, ObjectPredicate, TypedTopic} from '../triples/triple.js';
import {HasEnumType} from '../triples/wellKnown.js';
import {shortStr} from '../triples/term_utils.js';
import {HasEnumType, TypedTopic} from '../triples/wellKnown.js';
import {ClassMap} from '../ts/class.js';
import {EnumValue} from '../ts/enum.js';
import {assertIs} from '../util/assert.js';

/**
* Annotates classes with any Enum values they blong to.
Expand All @@ -32,17 +35,18 @@ export function ProcessEnums(topics: readonly TypedTopic[], classes: ClassMap) {
if (!HasEnumType(topic.types)) continue;

// Everything Here should be an enum.
const enumValue = new EnumValue(topic.Subject, topic.types, classes);
assertIs(topic.subject, (s): s is NamedNode => s.termType === 'NamedNode');
const enumValue = new EnumValue(topic.subject, topic.types, classes);

const skipped: ObjectPredicate[] = [];
for (const v of topic.values) {
const skipped: Quad[] = [];
for (const v of topic.quads) {
if (!enumValue.add(v)) skipped.push(v);
}

if (skipped.length > 0) {
Log(
`For Enum Item ${topic.Subject.name}, did not process:\n\t${skipped
.map(Format)
`For Enum Item ${shortStr(topic.subject)}, did not process:\n\t${skipped
.map(q => `(${shortStr(q.predicate)}, ${shortStr(q.object)})`)
.join('\n\t')}`
);
}
Expand Down
18 changes: 11 additions & 7 deletions packages/schema-dts-gen/src/transform/toProperty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {NamedNode, Quad} from 'n3';
import {shortStr} from '../index.js';
import {Log} from '../logging/index.js';
import {Format, ObjectPredicate, TypedTopic} from '../triples/triple.js';
import {IsPropertyType} from '../triples/wellKnown.js';

import {IsPropertyType, TypedTopic} from '../triples/wellKnown.js';
import {ClassMap} from '../ts/class.js';
import {PropertyType} from '../ts/property.js';
import {assertIs} from '../util/assert.js';

/**
* Annotates classes with any Property values they blong to.
Expand All @@ -33,9 +36,10 @@ export function ProcessProperties(
// Skip Topics that have no 'Property' Type.
if (!topic.types.some(IsPropertyType)) continue;

const rest: ObjectPredicate[] = [];
const property = new PropertyType(topic.Subject);
for (const value of topic.values) {
const rest: Quad[] = [];
assertIs(topic.subject, (s): s is NamedNode => s.termType === 'NamedNode');
const property = new PropertyType(topic.subject);
for (const value of topic.quads) {
const added = property.add(value, classes);
if (!added) {
rest.push(value);
Expand All @@ -44,8 +48,8 @@ export function ProcessProperties(
// Go over RangeIncludes or DomainIncludes:
if (rest.length > 0) {
Log(
`Still unadded for property: ${topic.Subject.name}:\n\t${rest
.map(Format)
`Still unadded for property: ${shortStr(topic.subject)}:\n\t${rest
.map(q => `(${shortStr(q.predicate)} ${shortStr(q.object)})`)
.join('\n\t')}`
);
}
Expand Down
7 changes: 4 additions & 3 deletions packages/schema-dts-gen/src/transform/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const {
} = ts;

import {asTopicArray} from '../triples/operators.js';
import {Triple} from '../triples/triple.js';
import {Sort} from '../ts/class.js';
import {Context} from '../ts/context.js';

Expand All @@ -33,6 +32,8 @@ import {ProcessEnums} from './toEnum.js';
import {ProcessProperties} from './toProperty.js';
import {HelperTypes} from '../ts/helper_types.js';

import {Store} from 'n3';

/**
* Writes TypeScript declarations for all Classes, Typedefs, and Enums
* representing the ontology passed in the 'triples' parameter.
Expand All @@ -47,12 +48,12 @@ import {HelperTypes} from '../ts/helper_types.js';
* @returns Promise indicating completion.
*/
export async function WriteDeclarations(
triples: Triple[],
graph: Store,
includeDeprecated: boolean,
context: Context,
write: (content: string) => Promise<void> | void
) {
const topics = asTopicArray(triples);
const topics = asTopicArray(graph);

const classes = ProcessClasses(topics);
ProcessProperties(topics, classes);
Expand Down
Loading

0 comments on commit a134410

Please sign in to comment.