NeetoCourse allows anyone to build interactive courses where they can add codeblocks and assessments. This allows the user to run their code, see the output and check if their solution is correct or not. Check out Bigbinary Academy's JavaScript course to see this in action.
Let's see how we evaluate JavaScript code and check if the output matches the corresponding solution.
For a simple synchronous code, first thing we need to check is if everything logged by the user is same as that of the solution code. What we do here is aggregate all the logs to an array and then compare that array with the array generated by the solution code. This is done by transforming the code using an AST library
Take this exercise as an example.
Now let's see the transformed code.
Here pushTologs
replaces the console.log
function and logsAggregator
is an
array which stores all the logs. We also replace throw
statements with
pushToLogs
to evaluate exceptions.
We also perform serialization to make comparison easier. The transformed code is then ran as an IIFE and the result is used for comparison.
We run the user submitted code in an iframe so that any bug in the submitted code doesn't mess up the page.
Let's see how this "code transformation" works. We mentioned the use of an AST library. AST(abstract syntax tree) is a tree representation of the code which helps the compiler to understand the structure of the code. Let's use a tool called AST Explorer to see how the AST looks like for the below code.
const priceOfPencil = 5;
console.log("Price of 1 pencil:");
console.log(priceOfPencil);
Here, MemberExpression
is a node and in that node we can use object.name
.
See the underline highlights using pink color.
Using object.name
we can get to value console
. See the green arrow.
Similarly using property.name
we can get to log
.
Now our goal is to walk the tree and replace all console.log
statements with
pushToLogs
statement. For walking and replacing the value we will use the
replace
function provided by the library.
replace(tree, node => {
const { callee } = node;
// create the node that needs to be put instead of console.log
const pushToLogsExpression = parse("pushToLogs()").body[0].expression;
// check for the console.log node
if (
callee?.type === "MemberExpression" &&
callee?.object?.name === "console" &&
callee?.property?.name === "log"
) {
pushToLogsExpression.arguments = node.arguments;
node = pushToLogsExpression;
}
return node;
});
Here we are creating a new node by parsing the string "pushToLogs()"
. We are
then adding the arguments of the console.log
to the pushToLogs
node. When we
return this new node, the code transformation is complete.
Evaluating async code is a bit tricky since we won't get the output of the code right away. What we do in this case is transform the code to make it synchronous. For evaluating the output, these are the information we need:
We will transform the code in such a way that these information are available to us. Let's see how the code is transformed in the following cases:
In this case the function that needs to be executed after the specified timeout
is executed inline. And the delay value is just added to the logsAggregator
for record keeping. There will be no delay in the evaluation.
We do the same for setInterval
.
In both the cases we evaluate the function "inline" and then compare the console.log outputs.
What if we want an exercise involving clearTimeout
? We simply add
Timeout cleared
to the logsAggregator
.
Without going too much in details, to evaluate Promises all we did was move the
callbacks from then
method of the promise to arguments of a function.
We take care of async/await
code in similar style.
What happens in case of promise chaining?
Here, if we detect that there are more than one then
calls, then the second
body of then
is passed as resolveFn
to the function that the first then
returns. This can go multiple levels based on the chaining.
In order words we go back to adding "callbacks".
This is how we evaluate javascript in NeetoCourse. We evaluate HTML, CSS and SQL similarly on the browser. We have also recently added evaluation of HTML Canvas. Evaluation of HTML Canvas animations is on the roadmap. But these can be a story for another day.
Want to see code evaluation in action. Checkout this question from our JavaScript course.
Interested to know more about NeetoCourse? Follow @NeetoCourse to see what we're up to.
If this blog was helpful, check out our full blog archive.