Playground highlights and shows info/errors from build
This commit is contained in:
5
playground/README.md
Normal file
5
playground/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
- Run `make` in newt directory
|
||||
- Run `./build`
|
||||
- Run `vite`
|
||||
- Click on url
|
||||
|
||||
9
playground/build
Executable file
9
playground/build
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
echo Builds the workMain.js and copies newt.js
|
||||
esbuild --bundle node_modules/monaco-editor/esm/vs/editor/editor.worker.js > public/workerMain.js
|
||||
# bare javascript, it fakes node api for the idris code in newt.js
|
||||
esbuild src/shim.ts > public/shim.js
|
||||
cp ../build/exec/newt.js public
|
||||
# uncomment to make this smaller
|
||||
# esbuild --minify ../build/exec/newt.min.js > public/newt.js
|
||||
|
||||
@@ -2,18 +2,17 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + TS</title>
|
||||
<title>Newt Playground</title>
|
||||
<script src="shim.js"></script>
|
||||
<script src="newt.js"></script>
|
||||
<script src="hack.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="wrapper">
|
||||
<div id="editor"></div>
|
||||
<div id="result">result</div>
|
||||
<div id="result"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
class Buffer extends ArrayBuffer {
|
||||
static alloc(n) {
|
||||
|
||||
return new Buffer(n);
|
||||
}
|
||||
indexOf(n) {
|
||||
let view = new Uint8Array(this);
|
||||
return view.indexOf(n);
|
||||
}
|
||||
|
||||
static concat(bufs) {
|
||||
let size = bufs.reduce((a, b) => (a += b.byteLength), 0);
|
||||
console.log('concat', size, bufs);
|
||||
let rval = new Buffer(size);
|
||||
let view = new Uint8Array(rval);
|
||||
let off = 0;
|
||||
for (let buf of bufs) {
|
||||
view.set(new Uint8Array(buf), off);
|
||||
off += buf.byteLength;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
toString() {
|
||||
return dec.decode(this)
|
||||
}
|
||||
}
|
||||
let dec = new TextDecoder();
|
||||
let enc = new TextEncoder();
|
||||
let files = {
|
||||
};
|
||||
|
||||
let fds = [];
|
||||
|
||||
let shim = {
|
||||
os: {
|
||||
platform() {
|
||||
return "linux";
|
||||
},
|
||||
},
|
||||
fs: {
|
||||
openSync(fn) {
|
||||
console.log("openSync", fn);
|
||||
let te = new TextEncoder();
|
||||
let fd = fds.length;
|
||||
if (!files[fn]) throw new Error(`${fn} not found`)
|
||||
let buf = te.encode(files[fn]);
|
||||
let pos = 0;
|
||||
fds.push({ buf, pos });
|
||||
// we'll mutate the pointer as stuff is read
|
||||
return fd;
|
||||
},
|
||||
readSync(fd, buf, start, len) {
|
||||
let hand = fds[fd]
|
||||
let avail = hand.buf.byteLength - hand.pos
|
||||
let rd = Math.min(avail, len)
|
||||
let src = new Uint8Array(hand.buf)
|
||||
let dest = new Uint8Array(buf)
|
||||
for (let i=0;i<rd;i++)
|
||||
dest[start+i] = src[hand.pos++]
|
||||
console.log('read', rd)
|
||||
return rd
|
||||
},
|
||||
closeSync(fd) {
|
||||
delete fds[fd]
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
shim.fs = new Proxy(shim.fs, {
|
||||
get(target, prop, receiver) {
|
||||
console.log("fs get", prop);
|
||||
return target[prop];
|
||||
},
|
||||
});
|
||||
|
||||
const process = {
|
||||
platform: "linux",
|
||||
argv: ["", ""],
|
||||
stdout: {
|
||||
// We'll want to replace this one
|
||||
write: console.log,
|
||||
},
|
||||
exit(v) {
|
||||
console.log("exit", v);
|
||||
},
|
||||
// stdin: { fd: 0 },
|
||||
};
|
||||
|
||||
const require = (x) => shim[x];
|
||||
18
playground/src/global.d.ts
vendored
18
playground/src/global.d.ts
vendored
@@ -1,13 +1,15 @@
|
||||
export {}
|
||||
declare module "*.css";
|
||||
export {};
|
||||
declare global {
|
||||
interface Process {
|
||||
platform: string;
|
||||
stdout: {
|
||||
write(s: string): void
|
||||
},
|
||||
argv: string[]
|
||||
|
||||
write(s: string): void;
|
||||
};
|
||||
argv: string[];
|
||||
exit(_: number): void;
|
||||
}
|
||||
let files : Record<string,string>
|
||||
let process : Process
|
||||
let __mainExpression_0 : () => unknown
|
||||
let files: Record<string, string>;
|
||||
let process: Process;
|
||||
let newtMain: () => unknown;
|
||||
}
|
||||
|
||||
@@ -1,45 +1,106 @@
|
||||
import './style.css'
|
||||
import {newtLanguage} from './monarch.ts'
|
||||
// import "./style.css";
|
||||
import { newtConfig, newtTokens } from "./monarch.ts";
|
||||
import * as monaco from "monaco-editor";
|
||||
|
||||
import * as monaco from 'monaco-editor'
|
||||
monaco.languages.register({ id: "newt" });
|
||||
monaco.languages.setMonarchTokensProvider("newt", newtTokens);
|
||||
monaco.languages.setLanguageConfiguration("newt", newtConfig);
|
||||
|
||||
self.MonacoEnvironment = {
|
||||
getWorkerUrl(moduleId, label) {
|
||||
console.log("Get worker", moduleId);
|
||||
return moduleId;
|
||||
},
|
||||
};
|
||||
|
||||
monaco.languages.setMonarchTokensProvider("newt", newtLanguage);
|
||||
|
||||
let container = document.getElementById('editor')!
|
||||
let result = document.getElementById('result')!
|
||||
const editor = monaco.editor.create(container, {
|
||||
value: "module Main\n\n",
|
||||
language: "newt",
|
||||
theme: "vs",
|
||||
|
||||
automaticLayout: true,
|
||||
// I keep pressing this.
|
||||
document.addEventListener('keydown', (ev) => {
|
||||
if (ev.metaKey && ev.code == 'KeyS') ev.preventDefault();
|
||||
})
|
||||
|
||||
let timeout : number | undefined
|
||||
let value = localStorage.code || "module Main\n";
|
||||
|
||||
let container = document.getElementById("editor")!;
|
||||
let result = document.getElementById("result")!;
|
||||
const editor = monaco.editor.create(container, {
|
||||
value,
|
||||
language: "newt",
|
||||
theme: "vs",
|
||||
automaticLayout: true,
|
||||
minimap: { enabled: false },
|
||||
});
|
||||
|
||||
let timeout: number | undefined;
|
||||
|
||||
function run(s: string) {
|
||||
console.log('run', s)
|
||||
process.argv = ['','', 'src/Main.newt']
|
||||
files['src/Main.newt'] = s
|
||||
result.innerHTML = ''
|
||||
__mainExpression_0()
|
||||
console.log("run", s);
|
||||
process.argv = ["", "", "src/Main.newt", "-o", "out.js"];
|
||||
console.log("args", process.argv);
|
||||
files["src/Main.newt"] = s;
|
||||
result.innerHTML = "";
|
||||
stdout = ''
|
||||
newtMain();
|
||||
processOutput();
|
||||
}
|
||||
|
||||
|
||||
let stdout = ''
|
||||
// We'll want to collect and put info in the monaco
|
||||
process.stdout.write = (s) => {
|
||||
console.log('write', s)
|
||||
let div = document.createElement('div')
|
||||
div.className = 'log'
|
||||
div.textContent = s
|
||||
result.appendChild(div)
|
||||
stdout += s
|
||||
};
|
||||
|
||||
// Adapted from the vscode extension, but types are slightly different
|
||||
// and positions are 1-based.
|
||||
const processOutput = () => {
|
||||
result.innerText = stdout
|
||||
|
||||
let model = editor.getModel()!
|
||||
let markers: monaco.editor.IMarkerData[] = []
|
||||
let lines = stdout.split('\n')
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
const match = line.match(/(INFO|ERROR) at \((\d+), (\d+)\):\s*(.*)/);
|
||||
if (match) {
|
||||
let [_full, kind, line, col, message] = match;
|
||||
let lineNumber = +line+1
|
||||
let column = +col+1;
|
||||
let start = {column, lineNumber};
|
||||
// we don't have the full range, so grab the surrounding word
|
||||
let endColumn = column + 1
|
||||
let word = model.getWordAtPosition(start)
|
||||
if (word) endColumn = word.endColumn
|
||||
|
||||
// heuristics to grab the entire message:
|
||||
// anything indented
|
||||
// Context:, or Goal: are part of PRINTME
|
||||
// unexpected / expecting appear in parse errors
|
||||
while (
|
||||
lines[i + 1]?.match(/^( )/)
|
||||
) {
|
||||
message += "\n" + lines[++i];
|
||||
}
|
||||
const severity = kind === 'ERROR' ? monaco.MarkerSeverity.Error : monaco.MarkerSeverity.Info
|
||||
|
||||
markers.push({
|
||||
severity,
|
||||
message,
|
||||
startLineNumber: lineNumber,
|
||||
endLineNumber: lineNumber,
|
||||
startColumn: column,
|
||||
endColumn,
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log('setMarkers', markers)
|
||||
monaco.editor.setModelMarkers(model, 'newt', markers)
|
||||
}
|
||||
|
||||
editor.onDidChangeModelContent((ev) => {
|
||||
console.log('mc', ev)
|
||||
let value = editor.getValue()
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => run(value), 1000)
|
||||
})
|
||||
console.log('REGISTER')
|
||||
console.log("mc", ev);
|
||||
let value = editor.getValue();
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => run(value), 1000);
|
||||
localStorage.code = value;
|
||||
});
|
||||
|
||||
run(value);
|
||||
|
||||
@@ -1,8 +1,59 @@
|
||||
import * as monaco from 'monaco-editor'
|
||||
import * as monaco from "monaco-editor";
|
||||
|
||||
export let newtLanguage: monaco.languages.IMonarchLanguage = {
|
||||
export let newtConfig: monaco.languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
// symbol used for single line comment. Remove this entry if your language does not support line comments
|
||||
lineComment: "--",
|
||||
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
|
||||
blockComment: ["/-", "-/"],
|
||||
},
|
||||
// symbols used as brackets
|
||||
brackets: [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
],
|
||||
// symbols that are auto closed when typing
|
||||
autoClosingPairs: [
|
||||
{ open: "{", close: "}" },
|
||||
{ open: "[", close: "]" },
|
||||
{ open: "(", close: ")" },
|
||||
{ open: '"', close: '"' },
|
||||
{ open: "'", close: "'" },
|
||||
],
|
||||
// symbols that can be used to surround a selection
|
||||
surroundingPairs: [
|
||||
{ open: "{", close: "}" },
|
||||
{ open: "[", close: "]" },
|
||||
{ open: "(", close: ")" },
|
||||
{ open: '"', close: '"' },
|
||||
{ open: "'", close: "'" },
|
||||
],
|
||||
onEnterRules: [
|
||||
{
|
||||
beforeText: /^\s+$/,
|
||||
action: {
|
||||
indentAction: monaco.languages.IndentAction.Outdent,
|
||||
},
|
||||
},
|
||||
{
|
||||
beforeText: /\bwhere$/,
|
||||
action: {
|
||||
indentAction: monaco.languages.IndentAction.Indent,
|
||||
},
|
||||
},
|
||||
{
|
||||
beforeText: /\bof$/,
|
||||
action: {
|
||||
indentAction: monaco.languages.IndentAction.Indent,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export let newtTokens: monaco.languages.IMonarchLanguage = {
|
||||
// Set defaultToken to invalid to see what you do not tokenize yet
|
||||
// defaultToken: 'invalid',
|
||||
defaultToken: "invalid",
|
||||
|
||||
keywords: [
|
||||
"let",
|
||||
|
||||
112
playground/src/shim.ts
Normal file
112
playground/src/shim.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
class Buffer extends ArrayBuffer {
|
||||
static alloc(n: number) {
|
||||
return new Buffer(n);
|
||||
}
|
||||
indexOf(n: number) {
|
||||
let view = new Uint8Array(this);
|
||||
return view.indexOf(n);
|
||||
}
|
||||
|
||||
static concat(bufs: Buffer[]) {
|
||||
let size = bufs.reduce((a, b) => (a += b.byteLength), 0);
|
||||
let rval = new Buffer(size);
|
||||
let view = new Uint8Array(rval);
|
||||
let off = 0;
|
||||
for (let buf of bufs) {
|
||||
view.set(new Uint8Array(buf), off);
|
||||
off += buf.byteLength;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
toString() {
|
||||
return new TextDecoder().decode(this);
|
||||
}
|
||||
}
|
||||
|
||||
let files: Record<string, string> = {};
|
||||
interface Handle {
|
||||
name: string;
|
||||
mode: string;
|
||||
pos: number;
|
||||
buf: Buffer;
|
||||
}
|
||||
let fds: Handle[] = [];
|
||||
|
||||
let shim: any = {
|
||||
os: {
|
||||
platform() {
|
||||
return "linux";
|
||||
},
|
||||
},
|
||||
fs: {
|
||||
openSync(name: string, mode: string) {
|
||||
console.log("open", name, arguments);
|
||||
let te = new TextEncoder();
|
||||
|
||||
let fd = fds.findIndex((x) => !x);
|
||||
if (fd < 0) fd = fds.length;
|
||||
let buf;
|
||||
let pos = 0;
|
||||
if (mode == "w") {
|
||||
buf = new Buffer(0);
|
||||
} else {
|
||||
if (!files[name]) throw new Error(`${name} not found`);
|
||||
buf = te.encode(files[name]);
|
||||
}
|
||||
fds[fd] = { buf, pos, mode, name };
|
||||
// we'll mutate the pointer as stuff is read
|
||||
return fd;
|
||||
},
|
||||
writeSync(fd: number, line: string) {
|
||||
if (typeof line !== "string") throw new Error("not implemented");
|
||||
let handle = fds[fd];
|
||||
if (!handle) throw new Error(`bad fd ${fd}`)
|
||||
let buf2 = new TextEncoder().encode(line);
|
||||
handle.buf = Buffer.concat([handle.buf, buf2])
|
||||
},
|
||||
chmodSync(fn: string, mode: number) {
|
||||
console.log('chmod', fn, mode)
|
||||
},
|
||||
readSync(fd: number, buf: Buffer, start: number, len: number) {
|
||||
let hand = fds[fd];
|
||||
let avail = hand.buf.byteLength - hand.pos;
|
||||
let rd = Math.min(avail, len);
|
||||
let src = new Uint8Array(hand.buf);
|
||||
let dest = new Uint8Array(buf);
|
||||
for (let i = 0; i < rd; i++) dest[start + i] = src[hand.pos++];
|
||||
return rd;
|
||||
},
|
||||
closeSync(fd: number) {
|
||||
let handle = fds[fd];
|
||||
if (handle.mode == "w") {
|
||||
files[handle.name] = new TextDecoder().decode(handle.buf);
|
||||
}
|
||||
delete fds[fd];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Spy on Idris' calls to see what we need to fill in
|
||||
shim.fs = new Proxy(shim.fs, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop in target) {
|
||||
return (target as any)[prop];
|
||||
}
|
||||
throw new Error(`implement fs.${String(prop)}`)
|
||||
},
|
||||
});
|
||||
|
||||
const process: Process = {
|
||||
platform: "linux",
|
||||
argv: ["", ""],
|
||||
stdout: {
|
||||
// We'll want to replace this one
|
||||
write: console.log,
|
||||
},
|
||||
exit(v: number) {
|
||||
console.log("exit", v);
|
||||
},
|
||||
// stdin: { fd: 0 },
|
||||
};
|
||||
|
||||
const require = (x: string) => shim[x];
|
||||
Reference in New Issue
Block a user