GrammarBuilder.java
/*
* Copyright 2013 Michael Mackenzie High
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mackenziehigh.snowflake;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* This class provides a concrete grammar builder that can build grammars for immediate usage.
*
* @author Mackenzie High
*/
public final class GrammarBuilder implements IGrammarBuilder
{
/**
* {@inheritDoc}
*/
public IGrammarBuilder setRoot(String name)
{
Utils.checkNonNull(name);
this.root = name;
return this;
}
/**
* {@inheritDoc}
*/
public IGrammarBuilder setTraceCount(final int count)
{
if(count < 0) { throw new IllegalArgumentException(); }
this.trace_count = count;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder and(String name, String item)
{
Utils.checkNonNull(name);
Utils.checkNonNull(item);
final DesignRuleAnd design = new DesignRuleAnd();
design.name = name;
design.item = item;
this.grammar_rules.add(design);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder not(String name, String item)
{
Utils.checkNonNull(name);
Utils.checkNonNull(item);
final DesignRuleNot design = new DesignRuleNot();
design.name = name;
design.item = item;
this.grammar_rules.add(design);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder sequence(String name, String... elements)
{
Utils.checkNonNull(name);
Utils.checkNonNullArray(elements);
final DesignRuleSequence design = new DesignRuleSequence();
design.name = name;
for(String element : elements) { design.elements.add(element); }
this.grammar_rules.add(design);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder sequenceDLR(String name, String base, String... shared)
{
Utils.checkNonNull(name);
Utils.checkNonNull(base);
Utils.checkNonNullArray(shared);
final DesignRuleSequenceDLR design = new DesignRuleSequenceDLR();
design.name = name;
design.base = base;
for(String element : shared) { design.shared.add(element); }
this.grammar_rules.add(design);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder choose(String name, String... options)
{
Utils.checkNonNull(name);
Utils.checkNonNullArray(options);
final DesignRuleChoice design = new DesignRuleChoice();
design.name = name;
for(String option : options) { design.options.add(option); }
this.grammar_rules.add(design);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder repeat(String name, String item, int minimum, int maximum)
{
Utils.checkNonNull(name);
Utils.checkNonNull(item);
if(minimum > maximum) { throw new IllegalArgumentException("minimum > maximum"); }
final DesignRuleRepetition design = new DesignRuleRepetition();
design.name = name;
design.item = item;
design.minimum = minimum;
design.maximum = maximum;
this.grammar_rules.add(design);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder star(String name, String item)
{
return repeat(name, item, 0, Integer.MAX_VALUE);
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder plus(String name, String item)
{
return repeat(name, item, 1, Integer.MAX_VALUE);
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder option(String name, String item)
{
return repeat(name, item, 0, 1);
}
/**
* {@inheritDoc}
*/
@Override
public IGrammarBuilder str(String name, String string)
{
Utils.checkNonNull(name);
Utils.checkNonNull(string);
final DesignRuleString design = new DesignRuleString();
design.name = name;
design.string = string;
this.grammar_rules.add(design);
return this;
}
/**
* {@inheritDoc}
*/
public IGrammarBuilder chr(String name, String character)
{
Utils.checkNonNull(name);
Utils.checkNonNull(character);
final DesignRuleCharacter design = new DesignRuleCharacter();
design.name = name;
design.character = character;
this.grammar_rules.add(design);
return this;
}
/**
* {@inheritDoc}
*/
public IGrammarBuilder range(String name, char character)
{
return range(name, character, character);
}
/**
* {@inheritDoc}
*/
public IGrammarBuilder range(String name, char minimum, char maximum)
{
Utils.checkNonNull(name);
declareCC(name, new CharacterClassRange(minimum, maximum));
return this;
}
/**
* {@inheritDoc}
*/
public IGrammarBuilder combine(String name, String... classes)
{
Utils.checkNonNull(name);
Utils.checkNonNull(classes);
final List<ICharacterClass> list = new LinkedList<ICharacterClass>();
for(String x : classes) { list.add(findCC(x)); }
declareCC(name, new CharacterClassCombination(list.toArray(new ICharacterClass[0])));
return this;
}
/**
* {@inheritDoc}
*/
public IGrammarBuilder exclude(String name, String include, String exclude)
{
Utils.checkNonNull(name);
Utils.checkNonNull(include);
Utils.checkNonNull(exclude);
declareCC(name, new CharacterClassExclusion(findCC(include), findCC(exclude)));
return this;
}
/**
* {@inheritDoc}
*/
public IGrammarBuilder negate(String name, String negates)
{
Utils.checkNonNull(name);
Utils.checkNonNull(negates);
declareCC(name, new CharacterClassNegation(findCC(negates)));
return this;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This method performs the actual construction of the new grammar and returns the result.
*
* <p>Each invocation of this method returns a new object.</p>
*
* @return the completed grammar.
* @throws IllegalStateException if the grammar cannot be built.
*/
public Grammar build()
{
final Map<String, Rule> rules = new TreeMap<String, Rule>();
rules.put("END", new RuleEnd());
for(RuleDesign design : grammar_rules) { design.declare(rules); }
for(RuleDesign design : grammar_rules) { design.initialize(rules); }
if(rules.containsKey(root) == false)
{
throw new IllegalStateException("The root grammar rule does not exist.");
}
final Grammar grammar = new Grammar(rules.get(root), trace_count);
return grammar;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This is the name of the root grammar rule.
*/
private String root = "";
/**
* These are the rules in the grammar being built.
*/
private final List<RuleDesign> grammar_rules = new LinkedList<RuleDesign>();
/**
* These are the character classes in the grammar being built.
*/
private final Map<String, ICharacterClass> classes = new TreeMap<String, ICharacterClass>();
/**
* This is the maximum number of backtrace records to store at any given moment.
*/
private int trace_count = 1024;
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This method adds a new rule object to a given map.
*
* @param map maps the name of a rule to the rule itself.
* @param rule is the rule to add to the map
* @throws IllegalStateException if the rule has the same name as a previously declared rule.
*/
private static void declareRule(final Map<String, Rule> map,
final Rule rule)
{
if(map.containsKey(rule.name()))
{
throw new IllegalStateException("Duplicate Rule: " + rule.name());
}
else
{
map.put(rule.name(), rule);
}
}
/**
* This method retrieves a named rule from a given map.
*
* @param map maps the name of a rule to the rule itself.
* @param name is the name of the rule to return.
* @return the rule with the given name.
* @throws IllegalStateException if there is no rule in the map with the given name.
*/
private static Rule findRule(final Map<String, Rule> map,
final String name)
{
if(map.containsKey(name))
{
return map.get(name);
}
else
{
throw new IllegalStateException("No Such Rule: " + name);
}
}
/**
* This method retrieves zero or more named rules from a given map.
*
* <p>The order of the returned array is the same as the list of names.</p>
*
* @param map maps the name of a rule to the rule itself.
* @param names are the names of the rules to return.
* @return an array containing the retrieved rules.
* @throws IllegalStateException if there is no rule in the map with one of the given names.
*/
private static Rule[] findRules(final Map<String, Rule> rules,
final List<String> names)
{
final Rule[] array = new Rule[names.size()];
int i = 0;
for(String name : names) { array[i++] = findRule(rules, name); }
return array;
}
/**
* This method adds a new character-class to the grammar that is being built.
*
* @param name is the name of the character-class.
* @param clazz is the character-class itself.
* @throws IllegalStateException if the character-class does not have a unique name.
*/
private void declareCC(final String name, final ICharacterClass clazz)
{
if(classes.containsKey(name))
{
throw new IllegalStateException("Duplicate Character-Class: " + name);
}
else
{
classes.put(name, clazz);
}
}
/**
* This method retrieves a named character-class from a given map.
*
* @param name is the name of the character-class to retrieve.
* @return the desired character-class.
* @throws IllegalStateException if there is no character-class in the map with the given name.
*/
private ICharacterClass findCC(final String name)
{
if(classes.containsKey(name))
{
return classes.get(name);
}
else
{
throw new IllegalStateException("No Such Character-Class: " + name);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
/**
* An instance of this interface is the design of a grammar rule.
*/
private interface RuleDesign
{
/**
* This method adds this rule to a given map.
*
* @param rules maps the name of a rule to the rule itself.
* @throws IllegalStateException if this rule shares its name with another rule.
*/
public void declare(Map<String, Rule> rules);
/**
* This method initializes the rule object that implements this grammar rule.
*
* @param rules maps the name of a rule to the rule itself.
* @throws IllegalStateException if rule is needed that does not exist.
*/
public void initialize(Map<String, Rule> rules);
}
/**
* An instance of this class is the design of an and-rule.
*/
private final class DesignRuleAnd implements RuleDesign
{
public String name;
public String item;
public void declare(Map<String, Rule> rules)
{
GrammarBuilder.declareRule(rules, new RuleAnd(name));
}
public void initialize(Map<String, Rule> rules)
{
final RuleAnd rule = (RuleAnd) findRule(rules, name);
rule.item = findRule(rules, item);
}
}
/**
* An instance of this class is the design of an not-rule.
*/
private final class DesignRuleNot implements RuleDesign
{
public String name;
public String item;
public void declare(Map<String, Rule> rules)
{
GrammarBuilder.declareRule(rules, new RuleNot(name));
}
public void initialize(Map<String, Rule> rules)
{
final RuleNot rule = (RuleNot) findRule(rules, name);
rule.item = findRule(rules, item);
}
}
/**
* An instance of this class is the design of an sequence-rule.
*/
private final class DesignRuleSequence implements RuleDesign
{
public String name;
public final List<String> elements = new LinkedList<String>();
public void declare(Map<String, Rule> rules)
{
GrammarBuilder.declareRule(rules, new RuleSequence(name));
}
public void initialize(Map<String, Rule> rules)
{
final RuleSequence rule = (RuleSequence) findRule(rules, name);
rule.elements = findRules(rules, elements);
}
}
/**
* An instance of this class is the design of an sequence-dlr-rule.
*/
private final class DesignRuleSequenceDLR implements RuleDesign
{
public String name;
public String base;
public final List<String> shared = new LinkedList<String>();
public void declare(Map<String, Rule> rules)
{
GrammarBuilder.declareRule(rules, new RuleSequenceDLR(name));
}
public void initialize(Map<String, Rule> rules)
{
final RuleSequenceDLR rule = (RuleSequenceDLR) findRule(rules, name);
final Rule base_rule = findRule(rules, base);
rule.base_case = base_rule;
rule.shared.elements = findRules(rules, shared);
}
}
/**
* An instance of this class is the design of a choice-rule.
*/
private final class DesignRuleChoice implements RuleDesign
{
public String name;
public final List<String> options = new LinkedList<String>();
public void declare(Map<String, Rule> rules)
{
GrammarBuilder.declareRule(rules, new RuleChoice(name));
}
public void initialize(Map<String, Rule> rules)
{
final RuleChoice rule = (RuleChoice) findRule(rules, name);
rule.options = findRules(rules, options);
}
}
/**
* An instance of this class is the design of a repetition-rule.
*/
private final class DesignRuleRepetition implements RuleDesign
{
public String name;
public String item;
public int minimum;
public int maximum;
public void declare(Map<String, Rule> rules)
{
GrammarBuilder.declareRule(rules, new RuleRepetition(name));
}
public void initialize(Map<String, Rule> rules)
{
final RuleRepetition rule = (RuleRepetition) findRule(rules, name);
rule.item = findRule(rules, item);
rule.minimum = minimum;
rule.maximum = maximum;
}
}
/**
* An instance of this class is the design of a string-rule.
*/
private final class DesignRuleString implements RuleDesign
{
public String name;
public String string;
public void declare(Map<String, Rule> rules)
{
GrammarBuilder.declareRule(rules, new RuleString(name));
}
public void initialize(Map<String, Rule> rules)
{
final RuleString rule = (RuleString) findRule(rules, name);
rule.string = string.toCharArray();
}
}
/**
* An instance of this class is the design of a character-rule.
*/
private final class DesignRuleCharacter implements RuleDesign
{
public String name;
public String character;
public void declare(Map<String, Rule> rules)
{
GrammarBuilder.declareRule(rules, new RuleCharacter(name));
}
public void initialize(Map<String, Rule> rules)
{
final RuleCharacter rule = (RuleCharacter) findRule(rules, name);
rule.character_class = findCC(character);
}
}
}