Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS-16 Sir to Go #4

Open
wants to merge 1 commit into
base: sir-to-ts-compiler
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { ILexingError, IRecognitionException } from "chevrotain";
import { Command } from "commander";
import { readFileSync, writeFileSync } from "fs";
import { formatGoFile } from "./generator/common/go/index.js";
import { Extension } from "./generator/common/types.js";
import { generateSia, getExtension } from "./generator/index.js";
import { compile } from "./index.js";
import { logError } from "./utils/log.js";
Expand Down Expand Up @@ -48,6 +50,11 @@ program
const newFileName = file.replace(".sia", `.${extension}`);
const generatedSia = await generateSia(sir, extension);
writeFileSync(newFileName, generatedSia);

if (extension === Extension.GO) {
formatGoFile(newFileName);
}

console.info(`Sia file written to ${newFileName}`);
} catch (error) {
logError(src, file, error as ILexingError | IRecognitionException);
Expand Down
207 changes: 207 additions & 0 deletions src/generator/common/go/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import { exec } from "child_process";
import { promisify } from "util";
import { FieldDefinition, SchemaDefinition } from "../../../visitor.js";
import { getStringTypeFromLength, isAnyString, isByteArray } from "../index.js";
import { SiaType } from "../types.js";
import {
siaTypeArraySizeFunctionMap,
siaTypeFunctionMap,
siaTypeSerializerArrayItemMap,
} from "./maps.js";
import {
capitalizeFirstLetter,
createAttributeString,
createCustomSerializerFunctionCallString,
createIfConditionString,
createSiaAddTypeFunctionCallString,
generateTypeFieldString,
} from "./strings.js";

const makeArrayType = (type: string) => {
return `[]${type}`;
};

export const hasBigInt = (schemas: SchemaDefinition[]): boolean => {
return schemas.some((schema) =>
schema.fields.some((field) => field.type.startsWith("bigint")),
);
};

const getGoType = (fieldType: string, isArray: boolean): string => {
if (isAnyString(fieldType as SiaType)) {
return isArray ? makeArrayType("string") : "string";
}
if (fieldType.startsWith("byte")) {
return isArray ? makeArrayType("byte") : "Buffer";
}
if (fieldType.startsWith("bigint")) {
return isArray ? makeArrayType("big.Int") : "big.Int";
}
return isArray ? makeArrayType(fieldType) : fieldType;
};

export const generateInterfaceField = (field: FieldDefinition) => {
const type = getGoType(field.type, Boolean(field.isArray));
return generateTypeFieldString(field.name, type, field.optional);
};

export const generateTypeFields = (fields: FieldDefinition[]) => {
return fields.map((field) => {
return generateInterfaceField(field);
});
};

export const getDefaultValueForType = (
field: SchemaDefinition["fields"][0],
): string => {
if (field.optional) {
return "nil";
}
if (field.isArray) {
if (isAnyString(field.type as SiaType)) {
return `make([]string, 0)`;
}
if (isByteArray(field.type as SiaType)) {
return `make([]byte, 0)`;
}
if (field.type.startsWith("bigint")) {
return `make([]big.Int, 0)`;
}
return `make([]${field.type}, 0)`;
}
if (field.defaultValue) {
return `"${field.defaultValue}"`;
}
if (field.type.startsWith("int") || field.type.startsWith("uint")) {
return "0";
}
if (field.type === "bool") {
return "false";
}
return '""';
};

export const generateAttribute = (
field: FieldDefinition,
schemas: SchemaDefinition[],
): string => {
if (Object.values(SiaType).includes(field.type as SiaType)) {
return createAttributeString(field.name, getDefaultValueForType(field));
} else {
return generateNestedObjectAttribute(field, schemas);
}
};

export const generateNestedObjectAttribute = (
field: SchemaDefinition["fields"][0],
schemas: SchemaDefinition[],
): string => {
const referencedSchema = schemas.find((s) => s.name === field.type);
if (!referencedSchema) {
throw new Error(`Referenced schema ${field.type} not found`);
}

const nestedFields = referencedSchema.fields
.map((nestedField) => generateAttribute(nestedField, schemas))
.join("\n");

return createAttributeString(
field.type,
`${field.optional ? "&" : ""}${field.type}{\n${nestedFields}\n}`,
);
};

const execAsync = promisify(exec);

const isGoInstalled = async (): Promise<boolean> => {
try {
await execAsync("go version");
return true;
} catch {
return false;
}
};

export const formatGoFile = async (filePath: string) => {
if (await isGoInstalled()) {
await execAsync(`go fmt ${filePath}`);
console.info("Go file formatted");
} else {
console.warn("Go is not installed. Skipping formatting...");
}
};

export const generateArraySerializer = (
fieldType: SiaType,
fieldName: string,
arraySize?: number,
) => {
if (isByteArray(fieldType)) {
return createSiaAddTypeFunctionCallString(
siaTypeFunctionMap[fieldType],
fieldName,
);
} else {
const serializer =
siaTypeSerializerArrayItemMap[
fieldType as keyof typeof siaTypeSerializerArrayItemMap
];
return createSiaAddTypeFunctionCallString(
arraySize
? siaTypeArraySizeFunctionMap[arraySize]
: siaTypeArraySizeFunctionMap[8],
fieldName,
serializer,
);
}
};

export const generateSchemaFunctionBody = (fields: FieldDefinition[]) => {
let fnBody = "";

fields.forEach((field) => {
let fieldType = field.type as SiaType;
const fieldName = `obj.${capitalizeFirstLetter(field.name)}`;

if (fieldType === SiaType.String) {
fieldType = getStringTypeFromLength(field.max);
}

let serializer = "";

if (field.isArray) {
serializer = generateArraySerializer(
fieldType,
`${field.optional ? "*" : ""}${fieldName}`,
field.arraySize,
);
} else if (isAnyString(fieldType) && field.encoding === "ascii") {
serializer = createSiaAddTypeFunctionCallString(
"AddAscii",
`${field.optional ? "*" : ""}${fieldName}`,
);
} else if (!Object.values(SiaType).includes(fieldType)) {
serializer = createCustomSerializerFunctionCallString(
fieldType,
"sia",
`${field.optional ? "" : "&"}${fieldName}`,
);
} else {
const fn = siaTypeFunctionMap[fieldType];
if (fn) {
serializer = createSiaAddTypeFunctionCallString(
fn,
`${field.optional ? "*" : ""}${fieldName}`,
);
}
}

if (field.optional) {
fnBody += createIfConditionString(`${fieldName} != nil`, serializer);
} else {
fnBody += serializer;
}
});

return fnBody;
};
52 changes: 52 additions & 0 deletions src/generator/common/go/maps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { SiaType } from "../types.js";

export const siaTypeFunctionMap: Record<SiaType, string> = {
[SiaType.Int8]: "AddInt8",
[SiaType.Int16]: "AddInt16",
[SiaType.Int32]: "AddInt32",
[SiaType.Int64]: "AddInt64",
[SiaType.UInt8]: "AddUInt8",
[SiaType.UInt16]: "AddUInt16",
[SiaType.UInt32]: "AddUInt32",
[SiaType.UInt64]: "AddUInt64",
[SiaType.String]: "AddStringN",
[SiaType.String8]: "AddString8",
[SiaType.String16]: "AddString16",
[SiaType.String32]: "AddString32",
[SiaType.String64]: "AddString64",
[SiaType.ByteArray8]: "AddByteArray8",
[SiaType.ByteArray16]: "AddByteArray16",
[SiaType.ByteArray32]: "AddByteArray32",
[SiaType.ByteArray64]: "AddByteArray64",
[SiaType.Bool]: "AddBool",
[SiaType.BigInt]: "AddBigInt",
};

export const siaTypeSerializerArrayItemMap: Record<SiaType, string> = {
[SiaType.Int8]: "SerializeInt8ArrayItem",
[SiaType.Int16]: "SerializeInt16ArrayItem",
[SiaType.Int32]: "SerializeInt32ArrayItem",
[SiaType.Int64]: "SerializeInt64ArrayItem",
[SiaType.UInt8]: "SerializeUInt8ArrayItem",
[SiaType.UInt16]: "SerializeUInt16ArrayItem",
[SiaType.UInt32]: "SerializeUInt32ArrayItem",
[SiaType.UInt64]: "SerializeUInt64ArrayItem",
[SiaType.String]: "SerializeStringArrayItem",
[SiaType.String8]: "SerializeString8ArrayItem",
[SiaType.String16]: "SerializeString16ArrayItem",
[SiaType.String32]: "SerializeString32ArrayItem",
[SiaType.String64]: "SerializeString64ArrayItem",
[SiaType.ByteArray8]: "SerializeByteArray8ArrayItem",
[SiaType.ByteArray16]: "SerializeByteArray16ArrayItem",
[SiaType.ByteArray32]: "SerializeByteArray32ArrayItem",
[SiaType.ByteArray64]: "SerializeByteArray64ArrayItem",
[SiaType.Bool]: "SerializeBoolArrayItem",
[SiaType.BigInt]: "SerializeBigIntArrayItem",
};

export const siaTypeArraySizeFunctionMap: Record<number, string> = {
[8]: "AddArray8",
[16]: "AddArray16",
[32]: "AddArray32",
[64]: "AddArray64",
};
83 changes: 83 additions & 0 deletions src/generator/common/go/strings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
export const capitalizeFirstLetter = (str: string) => {
return str.charAt(0).toUpperCase() + str.slice(1);
};

export const createSiaImportString = (hasBigInt: boolean) => {
let importString = "import (\n";
if (hasBigInt) {
importString += '"math/big"\n';
}
importString += 'sializer "github.com/pouya-eghbali/go-sia/v2/pkg"\n';

return importString + ")";
};

export const generateTypeString = (typeName: string, body: string) => {
return `type ${typeName} struct {\n${body}\n}`;
};

export const generateTypeFieldString = (
name: string,
type: string,
optional: boolean = false,
) => {
return `${capitalizeFirstLetter(name)} ${optional ? "*" : ""}${type}`;
};

export const createAttributeString = (name: string, value: string) => {
return `${capitalizeFirstLetter(name)}: ${value},`;
};

export const createNamedObjectString = (
name: string,
body: string,
type: string,
) => {
return `var ${name} = ${type}{\n${body}\n};\n`;
};

export const createSiaInstanceString = (schemaName: string) => {
return `var ${schemaName.toLowerCase()}Sia = sializer.New()\n`;
};

export const createSiaAddTypeFunctionCallString = (
fn: string,
fieldName: string,
serializer?: string,
) => {
const serializerArg = serializer ? `, sializer.${serializer}` : "";
return `sia.${fn}(${fieldName}${serializerArg})\n`;
};

export const createIfConditionString = (condition: string, body: string) => {
return `if ${condition} {\n${body}}\n`;
};

export const createCustomSerializerFunctionCallString = (
serializer: string,
siaInstance: string,
fieldName: string,
) => {
return `serialize${capitalizeFirstLetter(serializer)}(${siaInstance}, ${fieldName})\n`;
};

export const createCustomSerializerFunctionDeclarationString = (
fnName: string,
signature: string,
body: string,
) => {
return `func ${fnName}(${signature}) *sializer.Sia {\n${body}return sia\n}\n`;
};

export const createSiaResultString = (
schemaName: string,
instanceName: string,
) => {
return `
func main() {
serialize${schemaName}(${instanceName}, &${schemaName.toLowerCase()})
result := ${instanceName}.Content()
_ = result
}
`;
};
1 change: 1 addition & 0 deletions src/generator/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export enum SiaType {
export enum Extension {
JS = "js",
TS = "ts",
GO = "go",
}

export interface Generator {
Expand Down
Loading