First Commit
Yes, that is what I do at 31.12.2021 at 22:39 local time...
This commit is contained in:
232
src/ir.ts
Normal file
232
src/ir.ts
Normal file
@ -0,0 +1,232 @@
|
||||
import type { Parsed, StatementNode } from "./parser";
|
||||
import dbg from "debug";
|
||||
const log = dbg("app");
|
||||
|
||||
const builtin = ["number", "string", "boolean"];
|
||||
|
||||
export class IRError extends Error {
|
||||
constructor(public statement: StatementNode, message: string) {
|
||||
super("Error Compiling: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
export interface TypeFieldDefinition {
|
||||
name: string;
|
||||
type: string;
|
||||
array: boolean;
|
||||
map?: string;
|
||||
}
|
||||
|
||||
export interface TypeDefinition {
|
||||
name: string;
|
||||
depends: string[];
|
||||
fields: TypeFieldDefinition[];
|
||||
}
|
||||
|
||||
export interface EnumValueDefinition {
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface EnumDefinition {
|
||||
name: string;
|
||||
values: EnumValueDefinition[];
|
||||
}
|
||||
|
||||
export interface ServiceFunctionParamsDefinition {
|
||||
name: string;
|
||||
inputs: { type: string; name: string }[];
|
||||
return: string | undefined;
|
||||
}
|
||||
export type ServiceFunctionDefinition = ServiceFunctionParamsDefinition;
|
||||
|
||||
export interface ServiceDefinition {
|
||||
name: string;
|
||||
depends: string[];
|
||||
functions: ServiceFunctionDefinition[];
|
||||
}
|
||||
|
||||
export type Step = [
|
||||
"type" | "enum" | "service",
|
||||
TypeDefinition | EnumDefinition | ServiceDefinition
|
||||
];
|
||||
|
||||
export type IR = Step[];
|
||||
|
||||
export default function get_ir(parsed: Parsed): IR {
|
||||
log("Generatie IR from parse output");
|
||||
let defined: string[] = [];
|
||||
let types: string[] = [];
|
||||
let enums: string[] = [];
|
||||
|
||||
// Verifiy and generating steps
|
||||
|
||||
let steps: Step[] = [];
|
||||
|
||||
parsed.forEach((statement) => {
|
||||
log("Working on statement of type %s", statement.type);
|
||||
if (statement.type == "import")
|
||||
throw new IRError(
|
||||
statement,
|
||||
"Import statements are invalid at this step!"
|
||||
);
|
||||
|
||||
if (statement.type === "type") {
|
||||
if (defined.indexOf(statement.name) >= 0) {
|
||||
throw new IRError(
|
||||
statement,
|
||||
`Type ${statement.name} already defined!`
|
||||
);
|
||||
}
|
||||
|
||||
let depends: string[] = [];
|
||||
const fields = statement.fields.map<TypeFieldDefinition>((field) => {
|
||||
if (field.type !== "type_field") {
|
||||
throw new IRError(field, "Invalid statement!");
|
||||
}
|
||||
|
||||
if (defined.indexOf(field.fieldtype) < 0) {
|
||||
if (builtin.indexOf(field.fieldtype) < 0) {
|
||||
throw new IRError(
|
||||
field,
|
||||
`Type ${field.fieldtype} is not defined!`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (depends.indexOf(field.fieldtype) < 0)
|
||||
depends.push(field.fieldtype);
|
||||
}
|
||||
|
||||
if (
|
||||
field.map &&
|
||||
field.map !== "number" &&
|
||||
field.map !== "string"
|
||||
) {
|
||||
throw new IRError(
|
||||
field,
|
||||
`Type ${field.map} is not valid as map key!`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
name: field.name,
|
||||
type: field.fieldtype,
|
||||
array: field.array,
|
||||
map: field.map,
|
||||
};
|
||||
});
|
||||
steps.push([
|
||||
statement.type,
|
||||
{
|
||||
name: statement.name,
|
||||
depends,
|
||||
fields,
|
||||
},
|
||||
]);
|
||||
defined.push(statement.name);
|
||||
types.push(statement.name);
|
||||
} else if (statement.type === "enum") {
|
||||
if (defined.indexOf(statement.name) >= 0) {
|
||||
throw new IRError(
|
||||
statement,
|
||||
`Type ${statement.name} already defined!`
|
||||
);
|
||||
}
|
||||
|
||||
let last = -1;
|
||||
let values = statement.values.map<EnumValueDefinition>((valueS) => {
|
||||
let value = last + 1;
|
||||
if (valueS.value) {
|
||||
if (valueS.value <= last) {
|
||||
throw new IRError(
|
||||
statement,
|
||||
"Enum value must be larger than the previous one!"
|
||||
);
|
||||
} else {
|
||||
value = valueS.value;
|
||||
}
|
||||
}
|
||||
|
||||
last = value;
|
||||
|
||||
return {
|
||||
name: valueS.name,
|
||||
value,
|
||||
};
|
||||
});
|
||||
steps.push([
|
||||
"enum",
|
||||
{
|
||||
name: statement.name,
|
||||
values,
|
||||
} as EnumDefinition,
|
||||
]);
|
||||
defined.push(statement.name);
|
||||
enums.push(statement.name);
|
||||
} else if (statement.type === "service") {
|
||||
if (defined.indexOf(statement.name) >= 0) {
|
||||
throw new IRError(
|
||||
statement,
|
||||
`Type ${statement.name} already defined!`
|
||||
);
|
||||
}
|
||||
|
||||
let depends: string[] = [];
|
||||
let alreadyFoundFunctions = new Set<string>();
|
||||
let functions = statement.functions.map((fnc) => {
|
||||
if (alreadyFoundFunctions.has(fnc.name))
|
||||
throw new IRError(
|
||||
fnc,
|
||||
`Function with name ${fnc.name} already defined!`
|
||||
);
|
||||
alreadyFoundFunctions.add(fnc.name);
|
||||
if (fnc.return_type) {
|
||||
if (defined.indexOf(fnc.return_type) >= 0) {
|
||||
if (!depends.some((a) => a === fnc.return_type))
|
||||
depends.push(fnc.return_type);
|
||||
} else {
|
||||
if (builtin.indexOf(fnc.return_type) < 0) {
|
||||
throw new IRError(
|
||||
fnc,
|
||||
`Type ${fnc.return_type} is not defined`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const input of fnc.inputs) {
|
||||
if (defined.indexOf(input.type) >= 0) {
|
||||
if (!depends.some((a) => a === input.type))
|
||||
depends.push(input.type);
|
||||
} else {
|
||||
if (builtin.indexOf(input.type) < 0) {
|
||||
throw new IRError(
|
||||
fnc,
|
||||
`Type ${input.type} is not defined`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: fnc.name,
|
||||
inputs: fnc.inputs,
|
||||
return: fnc.return_type,
|
||||
} as ServiceFunctionDefinition;
|
||||
});
|
||||
|
||||
steps.push([
|
||||
"service",
|
||||
{
|
||||
name: statement.name,
|
||||
depends,
|
||||
functions,
|
||||
} as ServiceDefinition,
|
||||
]);
|
||||
} else {
|
||||
throw new IRError(statement, "Invalid statement!");
|
||||
}
|
||||
});
|
||||
|
||||
return steps;
|
||||
}
|
Reference in New Issue
Block a user