diff --git a/package.json b/package.json index 90822ba..1a2f96c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@superindustries/language", - "version": "0.1.4", - "description": "Level 5 autonomous, self-driving API client, https://superface.ai", + "version": "0.2.0", + "description": "Superface profile and map language ASTs, https://superface.ai", "main": "dist/language.js", "source": "src/index.ts", "module": "dist/language.modern.js", diff --git a/src/interfaces/ast/index.ts b/src/interfaces/ast/index.ts index 46a701a..9199480 100644 --- a/src/interfaces/ast/index.ts +++ b/src/interfaces/ast/index.ts @@ -1,2 +1,3 @@ export * from './map-ast'; export * from './map-ast.utils'; +export * from './profile-ast'; diff --git a/src/interfaces/ast/map-ast.ts b/src/interfaces/ast/map-ast.ts index 1cfe42b..5ffb7a8 100644 --- a/src/interfaces/ast/map-ast.ts +++ b/src/interfaces/ast/map-ast.ts @@ -1,3 +1,5 @@ +import { Location, Span } from './source'; + export type MapNodeKind = | 'EvalDefinition' | 'HTTPOperationDefinition' @@ -16,14 +18,10 @@ export type MapNodeKind = | 'StepDefinition' | 'VariableExpressionsDefinition'; -export interface Location { - start: number; - end: number; -} - export interface MapASTNodeBase { kind: MapNodeKind; - loc?: Location; + span?: Span; + location?: Location; } export interface JSExpressionNode extends MapASTNodeBase { @@ -111,7 +109,7 @@ export interface OperationDefinitionNode extends MapASTNodeBase { stepsDefinition: StepDefinitionNode[]; } -export interface ProfileIdNode extends MapASTNodeBase { +export interface MapProfileIdNode extends MapASTNodeBase { kind: 'ProfileId'; profileId: string; } @@ -123,7 +121,7 @@ export interface ProviderNode extends MapASTNodeBase { export interface MapNode extends MapASTNodeBase { kind: 'Map'; - profileId: ProfileIdNode; + profileId: MapProfileIdNode; provider: ProviderNode; } @@ -154,7 +152,7 @@ export type MapASTNode = | OperationCallDefinitionNode | OperationDefinitionNode | OutcomeDefinitionNode - | ProfileIdNode + | MapProfileIdNode | ProviderNode | StepDefinitionNode | VariableExpressionDefinitionNode; diff --git a/src/interfaces/ast/map-ast.utils.ts b/src/interfaces/ast/map-ast.utils.ts index 251f06c..3669543 100644 --- a/src/interfaces/ast/map-ast.utils.ts +++ b/src/interfaces/ast/map-ast.utils.ts @@ -8,11 +8,11 @@ import { MapDocumentNode, MapExpressionDefinitionNode, MapNode, + MapProfileIdNode, NetworkOperationDefinitionNode, OperationCallDefinitionNode, OperationDefinitionNode, OutcomeDefinitionNode, - ProfileIdNode, ProviderNode, StepDefinitionNode, VariableExpressionDefinitionNode, @@ -90,9 +90,9 @@ export function assertIsOperationDefinitionNode( checkNode(node, 'OperationDefinition'); } -export function assertIsProfileIdNode( +export function assertIsMapProfileIdNode( node: MapASTNode -): asserts node is ProfileIdNode { +): asserts node is MapProfileIdNode { checkNode(node, 'ProfileId'); } @@ -182,7 +182,7 @@ export function isOperationDefinitionNode( return node.kind === 'OperationDefinition'; } -export function isProfileIdNode(node: MapASTNode): node is ProfileIdNode { +export function isProfileIdNode(node: MapASTNode): node is MapProfileIdNode { return node.kind === 'ProfileId'; } diff --git a/src/interfaces/ast/profile-ast.ts b/src/interfaces/ast/profile-ast.ts index b32b583..e154322 100644 --- a/src/interfaces/ast/profile-ast.ts +++ b/src/interfaces/ast/profile-ast.ts @@ -1,172 +1,234 @@ -export type ScalarTypeName = 'boolean' | 'number' | 'string'; +import { Location, Span } from './source'; -export interface ModelTypeName extends ProfileASTNodeBase { - kind: 'ModelTypeName'; - name: string; -} - -export interface ObjectModelTypeName extends ProfileASTNodeBase { - kind: 'ObjectModelTypeName'; - name: string; -} +// BASE // export type ProfileNodeKind = - | 'ProfileDocument' - | 'Profile' - | 'ProfileId' - | 'UseCaseDefinition' - | 'FieldDefinition' - | 'ScalarModelDefinition' + // TYPES + | 'ScalarTypeName' | 'ObjectModelDefinition' + | 'FieldDefinition' | 'UnionModelDefinition' | 'EnumModelDefinition' | 'EnumValueDefinition' - | 'NamedType' + | 'ModelTypeName' | 'ListType' | 'NonNullType' - | 'ModelTypeName' - | 'ObjectModelTypeName'; - -// TODO: Same as in Map AST -> reuse? -export interface Location { - start: number; - end: number; -} + // MODELS + | 'ScalarModelDefinition' + // USECASE + | 'UseCaseDefinition' + // DOCUMENT + | 'ProfileId' + | 'Profile' + | 'ProfileDocument'; -// TODO: Similar as in Map AST -> reuse? export interface ProfileASTNodeBase { kind: ProfileNodeKind; - loc?: Location; -} - -// TODO: Same as in Map AST -> reuse? -export interface ProfileIdNode extends ProfileASTNodeBase { - kind: 'ProfileId'; - profileId: string; + span?: Span; + location?: Location; } +/** Node preceded by documenting string literal */ export interface DocumentedNode { title?: string; description?: string; } -export interface ProfileNode extends ProfileASTNodeBase, DocumentedNode { - kind: 'Profile'; - profileId: ProfileIdNode; -} +// TYPES // -export interface ProfileDocumentNode extends ProfileASTNodeBase { - kind: 'ProfileDocument'; - profile: ProfileNode; - definitions: DocumentDefinition[]; +/** From keywords: `Boolean`, `Number` and `String` */ +export interface ScalarTypeName extends ProfileASTNodeBase { + kind: 'ScalarTypeName'; + name: 'boolean' | 'number' | 'string'; } -export enum ProfileUseCaseSafety { - safe = 'safe', - unsafe = 'unsafe', - idempotent = 'idempotent', +/** + * Construct of form: + * `{ fields... }` + */ +export interface ObjectModelDefinitionNode + extends ProfileASTNodeBase { + kind: 'ObjectModelDefinition'; + fields: FieldDefinition[]; +} +/** + * Construct of form: + * `ident: type` or `ident` + * that appear inside object model definitions + */ +export interface FieldDefinition extends ProfileASTNodeBase, DocumentedNode { + kind: 'FieldDefinition'; + fieldName: string; + type?: Type; +} +/** + * Construct of form: + * `type | type | ...` + */ +export interface UnionModelDefinitionNode + extends ProfileASTNodeBase { + kind: 'UnionModelDefinition'; + types: Type[]; +} +/** + * Construct of form: + * `enum { values... }` + */ +export interface EnumModelDefinitionNode + extends ProfileASTNodeBase { + kind: 'EnumModelDefinition'; + enumValues: EnumValueDefinition[]; } +/** + * Variant of an enum definition. + * + * These are either string or number literals + */ +export interface EnumValueDefinition extends ProfileASTNodeBase { + kind: 'EnumValueDefinition'; + enumValue: string | number | boolean; +} + +export type AnonymousModelDefinitionNode = + | ObjectModelDefinitionNode + | UnionModelDefinitionNode + | EnumModelDefinitionNode; -export interface NamedType extends ProfileASTNodeBase { - kind: 'NamedType'; - type: ModelTypeName | ScalarTypeName; +/** + * Name of a model type parsed from identifiers. + * + * This is the name of a user defined type. + */ +export interface ModelTypeName extends ProfileASTNodeBase { + kind: 'ModelTypeName'; + name: string; } +/** Array type: `[type]` */ export interface ListType extends ProfileASTNodeBase { kind: 'ListType'; type: Type; } - +/** Non-null assertion operator: `type!` */ export interface NonNullType extends ProfileASTNodeBase { kind: 'NonNullType'; - type: NamedType | ListType | AnonymousModelDefinitionNode; + type: + | ScalarTypeName + | ModelTypeName + | ListType + | AnonymousModelDefinitionNode; } export type Type = - | NamedType + | ScalarTypeName + | AnonymousModelDefinitionNode + | ModelTypeName | ListType - | NonNullType - | AnonymousModelDefinitionNode; + | NonNullType; -export interface FieldDefinition extends ProfileASTNodeBase, DocumentedNode { - kind: 'FieldDefinition'; - fieldName: string; - type: Type; - exampleValue?: string; -} - -export interface ProfileUseCaseDefinitionNode - extends ProfileASTNodeBase, - DocumentedNode { - kind: 'UseCaseDefinition'; - useCaseName: string; - safety: ProfileUseCaseSafety; - input?: ObjectModelDefinitionNode | ObjectModelTypeName; - result: ObjectModelDefinitionNode | ObjectModelTypeName; - errors?: ObjectModelTypeName[]; -} +// MODELS // +/** + * Construct of form: + * `field ident: ScalarType` or `field ident: ModelType` + * + * This is basically a type alias. + */ export interface NamedScalarModelDefinitionNode extends ProfileASTNodeBase, DocumentedNode { kind: 'ScalarModelDefinition'; modelName: ModelTypeName; - baseType: ScalarTypeName; -} - -export interface ObjectModelDefinitionNode - extends ProfileASTNodeBase, - DocumentedNode { - kind: 'ObjectModelDefinition'; - fields: FieldDefinition[]; + baseType: ScalarTypeName | ModelTypeName; } +/** + * Construct of form: + * `model ident { fields... }` + */ export interface NamedObjectModelDefinitionNode - extends ObjectModelDefinitionNode { - modelName: ObjectModelTypeName; -} - -export interface UnionModelDefinitionNode - extends ProfileASTNodeBase, - DocumentedNode { - kind: 'UnionModelDefinition'; + extends ObjectModelDefinitionNode, DocumentedNode { modelName: ModelTypeName; } +/** + * Construct of form: + * `field ident: type | type | ...` + */ export interface NamedUnionModelDefinitionNode - extends UnionModelDefinitionNode { + extends UnionModelDefinitionNode, DocumentedNode { modelName: ModelTypeName; } -export interface EnumModelDefinitionNode - extends ProfileASTNodeBase, - DocumentedNode { - kind: 'EnumModelDefinition'; - enumValues: EnumValueDefinition[]; -} - -export interface NamedEnumModelDefinitionNode extends EnumModelDefinitionNode { +/** + * Construct of form: + * `enum ident { values ... }` + */ +export interface NamedEnumModelDefinitionNode extends EnumModelDefinitionNode, DocumentedNode { modelName: ModelTypeName; } -export interface EnumValueDefinition - extends ProfileASTNodeBase, - DocumentedNode { - kind: 'EnumValueDefinition'; - enumValue: string | number; -} - -export type AnonymousModelDefinitionNode = - | ObjectModelDefinitionNode - | UnionModelDefinitionNode - | EnumModelDefinitionNode; - export type ProfileModelDefinitionNode = | NamedScalarModelDefinitionNode | NamedObjectModelDefinitionNode | NamedUnionModelDefinitionNode | NamedEnumModelDefinitionNode; +// USECASE // + +/** Usecase safety indicator, corresponds to decorators */ +export enum ProfileUseCaseSafety { + safe = 'safe', + unsafe = 'unsafe', + idempotent = 'idempotent', +} + +/** +* Construct of form: +``` +usecase ident @deco { + input: type + result: type + errors: [ + type + type + ... + ] +} +``` +*/ +export interface ProfileUseCaseDefinitionNode + extends ProfileASTNodeBase, + DocumentedNode { + kind: 'UseCaseDefinition'; + useCaseName: string; + safety: ProfileUseCaseSafety; + input?: Type; + result: Type; + asyncResult: boolean; + errors?: Type[]; +} + +// DOCUMENT // + +/** `profile: string` */ +export interface ProfileProfileIdNode extends ProfileASTNodeBase { + kind: 'ProfileId'; + profileId: string; +} +/** + * The node containing document information at the top of the document. + */ +export interface ProfileNode extends ProfileASTNodeBase, DocumentedNode { + kind: 'Profile'; + profileId: ProfileProfileIdNode; +} +/** Node enclosing the whole document */ +export interface ProfileDocumentNode extends ProfileASTNodeBase { + kind: 'ProfileDocument'; + profile: ProfileNode; + definitions: DocumentDefinition[]; +} export type DocumentDefinition = | ProfileUseCaseDefinitionNode | ProfileModelDefinitionNode; diff --git a/src/interfaces/ast/source.ts b/src/interfaces/ast/source.ts new file mode 100644 index 0000000..c6183dc --- /dev/null +++ b/src/interfaces/ast/source.ts @@ -0,0 +1,17 @@ +/** + * Human-readable location of a token inside source code. + * + * Both `line` and `column` are indexed from 1. + */ +export type Location = { + line: number; + column: number; +}; + +/** + * Span of one node inside source code. + */ +export type Span = { + start: number; + end: number; +}; \ No newline at end of file