feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
144
frontend/infra/idl/idl-parser/src/unify/index.ts
Normal file
144
frontend/infra/idl/idl-parser/src/unify/index.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { logAndThrowError , mergeObject, getPosixPath } from '../utils';
|
||||
import { parseThriftContent } from './thrift';
|
||||
import { parseProtoContent } from './proto';
|
||||
import { type UnifyDocument } from './type';
|
||||
|
||||
type FileType = 'thrift' | 'proto';
|
||||
|
||||
// export statements
|
||||
export * from './type';
|
||||
|
||||
export interface ParseOption {
|
||||
root?: string;
|
||||
namespaceRefer?: boolean;
|
||||
cache?: boolean;
|
||||
ignoreGoTag?: boolean;
|
||||
ignoreGoTagDash?: boolean;
|
||||
preproccess?: (param: { content: string; path?: string }) => string;
|
||||
searchPaths?: string[];
|
||||
}
|
||||
|
||||
const parseOptionDefault: ParseOption = {
|
||||
root: '.',
|
||||
namespaceRefer: true,
|
||||
cache: false,
|
||||
ignoreGoTag: false,
|
||||
ignoreGoTagDash: false,
|
||||
searchPaths: [],
|
||||
};
|
||||
|
||||
// the key of fileContentMap should be absolute path
|
||||
export function parse(
|
||||
filePath: string,
|
||||
option: ParseOption = {},
|
||||
fileContentMap?: Record<string, string>,
|
||||
): UnifyDocument {
|
||||
const {
|
||||
root,
|
||||
namespaceRefer,
|
||||
cache,
|
||||
ignoreGoTag,
|
||||
ignoreGoTagDash,
|
||||
preproccess,
|
||||
searchPaths,
|
||||
} = mergeObject(parseOptionDefault, option) as Required<ParseOption>;
|
||||
|
||||
const fullRootDir = getPosixPath(path.resolve(process.cwd(), root));
|
||||
let fullFilePath = getPosixPath(path.resolve(fullRootDir, filePath));
|
||||
let idlFileType: FileType = 'thrift';
|
||||
let content = '';
|
||||
|
||||
if (/\.thrift$/.test(filePath)) {
|
||||
fullFilePath = getPosixPath(path.resolve(fullRootDir, filePath));
|
||||
if (fileContentMap) {
|
||||
content = fileContentMap[filePath];
|
||||
if (typeof content === 'undefined') {
|
||||
logAndThrowError(`file "${filePath}" does not exist in fileContentMap`);
|
||||
}
|
||||
} else {
|
||||
if (!fs.existsSync(fullFilePath)) {
|
||||
const message = `no such file: ${fullFilePath}`;
|
||||
logAndThrowError(message);
|
||||
}
|
||||
|
||||
content = fs.readFileSync(fullFilePath, 'utf8');
|
||||
}
|
||||
} else if (/\.proto$/.test(filePath)) {
|
||||
idlFileType = 'proto';
|
||||
fullFilePath = getPosixPath(path.resolve(fullRootDir, filePath));
|
||||
if (fileContentMap) {
|
||||
content = fileContentMap[filePath];
|
||||
if (typeof content === 'undefined') {
|
||||
logAndThrowError(`file "${filePath}" does not exist in fileContentMap`);
|
||||
}
|
||||
} else {
|
||||
if (!fs.existsSync(fullFilePath)) {
|
||||
const message = `no such file: ${fullFilePath}`;
|
||||
logAndThrowError(message);
|
||||
}
|
||||
|
||||
content = fs.readFileSync(fullFilePath, 'utf8');
|
||||
}
|
||||
} else {
|
||||
const message = `invalid filePath: "${filePath}"`;
|
||||
logAndThrowError(message);
|
||||
}
|
||||
|
||||
const absoluteFilePath = getPosixPath(
|
||||
path.relative(fullRootDir, fullFilePath),
|
||||
);
|
||||
if (typeof preproccess === 'function') {
|
||||
content = preproccess({ content, path: absoluteFilePath });
|
||||
}
|
||||
|
||||
if (idlFileType === 'thrift') {
|
||||
const looseAbsoluteFilePath = absoluteFilePath.replace(/\.thrift$/, '');
|
||||
const document = parseThriftContent(
|
||||
content,
|
||||
{
|
||||
loosePath: looseAbsoluteFilePath,
|
||||
rootDir: fullRootDir,
|
||||
namespaceRefer,
|
||||
cache,
|
||||
ignoreGoTag,
|
||||
ignoreGoTagDash,
|
||||
searchPaths,
|
||||
},
|
||||
fileContentMap,
|
||||
);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
const looseAbsoluteFilePath = absoluteFilePath.replace(/\.proto$/, '');
|
||||
const document = parseProtoContent(
|
||||
content,
|
||||
{
|
||||
loosePath: looseAbsoluteFilePath,
|
||||
rootDir: fullRootDir,
|
||||
cache,
|
||||
searchPaths,
|
||||
},
|
||||
fileContentMap,
|
||||
);
|
||||
|
||||
return document;
|
||||
}
|
||||
1014
frontend/infra/idl/idl-parser/src/unify/proto.ts
Normal file
1014
frontend/infra/idl/idl-parser/src/unify/proto.ts
Normal file
File diff suppressed because it is too large
Load Diff
712
frontend/infra/idl/idl-parser/src/unify/thrift.ts
Normal file
712
frontend/infra/idl/idl-parser/src/unify/thrift.ts
Normal file
@@ -0,0 +1,712 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint no-param-reassign: ["error", { "props": false }], import/prefer-default-export: off */
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import * as t from '@lancewuz/thrift-parser';
|
||||
|
||||
import { logAndThrowError, getPosixPath } from '../utils';
|
||||
import * as extensionUtil from '../common/extension_util';
|
||||
import { convertIntToString } from './util';
|
||||
import {
|
||||
SyntaxType,
|
||||
type Annotation,
|
||||
type ServiceDefinition,
|
||||
type FunctionDefinition,
|
||||
type EnumDefinition,
|
||||
type ExtensionConfig,
|
||||
type FieldExtensionConfig,
|
||||
type ServiceExtensionConfig,
|
||||
type FunctionExtensionConfig,
|
||||
type Comment,
|
||||
type Identifier,
|
||||
type ContainerType,
|
||||
type MapType,
|
||||
type StructDefinition,
|
||||
type TypedefDefinition,
|
||||
type TextLocation,
|
||||
type FunctionType,
|
||||
type FieldType,
|
||||
type UnifyStatement,
|
||||
type Annotations,
|
||||
type EnumMember,
|
||||
type UnifyDocument,
|
||||
type FieldDefinition,
|
||||
type ConstDefinition,
|
||||
type ConstValue,
|
||||
} from './type';
|
||||
|
||||
// cache parsed document
|
||||
const fileDocumentMap: Record<string, t.ThriftDocument> = {};
|
||||
const namespaceDefault = 'root';
|
||||
let enumNames: string[] = [];
|
||||
let cache = true;
|
||||
let absoluteFileContentMap: Record<string, string> | undefined;
|
||||
let rootDir = '';
|
||||
let entryLooseAbsoluteFilePath = '';
|
||||
let ignoreGoTag = false;
|
||||
let ignoreGoTagDash = false;
|
||||
let addNamespaceValue: ((fieldType: FunctionType) => void) | undefined;
|
||||
let searchPaths: string[] = [];
|
||||
|
||||
const mergeConfig = (
|
||||
config: Partial<ExtensionConfig>,
|
||||
config2: Partial<ExtensionConfig>,
|
||||
): ExtensionConfig => {
|
||||
const mergedTags: string[] = [];
|
||||
if (config?.tag) {
|
||||
mergedTags.push(config.tag);
|
||||
}
|
||||
if (config2?.tag) {
|
||||
mergedTags.push(config2.tag);
|
||||
}
|
||||
const res = Object.assign(config, config2);
|
||||
if (mergedTags.length > 0) {
|
||||
res.tag = mergedTags.join(',');
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
function extractExtensionConfigFromAnnotation(
|
||||
annotation: Annotation,
|
||||
): false | ExtensionConfig {
|
||||
let key = annotation.name.value;
|
||||
const value = (annotation.value && annotation.value.value) || '';
|
||||
|
||||
if (/^((agw\.)|(api\.)|(go\.tag))/.test(key) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (/^((agw\.)|(api\.))/.test(key)) {
|
||||
key = key.slice(4);
|
||||
}
|
||||
|
||||
const config = extensionUtil.extractExtensionConfig(key, value, ignoreGoTag);
|
||||
return config;
|
||||
}
|
||||
|
||||
function getFieldExtensionConfig(
|
||||
fieldName: string,
|
||||
fieldType: FunctionType,
|
||||
annotations?: Annotations,
|
||||
): FieldExtensionConfig {
|
||||
if (!annotations) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const extensionConfig: FieldExtensionConfig = {};
|
||||
for (const annotation of annotations.annotations) {
|
||||
const config = extractExtensionConfigFromAnnotation(annotation);
|
||||
if (config) {
|
||||
if ('key' in config) {
|
||||
// a key which is the same with the field name will make no sense
|
||||
if (config.key === fieldName) {
|
||||
delete config.key;
|
||||
}
|
||||
} else if (config.position === 'path') {
|
||||
if (
|
||||
[
|
||||
SyntaxType.DoubleKeyword,
|
||||
SyntaxType.BoolKeyword,
|
||||
SyntaxType.ByteKeyword,
|
||||
SyntaxType.ListKeyword,
|
||||
SyntaxType.SetKeyword,
|
||||
SyntaxType.MapKeyword,
|
||||
SyntaxType.VoidKeyword,
|
||||
].includes(fieldType.type)
|
||||
) {
|
||||
const fullFilePath = path.resolve(
|
||||
rootDir,
|
||||
`${entryLooseAbsoluteFilePath}.proto`,
|
||||
);
|
||||
const message = `path parameter '${fieldName}' should be string or integer`;
|
||||
const fullMessage = `${message} (${fullFilePath})`;
|
||||
logAndThrowError(fullMessage, message);
|
||||
}
|
||||
}
|
||||
|
||||
mergeConfig(extensionConfig, config);
|
||||
}
|
||||
}
|
||||
|
||||
const res = extensionUtil.filterFieldExtensionConfig(extensionConfig);
|
||||
return res;
|
||||
}
|
||||
|
||||
function getFuncExtensionConfig(
|
||||
annotations?: Annotations,
|
||||
): FunctionExtensionConfig {
|
||||
if (!annotations) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const extensionConfig: FunctionExtensionConfig = {};
|
||||
for (const annotation of annotations.annotations) {
|
||||
const config = extractExtensionConfigFromAnnotation(annotation);
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (config) {
|
||||
Object.assign(extensionConfig, config);
|
||||
}
|
||||
}
|
||||
|
||||
return extensionUtil.filterFunctionExtensionConfig(extensionConfig);
|
||||
}
|
||||
|
||||
function getServiceExtensionConfig(
|
||||
annotations?: Annotations,
|
||||
): ServiceExtensionConfig {
|
||||
if (!annotations) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const extensionConfig: ServiceExtensionConfig = {};
|
||||
for (const annotation of annotations.annotations) {
|
||||
const config = extractExtensionConfigFromAnnotation(annotation);
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (config) {
|
||||
Object.assign(extensionConfig, config);
|
||||
}
|
||||
}
|
||||
|
||||
return extensionUtil.filterServiceExtensionConfig(extensionConfig);
|
||||
}
|
||||
|
||||
function reviseFieldComments(fields: (FieldDefinition | EnumMember)[]): void {
|
||||
/* istanbul ignore next */
|
||||
if (fields.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// move previous comments to current field
|
||||
for (let i = fields.length - 1; i > 0; i--) {
|
||||
const currentField = fields[i];
|
||||
const prevField = fields[i - 1];
|
||||
const prevFieldEndLine = (prevField.loc as TextLocation).end.line;
|
||||
const prevFieldComments = prevField.comments;
|
||||
|
||||
for (let j = 0; j < prevFieldComments.length; j++) {
|
||||
if (
|
||||
(prevFieldComments[j].loc as TextLocation).end.line > prevFieldEndLine
|
||||
) {
|
||||
const dislocatedComments = prevFieldComments.splice(
|
||||
j,
|
||||
prevFieldComments.length - j,
|
||||
);
|
||||
currentField.comments = [
|
||||
...dislocatedComments,
|
||||
...currentField.comments,
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move next comments to current field
|
||||
for (let i = 0; i < fields.length - 1; i++) {
|
||||
const currentField = fields[i];
|
||||
const nextField = fields[i + 1];
|
||||
const currentFieldEndLine = (currentField.loc as TextLocation).end.line;
|
||||
const nextFieldFirstComment = nextField.comments[0];
|
||||
|
||||
if (
|
||||
nextFieldFirstComment &&
|
||||
(nextFieldFirstComment.loc as TextLocation).end.line ===
|
||||
currentFieldEndLine
|
||||
) {
|
||||
const dislocatedComment = nextField.comments.shift() as Comment;
|
||||
currentField.comments.push(dislocatedComment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reviseFuncComments(functions: FunctionDefinition[]): void {
|
||||
if (functions.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < functions.length - 1; i++) {
|
||||
const currentFunc = functions[i];
|
||||
const nextFunc = functions[i + 1];
|
||||
const currentFuncEndLine = (currentFunc.loc as TextLocation).end.line;
|
||||
const nextFuncFirstComment = nextFunc.comments[0];
|
||||
|
||||
if (
|
||||
nextFuncFirstComment &&
|
||||
(nextFuncFirstComment.loc as TextLocation).end.line === currentFuncEndLine
|
||||
) {
|
||||
const dislocatedComment = nextFunc.comments.shift() as Comment;
|
||||
currentFunc.comments.push(dislocatedComment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getUnifyNamespace(
|
||||
looseAbsoluteFilePath: string,
|
||||
astNamespaces?: t.NamespaceDefinition[],
|
||||
): {
|
||||
namespace: string;
|
||||
unifyNamespace: string;
|
||||
} {
|
||||
let namespace = '';
|
||||
let unifyNamespace = '';
|
||||
if (astNamespaces && astNamespaces.length > 0) {
|
||||
const namespaceMap: Record<string, t.NamespaceDefinition> = {};
|
||||
for (const astNamespace of astNamespaces) {
|
||||
const scopeName = astNamespace.scope.value;
|
||||
namespaceMap[scopeName] = astNamespace;
|
||||
}
|
||||
|
||||
const astNamespaceCurrent =
|
||||
namespaceMap.js || namespaceMap.go || namespaceMap.py;
|
||||
if (astNamespaceCurrent) {
|
||||
namespace = astNamespaceCurrent.name.value;
|
||||
unifyNamespace = namespace;
|
||||
} else if (namespaceMap.java) {
|
||||
namespace = namespaceMap.java.name.value.split('.').pop() as string;
|
||||
unifyNamespace = namespace;
|
||||
} else {
|
||||
const message = 'a js namespace should be specifed';
|
||||
const fullFilePath = path.resolve(
|
||||
rootDir,
|
||||
`${looseAbsoluteFilePath}.thrift`,
|
||||
);
|
||||
const infoMessage = `${message} (${fullFilePath})`;
|
||||
logAndThrowError(infoMessage, message);
|
||||
}
|
||||
} else {
|
||||
namespace = '';
|
||||
unifyNamespace = namespaceDefault;
|
||||
}
|
||||
|
||||
unifyNamespace = unifyNamespace
|
||||
.replace(/\./g, '_')
|
||||
.replace(/[^a-zA-Z0-9_]/g, '');
|
||||
|
||||
return { namespace, unifyNamespace };
|
||||
}
|
||||
|
||||
function createAddNamespaceReferValue(
|
||||
filenameNamespaceMap: Record<string, string>,
|
||||
namespace: string,
|
||||
): (fieldType: FunctionType) => void {
|
||||
const regExpNamespaceMap = new Map<RegExp, string>();
|
||||
Object.keys(filenameNamespaceMap).forEach(filename => {
|
||||
const regExp = new RegExp(`^${filename}(\\.[^\\.]*)$`);
|
||||
regExpNamespaceMap.set(regExp, filenameNamespaceMap[filename]);
|
||||
});
|
||||
|
||||
function addNamespaceReferValue(fieldType: FunctionType): void {
|
||||
if ((fieldType as Identifier).type === SyntaxType.Identifier) {
|
||||
const identifierValue = (fieldType as Identifier).value;
|
||||
|
||||
if (!identifierValue.includes('.')) {
|
||||
(fieldType as Identifier).namespaceValue =
|
||||
`${namespace}.${identifierValue}`;
|
||||
} else {
|
||||
const parts = identifierValue.split('.');
|
||||
if (parts.length === 2 && enumNames.includes(parts[0])) {
|
||||
(fieldType as Identifier).namespaceValue =
|
||||
`${namespace}.${identifierValue}`;
|
||||
} else {
|
||||
for (const regExp of regExpNamespaceMap.keys()) {
|
||||
if (regExp.test(identifierValue)) {
|
||||
const namespaceName = regExpNamespaceMap.get(regExp);
|
||||
(fieldType as Identifier).namespaceValue =
|
||||
identifierValue.replace(regExp, `${namespaceName}$1`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((fieldType as ContainerType).valueType) {
|
||||
addNamespaceReferValue(
|
||||
(fieldType as ContainerType).valueType as FunctionType,
|
||||
);
|
||||
|
||||
if ((fieldType as MapType).keyType) {
|
||||
addNamespaceReferValue((fieldType as MapType).keyType as FunctionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return addNamespaceReferValue;
|
||||
}
|
||||
|
||||
function getAddNamespaceReferValue(
|
||||
astIncludes: t.IncludeDefinition[],
|
||||
namespace: string,
|
||||
): ((fieldType: FunctionType) => void) | undefined {
|
||||
const filenameNamespaceMap: Record<string, string> = {};
|
||||
|
||||
for (const astInclude of astIncludes) {
|
||||
const idlFilePath = astInclude.path.value;
|
||||
const looseIdlFilePath = idlFilePath.replace(/\.thrift$/, '');
|
||||
const looseFilename = looseIdlFilePath.split('/').pop() as string;
|
||||
// try relative path
|
||||
let looseAbsoluteFilePath = getPosixPath(
|
||||
path.join(path.dirname(entryLooseAbsoluteFilePath), looseIdlFilePath),
|
||||
);
|
||||
|
||||
// try absulote path
|
||||
const alternativeLooseAbsoluteFilePath = looseIdlFilePath;
|
||||
|
||||
let document =
|
||||
fileDocumentMap[looseAbsoluteFilePath] ||
|
||||
fileDocumentMap[alternativeLooseAbsoluteFilePath];
|
||||
if (!document) {
|
||||
let content = '';
|
||||
if (absoluteFileContentMap) {
|
||||
content = absoluteFileContentMap[`${looseAbsoluteFilePath}.thrift`];
|
||||
if (typeof content === 'undefined') {
|
||||
content =
|
||||
absoluteFileContentMap[
|
||||
`${alternativeLooseAbsoluteFilePath}.thrift`
|
||||
];
|
||||
if (typeof content === 'undefined') {
|
||||
logAndThrowError(
|
||||
`file ${looseAbsoluteFilePath}.thrift does not exist in fileContentMap`,
|
||||
);
|
||||
}
|
||||
|
||||
looseAbsoluteFilePath = alternativeLooseAbsoluteFilePath;
|
||||
}
|
||||
} else {
|
||||
let fullFilePath = getPosixPath(
|
||||
path.resolve(rootDir, `${looseAbsoluteFilePath}.thrift`),
|
||||
);
|
||||
|
||||
// Search
|
||||
if (!fs.existsSync(fullFilePath)) {
|
||||
const filePaths = [rootDir, ...searchPaths].map(searchPath =>
|
||||
getPosixPath(
|
||||
path.resolve(
|
||||
rootDir,
|
||||
searchPath,
|
||||
`${alternativeLooseAbsoluteFilePath}.thrift`,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const existedFilePath = filePaths.find(filePath =>
|
||||
fs.existsSync(filePath),
|
||||
);
|
||||
if (typeof existedFilePath === 'undefined') {
|
||||
logAndThrowError(`file ${filePaths[0]} does not exist`);
|
||||
} else {
|
||||
fullFilePath = existedFilePath;
|
||||
looseAbsoluteFilePath = path
|
||||
.relative(rootDir, existedFilePath)
|
||||
.replace(/\.thrift$/, '');
|
||||
}
|
||||
}
|
||||
|
||||
content = fs.readFileSync(fullFilePath, 'utf8');
|
||||
}
|
||||
|
||||
document = parseContent(content, looseAbsoluteFilePath);
|
||||
} else if (!fileDocumentMap[looseAbsoluteFilePath]) {
|
||||
looseAbsoluteFilePath = alternativeLooseAbsoluteFilePath;
|
||||
}
|
||||
|
||||
const astNamespaces: t.NamespaceDefinition[] = [];
|
||||
for (const statement of document.body) {
|
||||
if (statement.type === t.SyntaxType.NamespaceDefinition) {
|
||||
astNamespaces.push(statement as t.NamespaceDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
const { unifyNamespace } = getUnifyNamespace(
|
||||
looseAbsoluteFilePath,
|
||||
astNamespaces,
|
||||
);
|
||||
filenameNamespaceMap[looseFilename] = unifyNamespace;
|
||||
}
|
||||
|
||||
return createAddNamespaceReferValue(filenameNamespaceMap, namespace);
|
||||
}
|
||||
|
||||
function convertTypedefDefinition(
|
||||
astTypedef: TypedefDefinition,
|
||||
): TypedefDefinition {
|
||||
const typedefDefinition: TypedefDefinition = { ...astTypedef };
|
||||
// const { name, definitionType} = typedefDefinition
|
||||
if (typeof addNamespaceValue === 'function') {
|
||||
addNamespaceValue(typedefDefinition.definitionType);
|
||||
addNamespaceValue(typedefDefinition.name);
|
||||
}
|
||||
|
||||
return typedefDefinition;
|
||||
}
|
||||
|
||||
function addNamespaceValueToConstValue(constValue: ConstValue) {
|
||||
if (typeof addNamespaceValue === 'function') {
|
||||
if (constValue.type === SyntaxType.Identifier) {
|
||||
addNamespaceValue(constValue);
|
||||
} else if (constValue.type === SyntaxType.ConstMap) {
|
||||
for (const property of constValue.properties) {
|
||||
addNamespaceValueToConstValue(property.name);
|
||||
addNamespaceValueToConstValue(property.initializer);
|
||||
}
|
||||
} else if (constValue.type === SyntaxType.ConstList) {
|
||||
for (const element of constValue.elements) {
|
||||
addNamespaceValueToConstValue(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function convertConstDefinition(astConst: ConstDefinition): ConstDefinition {
|
||||
const constDefinition: ConstDefinition = { ...astConst };
|
||||
if (typeof addNamespaceValue === 'function') {
|
||||
addNamespaceValue(constDefinition.name);
|
||||
addNamespaceValue(constDefinition.fieldType);
|
||||
addNamespaceValueToConstValue(constDefinition.initializer);
|
||||
}
|
||||
|
||||
return constDefinition;
|
||||
}
|
||||
|
||||
function convertStructDefinition(
|
||||
astStruct: StructDefinition,
|
||||
): StructDefinition {
|
||||
const structDefinition: StructDefinition = { ...astStruct };
|
||||
const { name, fields } = structDefinition;
|
||||
const newName = name;
|
||||
if (addNamespaceValue) {
|
||||
addNamespaceValue(newName);
|
||||
}
|
||||
|
||||
for (const field of fields) {
|
||||
const { fieldType, annotations } = field;
|
||||
const fieldName = field.name.value;
|
||||
if (addNamespaceValue) {
|
||||
addNamespaceValue(fieldType);
|
||||
}
|
||||
|
||||
field.extensionConfig = getFieldExtensionConfig(
|
||||
fieldName,
|
||||
fieldType,
|
||||
annotations,
|
||||
);
|
||||
|
||||
if ((field.extensionConfig.tag || '').includes('omitempty')) {
|
||||
field.requiredness = 'optional';
|
||||
}
|
||||
}
|
||||
|
||||
reviseFieldComments(fields);
|
||||
const newFields: FieldDefinition[] = [];
|
||||
for (const field of fields) {
|
||||
const tag = (field.extensionConfig && field.extensionConfig.tag) || '';
|
||||
if (tag.includes('int2str')) {
|
||||
field.fieldType = convertIntToString(field.fieldType as FieldType);
|
||||
}
|
||||
|
||||
if (ignoreGoTagDash || !tag.includes('ignore')) {
|
||||
newFields.push(field);
|
||||
}
|
||||
}
|
||||
|
||||
structDefinition.fields = newFields;
|
||||
structDefinition.name = newName;
|
||||
return structDefinition;
|
||||
}
|
||||
|
||||
function convertEnumDefinition(astEnum: EnumDefinition): EnumDefinition {
|
||||
const enumDefinition: EnumDefinition = { ...astEnum };
|
||||
const { name, members } = enumDefinition;
|
||||
enumNames.push(name.value);
|
||||
reviseFieldComments(members);
|
||||
if (addNamespaceValue) {
|
||||
addNamespaceValue(name);
|
||||
}
|
||||
|
||||
return enumDefinition;
|
||||
}
|
||||
|
||||
function convertFunctionDefinition(
|
||||
astFunc: FunctionDefinition,
|
||||
): FunctionDefinition {
|
||||
const functionDefinition: FunctionDefinition = { ...astFunc };
|
||||
const { returnType, fields, annotations, name } = functionDefinition;
|
||||
|
||||
if (addNamespaceValue) {
|
||||
addNamespaceValue(name);
|
||||
addNamespaceValue(returnType);
|
||||
for (const field of fields) {
|
||||
addNamespaceValue(field.fieldType);
|
||||
}
|
||||
}
|
||||
|
||||
functionDefinition.extensionConfig = getFuncExtensionConfig(annotations);
|
||||
return functionDefinition;
|
||||
}
|
||||
|
||||
function convertServiceDefinition(
|
||||
astService: ServiceDefinition,
|
||||
): ServiceDefinition {
|
||||
const serviceDefinition: ServiceDefinition = { ...astService };
|
||||
const { annotations, name } = serviceDefinition;
|
||||
const functions: FunctionDefinition[] = [];
|
||||
for (const astFunc of astService.functions) {
|
||||
functions.push(convertFunctionDefinition(astFunc));
|
||||
}
|
||||
|
||||
if (addNamespaceValue) {
|
||||
addNamespaceValue(name);
|
||||
}
|
||||
|
||||
reviseFuncComments(functions);
|
||||
serviceDefinition.functions = functions;
|
||||
serviceDefinition.extensionConfig = getServiceExtensionConfig(annotations);
|
||||
return serviceDefinition;
|
||||
}
|
||||
|
||||
function parseContent(
|
||||
content: string,
|
||||
looseAbsoluteFilePath: string,
|
||||
): t.ThriftDocument {
|
||||
if (fileDocumentMap[looseAbsoluteFilePath]) {
|
||||
return fileDocumentMap[looseAbsoluteFilePath];
|
||||
}
|
||||
|
||||
const document: t.ThriftDocument | t.ThriftErrors = t.parse(content);
|
||||
if ((document as t.ThriftErrors).type === t.SyntaxType.ThriftErrors) {
|
||||
const error = (document as t.ThriftErrors).errors[0];
|
||||
const { start } = error.loc;
|
||||
const fullFilePath = path.resolve(
|
||||
rootDir,
|
||||
`${looseAbsoluteFilePath}.thrift`,
|
||||
);
|
||||
const message = `${error.message}(${fullFilePath}:${start.line}:${start.column})`;
|
||||
logAndThrowError(message, error.message);
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
fileDocumentMap[looseAbsoluteFilePath] = document as t.ThriftDocument;
|
||||
}
|
||||
|
||||
return document as t.ThriftDocument;
|
||||
}
|
||||
|
||||
export function parseThriftContent(
|
||||
content: string,
|
||||
option: {
|
||||
loosePath: string;
|
||||
rootDir: string;
|
||||
cache: boolean;
|
||||
searchPaths: string[];
|
||||
namespaceRefer: boolean;
|
||||
ignoreGoTag: boolean;
|
||||
ignoreGoTagDash: boolean;
|
||||
},
|
||||
fileContentMap?: Record<string, string>,
|
||||
): UnifyDocument {
|
||||
rootDir = option.rootDir;
|
||||
entryLooseAbsoluteFilePath = option.loosePath;
|
||||
cache = option.cache;
|
||||
ignoreGoTag = option.ignoreGoTag;
|
||||
ignoreGoTagDash = option.ignoreGoTagDash;
|
||||
searchPaths = option.searchPaths;
|
||||
absoluteFileContentMap = fileContentMap;
|
||||
addNamespaceValue = undefined;
|
||||
enumNames = [];
|
||||
|
||||
// parse file content
|
||||
const document = parseContent(content, entryLooseAbsoluteFilePath);
|
||||
const statementGroup: Record<string, t.ThriftStatement[]> = {};
|
||||
for (const statement of (document as t.ThriftDocument).body) {
|
||||
let { type } = statement;
|
||||
|
||||
// NOTE: in the latest version of Thrift, union is similar to struct except that all fields are converted to 'optional'.
|
||||
// the idl parse shields the difference, so we can dispose them together.
|
||||
if (type === t.SyntaxType.UnionDefinition) {
|
||||
type = t.SyntaxType.StructDefinition;
|
||||
statement.type = t.SyntaxType.StructDefinition;
|
||||
}
|
||||
|
||||
if (!statementGroup[type]) {
|
||||
statementGroup[type] = [statement];
|
||||
} else {
|
||||
statementGroup[type].push(statement);
|
||||
}
|
||||
}
|
||||
|
||||
const { unifyNamespace, namespace } = getUnifyNamespace(
|
||||
entryLooseAbsoluteFilePath,
|
||||
statementGroup[t.SyntaxType.NamespaceDefinition] as t.NamespaceDefinition[],
|
||||
);
|
||||
if (option.namespaceRefer) {
|
||||
addNamespaceValue = getAddNamespaceReferValue(
|
||||
(statementGroup[t.SyntaxType.IncludeDefinition] ||
|
||||
[]) as t.IncludeDefinition[],
|
||||
unifyNamespace,
|
||||
);
|
||||
}
|
||||
|
||||
const statements: UnifyStatement[] = [];
|
||||
if (statementGroup[t.SyntaxType.TypedefDefinition]) {
|
||||
for (const astTypedef of statementGroup[t.SyntaxType.TypedefDefinition]) {
|
||||
statements.push(convertTypedefDefinition(astTypedef as any));
|
||||
}
|
||||
}
|
||||
|
||||
if (statementGroup[SyntaxType.EnumDefinition]) {
|
||||
for (const astEnum of statementGroup[t.SyntaxType.EnumDefinition]) {
|
||||
statements.push(convertEnumDefinition(astEnum as any));
|
||||
}
|
||||
}
|
||||
|
||||
if (statementGroup[t.SyntaxType.ConstDefinition]) {
|
||||
for (const astConst of statementGroup[t.SyntaxType.ConstDefinition]) {
|
||||
statements.push(convertConstDefinition(astConst as any));
|
||||
}
|
||||
}
|
||||
|
||||
if (statementGroup[SyntaxType.StructDefinition]) {
|
||||
for (const astStruct of statementGroup[t.SyntaxType.StructDefinition]) {
|
||||
statements.push(convertStructDefinition(astStruct as any));
|
||||
}
|
||||
}
|
||||
|
||||
if (statementGroup[SyntaxType.ServiceDefinition]) {
|
||||
for (const astService of statementGroup[t.SyntaxType.ServiceDefinition]) {
|
||||
statements.push(convertServiceDefinition(astService as any));
|
||||
}
|
||||
}
|
||||
|
||||
const includes: string[] = [];
|
||||
if (statementGroup[t.SyntaxType.IncludeDefinition]) {
|
||||
for (const astInclude of statementGroup[t.SyntaxType.IncludeDefinition]) {
|
||||
includes.push((astInclude as t.IncludeDefinition).path.value);
|
||||
}
|
||||
}
|
||||
|
||||
const unifyDocument: UnifyDocument = {
|
||||
type: SyntaxType.UnifyDocument,
|
||||
namespace,
|
||||
unifyNamespace,
|
||||
includes,
|
||||
statements,
|
||||
includeRefer: {},
|
||||
};
|
||||
|
||||
return unifyDocument;
|
||||
}
|
||||
407
frontend/infra/idl/idl-parser/src/unify/type.ts
Normal file
407
frontend/infra/idl/idl-parser/src/unify/type.ts
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
type FieldExtensionConfig,
|
||||
type ServiceExtensionConfig,
|
||||
type FunctionExtensionConfig,
|
||||
} from '../common/extension_type';
|
||||
|
||||
export * from '../common/extension_type';
|
||||
|
||||
export interface Node {
|
||||
type: SyntaxType;
|
||||
}
|
||||
|
||||
export interface SyntaxNode extends Node {
|
||||
loc?: TextLocation;
|
||||
}
|
||||
|
||||
export interface StructLike {
|
||||
name: Identifier;
|
||||
fields: Array<FieldDefinition>;
|
||||
annotations?: Annotations;
|
||||
comments: Array<Comment>;
|
||||
loc: TextLocation;
|
||||
}
|
||||
|
||||
export interface TextLocation {
|
||||
start: TextPosition;
|
||||
end: TextPosition;
|
||||
}
|
||||
|
||||
export interface TextPosition {
|
||||
line: number;
|
||||
column: number;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface Token extends SyntaxNode {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface UnifyDocument extends Node {
|
||||
type: SyntaxType.UnifyDocument;
|
||||
statements: UnifyStatement[];
|
||||
namespace: string;
|
||||
unifyNamespace: string;
|
||||
includes: string[];
|
||||
includeRefer: Record<string, string>;
|
||||
}
|
||||
|
||||
export type UnifyStatement =
|
||||
| EnumDefinition
|
||||
| StructDefinition
|
||||
| TypedefDefinition
|
||||
| ConstDefinition
|
||||
| ServiceDefinition;
|
||||
|
||||
export type CommentType = SyntaxType.CommentLine | SyntaxType.CommentBlock;
|
||||
|
||||
export type Comment = CommentLine | CommentBlock;
|
||||
|
||||
export interface CommentLine extends SyntaxNode {
|
||||
type: SyntaxType.CommentLine;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface CommentBlock extends SyntaxNode {
|
||||
type: SyntaxType.CommentBlock;
|
||||
value: Array<string>;
|
||||
}
|
||||
|
||||
export interface Annotations extends SyntaxNode {
|
||||
annotations: Array<Annotation>;
|
||||
}
|
||||
|
||||
export interface Annotation extends SyntaxNode {
|
||||
name: Identifier;
|
||||
value?: StringLiteral;
|
||||
}
|
||||
|
||||
export interface PrimarySyntax extends SyntaxNode {
|
||||
comments: Array<Comment>;
|
||||
}
|
||||
|
||||
export type FieldType = BaseType | ContainerType | Identifier;
|
||||
|
||||
export type FunctionType = FieldType | VoidType;
|
||||
|
||||
export type KeywordType =
|
||||
| SyntaxType.StringKeyword
|
||||
| SyntaxType.DoubleKeyword
|
||||
| SyntaxType.BoolKeyword
|
||||
| SyntaxType.I8Keyword
|
||||
| SyntaxType.I16Keyword
|
||||
| SyntaxType.I32Keyword
|
||||
| SyntaxType.I64Keyword
|
||||
| SyntaxType.BinaryKeyword
|
||||
| SyntaxType.ByteKeyword;
|
||||
|
||||
export interface VoidType extends SyntaxNode {
|
||||
type: SyntaxType.VoidKeyword;
|
||||
}
|
||||
|
||||
export type ContainerType = SetType | MapType | ListType;
|
||||
|
||||
export interface BaseType extends SyntaxNode {
|
||||
type: KeywordType;
|
||||
annotations?: Annotations;
|
||||
}
|
||||
|
||||
export interface SetType extends SyntaxNode {
|
||||
type: SyntaxType.SetType;
|
||||
valueType: FieldType;
|
||||
annotations?: Annotations;
|
||||
}
|
||||
|
||||
export interface ListType extends SyntaxNode {
|
||||
type: SyntaxType.ListType;
|
||||
valueType: FieldType;
|
||||
annotations?: Annotations;
|
||||
}
|
||||
|
||||
export interface MapType extends SyntaxNode {
|
||||
type: SyntaxType.MapType;
|
||||
keyType: FieldType;
|
||||
valueType: FieldType;
|
||||
annotations?: Annotations;
|
||||
}
|
||||
|
||||
export type ConstValue =
|
||||
| StringLiteral
|
||||
| IntConstant
|
||||
| DoubleConstant
|
||||
| BooleanLiteral
|
||||
| ConstMap
|
||||
| ConstList
|
||||
| Identifier;
|
||||
|
||||
export interface ConstDefinition extends PrimarySyntax {
|
||||
type: SyntaxType.ConstDefinition;
|
||||
name: Identifier;
|
||||
fieldType: FieldType;
|
||||
initializer: ConstValue;
|
||||
annotations?: Annotations;
|
||||
}
|
||||
|
||||
export type FieldRequired = 'required' | 'optional';
|
||||
|
||||
export interface InterfaceWithFields extends PrimarySyntax {
|
||||
name: Identifier;
|
||||
fields: Array<FieldDefinition>;
|
||||
annotations?: Annotations;
|
||||
options?: Record<string, string>;
|
||||
nested?: Record<string, EnumDefinition | StructDefinition>;
|
||||
}
|
||||
|
||||
export interface StructDefinition extends InterfaceWithFields {
|
||||
type: SyntaxType.StructDefinition;
|
||||
}
|
||||
|
||||
export interface FieldDefinition extends PrimarySyntax {
|
||||
type: SyntaxType.FieldDefinition;
|
||||
name: Identifier;
|
||||
fieldID: FieldID | null;
|
||||
fieldType: FunctionType;
|
||||
requiredness?: FieldRequired | null;
|
||||
defaultValue?: ConstValue | null;
|
||||
annotations?: Annotations;
|
||||
options?: Record<string, string>;
|
||||
extensionConfig?: FieldExtensionConfig;
|
||||
}
|
||||
|
||||
export interface FieldID extends SyntaxNode {
|
||||
type: SyntaxType.FieldID;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface EnumDefinition extends PrimarySyntax {
|
||||
type: SyntaxType.EnumDefinition;
|
||||
name: Identifier;
|
||||
members: Array<EnumMember>;
|
||||
annotations?: Annotations;
|
||||
options?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface EnumMember extends PrimarySyntax {
|
||||
type: SyntaxType.EnumMember;
|
||||
name: Identifier;
|
||||
initializer?: IntConstant | null;
|
||||
annotations?: Annotations;
|
||||
}
|
||||
|
||||
export interface TypedefDefinition extends PrimarySyntax {
|
||||
type: SyntaxType.TypedefDefinition;
|
||||
name: Identifier;
|
||||
definitionType: FieldType;
|
||||
annotations?: Annotations;
|
||||
}
|
||||
|
||||
export interface ServiceDefinition extends PrimarySyntax {
|
||||
type: SyntaxType.ServiceDefinition;
|
||||
name: Identifier;
|
||||
extends?: Identifier | null;
|
||||
functions: Array<FunctionDefinition>;
|
||||
annotations?: Annotations;
|
||||
options?: Record<string, string>;
|
||||
extensionConfig?: ServiceExtensionConfig;
|
||||
}
|
||||
|
||||
export interface FunctionDefinition extends PrimarySyntax {
|
||||
type: SyntaxType.FunctionDefinition;
|
||||
name: Identifier;
|
||||
oneway: boolean;
|
||||
returnType: FunctionType;
|
||||
fields: Array<FieldDefinition>;
|
||||
throws: Array<FieldDefinition>;
|
||||
modifiers: Array<Token>;
|
||||
annotations?: Annotations;
|
||||
options?: Record<string, string>;
|
||||
extensionConfig?: FunctionExtensionConfig;
|
||||
}
|
||||
|
||||
export interface ParametersDefinition extends SyntaxNode {
|
||||
type: SyntaxType.ParametersDefinition;
|
||||
fields: Array<FieldDefinition>;
|
||||
}
|
||||
|
||||
export interface StringLiteral extends SyntaxNode {
|
||||
type: SyntaxType.StringLiteral;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface BooleanLiteral extends SyntaxNode {
|
||||
type: SyntaxType.BooleanLiteral;
|
||||
value: boolean;
|
||||
}
|
||||
|
||||
export interface IntegerLiteral extends SyntaxNode {
|
||||
type: SyntaxType.IntegerLiteral;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface HexLiteral extends SyntaxNode {
|
||||
type: SyntaxType.HexLiteral;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface FloatLiteral extends SyntaxNode {
|
||||
type: SyntaxType.FloatLiteral;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ExponentialLiteral extends SyntaxNode {
|
||||
type: SyntaxType.ExponentialLiteral;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface IntConstant extends SyntaxNode {
|
||||
type: SyntaxType.IntConstant;
|
||||
value: IntegerLiteral | HexLiteral;
|
||||
}
|
||||
|
||||
export interface DoubleConstant extends SyntaxNode {
|
||||
type: SyntaxType.DoubleConstant;
|
||||
value: FloatLiteral | ExponentialLiteral;
|
||||
}
|
||||
|
||||
export interface ConstMap extends SyntaxNode {
|
||||
type: SyntaxType.ConstMap;
|
||||
properties: Array<PropertyAssignment>;
|
||||
}
|
||||
|
||||
export interface ConstList extends SyntaxNode {
|
||||
type: SyntaxType.ConstList;
|
||||
elements: Array<ConstValue>;
|
||||
}
|
||||
|
||||
export interface PropertyAssignment extends SyntaxNode {
|
||||
type: SyntaxType.PropertyAssignment;
|
||||
name: ConstValue;
|
||||
initializer: ConstValue;
|
||||
}
|
||||
|
||||
export interface Identifier extends SyntaxNode {
|
||||
type: SyntaxType.Identifier;
|
||||
value: string;
|
||||
|
||||
// value with one level namespace
|
||||
namespaceValue?: string;
|
||||
annotations?: Annotations;
|
||||
}
|
||||
|
||||
export enum SyntaxType {
|
||||
UnifyDocument = 'UnifyDocument',
|
||||
|
||||
Identifier = 'Identifier',
|
||||
FieldID = 'FieldID',
|
||||
|
||||
// Statements
|
||||
ConstDefinition = 'ConstDefinition',
|
||||
StructDefinition = 'StructDefinition',
|
||||
EnumDefinition = 'EnumDefinition',
|
||||
ServiceDefinition = 'ServiceDefinition',
|
||||
TypedefDefinition = 'TypedefDefinition',
|
||||
|
||||
// Fields
|
||||
FieldDefinition = 'FieldDefinition',
|
||||
FunctionDefinition = 'FunctionDefinition',
|
||||
ParametersDefinition = 'ParametersDefinition',
|
||||
|
||||
// Type Annotations
|
||||
FieldType = 'FieldType',
|
||||
BaseType = 'BaseType',
|
||||
SetType = 'SetType',
|
||||
MapType = 'MapType',
|
||||
ListType = 'ListType',
|
||||
|
||||
// Values
|
||||
ConstValue = 'ConstValue',
|
||||
IntConstant = 'IntConstant',
|
||||
DoubleConstant = 'DoubleConstant',
|
||||
|
||||
ConstList = 'ConstList',
|
||||
ConstMap = 'ConstMap',
|
||||
EnumMember = 'EnumMember',
|
||||
|
||||
// Literals
|
||||
CommentLine = 'CommentLine',
|
||||
CommentBlock = 'CommentBlock',
|
||||
StringLiteral = 'StringLiteral',
|
||||
IntegerLiteral = 'IntegerLiteral',
|
||||
FloatLiteral = 'FloatLiteral',
|
||||
HexLiteral = 'HexLiteral',
|
||||
ExponentialLiteral = 'ExponentialLiteral',
|
||||
BooleanLiteral = 'BooleanLiteral',
|
||||
PropertyAssignment = 'PropertyAssignment',
|
||||
|
||||
// Tokens
|
||||
LeftParenToken = 'LeftParenToken',
|
||||
RightParenToken = 'RightParenToken',
|
||||
LeftBraceToken = 'LeftBraceToken',
|
||||
RightBraceToken = 'RightBraceToken',
|
||||
LeftBracketToken = 'LeftBracketToken',
|
||||
RightBracketToken = 'RightBracketToken',
|
||||
CommaToken = 'CommaToken',
|
||||
DotToken = 'DotToken',
|
||||
MinusToken = 'MinusToken',
|
||||
SemicolonToken = 'SemicolonToken',
|
||||
ColonToken = 'ColonToken',
|
||||
StarToken = 'StarToken',
|
||||
EqualToken = 'EqualToken',
|
||||
LessThanToken = 'LessThanToken',
|
||||
GreaterThanToken = 'GreaterThanToken',
|
||||
|
||||
// Keywords
|
||||
NamespaceKeyword = 'NamespaceKeyword',
|
||||
IncludeKeyword = 'IncludeKeyword',
|
||||
CppIncludeKeyword = 'CppIncludeKeyword',
|
||||
ExceptionKeyword = 'ExceptionKeyword',
|
||||
ServiceKeyword = 'ServiceKeyword',
|
||||
ExtendsKeyword = 'ExtendsKeyword',
|
||||
RequiredKeyword = 'RequiredKeyword',
|
||||
OptionalKeyword = 'OptionalKeyword',
|
||||
FalseKeyword = 'FalseKeyword',
|
||||
TrueKeyword = 'TrueKeyword',
|
||||
ConstKeyword = 'ConstKeyword',
|
||||
DoubleKeyword = 'DoubleKeyword',
|
||||
StructKeyword = 'StructKeyword',
|
||||
TypedefKeyword = 'TypedefKeyword',
|
||||
UnionKeyword = 'UnionKeyword',
|
||||
StringKeyword = 'StringKeyword',
|
||||
BinaryKeyword = 'BinaryKeyword',
|
||||
BoolKeyword = 'BoolKeyword',
|
||||
ByteKeyword = 'ByteKeyword',
|
||||
EnumKeyword = 'EnumKeyword',
|
||||
SenumKeyword = 'SenumKeyword',
|
||||
ListKeyword = 'ListKeyword',
|
||||
SetKeyword = 'SetKeyword',
|
||||
MapKeyword = 'MapKeyword',
|
||||
I8Keyword = 'I8Keyword',
|
||||
I16Keyword = 'I16Keyword',
|
||||
I32Keyword = 'I32Keyword',
|
||||
I64Keyword = 'I64Keyword',
|
||||
ThrowsKeyword = 'ThrowsKeyword',
|
||||
VoidKeyword = 'VoidKeyword',
|
||||
OnewayKeyword = 'OnewayKeyword',
|
||||
|
||||
// Other
|
||||
Annotation = 'Annotation',
|
||||
Annotations = 'Annotations',
|
||||
|
||||
EOF = 'EOF',
|
||||
}
|
||||
41
frontend/infra/idl/idl-parser/src/unify/util.ts
Normal file
41
frontend/infra/idl/idl-parser/src/unify/util.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SyntaxType, type ContainerType, type MapType, type FieldType } from './type';
|
||||
|
||||
export function convertIntToString(fType: FieldType): FieldType {
|
||||
const fieldType = { ...fType };
|
||||
const intTypes = [
|
||||
SyntaxType.I8Keyword,
|
||||
SyntaxType.I16Keyword,
|
||||
SyntaxType.I32Keyword,
|
||||
SyntaxType.I64Keyword,
|
||||
];
|
||||
if (intTypes.includes(fieldType.type)) {
|
||||
fieldType.type = SyntaxType.StringKeyword;
|
||||
} else if ((fieldType as ContainerType).valueType) {
|
||||
(fieldType as ContainerType).valueType = convertIntToString(
|
||||
(fieldType as ContainerType).valueType,
|
||||
);
|
||||
if ((fieldType as MapType).keyType) {
|
||||
(fieldType as MapType).keyType = convertIntToString(
|
||||
(fieldType as MapType).keyType,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return fieldType;
|
||||
}
|
||||
Reference in New Issue
Block a user