-
Notifications
You must be signed in to change notification settings - Fork 16
/
mirror-playground.html
200 lines (163 loc) · 7.32 KB
/
mirror-playground.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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mirror Playground</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
header {
font-size: 24px;
margin-bottom: 10px;
}
.explanation {
margin-bottom: 20px;
}
.textbox {
width: 16em;
height: 30px;
margin-bottom: 20px;
}
.editor-container {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.editor {
width: 49%;
height: 300px;
border: 1px solid #ccc;
}
.scrollable-box {
width: 99%;
height: 4em;
background-color: rgb(235, 235, 235);
overflow-y: auto;
overflow-x: hidden;
word-wrap: break-word;
padding: 0.5em;
box-sizing: border-box;
border: 1px solid darkgray;
}
.button-container {
margin-bottom: 20px;
}
.button-container button {
padding: 10px 15px;
margin-right: 10px;
}
.status-label {
font-size: 16px;
font-weight: bold;
}
</style>
</head>
<body>
<h1>Playground for Mirror</h1>
<p class="explanation">
The Mirror programming language powered by programming by example and AI.
</p>
<input type="text" class="textbox" id="api-key" placeholder="Your throwaway OpenAI API key">
<div class="editor-container">
<div id="editor-source" class="editor"></div>
<div id="editor-intermediate" class="editor"></div>
</div>
<div class="button-container">
<button id="button-compile">Compile</button>
<button id="button-run">Run</button>
</div>
<div class="scrollable-box" id="output"></div>
<br><div class="status-label" id="status">Status: Ready</div><br><br>
<hr>
<h3>Documentation</h3>
<p>See the <a href="https://austinhenley.com/blog/mirrorlang.html">blog post</a> for more information about Mirror.</p>
<p>The Mirror programming language allows you to express behavior <i>only</i> through examples. These examples are then passed to an LLM to generate JavaScript code that satisfies the example behavior (ideally). You can then execute those functions.</p>
<p>The syntax of the language allows function signatures, examples, and function expressions. The only types allowed are <i>number</i>, <i>bool</i>, <i>string</i>, list of any type (e.g., <i>list[bool]</i>), and dictionary.</p>
<p>Please submit any improvements to the GitHub <a href="https://github.com/AZHenley/Mirror">repo</a>.</p>
<script src="https://cdn.jsdelivr.net/npm/ace-builds@1.23.4/src-min-noconflict/ace.min.js"></script>
<script src="mirror.js"></script>
<script>
var editor1 = ace.edit("editor-source");
//editor1.setTheme("ace/theme/monokai");
//editor1.session.setMode("ace/mode/javascript");
const initialText = `signature foo(x: number, y: number) -> number
example foo(10, 3) = 101010
example foo(1, 1) = 1
example foo(0, 10) = 0
signature bar(x: number) -> string
example bar(9) = "length is 1"
example bar(3210) = "length is 4"
bar(foo(123, 3))`;
editor1.setValue(initialText);
editor1.clearSelection();
editor1.moveCursorTo(0, 0);
var editor2 = ace.edit("editor-intermediate");
//editor2.setTheme("ace/theme/monokai");
editor2.session.setMode("ace/mode/javascript");
editor2.container.style.background = "lightgray";
editor2.setReadOnly(true);
// Compile button action.
document.getElementById("button-compile").addEventListener("click", async function() {
const apiKey = document.getElementById("api-key").value;
if (!apiKey) {
document.getElementById("status").textContent = "Status: Enter your OpenAI API key."
return;
}
document.getElementById("status").textContent = "Status: Compiling...";
editor2.setValue("Compiling...")
// Parse!
const code = editor1.getValue();
let reorganizedASToutput = "";
let expressionsoutput = "";
try {
// Create a new Mirror parser instance and parse the code.
const parser = new MirrorParser(code);
const ast = parser.parse();
const expressions = extractExpressions(ast);
const reorganizedAST = groupSignaturesWithExamples(ast);
// Format the AST as a string and display it in editor2.
const ASToutput = JSON.stringify(ast, null, 2);
expressionsoutput = JSON.stringify(expressions)
reorganizedASToutput = JSON.stringify(reorganizedAST)
// For debugging:
// editor2.setValue(ASToutput + "\n\n@@@\n\n" + expressionsoutput + "\n\n@@@\n\n" + reorganizedASToutput);
// editor2.clearSelection(); // Clear any selected text.
document.getElementById("status").textContent = "Status: Code parsed successfully!";
} catch (error) {
// Display error in editor2 if parsing fails.
editor2.setValue("Error: " + error.message);
editor2.clearSelection();
document.getElementById("status").textContent = "Status: Parsing error!";
return;
}
// Compile!
const compiler = new MirrorCompiler();
const prompt = "I want you to generate the function body in JavaScript for the function signature that I give you. I will also give you several examples of inputs with the expected results. Do not use any additional libraries. Do not give any explanation. Do not format it as Markdown. Only give the function and the function call expressions afterwards if applicable.\n\n" + reorganizedASToutput + "\n\n" + expressionsoutput;
// TODO: This really should make separate calls to OpenAI to generate each function body. Then our code should emit the JS function signature and use the function body from OpenAI.
// TODO: Similarly, the function call expressions should not go to OpenAI. The parser should have a function to emit them.
try {
const response = await compiler.callOpenAI(apiKey, prompt);
let generatedCode = response;
// TODO: We should first verify that the examples do indeed run as specified.
editor2.setValue(generatedCode);
editor2.clearSelection();
document.getElementById("status").textContent = "Status: Response received from OpenAI!";
} catch (error) {
editor2.setValue("Error: " + error.message);
editor2.clearSelection();
document.getElementById("status").textContent = "Status: API call error!";
}
});
// Run button action.
document.getElementById("button-run").addEventListener("click", async function() {
let jscode = editor2.getValue();
document.getElementById("output").textContent = eval(jscode);
document.getElementById("status").textContent = "Status: Executed successfully!";
// TODO: This will only print the last result. Should we instead print the return value from every function call made by the user (not made by the generated functions)?
});
</script>
</body>
</html>