Skip to content

Commit

Permalink
Allow functions to return nothing
Browse files Browse the repository at this point in the history
  • Loading branch information
AjaniBilby committed Mar 23, 2024
1 parent fb64665 commit f5ac04d
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 36 deletions.
2 changes: 1 addition & 1 deletion source/bnf/syntax.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ block ::= %( "{" w* ) block_stmt* %( w* "}" w* ) ;
func_call ::= access func_call_body;
func_call_body ::= %( w* "(" w* ) ( expr %w* ( %( "," w* ) expr %w* )* )? %( ")" w* ) ;

return ::= %"return" "_call"? %w+ expr? %( ";" w* );
return ::= %"return" "_call"? ( %w+ expr)? %( w* ";" w* );
raise ::= %"raise" %w+ expr %( ";" w* ); # TODO rename to lift
# drop ::= %"drop" %w+ expr %( ";" w* );

Expand Down
11 changes: 10 additions & 1 deletion source/bnf/syntax.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,16 @@ export type Term_Return = {
ref: _Shared.ReferenceRange,
value: [
{ type: '(...)?', value: [] | [_Literal & {value: "\x5fcall"}], start: number, end: number, count: number, ref: _Shared.ReferenceRange },
{ type: '(...)?', value: [] | [Term_Expr], start: number, end: number, count: number, ref: _Shared.ReferenceRange }
{ type: '(...)?', value: [] | [{
type: '(...)',
start: number,
end: number,
count: number,
ref: _Shared.ReferenceRange,
value: [
Term_Expr
]
}], start: number, end: number, count: number, ref: _Shared.ReferenceRange }
]
}
export declare function Parse_Return (i: string, refMapping?: boolean): _Shared.ParseError | {
Expand Down
2 changes: 1 addition & 1 deletion source/bnf/syntax.js

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions source/compiler/codegen/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ReferenceRange } from "~/parser.ts";
import { CompileExpr } from "~/compiler/codegen/expression/index.ts";
import { Variable } from "~/compiler/codegen/variable.ts";
import { Block } from "~/wasm/instruction/control-flow.ts";
import { VirtualType } from "~/compiler/intrinsic.ts";

export class Context {
file: File;
Expand Down Expand Up @@ -281,7 +282,7 @@ function CompileStatement(ctx: Context, syntax: Syntax.Term_Statement) {


function CompileReturn(ctx: Context, syntax: Syntax.Term_Return): typeof never {
const maybe_expr = syntax.value[1].value[0];
const maybe_expr = syntax.value[1].value[0]?.value[0];
const isTail = syntax.value[0].value.length > 0;
const ref = syntax.ref;

Expand All @@ -291,7 +292,7 @@ function CompileReturn(ctx: Context, syntax: Syntax.Term_Return): typeof never {
);

// Guard: return none
if (ctx.function.returns.length === 0) {
if (ctx.function.returns instanceof VirtualType) {
if (maybe_expr) Panic(
`${colors.red("Error")}: This function should have no return value\n`,
{ path: ctx.file.path, name: ctx.file.name, ref }
Expand All @@ -300,7 +301,7 @@ function CompileReturn(ctx: Context, syntax: Syntax.Term_Return): typeof never {
ctx.scope.cleanup(true);
ctx.block.push(Instruction.return());
ctx.done = true;
return never;
return ctx.function.returns;
}

if (!maybe_expr) Panic(
Expand Down
2 changes: 1 addition & 1 deletion source/compiler/codegen/expression/operand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function CompileName(ctx: Context, syntax: Syntax.Term_Name) {
const variable = ctx.scope.getVariable(name, true);
if (!variable) {
const found = ctx.file.access(name);
if (found === null) Panic(`${colors.red("Error")}: Undeclared term ${name}\n`, {
if (found === null) Panic(`${colors.red("Error")}: Undeclared term ${colors.cyan(name)}\n`, {
path: ctx.file.path, name: ctx.file.name, ref: syntax.ref
});

Expand Down
4 changes: 3 additions & 1 deletion source/compiler/codegen/expression/postfix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ function CompileCall(ctx: Context, syntax: Syntax.Term_Expr_call, operand: Opera
const stackReg = ctx.file.owner.project.stackReg.ref;
let returnType: VirtualType | IntrinsicValue | LinearType = none;

if (operand.returns.length == 1) {
if (operand.returns instanceof VirtualType) {
returnType = operand.returns;
} else if (operand.returns.length == 1) {
const primary = operand.returns[0];
if (primary.type instanceof IntrinsicType) {
returnType = primary.type.value;
Expand Down
6 changes: 4 additions & 2 deletions source/compiler/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
import type Package from "./package.ts";
import type { Term_Access, Term_Function, Term_Program, Term_Structure } from "~/bnf/syntax.d.ts";

import { IntrinsicType, bool, u8, i8, u16, i16, i32, i64, u32, u64, f32, f64 } from "~/compiler/intrinsic.ts";
import { IntrinsicType, bool, u8, i8, u16, i16, i32, i64, u32, u64, f32, f64, none, never } from "~/compiler/intrinsic.ts";
import { AssertUnreachable, FlatAccess, FlattenAccess } from "~/helper.ts";
import { VirtualType } from "~/compiler/intrinsic.ts";
import { Parse } from "~/parser.ts";
import Structure from "~/compiler/structure.ts";
import Function from "~/compiler/function.ts";
import Global from "~/compiler/global.ts";
import Import from "~/compiler/import.ts";

export type Namespace = Function | Import | Global | Structure | IntrinsicType ;
export type Namespace = Function | Import | Global | Structure | IntrinsicType | VirtualType ;

// deno-lint-ignore no-explicit-any
export function IsNamespace(val: any): val is Namespace {
Expand All @@ -37,6 +38,7 @@ export class File {
this.path = path;

this.namespace = {
none, never,
bool, // virtual native types
u8, i8, u16, i16, // virtual native types
i32, i64, u32, u64, // native int types
Expand Down
74 changes: 48 additions & 26 deletions source/compiler/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import type { File, Namespace } from "./file.ts";

import { ReferenceRange, SourceView } from "~/parser.ts";
import { IsSolidType, SolidType } from "~/compiler/codegen/expression/type.ts";
import { IntrinsicType } from "~/compiler/intrinsic.ts";
import { IntrinsicType, VirtualType, never, none } from "~/compiler/intrinsic.ts";
import { Context } from "~/compiler/codegen/context.ts";
import { FuncRef } from "~/wasm/funcRef.ts";
import { Scope } from "~/compiler/codegen/scope.ts";
import { Panic } from "~/helper.ts";
import Structure from "~/compiler/structure.ts";


class Argument {
Expand All @@ -36,15 +37,15 @@ export default class Function {
isLinked: boolean;

arguments: Argument[];
returns: Argument[];
returns: Argument[] | VirtualType;

constructor(owner: File, ast: Term_Function) {
this.owner = owner;
this.name = ast.value[0].value[0].value;
this.ast = ast;
this.ref = null;

this.returns = [];
this.returns = never;
this.arguments = [];

this.isLinking = false;
Expand Down Expand Up @@ -91,9 +92,20 @@ export default class Function {
if (types === null) return;

for (let i=0; i<raw_args.length; i++) {
const type = types[i];
if (type instanceof VirtualType) {
const file = this.getFile();
console.error(
`${colors.red("Error")}: Function parameters must be a solid type\n`
+ SourceView(file.path, file.name, raw_args[i].ref)
)
file.markFailure();
continue;
}

this.arguments.push(new Argument(
raw_args[i].value[0].value,
types[i],
type,
raw_args[i].ref
));
}
Expand All @@ -103,11 +115,18 @@ export default class Function {
);
if (returnTypes === null) return;

this.returns.push(new Argument(
"return",
returnTypes[0],
head.value[2].ref
));
const retType = returnTypes[0];
if (retType instanceof VirtualType) {
this.returns = retType;
} else {
this.returns = [
new Argument(
"return",
retType,
head.value[2].ref
)
];
}

this.isLinked = true;
}
Expand All @@ -119,27 +138,30 @@ export default class Function {
if (!this.isLinked) return; // Failed to link
this.isCompiled = true;


const args: number[] = [];
const rets: number[] = [];
if (Array.isArray(this.returns)) for (const arg of this.returns) {
if (!(arg.type instanceof IntrinsicType)) args.push(arg.type.getBitcode());
else rets.push(arg.type.getBitcode());
}

for (const arg of this.arguments) {
args.push(arg.type.getBitcode());
}

const project = this.getFile().owner.project;
const func = project.module.makeFunction(
[
...this.returns
.filter(x => !(x.type instanceof IntrinsicType))
.map(x => x.type.getBitcode()),
...this.arguments
.map(x => x.type.getBitcode())
],
this.returns
.filter(x => x.type instanceof IntrinsicType)
.map(x => x.type.getBitcode()),
);
const func = project.module.makeFunction( args, rets );
this.ref = func.ref;




const scope = new Scope(func);
const ctx = new Context(this.getFile(), this, scope, func.code);

for (const ret of this.returns) {
if (ret.type instanceof IntrinsicType) continue;
scope.registerArgument(ctx, ret.name, ret.type, ret.ref);
if (Array.isArray(this.returns)) for (const ret of this.returns) {
if (ret.type instanceof Structure) scope.registerArgument(ctx, ret.name, ret.type, ret.ref);
}

for (const arg of this.arguments) {
Expand All @@ -162,7 +184,7 @@ export default class Function {


function LinkTypes(scope: File, syntax: Term_Access[]) {
const out: SolidType[] = [];
const out: Array<SolidType | VirtualType> = [];

let failed = false;
for (const arg of syntax) {
Expand All @@ -177,7 +199,7 @@ function LinkTypes(scope: File, syntax: Term_Access[]) {
)
failed = true;
continue;
} else if (!IsSolidType(res)) {
} else if (!IsSolidType(res) && !(res instanceof VirtualType)) {
console.error(
`${colors.red("Error")}: Function parameters must be a solid type\n`
+ SourceView(scope.path, scope.name, arg.ref)
Expand Down

0 comments on commit f5ac04d

Please sign in to comment.