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

Basic String Constants #13

Merged
merged 6 commits into from
Apr 18, 2024
Merged
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
14 changes: 9 additions & 5 deletions source/bnf/syntax.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,19 @@ constant ::= boolean
| string
| float | integer ;

string ::= string_ascii | string_utf8 ;
string_ascii ::= %"\'" ( str_escape | !( "\'" ) )* %"\'" ;
string_utf8 ::= %"\"" ( str_escape | !( "\"" ) )* %"\"" ;
str_escape ::= %"\\" !"" ;
string ::= string_plain | string_template ;
string_plain ::= %"\"" ( str_hex_u8 | str_escape | !"\"" )* %"\"" ;
str_hex_u8 ::= %"\\x" ...( hexidecimal hexidecimal ) ;
str_escape ::= %"\\" !"" ;
string_template ::= %"\`" ( str_hex_u8 | str_escape | string_tag | !"`" )* %"`" ;
string_tag ::= %"${" expr %"}" ;

boolean ::= "true" | "false" ;

void ::= "void" ;

hexidecimal ::= "0"->"9" | "a" -> "f";

integer ::= ...integer_u ;
integer_u ::= ( digit_nz digit* ) | zero ;
zero ::= "0" ;
Expand Down Expand Up @@ -127,7 +131,7 @@ statement ::= expr %terminate ;
# External
#=============================
external ::= %( "external" w+ ) ( ext_import | ext_export ) ;
ext_import ::= %( "import" w* "{" w* ) ext_imports* %( w* "}" w* "from" w*) string %(w* ";" w*) ;
ext_import ::= %( "import" w* "{" w* ) ext_imports* %( w* "}" w* "from" w*) string_plain %(w* ";" w*) ;
ext_imports ::= function | ext_import_var ;
ext_import_var ::= %( "let" w* ) name %( w* ":" w* ) access %(w* ";" w*);
ext_export ::= "export" ;
75 changes: 63 additions & 12 deletions source/bnf/syntax.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export type Term_String = {
count: number,
ref: _Shared.ReferenceRange,
value: [
(Term_String_ascii | Term_String_utf8)
(Term_String_plain | Term_String_template)
]
}
export declare function Parse_String (i: string, refMapping?: boolean): _Shared.ParseError | {
Expand All @@ -245,35 +245,35 @@ export declare function Parse_String (i: string, refMapping?: boolean): _Shared.
isPartial: boolean
}

export type Term_String_ascii = {
type: 'string_ascii',
export type Term_String_plain = {
type: 'string_plain',
start: number,
end: number,
count: number,
ref: _Shared.ReferenceRange,
value: [
{ type: '(...)*', value: Array<(Term_Str_escape | _Literal)>, start: number, end: number, count: number, ref: _Shared.ReferenceRange }
{ type: '(...)*', value: Array<(Term_Str_hex_u8 | Term_Str_escape | _Literal)>, start: number, end: number, count: number, ref: _Shared.ReferenceRange }
]
}
export declare function Parse_String_ascii (i: string, refMapping?: boolean): _Shared.ParseError | {
root: _Shared.SyntaxNode & Term_String_ascii,
export declare function Parse_String_plain (i: string, refMapping?: boolean): _Shared.ParseError | {
root: _Shared.SyntaxNode & Term_String_plain,
reachBytes: number,
reach: null | _Shared.Reference,
isPartial: boolean
}

export type Term_String_utf8 = {
type: 'string_utf8',
export type Term_Str_hex_u8 = {
type: 'str_hex_u8',
start: number,
end: number,
count: number,
ref: _Shared.ReferenceRange,
value: [
{ type: '(...)*', value: Array<(Term_Str_escape | _Literal)>, start: number, end: number, count: number, ref: _Shared.ReferenceRange }
_Literal
]
}
export declare function Parse_String_utf8 (i: string, refMapping?: boolean): _Shared.ParseError | {
root: _Shared.SyntaxNode & Term_String_utf8,
export declare function Parse_Str_hex_u8 (i: string, refMapping?: boolean): _Shared.ParseError | {
root: _Shared.SyntaxNode & Term_Str_hex_u8,
reachBytes: number,
reach: null | _Shared.Reference,
isPartial: boolean
Expand All @@ -296,6 +296,40 @@ export declare function Parse_Str_escape (i: string, refMapping?: boolean): _Sha
isPartial: boolean
}

export type Term_String_template = {
type: 'string_template',
start: number,
end: number,
count: number,
ref: _Shared.ReferenceRange,
value: [
{ type: '(...)*', value: Array<(Term_Str_hex_u8 | Term_Str_escape | Term_String_tag | _Literal)>, start: number, end: number, count: number, ref: _Shared.ReferenceRange }
]
}
export declare function Parse_String_template (i: string, refMapping?: boolean): _Shared.ParseError | {
root: _Shared.SyntaxNode & Term_String_template,
reachBytes: number,
reach: null | _Shared.Reference,
isPartial: boolean
}

export type Term_String_tag = {
type: 'string_tag',
start: number,
end: number,
count: number,
ref: _Shared.ReferenceRange,
value: [
Term_Expr
]
}
export declare function Parse_String_tag (i: string, refMapping?: boolean): _Shared.ParseError | {
root: _Shared.SyntaxNode & Term_String_tag,
reachBytes: number,
reach: null | _Shared.Reference,
isPartial: boolean
}

export type Term_Boolean = {
type: 'boolean',
start: number,
Expand Down Expand Up @@ -330,6 +364,23 @@ export declare function Parse_Void (i: string, refMapping?: boolean): _Shared.Pa
isPartial: boolean
}

export type Term_Hexidecimal = {
type: 'hexidecimal',
start: number,
end: number,
count: number,
ref: _Shared.ReferenceRange,
value: [
(_Literal | _Literal)
]
}
export declare function Parse_Hexidecimal (i: string, refMapping?: boolean): _Shared.ParseError | {
root: _Shared.SyntaxNode & Term_Hexidecimal,
reachBytes: number,
reach: null | _Shared.Reference,
isPartial: boolean
}

export type Term_Integer = {
type: 'integer',
start: number,
Expand Down Expand Up @@ -1271,7 +1322,7 @@ export type Term_Ext_import = {
ref: _Shared.ReferenceRange,
value: [
{ type: '(...)*', value: Array<Term_Ext_imports>, start: number, end: number, count: number, ref: _Shared.ReferenceRange },
Term_String
Term_String_plain
]
}
export declare function Parse_Ext_import (i: string, refMapping?: boolean): _Shared.ParseError | {
Expand Down
19 changes: 14 additions & 5 deletions source/bnf/syntax.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion source/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ project.module.exportFunction("main", mainFunc.ref);
project.module.startFunction(mainFunc.ref);

TimerStart("serialize");
await Deno.writeFile("out.wasm", project.module.toBinary());
await Deno.writeFile("out.wasm", project.toBinary());
TimerEnd("serialize");

TimerStart("wasm2wat");
Expand Down
78 changes: 63 additions & 15 deletions source/compiler/codegen/expression/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { IntrinsicValue } from "~/compiler/intrinsic.ts";
import { Instruction } from "~/wasm/index.ts";
import { SolidType } from "~/compiler/codegen/expression/type.ts";
import { Context } from "~/compiler/codegen/context.ts";
import { Panic } from "~/compiler/helper.ts";
import { File } from "~/compiler/file.ts";

export function CompileConstant(ctx: Context, syntax: Syntax.Term_Constant, expect?: SolidType): IntrinsicValue {
if (!(expect instanceof IntrinsicType)) expect = undefined;
Expand All @@ -15,7 +17,7 @@ export function CompileConstant(ctx: Context, syntax: Syntax.Term_Constant, expe
case "boolean": return CompileBool(ctx, val);
case "float": return CompileFloat(ctx, val, expect);
case "integer": return CompileInt(ctx, val, expect);
case "string": throw new Error("Unimplemented string constant");
case "string": return CompileString(ctx, val);
default: AssertUnreachable(val);
}
}
Expand Down Expand Up @@ -118,27 +120,73 @@ function CompileFloat(ctx: Context, syntax: Syntax.Term_Float, expect?: Intrinsi
return f32.value;
}

export function SimplifyString(syntax: Syntax.Term_String) {
const inner = syntax.value[0];
const type = inner.type === "string_ascii" ? "ascii" : "utf8";
let str = "";

for (const chunk of inner.value[0].value) {




const ESCAPE_SYMBOLS = {
"0": "\0",
"f": "\f",
"n": "\n",
"r": "\r",
"v": "\v",
};
export function SimplifyString(file: File, syntax: Syntax.Term_String_plain) {
let str = "";
for (const chunk of syntax.value[0].value) {
if (chunk.type == "literal") {
str += chunk.value;
continue;
}

const esc = chunk.value[0].value;
switch (esc) {
case "0": str += "\0"; break;
case "f": str += "\f"; break;
case "n": str += "\n"; break;
case "r": str += "\r"; break;
case "v": str += "\v"; break;
default: str += esc;
switch (chunk.type) {
case "str_hex_u8": {
const hex: string = chunk.value[0].value;
str += String.fromCharCode(parseInt(hex, 16));
break;
}
case "str_escape": {
const esc = chunk.value[0].value;
if (esc in ESCAPE_SYMBOLS) {
str += ESCAPE_SYMBOLS[esc as keyof typeof ESCAPE_SYMBOLS];
} else Panic(`Unknown string escape character "\\${esc}"`,
{ path: file.path, name: file.name, ref: chunk.ref }
);
break;
}
default: AssertUnreachable(chunk);
}
}

return { type, str }
return str;
}

function CompileString(ctx: Context, syntax: Syntax.Term_String) {
switch (syntax.value[0].type) {
case "string_plain": return CompilePlainString(ctx, syntax.value[0]);
case "string_template": return CompileTemplateString(ctx, syntax.value[0]);
}
AssertUnreachable(syntax.value[0]);
}

function CompilePlainString(ctx: Context, syntax: Syntax.Term_String_plain) {
const str = SimplifyString(ctx.file, syntax);
const module = ctx.file.owner.project.module;

const buffer = module.dataSect.addData(str, 1);

const ioVec = new Uint32Array(2);
ioVec[0] = buffer.offset;
ioVec[1] = buffer.data.byteLength;

const ptr = module.dataSect.addData(ioVec, 4);

ctx.block.push(Instruction.const.i32(ptr.offset));
return i32.value;
}

function CompileTemplateString(ctx: Context, syntax: Syntax.Term_String_template) {
throw new Error("Unimplemented template string compilation");
return i32.value;
}
6 changes: 2 additions & 4 deletions source/compiler/codegen/expression/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,9 @@ export function ResolveLinearType(ctx: Context, type: LinearType, ref: Reference
}

// Push the complete pointer to the stack
if (type.alloc) {
if (type.offset !== 0) {
ctx.block.push(Instruction.const.i32(type.offset));
ctx.block.push(Instruction.i32.add());
}

if (type.offset !== 0) ctx.block.push(Instruction.const.i32(type.offset));
};
return type;
}
4 changes: 2 additions & 2 deletions source/compiler/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@ function IngestStructure(file: File, syntax: Term_Structure) {
function IngestExternal(file: File, syntax: Term_External) {
if (syntax.value[0].type !== "ext_import") throw new Error(`Unsupported external export`);

const name = SimplifyString(syntax.value[0].value[1]);
const name = SimplifyString(file, syntax.value[0].value[1]);
for (const inner of syntax.value[0].value[0].value) {
const line = inner.value[0];
const type = line.type;
switch (type) {
case "function": {
IngestFunction(file, line, name.str);
IngestFunction(file, line, name);
} break;
case "ext_import_var": throw new Error(`Import global unimplemented`);
default: AssertUnreachable(type);
Expand Down
11 changes: 9 additions & 2 deletions source/compiler/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ export default class Project {
this.stackReg = this.module.registerGlobal(
Intrinsic.i32,
true,
Instruction.const.i32(0)
Instruction.const.i32( this.module.dataSect.tail )
);
this.stackBase = new BasePointer(BasePointerType.global, this.stackReg.ref);
this.module.exportGlobal("_stack", this.stackReg);

this.module.addMemory(1, 1);
const memRef = this.module.addMemory(1, 1);
this.module.exportMemory("memory", memRef);

this.flags = {
tailCall: true
Expand All @@ -42,4 +44,9 @@ export default class Project {
markFailure() {
this.failed = true;
}


toBinary() {
return this.module.toBinary();
}
}
5 changes: 5 additions & 0 deletions source/wasm/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Box, Byte } from "~/helper.ts";
import { Constant } from "~/wasm/instruction/constant.ts";
import { Function } from "~/wasm/function.ts";
import { FuncRef } from "~/wasm/funcRef.ts";
import { GlobalRegister } from "~/wasm/section/global.ts";



Expand Down Expand Up @@ -69,6 +70,10 @@ export default class Module {
return this.exportSect.bind(name, func);
}

exportGlobal(name: string, global: GlobalRegister) {
return this.exportSect.bind(name, global);
}

exportMemory(name: string, mem: MemoryRef) {
return this.exportSect.bind(name, mem);
}
Expand Down
19 changes: 13 additions & 6 deletions source/wasm/section/data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Byte } from "~/helper.ts";
import { Byte, LatentValue } from "~/helper.ts";
import { EncodeI32, EncodeU32 } from "~/wasm/type.ts";
import { AlignUpInteger } from "~/compiler/helper.ts";


const textEncoder = new TextEncoder();
Expand All @@ -16,22 +17,28 @@ class Entry {

export default class DataSection {
entries: Entry[];
tail: LatentValue<number>;

constructor() {
this.entries = [];
this.tail = new LatentValue();
this.tail.resolve(0, true);
}

addData(data: string | BufferSource, align: number) {
return this.setData(AlignUpInteger(this.tail.get(), align), data);
}

setData(offset: number, data: string | BufferSource) {
if (typeof(data) === "string") {
data = textEncoder.encode(data);
}

this.entries.push(new Entry(
offset,
data
));
const entry = new Entry(offset, data);
this.entries.push(entry);

return 0;
this.tail.resolve(Math.max(this.tail.get(), AlignUpInteger(offset + data.byteLength, 8)), true);
return entry;
}

toBinary (): Byte[] {
Expand Down
Loading
Loading