Skip to content
9 changes: 4 additions & 5 deletions src/main/java/code_generation/BasicCodeGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ public class BasicCodeGenerator implements CodeGenerator {
protected String postGeneration;

public BasicCodeGenerator(RuleConvertor ruleConvertor) {
this.ruleConvertor = ruleConvertor.getConversions();
this.ruleConvertor = ruleConvertor.conversions();

NullableTuple<String, String> bookends = ruleConvertor.getBookends();
NullableTuple<String, String> bookends = ruleConvertor.bookends();

this.preGeneration = bookends.value1();
this.postGeneration = bookends.value2();
Expand Down Expand Up @@ -150,17 +150,16 @@ public int getElementsGenerated() {

@Override
public String toString() {
String string = "";
String string = "parseState: ";

string += "parseState: ";
if(parseState == null) {
string += null;
}
else {
string += parseState.toString();
}
string += "\n";

string += "\n";
string += "elementsGenerated: " + elementsGenerated;

return string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ private void checkForCompleteBuild() {
//Note: Allowing null lexical components

if(msgParts.size() == 0) return; //No error

if(msgParts.size() == 1) throw new ParameterError(msgParts.get(0) + " not provided");

String msg = msgParts.get(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package component_construction.builders.grammar_objects;

import java.util.*;
import grammar_objects.*;

public class GrammarBuilder {

protected Set<Token> tokens = new HashSet<>();
protected Set<NonTerminal> nonTerminals = new HashSet<>();
protected List<ProductionRule> productionRules = new ArrayList<>();
protected NonTerminal sentinal;

/**
* Sets up the sentinal for the grammar being built
* @param sentinal The sentinal non-terminal
* @return This builder for method chaining
*/
public GrammarBuilder setSentinal(NonTerminal sentinal) {
this.sentinal = sentinal;
return this;
}

/**
* Adds a production rule for the grammar being built
* @param productionRule The production rule
* @return This builder for method chaining
*/
public GrammarBuilder addRule(ProductionRule productionRule) {
addTokens(productionRule);
productionRules.add(productionRule);
return this;
}

/**
* Adds a production rule for the grammar being built
* @param nonTerminal The NonTerminal for the rule
* @param elements In-order elements for the rule
* @return This builder for method chaining
*/
public GrammarBuilder addRule(NonTerminal nonTerminal, LexicalElement[] elements) {
return addRule(new ProductionRule(nonTerminal, elements));
}

/**
* Adds a production rule for the grammar being built
* @param nonTerminalName The name of the NonTerminal for this rule
* @param elements In-order elements for this rule
* @return This builder for method chaining
*/
public GrammarBuilder addRule(String nonTerminalName, LexicalElement[] elements) {
return addRule(new NonTerminal(nonTerminalName), elements);
}

/**
* Adds a collection of production rules to the grammar being built
* @param rules The collection of rules
* @return This builder for method chaining
*/
public GrammarBuilder addRules(Collection<ProductionRule> rules) {
for (ProductionRule productionRule : rules) {
addRule(productionRule);
}

return this;
}

/**
* Produces the grammar built through this builder
* @return The grammar that was built
*/
public Grammar produceGrammar() {
if (sentinal == null) throw new MissingSentinalException();
return new Grammar(tokens, nonTerminals, productionRules, sentinal);
}


private void addTokens(ProductionRule productionRule) {
nonTerminals.add(productionRule.nonTerminal());

for (LexicalElement element : productionRule.productionSequence()) {
if (element instanceof Token) { tokens.add((Token)element); }
else if (element instanceof NonTerminal) { nonTerminals.add((NonTerminal)element); }
}
}


public class MissingSentinalException extends RuntimeException {

public MissingSentinalException() {
super("Sentinal not defined");
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package component_construction.builders.grammar_objects;

import java.util.*;
import java.util.Map.Entry;

import code_generation.Generator;
import grammar_objects.*;
import helper_objects.NullableTuple;

public class RuleConvertorBuilder {

protected Grammar grammar;
protected Map<ProductionRule, Generator> conversions = new HashMap<>();
protected NullableTuple<String, String> bookends = null;
protected Generator defaultConversion = null;

public RuleConvertorBuilder(Grammar grammar) {
this.grammar = grammar;
}

public RuleConvertor produceConvertor() {
if (defaultConversion != null)
populateDefaultConversions();

return new RuleConvertor(grammar, conversions, bookends);
}

/**
* Sets the constant strings which will bookend produced generations
* @param bookends The strings to bookend produced generations
* @return This builder to allow method chaining
*/
public RuleConvertorBuilder setBookends(NullableTuple<String, String> bookends) {
this.bookends = bookends;
return this;
}

/**
* Sets the constant strings which will bookend produced generations
* @param startBookend The constant starting string
* @param endBookend The constant ending string
* @return This builder to allow method chaining
*/
public RuleConvertorBuilder setBookends(String startBookend, String endBookend) {
this.bookends = new NullableTuple<>(startBookend, endBookend);
return this;
}

/**
* Enables default conversions for each grammar rule
* The default conversion will concatenate the generations of each element within each rule
* @return This builder to allow method chaining
*/
public RuleConvertorBuilder enableDefaultConversions() {
this.defaultConversion = (elements) -> Arrays
.stream(elements)
.map((element) -> element.getGeneration().toString())
.reduce("", (subtotal, current) -> subtotal + current);

return this;
}

/**
* Enables default conversions for each grammar rule using the specified generator
* @param defaultGeneratorFunction The default generator to convert each grammar rule with
* @return This builder to allow method chaining
*/
public RuleConvertorBuilder enableDefaultConversions(Generator defaultGeneratorFunction) {
this.defaultConversion = defaultGeneratorFunction;
return this;
}

/**
* Sets the conversion function for the additional root rule of the grammar
* @param generatorFunction The conversion/generator function to be used
* @return This builder to allow method chaining
*/
public RuleConvertorBuilder setRootConversion(Generator generatorFunction) {
this.conversions.put(RuleConvertor.ROOT_RULE, generatorFunction);
return this;
}

/**
* Sets the conversion function for the specified grammar rule
* @param rule The grammar rule the conversion will be applied for
* @param generatorFunction The conversion/generator function to be used
* @return This builder to allow method chaining
*/
public RuleConvertorBuilder setConversion(ProductionRule rule, Generator generatorFunction) {
this.conversions.put(rule, generatorFunction);
return this;
}

/**
* Sets the conversion function for the specified grammar rule
* @param ruleNumber The index of the grammar rule the conversion will be applied for
* @param generatorFunction The conversion/generator function to be used
* @return This builder to allow method chaining
*/
public RuleConvertorBuilder setConversion(int ruleNumber, Generator generatorFunction) {
if(ruleNumber < 0)
throw new RuntimeException("The given rule number must be >= 0");

ProductionRule rule = grammar.getRule(ruleNumber);
conversions.put(rule, generatorFunction);

return this;
}

/**
* Adds the given conversion functions for the specified grammar rule
* @param conversions A map of grammar rules to generator functions
* @return This builder to allow method chaining
*/
public RuleConvertorBuilder setConversions(Map<ProductionRule, Generator> conversions) {
for (Entry<ProductionRule, Generator> entry : conversions.entrySet()) {
setConversion(entry.getKey(), entry.getValue());
}

return this;
}

/**
* Selects the specified grammar rule
* @param rule The grammar rule
* @return An object for applying options for the selected grammar rule
*/
public RuleOptions rule(ProductionRule rule) {
return new RuleOptions(this, rule);
}

/**
* Selects the specified grammar rule
* @param ruleNumber The index of the grammar rule
* @return An object for applying options for the selected grammar rule
*/
public RuleOptions rule(int ruleNumber) {
if(ruleNumber < 0)
throw new RuntimeException("The given rule number must be >= 0");

ProductionRule rule = grammar.getRule(ruleNumber);
return new RuleOptions(this, rule);
}


private void populateDefaultConversions() {
for (ProductionRule productionRule : grammar.productionRules()) {
if (conversions.containsKey(productionRule)) continue;

conversions.put(productionRule, defaultConversion);
}
}


/**
* Options that may be applied for a selected grammar rule
*/
protected record RuleOptions(RuleConvertorBuilder builder, ProductionRule rule) {

/**
* Sets the conversion for the slected grammar rule
* @param generatorFuction The conversion/generator function to be used
* @return The rule convertor builder
*/
public RuleConvertorBuilder setConversion(Generator generatorFuction) {
builder.conversions.put(rule, generatorFuction);
return builder;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import component_construction.builders.grammar_objects.GrammarBuilder;

/**
* A class for converting text written in BNF into Grammar objects
* Notes:
Expand Down Expand Up @@ -45,33 +47,10 @@ public class BNFConvertor implements GrammarFactory {
public BNFConvertor(String bnf) {
GrammarDetailsHolder detailsHolder = gatherGrammarDetails(bnf);

constructedGrammar = new Grammar() {
@Override
protected void setUpTokens(TokenOrganiser tokenOrganiser) {
for (Token token : detailsHolder.tokenHolder) {
tokenOrganiser.addToken(token);
}
}

@Override
protected NonTerminal setUpSentinal() {
return detailsHolder.sentinal;
}

@Override
protected void setUpNonTerminals(NonTerminalOrganiser nonTerminalOrganiser) {
for (NonTerminal nonTerminal : detailsHolder.nonTerminalHolder) {
nonTerminalOrganiser.addNonTerminal(nonTerminal);
}
}

@Override
protected void setUpProductionRules(RuleOrganiser ruleOrganiser) {
for (ProductionRule rule : detailsHolder.ruleHolder) {
ruleOrganiser.addRule(rule);
}
}
};
constructedGrammar = new GrammarBuilder()
.setSentinal(detailsHolder.sentinal)
.addRules(detailsHolder.ruleHolder)
.produceGrammar();
}

private GrammarDetailsHolder gatherGrammarDetails(String bnf) {
Expand Down
Loading
Loading