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

Playground eval #941

Merged
merged 4 commits into from
Feb 5, 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
42 changes: 27 additions & 15 deletions civet.dev/.vitepress/components/PlaygroundFull.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,30 +40,42 @@ function onInput(civet: string, js: string) {
updateUrl();
}

const defaultConsoleLog = console.log;
const evalOutput = ref<null | string>(null);
function runInBrowser() {
const output: string[] = [];
console.log = (...args) => {
defaultConsoleLog(...args);
output.push(
const evalComplete = ref<boolean>(true);

window.civetconsole = {};
Object.keys(console).forEach((key) => {
window.civetconsole[key] = (...args) => {
console[key](...args);
evalOutput.value +=
(key === 'log' ? '' : `[${key.toUpperCase()}] `) +
args
.map((arg) =>
typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg
typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg.toString()
)
.join(' ')
);
.join(' ') +
'\n';
};
});

function runInBrowser() {
const code = jsCode.value
evalOutput.value = ''
evalComplete.value = false
try {
eval(jsCode.value);
// Indirect eval call (via ?.) causes evaluation in global scope,
// instead of the scope of this function.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#direct_and_indirect_eval
const returned = eval?.(code);
if (returned !== undefined &&
!code.endsWith('civetconsole.log("[EVAL] "+x))')) {
evalOutput.value += `[EVAL] ${returned}\n`
}
} catch (err) {
output.push(err.toString());
console.error(err);
evalOutput.value += `[THROWN] ${err.toString()}\n`
}

evalOutput.value = output.join('\n');
console.log = defaultConsoleLog;
evalComplete.value = true
}

const clearTrigger = ref(false);
Expand Down Expand Up @@ -91,7 +103,7 @@ function clear() {
<div v-if="evalOutput !== null">
<h2>Console output</h2>
<pre
class="eval-output shiki one-dark-pro">{{ evalOutput }}<span v-if="!evalOutput">
class="eval-output shiki one-dark-pro">{{ evalOutput }}<span v-if="!evalOutput && evalComplete">
_._ _,-'""`-._ Nothing to show...
(,-.`._,'( |\`-/| /
`-.-' \ )-`( , o o)
Expand Down
34 changes: 32 additions & 2 deletions civet.dev/public/playground.worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,41 @@ onmessage = async (e) => {
const inputHtml = highlighter.codeToHtml(code, { lang: 'coffee' });

try {
let tsCode = Civet.compile(code);
let ast = Civet.compile(code, { ast: true });
let tsCode = Civet.generate(ast, {});
let jsCode = '';

if (jsOutput) {
jsCode = Civet.compile(code, { js: true });
// Wrap in IIFE if there's a top-level await
const topLevelAwait = Civet.lib.gatherRecursive(ast,
(n) => n.type === 'Await',
Civet.lib.isFunction
).length > 0
if (topLevelAwait) {
const prefix = /^(\s*|;|\'([^'\\]|\\.)*\'|\"([^"\\]|\\.)*\"|\/\/.*|\/\*[^]*?\*\/)*/.exec(code)[0]
const rest = code.slice(prefix.length)
.replace(/\/\/.*|\/\*[^]*?\*\//g, '')
const coffee = /['"]civet[^'"]*coffee(Compat|-compat|Do|-do)/.test(prefix)
ast = Civet.compile(
prefix +
(coffee ? 'do ->\n' : 'async do\n') +
rest.replace(/^/gm, ' '),
{ ast: true }
);
}

// Convert console to civetconsole for Playground execution
Civet.lib.gatherRecursive(ast,
(n) => n.type === 'Identifier' && n.children?.token === "console"
).forEach((node) => {
node.children.token = "civetconsole"
})

jsCode = Civet.generate(ast, { js: true })

if (topLevelAwait) {
jsCode += `.then((x)=>x!==undefined&&civetconsole.log("[EVAL] "+x))`
}
}

if (prettierOutput) {
Expand Down
3 changes: 2 additions & 1 deletion source/main.civet
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import parser from "./parser.hera"
{ parse } := parser
import generate, { prune } from "./generate.civet"
import * as lib from "./lib.civet"
import * as util from "./util.civet"
{ SourceMap } := util
export { parse, generate, util, prune }
export { parse, generate, prune, lib, util }

import StateCache from "./state-cache.civet"

Expand Down
Loading