@readable-js/core
Version:
ReadableJS is a toolkit for teachers to craft small demos that can be explored interactively.
102 lines (101 loc) • 5.28 kB
JavaScript
import { isPattern } from '../../../../../enode-type-check';
import * as escodegen from 'escodegen';
function helperLOC(loc) {
return [loc.start.line, loc.end.line];
}
const ge = generateReadableExpression;
export function generateReadableExpression(e, options, evaluateVar) {
const m2 = new Map([
['CallExpression', (exp) => [...helperLOC(exp.loc), `Call function ${evaluateVar[0]}`]],
['UpdateExpression', (exp) => [
...helperLOC(exp.loc),
`Increment <span class="${options.classNames.variable}">${evaluateVar[0]}</span> by <span class="${options.classNames.value}">1</span>`
]],
['AssignmentExpression', (exp) => {
const varValue = `<span class="${options.classNames.value}">` + "${" + evaluateVar[0] + "}" + "</span>";
const varName = `<span class="${options.classNames.variable}">${evaluateVar[0]}</span>`;
const right = `<span class="${options.classNames.expression}">${escodegen.generate(exp.right)}</span>`;
const mapAssignmentExp = new Map([
['=', (a) => `Set ${varName} to ${varValue}`],
['+=', (a) => `Add ${right} to ${varName} and set ${varName} to ${varValue}`],
['-=', (a) => `Subtract ${right} from ${varName} and set ${varName} to ${varValue}`],
['*=', (a) => `Multiply ${varName} by ${right} and set ${varName} to ${varValue}`],
['/=', (a) => `Divide ${varName} by ${right} and set ${varName} to ${varValue}`],
['%=', (a) => `Divide ${varName} by ${right} and set ${varName} to the remainder: ${varValue}`],
['**=', (a) => ``],
['<<=', (a) => ``],
['>>=', (a) => ``],
['>>>=', (a) => ``],
['|=', (a) => ``],
['^=', (a) => ``],
['&=', (a) => ``],
]);
const mapAssignmentExpGet = mapAssignmentExp.get(exp.operator);
return [...helperLOC(exp.loc), mapAssignmentExpGet ? mapAssignmentExpGet(exp) : ''];
}]
]);
const m2Get = m2.get(e.type);
return [m2Get ? m2Get(e) : [...helperLOC(e.loc), '']];
}
export function generateReadable(eNode, options, evaluateVar, ifConditionTest) {
const m = new Map([
// return multiple message, one for each variable declaration
["VariableDeclaration", (e) => {
if (!evaluateVar) {
throw new Error('Unexpected case: evaluateVar undefined in generateReadable VariableDeclaration parser.');
}
const messages = e.declarations.map((dec, i) => {
const varToName = new Map([
['let', 'variable'],
['var', 'variable'],
['const', 'constant'],
]);
let createMessage = '';
if (isPattern.Identifier(dec.id)) {
createMessage = `Create the ${varToName.get(e.kind)} <span class="${options.classNames.variable}">${dec.id.name}</span>`;
}
let initMessage = '';
if (!!dec.init) {
initMessage = ` and set it to <span class="${options.classNames.value}">${"${" + evaluateVar[i] + "}"}</span>`;
}
let message = createMessage + initMessage;
return [...helperLOC(dec.loc), message];
});
return messages;
}],
["IfStatement", (e) => {
return [
[
e.loc.start.line,
e.test.loc.end.line,
`${ifConditionTest ? 'Because ... evaluate to true' : 'Skip because ... evaluate to false'}`,
],
];
}],
["ExpressionStatement", (e) => {
const messages = generateReadableExpression(e.expression, options, evaluateVar);
return messages;
}],
['WhileStatement', (e) => {
return [
[e.loc.start.line, e.test.loc.end.line, ifConditionTest ? 'Because ...' : 'Because ... is not'],
[e.loc.end.line, e.loc.end.line, ifConditionTest ? '... and try again' : '... stop looping']
];
}],
['ForStatement', (e) => {
var _a, _b;
return [
[e.loc.start.line, (_b = (_a = e.test) === null || _a === void 0 ? void 0 : _a.loc.end.line) !== null && _b !== void 0 ? _b : e.loc.start.line, ifConditionTest ? 'Because ...' : 'Because ... is not'],
[e.loc.end.line, e.loc.end.line, ifConditionTest ? '... and try again' : '... stop looping']
];
}],
]);
const parser = m.get(eNode.type);
if (parser) {
return parser(eNode);
}
else {
console.warn(`No readable generated for type ${eNode.type}.`);
return [];
}
}