-
Notifications
You must be signed in to change notification settings - Fork 2
/
showcase.ts
126 lines (98 loc) · 5.21 KB
/
showcase.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// This test case compiles to WebAssembly today, highlights some of the features that have been
// implemented already and gives a quick outlook on the road ahead.
// Global variables can be constant
const aConstantGlobal: i32 = 42;
// Constant globals can be exported to JS from the entry file
export const anExportedConstantGlobal: f32 = 42.0;
// Global variables can be mutable
var aMutableGlobal: i32 = 42;
// Variables can infer their type
var anInferredI32 = 42; // infers i32 by default
var anInferredI64 = 0x100000000; // infers i64 because the value doesn't fit in 32 bits
var anInferredF64 = 42.0; // infers f64 because it is notated as a float
var anInferredF32 = <f32>42.0; // infers f32 by evaluating its initializer
// Unary expressions just work
import "./unary";
// Binary expressions just work
import "./binary";
// Logical expressions just work
import "./logical";
// Several WebAssembly and some common JavaScript built-ins are supported and compile to opcodes directly
import "./builtins";
// Speaking of imports: Exports and re-exports are supported as well
export { aConstantGlobal };
export { anExportedConstantGlobal as anAliasedConstantGlobal } from "./showcase";
// Elements can be arranged in namespaces
namespace ANamespace {
export var aNamespacedGlobal: i32 = 42;
export function aNamespacedFunction(a: i32): i32 { return a; } // functions just work
}
// The compiler supports built-in assertions (--noAssert disables them globally)
assert(ANamespace.aNamespacedFunction(ANamespace.aNamespacedGlobal) == 42);
// Enums become constant globals and thus can be exported from the entry file
export enum AnEnum {
ONE = 1, // values can use explicit initializers
TWO, // or assume the previous value + 1
// or be omitted
FOUR = AnEnum.TWO + 2, // or reference other values (and remain constant through precomputation)
FIVE, // and continue from there
FORTYTWO = aMutableGlobal, // or reference mutable values but then can't be exported
FORTYTHREE // and even continue from there without being exported (tsc doesn't allow this)
}
assert(AnEnum.ONE == 1);
assert(AnEnum.TWO == 2);
assert(AnEnum.FOUR == 4);
assert(AnEnum.FIVE == 5);
assert(AnEnum.FORTYTWO == 42);
assert(AnEnum.FORTYTHREE == 43);
// In fact, there are a couple of things asc just waves through where tsc refuses to
1, 2, 3; // for example not-so-useful comma expressions
function addGeneric<T>(left: T, right: T): T {
return left + right; // or maybe-useful generic math
}
// Speaking of generics: While there is no type inference yet, it just works
addGeneric<i32>(1, 2); // compiles and calls the i32 version
addGeneric<f32>(1, 2); // compiles and calls the f32 version
clz<i64>(0x8000); // most built-ins are generic as well
// Type aliases work but must be declared in the global scope for now
type double = f64;
addGeneric<double>(1, 2); // compiles and calls the f64 version
// Speaking of lazy compilation: Stuff that's not used is considered dead code and not compiled by default
function anUnusedFunction(): void { }
// That is, unless exported from the entry file, so it is considered reachable
export function anExportedFunction(): void { }
// Or, of course, `--noTreeShaking` is specified
// As you see, while classes, strings and arrays are still in the works, pretty much everything can
// be implemented already. Here are a few more sophisitcated examples of code that'll most likely
// make it into the standard library eventually:
import "./memcpy"; // until replaced by the proposed `move_memory` intrinsic (sic.)
// Speaking of classes: Some preliminary work has already been done, so while we can't properly
// instantiate them yet, we can point them at some raw memory
class AClass {
static aStaticField: AClass | null = null;
aField: i32;
}
class ADerivedClass extends AClass {
anotherField: f32;
get aWildAccessorAppears(): f32 { return this.anotherField; }
set aWildAccessorAppears(val: f32) { this.anotherField = val; }
}
var aClassInstance = changetype<ADerivedClass>(<usize>8);
aClassInstance.aField = 42;
aClassInstance.anotherField = 9000;
assert(load<i32>(8) == 42);
assert(load<f32>(12) == 9000);
aClassInstance.aWildAccessorAppears = 123;
assert(aClassInstance.aWildAccessorAppears == 123);
AClass.aStaticField = aClassInstance;
assert(ADerivedClass.aStaticField == aClassInstance);
// yet that's pretty much a work in progress, until...
// Speaking of the standard library:
// Ultimately, a memory manager should still be present regardless of the GC spec because making
// everything a GC-managed object impacts performance where GC isn't necessary. TLSF appears to
// be a viable candidate because it's relatively fast and small and an ARC-variant seems like a
// good internal alternative to a general-purpose GC if we can figure out reference cycles.
// With GC (and earlier: host-bindings) it will technically be possible to declare classes whose
// instances can cross the JS/WASM boundary natively, but then aren't stored in linear memory.
// Have a nice day!
// P.S: Interested in compilers? Nothing cooler to do with your spare time? Say hi!