diff src/nabble/naml/compiler/CommandSpec.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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nabble/naml/compiler/CommandSpec.java	Thu Mar 21 19:15:52 2019 -0600
@@ -0,0 +1,192 @@
+package nabble.naml.compiler;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+
+public final class CommandSpec {
+
+	public static CommandSpec.Builder DO() {
+		return new CommandSpec.Builder()
+			.scopedParameters("do")
+			.dotParameter("do")
+			.outputtedParameters("do")
+			.optionalParameters("do")
+		;
+	}
+
+	public static final CommandSpec DO = DO().build();
+
+	public static CommandSpec.Builder TEXT() {
+		return new CommandSpec.Builder()
+			.dotParameter("text")
+		;
+	}
+
+	public static final CommandSpec TEXT = TEXT().build();
+
+	public static final CommandSpec OPTIONAL_TEXT = TEXT()
+		.optionalParameters("text")
+		.build()
+	;
+
+	public final static CommandSpec EMPTY = new Builder().build();
+
+	public static CommandSpec.Builder NO_OUTPUT() {
+		return new CommandSpec.Builder()
+			.outputtedParameters()
+		;
+	}
+
+	public static final CommandSpec NO_OUTPUT = NO_OUTPUT().build();
+
+	final Set<String> parameters;
+	final Set<String> scopedParameters;
+	final String dotParameter;
+	final Set<String> requiredParameters;
+	private final Map<String,Set<String>> restrictedParams;
+	final boolean removeNulls;
+	final Set<String> outputtedParameters;
+	final Set<Class> requiredInStack;
+
+	public static class Builder {
+		private final Set<String> parameters = new HashSet<String>();
+		private Set<String> scopedParameters = null;
+		private String dotParameter = null;
+		private Set<String> optionalParameters = null;
+		private Map<String,Set<String>> restrictedParams = null;
+		private boolean removeNulls = true;
+		private Set<String> outputtedParameters = null;
+		private final Set<Class> requiredInStack = new HashSet<Class>();
+
+		public Builder parameters(String... parameters) {
+			this.parameters.addAll( Arrays.asList(parameters) );
+			return this;
+		}
+
+		public Builder scopedParameters(String... params) {
+			this.scopedParameters = new HashSet<String>( Arrays.asList(params) );
+			return this;
+		}
+
+		public Builder dotParameter(String param) {
+			this.dotParameter = param;
+			return this;
+		}
+
+		public Builder optionalParameters(String... params) {
+			if( optionalParameters == null )
+				optionalParameters = new HashSet<String>();
+			optionalParameters.addAll( Arrays.asList(params) );
+			return this;
+		}
+
+		public Builder restrictedParameter(String param,String... options) {
+			if( restrictedParams==null )
+				restrictedParams = new HashMap<String,Set<String>>();
+			restrictedParams.put( param, new HashSet<String>(Arrays.asList(options)) );
+			return this;
+		}
+
+		public Builder dontRemoveNulls() {
+			removeNulls = false;
+			return this;
+		}
+
+		public Builder outputtedParameters(String... params) {
+			if( outputtedParameters == null )
+				outputtedParameters = new HashSet<String>();
+			outputtedParameters.addAll( Arrays.asList(params) );
+			return this;
+		}
+
+		public Builder requiredInStack(Class... requiredInStack) {
+			this.requiredInStack.addAll( Arrays.asList(requiredInStack) );
+			return this;
+		}
+
+		public CommandSpec build() {
+			return new CommandSpec(this);
+		}
+	}
+
+	private CommandSpec(Builder builder) {
+		parameters = builder.parameters;
+		scopedParameters = builder.scopedParameters;
+		dotParameter = builder.dotParameter;
+		restrictedParams = builder.restrictedParams;
+		outputtedParameters = builder.outputtedParameters;
+		requiredInStack = builder.requiredInStack.isEmpty() ? Collections.<Class>emptySet() : builder.requiredInStack;
+		if( scopedParameters != null )
+			parameters.addAll(scopedParameters);
+		if( dotParameter != null )
+			parameters.add(dotParameter);
+		if( restrictedParams != null )
+			parameters.addAll(restrictedParams.keySet());
+		if( outputtedParameters != null )
+			parameters.addAll(outputtedParameters);
+		requiredParameters = new HashSet<String>(parameters);
+		if( builder.optionalParameters != null ) {
+			parameters.addAll(builder.optionalParameters);
+			requiredParameters.removeAll(builder.optionalParameters);
+		}
+		removeNulls = builder.removeNulls;
+		for( String s : parameters ) {
+			if( s.indexOf('-') != -1 )
+				throw new RuntimeException("macro attibute '"+s+"' may not contain '-'");
+		}
+	}
+
+	boolean hasScopedParam(String param) {
+		return scopedParameters!=null && scopedParameters.contains(param);
+	}
+
+	Set<String> getParameters() {
+		return Collections.unmodifiableSet(parameters);
+	}
+
+	boolean hasParameter(String param) {
+		return parameters.contains(param);
+	}
+
+	static final String dotWarning = "you may have forgotten the dot at the end of the enclosing tag name";
+
+	void check(Map<String,String> staticArgs,Map<String,Chunk> dynamicArgs,StackTrace stackTrace)
+		throws CompileException
+	{
+		Set<String> params = new HashSet<String>();
+		params.addAll(staticArgs.keySet());
+		params.addAll(dynamicArgs.keySet());
+		if( !parameters.containsAll(params) ) {
+			params.removeAll(parameters);
+			String msg = "parameter(s) "+params+" not allowed, only use "+parameters;
+			if( dotParameter != null )
+				msg += " or "+dotWarning;
+			throw new CompileException(stackTrace,msg);
+		}
+		if( !params.containsAll(requiredParameters) ) {
+			Set<String> required = new HashSet<String>(requiredParameters);
+			required.removeAll(params);
+			if( required.size()==1 && required.iterator().next().equals(dotParameter) )
+				throw new CompileMethodException(stackTrace,"dot_parameter "+required+" was not found but is required, "+dotWarning);
+			throw new CompileMethodException(stackTrace,"parameter(s) "+required+" were not found but are required");
+		}
+		if( restrictedParams!=null ) {
+			for( Map.Entry<String,Set<String>> entry : restrictedParams.entrySet() ) {
+				String name = entry.getKey();
+				Set<String> options = entry.getValue();
+				Chunk chunk = dynamicArgs.get(name);
+				if( chunk!=null && !(chunk==Chunk.NULL && removeNulls) )
+					throw new CompileException(stackTrace,"parameter '"+name+"' cannot be dynamic");
+				String value = staticArgs.get(name);
+				if( value != null && !options.contains(value) )
+					throw new CompileException(stackTrace,"parameter '"+name+"' cannot be '"+value+"', must be one of "+options);
+			}
+		}
+	}
+
+}