Mercurial Hosting > nabble
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); + } + } + } + +}