Civet
Civet copied to clipboard
try/for/if/do expressions don't support return/break/continue/yield
compiled :=
try
require('@danielx/civet').compile str, options
catch err
return "error"
transpiles to
const compiled = (() => {
try {
return require("@danielx/civet").compile(str, options);
} catch (err) {
return "error";
}
})();
I would have expected the return in the catch block to escape the entire function, not just the IIFE.
This is a general issue with IIFEs, including for expressions. I'm not sure what the best solution is. For now I guess we could throw an error if there's a return inside the try block...
CoffeeScript's solution is to raise a parse error but I think we can do better.
const returnSymbol = Symbol("return")
const compiled = (() => {
try {
return {[returnSymbol]:
require("@danielx/civet").compile(str, options)
};
} catch (err) {
return "error";
}
})();
if (typeof compiled === "object" && returnSymbol in compiled) return compiled[returnSymbol]
Something along those lines.
Agreed! Another option would be to use exceptions (again 😅 ):
try {
const compiled = (() => {
try {
return require("@danielx/civet").compile(str, options);
} catch (err) {
throw new ReturnError("error");
}
})();
} catch (e) {
if (e instanceof ReturnError) return e;
throw e;
}
But I think I prefer your solution so we don't mess with the source of exceptions...
Also related break label inside of nested iteration iifes (contingent on our eventual label support).
Another solution might be not using IFFEs for this at all. Code in the original post can be compiled into something like
let compiled$temp;
try {
compiled$temp = require("@danielx/civet").compile(str, options);
} catch (err) {
return "error";
}
const compiled = compiled$temp;
@juh9870 Yes, this approach is covered in #381.
Reading about Janet, I realize that this is a special case of a more general feature. See Janet's prompt, return, and signals in general (which is how I believe prompt and return are implemented). We could imagine exposing this to the user as well, and building this functionality on top of it:
out := ::here f()
function f()
break ::here = 5
---
class PromptHere {}
const out = (function(){
try {
return f()
} catch (e) {
if (e instanceof PromptHere) {
return e.value
} else {
throw e;
}
}
})()
function f() {
throw new PromptHere(5)
}