view src/nabble/naml/namespaces/BasicNamespace.java @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children
line wrap: on
line source

package nabble.naml.namespaces;

import fschmidt.util.java.ArrayStack;
import fschmidt.util.java.ObjectUtils;
import fschmidt.util.java.Stack;
import fschmidt.util.mail.MailAddress;
import nabble.naml.compiler.Command;
import nabble.naml.compiler.CommandSpec;
import nabble.naml.compiler.CompileException;
import nabble.naml.compiler.Encoder;
import nabble.naml.compiler.ExitException;
import nabble.naml.compiler.IPrintWriter;
import nabble.naml.compiler.Interpreter;
import nabble.naml.compiler.JavaCommand;
import nabble.naml.compiler.Macro;
import nabble.naml.compiler.Namespace;
import nabble.naml.compiler.ScopedInterpreter;
import nabble.naml.compiler.StackTrace;
import nabble.naml.compiler.Template;
import nabble.naml.compiler.TemplateRuntimeException;
import nabble.naml.compiler.NamlNullPointerException;
import nabble.view.web.template.DateNamespace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


@Namespace (
	name = "basic",
	global = true
)
public final class BasicNamespace {
	private static final Logger logger = LoggerFactory.getLogger(BasicNamespace.class);

	private final Template template;

	public BasicNamespace(Template template) {
		this.template = template;
	}

	public static final CommandSpec exit = CommandSpec.NO_OUTPUT;

	@CommandDoc(
		"Exits the page generation and returns the control back to the browser."
	)
	@Command public void exit(IPrintWriter out,Interpreter interp) {
		throw new ExitException();
	}

	public static final CommandSpec throw_runtime_exception = new CommandSpec.Builder()
		.dotParameter("text")
		.outputtedParameters()
		.build()
	;

	@CommandDoc(
		value= "Throws a runtime exception to show that something is wrong or broken. "+
		"If this command is called, the page will display the full stack trace of the error, which "+
		"should be investigated and fixed.",
		params = {"text=The message displayed by the exception"},
		seeAlso = {"throw_template_exception"}
	)
	@Command public void throw_runtime_exception(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		throw new RuntimeException(text);
	}

	public static final CommandSpec throw_template_exception = new CommandSpec.Builder()
		.dotParameter("name")
		.build()
	;

	@CommandDoc(
		value= "Throws an exception that can be caught by the @link{catch_exception} command.",
		params = {"name=The name of the exception, which should be used later for catching and handling."},
		seeAlso = {"ExceptionNamespace.exception"}
	)
	@Command public void throw_template_exception(IPrintWriter out,Interpreter interp)
		throws TemplateException
	{
		String name = interp.getArgString("name");
		throw TemplateException.newInstance(name);
	}

	@Command("true") public static void _true(IPrintWriter out,Interpreter interp) {
		out.print(true);
	}

	@Command("false") public static void _false(IPrintWriter out,Interpreter interp) {
		out.print(false);
	}

	public static final CommandSpec either = new CommandSpec.Builder()
		.parameters("condition1","condition2")
		.build()
	;

	@Command public static void either(IPrintWriter out,Interpreter interp) {
		out.print( interp.getArgAsBoolean("condition1") || interp.getArgAsBoolean("condition2") );
	}

	public static final CommandSpec both = new CommandSpec.Builder()
		.parameters("condition1","condition2")
		.build()
	;

	@Command public static void both(IPrintWriter out,Interpreter interp) {
		out.print( interp.getArgAsBoolean("condition1") && interp.getArgAsBoolean("condition2") );
	}

	public static final CommandSpec _if = new CommandSpec.Builder()
		.dotParameter("condition")
		.optionalParameters("then","else")
		.outputtedParameters("then","else")
		.dontRemoveNulls()
		.build()
	;

	@CommandDoc(
		value= "Calls the \"then\" block if the 'condition' (dot_parameter) is true. " +
			"Otherwise, calls the \"else\" block (if available).",
		params = {
			"condition=Condition to be tested",
			"then=Block for the true case",
			"else=Block for the false case"
		}
	)
	@Command("if") public static void _if(IPrintWriter out,Interpreter interp) {
		Object block = interp.getArg( interp.getArgAsBoolean("condition") ? "then" : "else" );
		if( block != null )
			out.print(block);
	}

	public static final CommandSpec not = new CommandSpec.Builder()
		.dotParameter("condition")
		.build()
	;

	@Command public static void not(IPrintWriter out,Interpreter interp) {
		out.print( !interp.getArgAsBoolean("condition") );
	}

	private static final CommandSpec optionalValue = new CommandSpec.Builder()
		.dotParameter("value")
		.optionalParameters("value")
		.build()
	;

	public static final CommandSpec hide_null = optionalValue;

	@Command public static void hide_null(IPrintWriter out,Interpreter interp) {
		String s = interp.getArgString("value");
		if( s != null )
			out.print(s);
	}


	public static final CommandSpec is_null = optionalValue;

	@Command public static void is_null(IPrintWriter out,Interpreter interp) {
		String s = interp.getArgString("value");
		out.print( s==null );
	}

	@Command("null") public static void _null(IPrintWriter out,Interpreter interp) {
		out.print((String)null);
	}

	public static final CommandSpec null_exception_to_null = optionalValue;

	@Command public static void null_exception_to_null(IPrintWriter out,Interpreter interp) {
		try {
			out.print( interp.getArgString("value") );
		} catch(NamlNullPointerException e) {
			out.print( (String)null );
		}
	}

	private static final Random RANDOM = new Random();

	public static final CommandSpec random = new CommandSpec.Builder()
		.parameters("max")
		.build()
	;

	@Command public void random(IPrintWriter out,Interpreter interp) {
		int max = interp.getArgAsInt("max");
		int r = RANDOM.nextInt(max);
		out.print(r);
	}

	public static final CommandSpec equal = new CommandSpec.Builder()
		.optionalParameters("value1","value2")
		.build()
	;

	@Command public static void equal(IPrintWriter out,Interpreter interp) {
		out.print( ObjectUtils.equals(interp.getArgString("value1"),interp.getArgString("value2")) );
	}

	public static final CommandSpec starts_with = new CommandSpec.Builder()
		.dotParameter("text")
		.parameters("prefix")
		.build()
	;

	@Command public void starts_with(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		String prefix = interp.getArgString("prefix");
		out.print( text.startsWith(prefix) );
	}

	public static final CommandSpec ends_with = new CommandSpec.Builder()
		.dotParameter("text")
		.parameters("suffix")
		.build()
	;

	@Command public void ends_with(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		String suffix = interp.getArgString("suffix");
		out.print( text.endsWith(suffix) );
	}

	public static final CommandSpec substring = new CommandSpec.Builder()
		.dotParameter("text")
		.parameters("begin")
		.optionalParameters("end")
		.build()
	;

	@Command public void substring(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		int begin = Integer.parseInt(interp.getArgString("begin"));
		String end = interp.getArgString("end");
		out.print( end==null ? text.substring(begin) : text.substring(begin,Integer.parseInt(end)) );
	}

	public static final CommandSpec contains_substring = new CommandSpec.Builder()
		.dotParameter("string")
		.parameters("substring")
		.build()
	;

	@Command public void contains_substring(IPrintWriter out,Interpreter interp) {
		out.print( interp.getArgString("string").indexOf(interp.getArgString("substring")) != -1 );
	}

	public static final CommandSpec regex_quote = new CommandSpec.Builder()
		.dotParameter("string")
		.build()
	;

	@Command public void regex_quote(IPrintWriter out,Interpreter interp) {
		out.print( Pattern.quote(interp.getArgString("string")) );
	}

	public static final CommandSpec append = new CommandSpec.Builder()
		.dotParameter("text")
		.parameters("suffix")
		.optionalParameters("text","except_if")
		.restrictedParameter("except_if","already-included")
		.build()
	;

	@Command public void append(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		out.print(text);
		if( text!=null && text.length() > 0 ) {
			String suffix = interp.getArgString("suffix");
			if( !( "already-included".equals(interp.getArgString("except_if"))
				&& text.toLowerCase().indexOf(suffix.toLowerCase()) != -1
			) )
				out.print(suffix);
		}
	}

	public static final CommandSpec prepend = new CommandSpec.Builder()
		.dotParameter("text")
		.parameters("prefix")
		.optionalParameters("text","except_if")
		.restrictedParameter("except_if","already-included")
		.build()
	;

	@Command public void prepend(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		if( text!=null && text.length() > 0 ) {
			String prefix = interp.getArgString("prefix");
			if( !( "already-included".equals(interp.getArgString("except_if"))
				&& text.toLowerCase().indexOf(prefix.toLowerCase()) != -1
			) )
				out.print(prefix);
		}
		out.print(text);
	}

	public static final CommandSpec separate = new CommandSpec.Builder()
		.parameters("text1","separator","text2")
		.build()
	;

	@Command public void separate(IPrintWriter out,Interpreter interp) {
		String text1 = interp.getArgString("text1");
		if( text1 == null )
			throw new NullPointerException("text1 is null");
		String text2 = interp.getArgString("text2");
		if( text2 == null )
			throw new NullPointerException("text2 is null");
		out.print( text1 );
		if( text1.trim().length() > 0 && text2.trim().length() > 0 )
			out.print( interp.getArg("separator") );
		out.print( text2 );
	}

/*
	public static final CommandSpec is_in_namespace = new CommandSpec.Builder()
		.parameters("name")
		.build()
	;

	@Command public static void is_in_namespace(IPrintWriter out,Interpreter interp) {
		out.print( interp.hasNamespace(interp.getArgString("name")) );
	}
*/

	public static final CommandSpec is_in_command = new CommandSpec.Builder()
		.parameters("name")
		.build()
	;

	@Command public static void is_in_command(IPrintWriter out,Interpreter interp) {
		out.print( interp.isInCommandStack(interp.getArgString("name")) );
	}


	public static final CommandSpec to_lower_case = CommandSpec.TEXT;

	@Command public void to_lower_case(IPrintWriter out,Interpreter interp) {
		out.print( interp.getArgString("text").toLowerCase() );
	}

	public static final CommandSpec to_upper_case = CommandSpec.TEXT;

	@Command public void to_upper_case(IPrintWriter out,Interpreter interp) {
		out.print( interp.getArgString("text").toUpperCase() );
	}

	public static final CommandSpec capitalize = CommandSpec.TEXT;

	@Command public void capitalize(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		String[] words = text.split("[ ]");
		StringBuilder b = new StringBuilder();
		for (String w : words) {
			if (b.length() > 0)
				b.append(' ');
			b.append(w.substring(0,1).toUpperCase());
			b.append(w.substring(1,w.length()));
		}
		out.print(b.toString());
	}

	public static final CommandSpec regex = new CommandSpec.Builder()
		.parameters("pattern","text")
		.scopedParameters("do")
		.dotParameter("do")
		.build()
	;

	@Command public static void regex(IPrintWriter out,ScopedInterpreter<RegexNamespace> interp) {
		Pattern p = Pattern.compile( interp.getArgString("pattern") );
		String s = interp.getArgString("text");
		Matcher m = p.matcher(s);
		RegexNamespace ns = new RegexNamespace(m,s);
		out.print( interp.getArg(ns,"do") );
	}

	public static final CommandSpec regex_replace_all = new CommandSpec.Builder()
		.dotParameter("text")
		.parameters("pattern","replacement")
		.build()
	;

	@Command public static void regex_replace_all(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		String pattern = interp.getArgString("pattern");
		String replacement = interp.getArgString("replacement");
		out.print( text.replaceAll(pattern, replacement) );
	}

	public static final CommandSpec regex_replace_first = new CommandSpec.Builder()
		.dotParameter("text")
		.parameters("pattern","replacement")
		.build()
	;

	@Command public static void regex_replace_first(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		String pattern = interp.getArgString("pattern");
		String replacement = interp.getArgString("replacement");
		out.print( text.replaceFirst(pattern, replacement) );
	}

	public static final CommandSpec string_replace_all = new CommandSpec.Builder()
		.dotParameter("text")
		.parameters("target","replacement")
		.build()
	;

	@Command public static void string_replace_all(IPrintWriter out,Interpreter interp) {
		out.print( interp.getArgString("text").replace(interp.getArgString("target"),interp.getArgString("replacement")) );
	}

	public static final CommandSpec trim = CommandSpec.TEXT;

	@Command public static void trim(IPrintWriter out,Interpreter interp) {
		String s = interp.getArgString("text");
		out.print( s==null ? null : s.trim() );
	}

	public static final CommandSpec encode = CommandSpec.OPTIONAL_TEXT;

	@Command public static void encode(IPrintWriter out,Interpreter interp) {
		String s = interp.getArgString("text");
		out.print( interp.encode(s) );
	}

	public static final CommandSpec use_text_encoder = CommandSpec.OPTIONAL_TEXT;

	@Command public void use_text_encoder(IPrintWriter out,Interpreter interp) {
		interp.setEncoder(Encoder.TEXT);
		String s = interp.getArgString("text");
		out.print(s);
	}

	public static final CommandSpec use_html_encoder = CommandSpec.OPTIONAL_TEXT;

	@Command public void use_html_encoder(IPrintWriter out,Interpreter interp) {
		interp.setEncoder(Encoder.HTML);
		String s = interp.getArgString("text");
		out.print(s);
	}

	public static final CommandSpec use_url_encoder = CommandSpec.OPTIONAL_TEXT;

	@Command public void use_url_encoder(IPrintWriter out,Interpreter interp) {
		interp.setEncoder(Encoder.URL);
		String s = interp.getArgString("text");
		out.print(s);
	}

	public static final CommandSpec is_empty = optionalValue;

	@Command public static void is_empty(IPrintWriter out,Interpreter interp) {
		String s = interp.getArgString("value");
		out.print( s==null || s.length() == 0 );
	}


	public static final CommandSpec var = new CommandSpec.Builder()
		.parameters("name")
		.build()
	;

	@Command public void var(IPrintWriter out,Interpreter interp) {
		throw new RuntimeException("never");
	}

	public static final CommandSpec set_var = new CommandSpec.Builder()
		.parameters("name")
		.dotParameter("value")
		.optionalParameters("value")
		.outputtedParameters()
		.build()
	;

	@Command public void set_var(IPrintWriter out,Interpreter interp) {
		throw new RuntimeException("never");
	}

	public static final CommandSpec uplevel_var = var;

	@Command public void uplevel_var(IPrintWriter out,Interpreter interp) {
		throw new RuntimeException("never");
	}

	public static final CommandSpec uplevel_set_var = set_var;

	@Command public void uplevel_set_var(IPrintWriter out,Interpreter interp) {
		throw new RuntimeException("never");
	}

	private final Map<String,String> globalVars = new HashMap<String,String>();

	public static final CommandSpec global_var = var;

	@Command public void global_var(IPrintWriter out,Interpreter interp) {
		String name = interp.getArgString("name");
		out.print( globalVars.get(name) );
	}

	public static final CommandSpec global_set_var = set_var;

	@Command public void global_set_var(IPrintWriter out,Interpreter interp) {
		String name = interp.getArgString("name");
		String value = interp.getArgString("value");
		globalVars.put(name,value);
	}

	public static final CommandSpec global_is_var_set = var;

	@Command public void global_is_var_set(IPrintWriter out,Interpreter interp) {
		String name = interp.getArgString("name");
		out.print( globalVars.containsKey(name) );
	}


	public static final CommandSpec _default = new CommandSpec.Builder()
		.dotParameter("text")
		.optionalParameters("text")
		.parameters("to")
		.build()
	;

	@Command("default") public static void _default(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text");
		out.print( text!=null ? text : interp.getArgString("to") );
	}

	private static final int whileLimit = 1000000;

	private static class BreakException extends TemplateRuntimeException {
		BreakException() {
			super("break called outside of while");
		}
	}

	public static final CommandSpec _while = new CommandSpec.Builder()
		.dotParameter("condition")
		.parameters("loop")
		.outputtedParameters("loop")
		.build()
	;

	@Command("while") public void _while(IPrintWriter out,Interpreter interp) {
		Object condition = interp.getArg("condition");
		Object block = interp.getArg("loop");
		int count = 0;
		try {
			while( Template.booleanValue(condition) ) {
				if( ++count > whileLimit )
					throw new RuntimeException("while loop limit of "+whileLimit+" iterations exceeded");
				out.print(block);
			}
		} catch(BreakException e) {}
	}

	@Command("break") public void _break(IPrintWriter out,Interpreter interp) {
		throw new BreakException();
	}


	@Command public void nop(IPrintWriter out,Interpreter interp) {}


	private static final class ExceptionInfo {
		private final String forStr;
		private final TemplateException ex;

		private ExceptionInfo(String forStr,TemplateException ex) {
			this.forStr = forStr;
			this.ex = ex;
		}
	}

	private Stack<ExceptionInfo> exceptionStack = new ArrayStack<ExceptionInfo>();

	public static final CommandSpec catch_exception = new CommandSpec.Builder()
		.parameters("id")
		.dotParameter("do")
		.outputtedParameters("do")
		.build()
	;

	@Command public void catch_exception(IPrintWriter out,Interpreter interp) {
		String idStr = interp.getArgString("id");
		TemplateException ex = null;
		try {
			out.print( interp.getArg("do") );
		} catch(TemplateRuntimeException e) {
			Throwable cause = e.getCause();
			if( cause instanceof TemplateException ) {
				ex = (TemplateException)cause;
			} else {
				throw e;
			}
		}
		exceptionStack.push( new ExceptionInfo(idStr,ex) );
	}

	@Namespace (
		name = "error",
		global = false
	)
	public static class ExceptionNamespace {
		protected final TemplateException ex;
		protected boolean isDone = false;

		public ExceptionNamespace(TemplateException ex) {
			this.ex = ex;
		}

		public static final CommandSpec exception = new CommandSpec.Builder()
			.parameters("name")
			.dotParameter("do")
			.build()
		;

		@Command public void exception(IPrintWriter out,Interpreter interp) {
			if( isDone )
				return;
			String name = interp.getArgString("name");
			if( ex.isNamed(name) ) {
				out.print( interp.getArg("do") );
				isDone = true;
			}
		}

		public static final CommandSpec unknown_exception = CommandSpec.DO;

		@Command public void unknown_exception(IPrintWriter out,ScopedInterpreter<UnknownException> interp) {
			if( isDone )
				return;
			out.print( interp.getArg(new UnknownException(ex), "do") );
			isDone = true;
		}

		protected static final CommandSpec scopedCommandSpec = new CommandSpec.Builder()
			.parameters("name")
			.dotParameter("do")
			.scopedParameters("do")
			.build()
		;

		protected final <N> void scopedException(IPrintWriter out,ScopedInterpreter<N> interp,N ns) {
			if( isDone )
				return;
			String name = interp.getArgString("name");
			if( ex.isNamed(name) ) {
				out.print(interp.getArg(ns,"do"));
				isDone = true;
			}
		}

		protected final <N> void unnamedScopedException(IPrintWriter out,ScopedInterpreter<N> interp,N ns) {
			if( isDone )
				return;
			out.print(interp.getArg(ns,"do"));
			isDone = true;
		}

	}

	@Namespace (
		name = "unknown_exception",
		global = false
	)
	public static class UnknownException extends ExceptionNamespace {

		public UnknownException(TemplateException ex) {
			super(ex);
		}

		@Command public void message(IPrintWriter out,Interpreter interp) {
			out.print(ex.getMessage());
		}
	}

	public static interface ExceptionNamespaceFactory<E extends ExceptionNamespace> {
		public E newExceptionNamespace(TemplateException ex);
	}

/*	private static final ExceptionNamespaceFactory<ExceptionNamespace> myExceptionNamespaceFactory =
		new ExceptionNamespaceFactory<ExceptionNamespace>() {
			public ExceptionNamespace newExceptionNamespace(TemplateException ex) {
				return new ExceptionNamespace(ex);
			}
		}
	;*/

	public static final CommandSpec handle_exception = CommandSpec.DO()
		.parameters("for")
		.requiredInStack(BasicNamespace.class)
		.build()
	;
/*
	@Command public void handle_exception(IPrintWriter out,ScopedInterpreter<ExceptionNamespace> interp) {
		handleException(out,interp,myExceptionNamespaceFactory);
	}
*/
	public <E extends ExceptionNamespace> void handleException(IPrintWriter out,ScopedInterpreter<E> interp,ExceptionNamespaceFactory<E> factory) {
		if( exceptionStack.isEmpty() )
			throw new RuntimeException("there are no exceptions available to be handled");
		ExceptionInfo ei = exceptionStack.pop();
		String forStr = interp.getArgString("for");
		if( !forStr.equals(ei.forStr) )
			throw new RuntimeException("found exception for: "+ei.forStr);
		if( ei.ex == null )
			return;
		E en = factory.newExceptionNamespace(ei.ex);
		out.print( interp.getArg(en,"do") );
		if( !en.isDone ) {
			logger.error("unexpected exception"+StackTrace.current(),en.ex);
			out.print( "Unexpected exception: " + en.ex );
		}
	}

	public static final CommandSpec has_exception = new CommandSpec.Builder()
		.parameters("for")
		.build()
	;

	@Command public void has_exception(IPrintWriter out,Interpreter interp) {
		if( exceptionStack.isEmpty() )
			throw new RuntimeException("there are no exceptions available to be handled");
		ExceptionInfo ei = exceptionStack.peek();
		String forStr = interp.getArgString("for");
		if( !forStr.equals(ei.forStr) )
			throw new RuntimeException("found exception for: "+ei.forStr);
		out.print( ei.ex != null );
	}

	public static final CommandSpec string_list = CommandSpec.DO()
		.optionalParameters("values","separator","trim")
		.build()
	;

	@Command public void string_list(IPrintWriter out,ScopedInterpreter<StringList> interp) {
		List<String> list = new ArrayList<String>();
		String csv = interp.getArgString("values");
		if( csv != null ) {
			String separator = interp.getArgString("separator");
			if( separator == null )
				separator = ",";
			boolean trim = interp.getArgAsBoolean("trim", true);
			for( String s : csv.split(separator) ) {
				list.add( trim? s.trim() : s );
			}
		}
		Object block = interp.getArg(new StringList(list),"do");
		out.print(block);
	}

	public static final CommandSpec string_map = new CommandSpec.Builder()
		.parameters("key")
		.dotParameter("entries")
		.build()
	;

	@Command public void string_map(IPrintWriter out, Interpreter interp) {
		String key = interp.getArgString("key");
		String[] entries = interp.getArgString("entries").split("\n");
		for (String entry : entries) {
			String[] keyValue = entry.split(":");
			if (key.equals(keyValue[0].trim())) {
				out.print(keyValue[1].trim());
				return;
			}
		}
		out.print((String) null);
	}

	public static final CommandSpec _int = CommandSpec.DO()
		.parameters("i")
		.build()
	;

	@Command("int") public void _int(IPrintWriter out,ScopedInterpreter<IntegerNamespace> interp) {
		out.print( interp.getArg(new IntegerNamespace(interp.getArgAsInt("i")),"do") );
	}

	public static final CommandSpec extract_email_address_from = new CommandSpec.Builder()
		.dotParameter("text")
		.build()
	;

	@Command public static void extract_email_address_from(IPrintWriter out,Interpreter interp) {
		String text = interp.getArgString("text").trim();
		String email = null;
		if (new MailAddress(text).isValid()) {
			email = text;
		} else {
			int posOpen = text.lastIndexOf('<');
			int posClose = text.lastIndexOf('>');
			if (posOpen >= 0 && posClose > posOpen) {
				String middle = text.substring(posOpen+1, posClose);
				if (new MailAddress(middle).isValid())
					email = middle;
			}
		}
		out.print(email);
	}

	@Command public void crlf(IPrintWriter out,Interpreter interp) {
		out.print("\r\n");
	}

	@Command public void space(IPrintWriter out,Interpreter interp) {
		out.print(' ');
	}

   	@Command public void double_quote(IPrintWriter out,Interpreter interp) {
		out.print("\"");
	}

	public static final CommandSpec has_module = new CommandSpec.Builder()
		.dotParameter("module")
		.build()
	;

	@Command public static void has_module(IPrintWriter out,Interpreter interp) {
		out.print( interp.hasModule(interp.getArgString("module")) );
	}

	public static final CommandSpec no_output = CommandSpec.NO_OUTPUT()
		.dotParameter("text")
		.build()
	;

	@Command public static void no_output(IPrintWriter out,Interpreter interp) {
		interp.getArgString("text");
	}


	@Command public static void lt(IPrintWriter out,Interpreter interp) {
		out.print('<');
	}

	@Command public static void gt(IPrintWriter out,Interpreter interp) {
		out.print('>');
	}

	public static final CommandSpec now = CommandSpec.DO;

	@Command public void now(IPrintWriter out,ScopedInterpreter<DateNamespace> interp) {
		out.print( interp.getArg(new DateNamespace(new Date()), "do" ));
	}

	public static final CommandSpec call_depth = CommandSpec.DO;

	@Command public void call_depth(IPrintWriter out,ScopedInterpreter<IntegerNamespace> interp) {
		out.print( interp.getArg(new IntegerNamespace(interp.callDepth()),"do") );
	}


	public static final CommandSpec _switch = CommandSpec.DO()
		.optionalParameters("value")
		.build()
	;

	@Command("switch") public void _switch(IPrintWriter out,ScopedInterpreter<SwitchNamespace> interp) {
		out.print( interp.getArg(new SwitchNamespace(interp.getArgString("value")),"do") );
	}

	@Namespace (
		name = "switch",
		global = false,
		transparent = true
	)
	public static final class SwitchNamespace {
		private final String value;
		private boolean isDone = false;

		private SwitchNamespace(String value) {
			this.value = value;
		}

		@Command public void switch_value(IPrintWriter out,Interpreter interp) {
			out.print( value );
		}

		public static final CommandSpec _case = new CommandSpec.Builder()
			.parameters("value")
			.dotParameter("do")
			.outputtedParameters()  // to trim surrounding white-space
			.build()
		;

		@Command("case") public void _case(IPrintWriter out,Interpreter interp) {
			if( !isDone && interp.getArgString("value").equals(value) ) {
				out.print( interp.getArg("do") );
				isDone = true;
			}
		}

		public static final CommandSpec default_case = new CommandSpec.Builder()
			.dotParameter("do")
			.outputtedParameters("do")
			.build()
		;

		@Command public void default_case(IPrintWriter out,Interpreter interp) {
			if( !isDone ) {
				out.print( interp.getArg("do") );
				isDone = true;
			}
		}

		public static final CommandSpec regex_case = new CommandSpec.Builder()
			.parameters("regex")
			.scopedParameters("do")
			.dotParameter("do")
			.outputtedParameters("do")
			.build()
		;

		@Command public void regex_case(IPrintWriter out,ScopedInterpreter<RegexNamespace> interp) {
			if( !isDone ) {
				Matcher m = Pattern.compile(interp.getArgString("regex")).matcher(value);
				if( m.matches() ) {
					out.print( interp.getArg(new RegexNamespace(m,value),"do") );
					isDone = true;
				}
			}
		}

	}



	@Namespace (
		name = "block",
		global = false
	)
	public static final class BlockNamespace {}

	private static final BlockNamespace BLOCK = new BlockNamespace();

	public static final CommandSpec block = CommandSpec.DO;

	@Command public void block(IPrintWriter out,ScopedInterpreter<BlockNamespace> interp) {
		out.print( interp.getArg(BLOCK,"do") );
	}



	@Namespace (
		name = "counter",
		global = false
	)
	public static final class CounterNamespace {
		private int counter = 0;

		public static final CommandSpec increment = CommandSpec.NO_OUTPUT;

		@Command public void increment(IPrintWriter out,Interpreter interp) {
			counter++;
		}

		@Command public void value(IPrintWriter out,Interpreter interp) {
			out.print(counter);
		}

	}

	private Map<String,CounterNamespace> counterMap = null;

	public static final CommandSpec counter = CommandSpec.DO()
		.parameters("name")
		.build()
	;

	@Command public void counter(IPrintWriter out,ScopedInterpreter<CounterNamespace> interp) {
		if( counterMap == null )
			counterMap = new HashMap<String,CounterNamespace>();
		String name = interp.getArgString("name");
		if( name==null )
			throw new NullPointerException("name is required");
		CounterNamespace ns = counterMap.get(name);
		if( ns == null ) {
			ns = new CounterNamespace();
			counterMap.put(name,ns);
		}
		out.print( interp.getArg(ns,"do") );
	}


	public static final CommandSpec compile_template = CommandSpec.NO_OUTPUT()
		.parameters("macro","namespaces")
		.build()
	;

	@Command public void compile_template(IPrintWriter out,Interpreter interp)
		throws ClassNotFoundException, CompileException
	{
		String macroName = interp.getArgString("macro");
		String namespacesStr = interp.getArgString("namespaces").trim();
		String[] namespaces = namespacesStr.length()==0 ? new String[0] : namespacesStr.split("\\s*,\\s*");
		if( interp.template().program().getTemplate( macroName, namespaces ) == null )
			throw new RuntimeException("macro '"+macroName+"' not found");
	}




	private static final CommandSpec ID = new CommandSpec.Builder()
		.parameters("id")
		.build();

	public static final CommandSpec command_exists = ID;

	@Command public void command_exists(IPrintWriter out, Interpreter interp) {
		out.print(template.program().getMeaning(interp.getArgString("id")) != null);
	}

	public static final CommandSpec command_is_binary = ID;

	@Command public void command_is_binary(IPrintWriter out, Interpreter interp) {
		out.print(JavaCommand.isJavaCommandId(interp.getArgString("id")));
	}

	public static final CommandSpec command_name = ID;

	@Command public void command_name(IPrintWriter out, Interpreter interp) {
		String id = interp.getArgString("id");
		out.print(Macro.getNameFromId(id));
	}

	public static final CommandSpec command_source_name = ID;

	@Command public void command_source_name(IPrintWriter out, Interpreter interp) {
		String id = interp.getArgString("id");
		out.print(Macro.getSourceFromId(id));
	}


	public static final CommandSpec get_macro_id = new CommandSpec.Builder()
		.parameters("macro_name")
		.build()
	;

	@Command public void get_macro_id(IPrintWriter out, Interpreter interp) {
		String name = interp.getArgString("macro_name");
		Collection<Macro> macros = template.program().getMacrosByName(name);
		if( macros.isEmpty() )
			throw new RuntimeException("macro not found");
		if( macros.size() != 1 )
			throw new RuntimeException("macro not unique");
		Macro macro = macros.iterator().next();
		out.print(macro.getId());
	}


}