'use strict';

const isStream = require('is-stream');
const escape = require('./html').escape;
const runtime = require('./runtime');
const parser = require('./parser');

function jsFromAst(ast) {
	const code = ["(function(isStream, escape, stringify, consumeStream, renderSection){"];

	let nextFunctionId = 0;
	function defun(nested) {
		const functionName = `f${nextFunctionId++}`;
		code.push(`function* ${functionName} (context) { ${nested} }\n`);
		return functionName;
	}

	let nextConstId = 0;
	function define(value) {
		const constName = `c${nextConstId++}`;
		code.push(`const ${constName} = ${JSON.stringify(value)};\n`);
		return constName;
	}

	const root = defun(ast.generateCode(defun, define));
	code.push(`return ${root};`);

	code.push("})");
	return code.join('');
}

function instantiateCode(code) {
	return eval(code)(isStream, escape, runtime.stringify, runtime.consumeStream, runtime.renderSection);
}

function coerceToAst(source) {
	if (typeof source === 'string') return parser(source);
	else return source;
}

function generateCode(source) {
	const ast = coerceToAst(source);
	return jsFromAst(ast);
}

function compile(source) {
	return instantiateCode(generateCode(source));
}

module.exports = {
	generateCode,
	compile,
};

@@ -163,16 +163,158 @@
 ser');%0A%0A
+function memoize(f) %7B%0A%09const cache = %7B%7D;%0A%09return x =%3E %7B%0A%09%09if (cache.hasOwnProperty(x)) return cache%5Bx%5D;%0A%09%09else return cache%5Bx%5D = f(x);%0A%09%7D;%0A%7D%0A%0A
 function
@@ -327,24 +327,24 @@
 mAst(ast) %7B%0A
-
 %09const code 
@@ -451,30 +451,39 @@
 0;%0A%09
-function defun
+const defun = memoize
 (nested
-)
+ =%3E
  %7B%0A%09
@@ -621,16 +621,18 @@
 Name;%0A%09%7D
+);
 %0A%0A%09let n
@@ -652,30 +652,39 @@
 0;%0A%09
-function defin
+const define = memoiz
 e(value
-)
+ =%3E
  %7B%0A%09
@@ -808,18 +808,20 @@
 stName;%0A
-
 %09%7D
+);
 %0A%0A%09const

Add memoization for constants in compiled templates