Skip to content

Commit

Permalink
#2178 Implement BNF to ANTLR's g4 format conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
homedirectory committed Feb 16, 2024
1 parent b73ad98 commit ccf75fd
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fielden.platform.eql;

import fielden.platform.eql.fling.BnfToG4;
import fielden.platform.eql.fling.BnfToHtml;
import fielden.platform.eql.fling.BnfToText;
import il.ac.technion.cs.fling.EBNF;
Expand Down Expand Up @@ -331,7 +332,8 @@ public enum EqlTerminal implements Terminal {
private CanonicalEqlGrammar() {}

// print-bnf html FILE -- creates an HTML document with the BNF
// print-bnf -- prints the BNF to stdout in human-readable format
// print-bnf g4 FILE -- creates an ANTLR g4 grammar for the BNF
// print-bnf text FILE -- prints the BNF to stdout in human-readable format
// verify -- verifies the BNF for correctness
public static void main(String[] args) throws IOException {
if (args.length < 1) {
Expand All @@ -341,17 +343,23 @@ public static void main(String[] args) throws IOException {

final String command = args[0];
if ("print-bnf".equals(command)) {
if (args.length > 1 && "html".equals(args[1])) {
String html = new BnfToHtml().bnfToHtml(canonical_bnf);
if (args.length > 1) {
String format = args[1];

final PrintStream out;
if (args.length > 2) {
out = new PrintStream(new FileOutputStream(args[2]));
} else {
out = System.out;
}
out.println(html);
} else {
System.out.println(new BnfToText().bnfToText(canonical_bnf));

if ("html".equals(format)) {
out.println(new BnfToHtml().bnfToHtml(canonical_bnf));
} else if ("g4".equals(format)) {
out.println(new BnfToG4().bnfToG4(canonical_bnf, "EQL"));
} else {
out.println(new BnfToText().bnfToText(canonical_bnf));
}
}
} else if ("verify".equals(command)) {
verifyBnf(canonical_bnf);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package fielden.platform.eql.fling;

import il.ac.technion.cs.fling.EBNF;
import il.ac.technion.cs.fling.internal.grammar.rules.*;

import java.util.LinkedHashMap;

import static java.util.stream.Collectors.*;
import static org.apache.commons.lang3.StringUtils.uncapitalize;

/**
* Converts a BNF to an ANTLR grammar in the g4 format.
*/
public class BnfToG4 {

public BnfToG4() {}

public String bnfToG4(EBNF bnf, String grammarName) {
var sb = new StringBuilder();

sb.append("grammar %s;\n\n".formatted(grammarName));
sb.append("start : %s EOF;\n\n".formatted(convert(bnf.ε)));

bnf.rules()
.collect(groupingBy(rule -> rule.variable, LinkedHashMap::new, toList()))
.entrySet().stream()
.map(entry -> {
var variable = entry.getKey();
var rules = entry.getValue();
return new ERule(variable, rules.stream().flatMap(ERule::bodies).toList());
})
.map(this::convert)
.forEach(rule -> {
sb.append(rule);
sb.append('\n');
sb.append('\n');
});

sb.append("""
WHITESPACE : [ \\r\\t\\n]+ -> skip ;
COMMENT : '//' .*? '\\n' -> skip ;
BLOCK_COMMENT : '/*' .*? '*/' -> skip ;
""");

return sb.toString();
}

protected String convert(ERule eRule) {
return "%s :\n %s\n;".formatted(
convert(eRule.variable),
eRule.bodies().map(this::convert).collect(joining("\n | ")));
}

protected String convert(Body body) {
return body.stream().map(this::convert).collect(joining(" "));
}

protected String convert(Component component) {
return component.isToken() ? convert(component.asToken())
: component.isQuantifier() ? convert(component.asQuantifier())
: component.isVariable() ? convert(component.asVariable())
: component.isTerminal() ? convert(component.asTerminal())
: fail("Unrecognised rule component: %s", component);
}

protected String convert(Token token) {
// TODO parameters
return convert(token.terminal);
// return !token.isParameterized() ? token.name()
// : token.name() + token.parameters().map(this::convert).collect(joining(", ", "(", ")"));
}

protected String convert(Variable variable) {
return uncapitalize(variable.name());
}

protected String convert(Terminal terminal) {
return "'%s'".formatted(terminal.name());
}

protected String convert(Quantifier quantifier) {
String q = switch (quantifier) {
case NoneOrMore $ -> "*";
case OneOrMore $ -> "+";
case Opt $ -> "?";
default -> fail("Unrecognised quantifier: %s", quantifier);
};
return quantifier.symbols().map(this::convert).collect(joining(" ", "(", ")" + q));
}

protected static <T> T fail(String formatString, Object... args) {
throw new RuntimeException(String.format(formatString, args));
}

}

0 comments on commit ccf75fd

Please sign in to comment.