Skip to content

Commit 6f805a8

Browse files
committed
Feature(compiler): Implement function declaration on the fly
1 parent a6b7ac9 commit 6f805a8

8 files changed

Lines changed: 118 additions & 48 deletions

File tree

src/backend/error/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
export {default} from './unsupported.error';
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
import * as ts from 'typescript';
3+
import {DiagnosticCategory} from 'typescript';
4+
5+
export default class UnsupportedError extends Error {
6+
protected node: ts.Node;
7+
8+
constructor(node: ts.Node, message: string) {
9+
super(message);
10+
11+
this.node = node;
12+
}
13+
14+
public toDiagnostic(): ts.Diagnostic {
15+
return {
16+
category: DiagnosticCategory.Error,
17+
code: 1000000,
18+
file: this.node.getSourceFile(),
19+
start: this.node.getStart(),
20+
length: this.node.getWidth(),
21+
messageText: this.message
22+
}
23+
}
24+
}

src/backend/llvm/c.mangler.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
import * as ts from 'typescript';
3+
4+
export class CMangler {
5+
static getFunctionName(name: string, parameters: ts.NodeArray<ts.ParameterDeclaration>): string {
6+
return name;
7+
}
8+
}

src/backend/llvm/cpp.mangler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export class CPPMangler {
99
switch (parameter.type.kind) {
1010
case ts.SyntaxKind.NumberKeyword:
1111
return 'd';
12+
case ts.SyntaxKind.StringKeyword:
13+
return 'c';
1214
default:
1315
throw new Error(
1416
`Unsupported mangling parameter type: ${parameter.type.kind}`

src/backend/llvm/index.ts

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import {Context} from "./context";
66
import {NativeTypeResolver} from "./native-type-resolver";
77
import UnsupportedError from "../error/unsupported.error";
88
import {NativeType} from "./native-type";
9+
import {RUNTIME_DEFINITION_FILE} from "@static-script/runtime";
10+
import {LANGUAGE_DEFINITION_FILE} from "../../constants";
11+
import {CMangler} from "./c.mangler";
12+
import {ManglerInterface} from "./mangler.interface";
913

1014
export function passReturnStatement(parent: ts.ReturnStatement, ctx: Context, builder: llvm.IRBuilder) {
1115
if (!parent.expression) {
@@ -162,6 +166,35 @@ function buildFromCallExpression(
162166
);
163167
}
164168

169+
function declareFunctionFromDefinition(
170+
stmt: ts.FunctionDeclaration,
171+
ctx: Context,
172+
builder: llvm.IRBuilder,
173+
mangler: ManglerInterface
174+
): llvm.Function {
175+
let fnType = llvm.FunctionType.get(
176+
stmt.type ? NativeTypeResolver.getType(ctx.typeChecker.getTypeFromTypeNode(stmt.type), ctx).getType() : llvm.Type.getVoidTy(ctx.llvmContext),
177+
stmt.parameters.map((parameters) => {
178+
if (parameters.type) {
179+
return NativeTypeResolver.getType(ctx.typeChecker.getTypeFromTypeNode(parameters.type), ctx).getType()
180+
}
181+
182+
throw new UnsupportedError(
183+
stmt,
184+
`Unsupported parameter`
185+
);
186+
}),
187+
false
188+
);
189+
190+
return llvm.Function.create(
191+
fnType,
192+
llvm.LinkageTypes.ExternalLinkage,
193+
mangler.getFunctionName(<string>stmt.name.escapedText, stmt.parameters),
194+
ctx.llvmModule
195+
);
196+
}
197+
165198
function buildFromIdentifier(identifier: ts.Identifier, ctx: Context, builder: llvm.IRBuilder): llvm.Value {
166199
const variable = ctx.scope.variables.get(<string>identifier.escapedText);
167200
if (variable) {
@@ -183,14 +216,24 @@ function buildFromIdentifier(identifier: ts.Identifier, ctx: Context, builder: l
183216

184217
const symbolDeclaration = <ts.FunctionDeclaration>symbol.declarations[0];
185218
if (symbolDeclaration.name) {
186-
const mangledFunctionName = CPPMangler.getFunctionName(
187-
<string>symbolDeclaration.name.escapedText,
188-
symbolDeclaration.parameters
189-
);
219+
const sourceFile = symbolDeclaration.getSourceFile();
220+
221+
if (sourceFile.fileName === RUNTIME_DEFINITION_FILE) {
222+
return declareFunctionFromDefinition(
223+
symbolDeclaration,
224+
ctx,
225+
builder,
226+
CPPMangler
227+
);
228+
}
190229

191-
const fn = ctx.llvmModule.getFunction(mangledFunctionName);
192-
if (fn) {
193-
return fn;
230+
if (sourceFile.fileName === LANGUAGE_DEFINITION_FILE) {
231+
return declareFunctionFromDefinition(
232+
symbolDeclaration,
233+
ctx,
234+
builder,
235+
CMangler
236+
);
194237
}
195238
}
196239
}
@@ -332,33 +375,16 @@ export function generateModuleFromProgram(program: ts.Program): llvm.Module {
332375
program.getTypeChecker()
333376
);
334377

335-
let putsFnType = llvm.FunctionType.get(llvm.Type.getInt32Ty(ctx.llvmContext), [
336-
llvm.Type.getInt8PtrTy(ctx.llvmContext)
337-
], false);
338-
ctx.llvmModule.getOrInsertFunction('puts', putsFnType);
339-
340-
let number2stringFnType = llvm.FunctionType.get(llvm.Type.getInt8PtrTy(ctx.llvmContext), [
341-
llvm.Type.getDoubleTy(ctx.llvmContext)
342-
], false);
343-
llvm.Function.create(
344-
number2stringFnType,
345-
llvm.LinkageTypes.ExternalLinkage,
346-
CPPMangler.getFunctionName("number2string", <any>[
347-
{
348-
type: { kind: ts.SyntaxKind.NumberKeyword }
349-
}
350-
]),
351-
ctx.llvmModule
352-
);
353-
354378
let mainFnType = llvm.FunctionType.get(llvm.Type.getVoidTy(ctx.llvmContext), false);
355379
let mainFn = llvm.Function.create(mainFnType, llvm.LinkageTypes.ExternalLinkage, "main", ctx.llvmModule);
356380

357381
let block = llvm.BasicBlock.create(ctx.llvmContext, "Entry", mainFn);
358382
let builder = new llvm.IRBuilder(block);
359383

360384
for (const sourceFile of program.getSourceFiles()) {
361-
sourceFile.forEachChild((node: ts.Node) => passNode(node, ctx, builder))
385+
if (!sourceFile.isDeclarationFile) {
386+
sourceFile.forEachChild((node: ts.Node) => passNode(node, ctx, builder))
387+
}
362388
}
363389

364390
builder.createRetVoid();
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//
2-
// export interface ManglerInterface {
3-
// getFunctionName(name: string): string;
4-
// }
1+
2+
import * as ts from "typescript";
3+
4+
export interface ManglerInterface {
5+
getFunctionName(name: string, parameters: ts.NodeArray<ts.ParameterDeclaration>): string;
6+
}

src/backend/llvm/native-type-resolver.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,27 @@ import {NativeType} from "./native-type";
66

77
export class NativeTypeResolver {
88
static getType(type: ts.Type, ctx: Context): NativeType {
9-
if (type.isLiteral()) {
10-
if (type.isNumberLiteral()) {
11-
return new NativeType(
12-
llvm.Type.getDoubleTy(
13-
ctx.llvmContext
14-
)
15-
);
16-
}
9+
if (type.isNumberLiteral() || (<any>type).intrinsicName === 'number') {
10+
return new NativeType(
11+
llvm.Type.getDoubleTy(
12+
ctx.llvmContext
13+
)
14+
);
15+
}
1716

18-
if (type.isStringLiteral()) {
19-
return new NativeType(
20-
llvm.Type.getInt8PtrTy(
21-
ctx.llvmContext
22-
)
23-
);
24-
}
17+
if (type.isStringLiteral() || (<any>type).intrinsicName === 'string') {
18+
return new NativeType(
19+
llvm.Type.getInt8PtrTy(
20+
ctx.llvmContext
21+
)
22+
);
23+
}
2524

26-
throw new Error(
27-
`Unsupported literal type`
25+
if ((<any>type).intrinsicName === 'void') {
26+
return new NativeType(
27+
llvm.Type.getVoidTy(
28+
ctx.llvmContext
29+
)
2830
);
2931
}
3032

src/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
import * as path from "path";
3+
4+
export const LANGUAGE_DEFINITION_FILE = path.join(__dirname, '..', 'staticscript.d.ts');

0 commit comments

Comments
 (0)