-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
285 lines (254 loc) · 7.4 KB
/
index.html
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
<!doctype html>
<html>
<head>
<title>Bismuth</title>
<style>
body {
text-align: center;
font-family: Arial;
}
h1, h2, h3, h4, p, div, ul {
text-align: justify;
}
#sourcecode {
float: left;
font-family: Consolas, "Courier New", monospace;
width: 65%;
max-width: 65%;
margin-top: 40px;
min-height: 900px;
}
#generatedC, #generatedJS {
width: 30%;
float: right;
white-space: pre;
font-family: Consolas, "Courier New", monospace;
font-size: 70%;
}
#errors {
font-family: Arial;
color: #A00;
}
</style>
</head>
<body>
<script src="define.js"></script>
<script src="build/build.js"></script>
<h1>Bismuth - Run Online</h1>
<p>Bismuth is a pure, strongly and statically-typed <em>imperative</em> programming language that keeps code predictable and easy to refactor.</p>
<p>Bismuth currently supports generic functions and data types, whose generic types can be constrained by user-defined interface.</p>
<p>
Functions specify their side-effects through <em>effect signatures</em>.
These signatures explicitly denote the side-effects that a function can perform.
</p>
<p>
Bismuth will support a linear type system which makes it very difficult to unintentionally leak resources.
</p>
<p>
The Bismuth compiler is written in TypeScript.
This page generates executable C; future versions will be able to generate JavaScript as well.
</p>
<select id="sample_wheel">
<option value="1">1. Hello, World!</option>
<option value="2">2. Basic Objects and Ownership</option>
<option value="3">3. Enums and Functional Programming</option>
</select>
<div id="code_1" hidden>
func main!IO {
print!("Hello, world.");
}
</div>
<div id="code_2" hidden>
// This is a struct type which has two fields called x and y.
struct Pair[A, B] {
var x: A;
var y: B;
}
// we're not (currently) obligated to put the effect signature in the function.
// however, it will help indicate that you perform IO
func printInt!IO(n: Int) {
// Bismuth doesn't have built-in Int printing (yet)
// so we'll write it ourself using C:
foreign#
// inside here is C code.
// it's emitted verbatim, except variable references,
// which must be translated:
printf("%d\n", (int)(intptr_t)@:use:[n]);
#;
// The syntax '@:use:[varname]' is used to translate the Bismuth variable into
// the corresponding generated C variable.
// All variables are declared with type void*, so we cast it to an int before printing.
}
func main!IO {
var p: Pair[Int, Int] = #Pair{x => 1, y => 2};
printInt!(p.x);
printInt!(p.y);
// If we uncomment the next line, we'll get an error.
// p currently has memory which needs to be freed. If we replace it,
// we won't be able to free it any more.
// p = #Pair{x => 3, y => 5};
discard p;
// now p is freed and we can replace it:
// If we uncomment the next line, we'll get an error because p currently
// has no value.
// printInt!(p.x);
p = #Pair{x => 4, y => 6};
// This line consumes p. Therefore we can't discard it (again) after.
printPair!(p);
// If we uncomment this next line, we'll get an error.
// discard p;
}
func printPair!IO(p: Pair[Int, Int]) {
print!("pair of");
printInt!(p.x);
print!("and");
printInt!(p.y);
discard p; // free the memory associated to p
}
</div>
<div id="code_3" hidden>
// Here we define a generic enum.
// It's either empty or nonempty (and a cons).
enum List {
case Empty;
case NonEmpty of Cons;
}
struct Cons {
var head: Int;
var tail: List;
}
// We don't want to consume the list to determine its length.
// Therefore, we take the list by reference.
func length(list: &List) -> Int {
match list
case Empty {
return 0;
}
case NonEmpty of var c: &Cons {
var rest: Int = length(c.tail);
return 1 + rest;
}
}
func iota(n: Int) -> List {
var result: List = #Empty;
var i: Int = 0;
while i < n {
result = #NonEmpty(#Cons{head => i, tail => result});
i = i + 1;
}
return result;
}
func printInt!IO(n: Int) {
foreign#
printf("%d\n", (int)(intptr_t)@:use:[n]);
#;
}
func consumeAndPrint!IO(list: List) {
match list
case Empty {
print!("no more list");
}
case NonEmpty of var c: Cons {
printInt!(c.head);
consumeAndPrint!(c.tail);
discard c; // free the struct's memory.
}
}
func main!IO {
var list: List = iota(10);
print!("the list has length");
printInt!(length(&list));
print!("the contents of the list are");
consumeAndPrint!(list);
}
</div>
<div id="code_4" hidden>
func main!IO {
print!("Hello, world.");
}
</div>
<textarea id="sourcecode">
func main!IO {
print!("Hello, world.");
}
</textarea>
<script>
document.getElementById("sourcecode").addEventListener("keydown", (e) => {
// https://stackoverflow.com/questions/6637341/use-tab-to-indent-in-textarea
if (e.keyCode == 9) {
e.preventDefault();
var start = e.target.selectionStart;
var end = e.target.selectionEnd;
// set textarea value to: text before caret + tab + text after caret
e.target.value = e.target.value.substr(0, start) + "\t" + e.target.value.substr(end);
// put caret at right position again
e.target.selectionStart = e.target.selectionEnd = start + 1;
return;
}
if (e.keyCode == 13) {
// add newline with tabs
e.preventDefault();
var start = e.target.selectionStart;
var end = e.target.selectionEnd;
let insert = "";
for (let i = start-1; i >= 0; i--) {
const c = e.target.value.charAt(i);
if (c == "\t") {
insert = c + insert;
} else if (c == "\n") {
break;
} else {
insert = "";
}
}
insert = "\n" + insert;
if (e.target.value.charAt(start-1) == "{") {
insert += "\t";
}
// set textarea value to: text before caret + tab + text after caret
e.target.value = e.target.value.substr(0, start) + insert + e.target.value.substr(end);
// put caret at right position again
e.target.selectionStart = e.target.selectionEnd = start + insert.length;
return;
}
if (e.key == "}") {
// de-indent by one tab before inserting
var start = e.target.selectionStart;
var end = e.target.selectionEnd;
if (e.target.value.charAt(start-1) == "\t") {
e.preventDefault();
e.target.value = e.target.value.substr(0, start-1) + "}" + e.target.value.substr(end);
e.target.selectionStart = e.target.selectionEnd = start;
}
return;
}
});
</script>
<script>
let compile = obtain("compile").compile;
let last = "";
let compiled = false;
setInterval(function() {
let current = document.getElementById('sourcecode').value;
if (current == last) {
if (!compiled) {
compile(current);
compiled = true;
}
} else {
compiled = false;
last = current;
}
}, 500);
</script>
<div id="errors"></div>
<div id="generatedJS" style="display:none"></div>
<hr/>
<div id="generatedC"></div>
<script>
document.getElementById("sample_wheel").addEventListener("change", e => {
document.getElementById("sourcecode").innerText = document.getElementById("code_" + e.target.value).innerText;
});
</script>
</body>
</html>