diff --git a/README.md b/README.md index 4247b15..627fa2a 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ # Antlr4Formatter -Building -==== -The project uses [maven wrapper](https://github.com/takari/maven-wrapper), so it's easy to build the project without worring about having the right Maven version installed locally. +## Building + +The project uses [maven wrapper](https://github.com/takari/maven-wrapper), so it's easy to build the project without worrying about having the right Maven version installed locally. To build `antlr4-formatter` and the standalone utility `antlr4-formatter-standalone` run: @@ -13,19 +13,15 @@ To build `antlr4-formatter` and the standalone utility `antlr4-formatter-standal ./mvnw clean package ``` -Using the standalone formatter -==== +## Using the standalone formatter To format a grammar use the script `formatFile.sh` passing the grammar file as an argument to the script. -Maven Coordinates -==== +## Maven Coordinates ``` com.khubla.antlr4formatter antlr4-formatter 1.1.0 jar -``` - - +``` \ No newline at end of file diff --git a/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4Formatter.java b/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4Formatter.java index feec3f6..4f79b0b 100644 --- a/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4Formatter.java +++ b/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4Formatter.java @@ -17,7 +17,17 @@ */ package com.khubla.antlr4formatter; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; @@ -39,104 +49,100 @@ * @author Tom Everett */ public class Antlr4Formatter { - - private Antlr4Formatter() {} - - private static final Logger LOG = LoggerFactory.getLogger(Antlr4Formatter.class); - - public static String format(String string) { - try { - if (null != string) { - StringWriter writer = new StringWriter(); - CodePointCharStream input = CharStreams.fromString(string); - formatGrammar(input, writer); - return writer.toString(); - } else { - return ""; - } - } catch (final Exception e) { - throw new RuntimeException("Exception reading and parsing file", e); - } - } - - private static void formatGrammar(CharStream input, Writer output) { - final ANTLRv4Lexer lexer = new ANTLRv4Lexer(input); - final CommonTokenStream commonTokenStream = new CommonTokenStream(lexer); - final ANTLRv4Parser parser = new ANTLRv4Parser(commonTokenStream); - final GrammarSpecContext grammarSpecContext = parser.grammarSpec(); - ParseTreeWalker.DEFAULT.walk(new Antlr4ParseTreeListenerImpl(output, commonTokenStream), grammarSpecContext); - } - - public static void format(InputStream inputStream, OutputStream outputStream) { - - if (null == inputStream) { - throw new IllegalArgumentException("Input stream must not be null!"); - } - final Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); - try (OutputStreamWriter writer = new OutputStreamWriter(outputStream)) { - formatGrammar(CharStreams.fromReader(reader), writer); - } catch (IOException e) { - LOG.error("Could not format file", e); - } - } - - public static void formatDirectory(String inputDirOption) throws Exception { - List files = new ArrayList<>(); - files = listFilesFromDirectory(inputDirOption, files, ".g4"); - for (final String filename : files) { - final File file = new File(filename); - formatSingleFile(file, file); - } - } - - public static void formatSingleFile(File inputFile, File outputFile) throws Exception { - - if (inputFile.exists()) { - - final File tempFile = File.createTempFile(inputFile.getName(), ".g4"); - - LOG.info("Formatting: {}", inputFile.getName()); - - format(new FileInputStream(inputFile), new FileOutputStream(tempFile)); - - Files.copy(tempFile.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - Files.delete(tempFile.toPath()); - } else { - throw new Exception("Unable to find: '" + inputFile + "'"); - } - } - - public static void formatSingleFile(String inputFilename, String outputFilename) throws Exception { - final File inputFile = new File(inputFilename); - File outputFile; - - if (null == outputFilename) { - outputFile = inputFile; - } else { - outputFile = new File(outputFilename); - } - - formatSingleFile(inputFile, outputFile); - } - - private static List listFilesFromDirectory(String dir, List files, String filter) { - final File file = new File(dir); - final String[] list = file.list(); - if (null != list) { - for (String s : list) { - final String fileName = dir + (dir.endsWith("/") ? "" : "/") + s; - final File f2 = new File(fileName); - if (!f2.isHidden()) { - if (f2.isDirectory()) { - listFilesFromDirectory(fileName + "/", files, filter); - } else { - if (fileName.endsWith(filter)) { - files.add(fileName); - } - } - } - } - } - return files; - } + private static final Logger LOG = LoggerFactory.getLogger(Antlr4Formatter.class); + + public static void format(InputStream inputStream, OutputStream outputStream) { + if (null == inputStream) { + throw new IllegalArgumentException("Input stream must not be null!"); + } + final Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + try (OutputStreamWriter writer = new OutputStreamWriter(outputStream)) { + formatGrammar(CharStreams.fromReader(reader), writer); + } catch (final IOException e) { + LOG.error("Could not format file", e); + } + } + + public static String format(String string) throws Antlr4FormatterException { + try { + if (null != string) { + final StringWriter writer = new StringWriter(); + final CodePointCharStream input = CharStreams.fromString(string); + formatGrammar(input, writer); + return writer.toString(); + } else { + return ""; + } + } catch (final Exception e) { + throw new Antlr4FormatterException("Exception reading and parsing file", e); + } + } + + public static void formatDirectory(String inputDirOption) throws Antlr4FormatterException { + List files = new ArrayList<>(); + files = listFilesFromDirectory(inputDirOption, files, ".g4"); + for (final String filename : files) { + final File file = new File(filename); + formatSingleFile(file, file); + } + } + + private static void formatGrammar(CharStream input, Writer output) { + final ANTLRv4Lexer lexer = new ANTLRv4Lexer(input); + final CommonTokenStream commonTokenStream = new CommonTokenStream(lexer); + final ANTLRv4Parser parser = new ANTLRv4Parser(commonTokenStream); + final GrammarSpecContext grammarSpecContext = parser.grammarSpec(); + ParseTreeWalker.DEFAULT.walk(new Antlr4ParseTreeListenerImpl(output, commonTokenStream), grammarSpecContext); + } + + public static void formatSingleFile(File inputFile, File outputFile) throws Antlr4FormatterException { + try { + if (inputFile.exists()) { + final File tempFile = File.createTempFile(inputFile.getName(), ".g4"); + LOG.info("Formatting: {}", inputFile.getName()); + format(new FileInputStream(inputFile), new FileOutputStream(tempFile)); + Files.copy(tempFile.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + Files.delete(tempFile.toPath()); + } else { + throw new Exception("Unable to find: '" + inputFile + "'"); + } + } catch (final Exception e) { + throw new Antlr4FormatterException("Exception fromatting file", e); + } + } + + public static void formatSingleFile(String inputFilename, String outputFilename) throws Antlr4FormatterException { + final File inputFile = new File(inputFilename); + File outputFile; + if (null == outputFilename) { + outputFile = inputFile; + } else { + outputFile = new File(outputFilename); + } + formatSingleFile(inputFile, outputFile); + } + + private static List listFilesFromDirectory(String dir, List files, String filter) { + final File file = new File(dir); + final String[] list = file.list(); + if (null != list) { + for (final String s : list) { + final String fileName = dir + (dir.endsWith("/") ? "" : "/") + s; + final File f2 = new File(fileName); + if (!f2.isHidden()) { + if (f2.isDirectory()) { + listFilesFromDirectory(fileName + "/", files, filter); + } else { + if (fileName.endsWith(filter)) { + files.add(fileName); + } + } + } + } + } + return files; + } + + private Antlr4Formatter() { + } } diff --git a/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4FormatterException.java b/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4FormatterException.java new file mode 100644 index 0000000..85bbedf --- /dev/null +++ b/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4FormatterException.java @@ -0,0 +1,34 @@ +/* + * Antlr4Formatter Copyright 2018, khubla.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.khubla.antlr4formatter; + +public class Antlr4FormatterException extends Exception { + private static final long serialVersionUID = 1L; + + public Antlr4FormatterException(Exception e) { + super(e); + } + + public Antlr4FormatterException(String message) { + super(message); + } + + public Antlr4FormatterException(String message, Exception e) { + super(message, e); + } +} \ No newline at end of file diff --git a/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4ParseTreeListenerImpl.java b/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4ParseTreeListenerImpl.java index cb529d0..17ecb30 100644 --- a/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4ParseTreeListenerImpl.java +++ b/antlr4-formatter/src/main/java/com/khubla/antlr4formatter/Antlr4ParseTreeListenerImpl.java @@ -37,295 +37,297 @@ import org.slf4j.LoggerFactory; public class Antlr4ParseTreeListenerImpl implements ParseTreeListener { + /** + * + */ + private static final Logger LOG = LoggerFactory.getLogger(Antlr4ParseTreeListenerImpl.class); + /** + * non space tokens + */ + private static final Set noSpacingBeforeTokens = new HashSet<>(Arrays.asList(new String[] { "?", "*", ";", ")" })); + private static final Set noSpacingAfterTokens = new HashSet<>(Arrays.asList(new String[] { "(" })); + /** + * indent + */ + private int indent = 0; + /** + * current context + */ + private ParserRuleContext ctx; + /** + * newline + */ + private boolean newline = true; + /** + * PrintStream + */ + private final Writer writer; + /** + * token stream + */ + private final CommonTokenStream commonTokenStream; + /** + * hidden token marker + */ + private int hiddenTokenPos = -1; + /** + * parenth count + */ + private int parenthcount = 0; + /** + * previous token + */ + private String previousToken = ""; - /** - * - */ - private static final Logger LOG = LoggerFactory.getLogger(Antlr4ParseTreeListenerImpl.class); - /** - * non space tokens - */ - private static final Set noSpacingBeforeTokens = new HashSet<>(Arrays.asList(new String[]{"?", "*", ";", ")"})); - private static final Set noSpacingAfterTokens = new HashSet<>(Arrays.asList(new String[]{"("})); - /** - * indent - */ - private int indent = 0; - /** - * current context - */ - private ParserRuleContext ctx; - /** - * newline - */ - boolean newline = true; - /** - * PrintStream - */ - private final Writer writer; - /** - * token stream - */ - private final CommonTokenStream commonTokenStream; - /** - * hidden token marker - */ - private int hiddenTokenPos = -1; - /** - * parenth count - */ - private int parenthcount = 0; - /** - * previous token - */ - private String previousToken = ""; + /** + * ctor + */ + public Antlr4ParseTreeListenerImpl(Writer writer, CommonTokenStream commonTokenStream) { + this.writer = writer; + this.commonTokenStream = commonTokenStream; + } - /** - * ctor - */ - public Antlr4ParseTreeListenerImpl(Writer writer, CommonTokenStream commonTokenStream) { - this.writer = writer; - this.commonTokenStream = commonTokenStream; - } + /** + * build indent + */ + private String buildIndent(int indent) { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < (indent * 3); i++) { + sb.append(" "); + } + return sb.toString(); + } - /** - * build indent - */ - String buildIndent(int indent) { - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < (indent * 3); i++) { - sb.append(" "); - } - return sb.toString(); - } + @Override + public void enterEveryRule(ParserRuleContext ctx) { + /* + * check for hidden tokens + */ + final int tokPos = ctx.getStart().getTokenIndex(); + if (tokPos > hiddenTokenPos) { + hiddenTokenPos = tokPos; + final List refChannel = commonTokenStream.getHiddenTokensToLeft(tokPos, ANTLRv4Lexer.OFF_CHANNEL); + if ((null != refChannel) && (refChannel.size() > 0)) { + for (final Token token : refChannel) { + /* + * print comments + */ + final String str = token.getText().trim(); + if (str.length() > 0) { + if (str.trim().startsWith("//") || str.trim().startsWith("/*")) { + if ((false == (ctx instanceof ANTLRv4Parser.GrammarSpecContext))) { + writeCR(); + } + write(token.getText()); + if ((true == ((ctx instanceof ANTLRv4Parser.GrammarSpecContext))) || (ctx instanceof ANTLRv4Parser.GrammarTypeContext)) { + writeCR(); + } + } + } + } + } + } + if (ctx instanceof ANTLRv4Parser.RuleSpecContext) { + this.ctx = ctx; + writeCR(); + } else if (ctx instanceof ANTLRv4Parser.GrammarSpecContext) { + this.ctx = ctx; + writeCR(); + } else if (ctx instanceof ANTLRv4Parser.OptionsSpecContext) { + this.ctx = ctx; + writeCR(); + } else if (ctx instanceof ANTLRv4Parser.TokensSpecContext) { + this.ctx = ctx; + writeCR(); + } else if (ctx instanceof ANTLRv4Parser.ChannelsSpecContext) { + this.ctx = ctx; + writeCR(); + } else if (ctx instanceof ANTLRv4Parser.ModeSpecContext) { + this.ctx = ctx; + writeCR(); + } else if (ctx instanceof ANTLRv4Parser.LexerRuleSpecContext) { + this.ctx = ctx; + writeCR(); + } else if (ctx instanceof ANTLRv4Parser.LabeledAltContext) { + this.ctx = ctx; + } else if (ctx instanceof ANTLRv4Parser.ActionBlockContext) { + this.ctx = ctx; + } + } - /** - * write a CR - */ - void CR() { - writeSimple("\n"); - writeSimple(buildIndent(indent)); - newline = true; - } + @Override + public void exitEveryRule(ParserRuleContext ctx) { + // do nothing + } - @Override - public void enterEveryRule(ParserRuleContext ctx) { - /* - * check for hidden tokens - */ - final int tokPos = ctx.getStart().getTokenIndex(); - if (tokPos > hiddenTokenPos) { - hiddenTokenPos = tokPos; - final List refChannel = commonTokenStream.getHiddenTokensToLeft(tokPos, ANTLRv4Lexer.OFF_CHANNEL); - if ((null != refChannel) && (refChannel.size() > 0)) { - for (final Token token : refChannel) { - /* - * print comments - */ - final String str = token.getText().trim(); - if (str.length() > 0) { - if (str.trim().startsWith("//") || str.trim().startsWith("/*")) { - if ((false == (ctx instanceof ANTLRv4Parser.GrammarSpecContext))) { - CR(); - } - write(token.getText()); - if ((true == ((ctx instanceof ANTLRv4Parser.GrammarSpecContext))) || (ctx instanceof ANTLRv4Parser.GrammarTypeContext)) { - CR(); - } - } - } - } - } - } - if (ctx instanceof ANTLRv4Parser.RuleSpecContext) { - this.ctx = ctx; - CR(); - } else if (ctx instanceof ANTLRv4Parser.GrammarSpecContext) { - this.ctx = ctx; - CR(); - } else if (ctx instanceof ANTLRv4Parser.OptionsSpecContext) { - this.ctx = ctx; - CR(); - } else if (ctx instanceof ANTLRv4Parser.TokensSpecContext) { - this.ctx = ctx; - CR(); - } else if (ctx instanceof ANTLRv4Parser.ChannelsSpecContext) { - this.ctx = ctx; - CR(); - } else if (ctx instanceof ANTLRv4Parser.ModeSpecContext) { - this.ctx = ctx; - CR(); - } else if (ctx instanceof ANTLRv4Parser.LexerRuleSpecContext) { - this.ctx = ctx; - CR(); - } else if (ctx instanceof ANTLRv4Parser.LabeledAltContext) { - this.ctx = ctx; - } else if (ctx instanceof ANTLRv4Parser.ActionBlockContext) { - this.ctx = ctx; - } - } + @Override + public void visitErrorNode(ErrorNode node) { + // do nothing + } - @Override - public void exitEveryRule(ParserRuleContext ctx) {} + @Override + public void visitTerminal(TerminalNode node) { + /* + * log the rule + */ + LOG.debug(ctx.getClass().getSimpleName() + " : " + node.getText()); + /* + * options indenting + */ + if ((ctx instanceof ANTLRv4Parser.OptionsSpecContext) || (ctx instanceof ANTLRv4Parser.ModeSpecContext) || (ctx instanceof ANTLRv4Parser.TokensSpecContext) + || (ctx instanceof ANTLRv4Parser.ChannelsSpecContext)) { + if (node.getSymbol().getType() == ANTLRv4Lexer.LBRACE) { + indent++; + writeCR(); + write(node); + } else if (node.getSymbol().getType() == ANTLRv4Lexer.RBRACE) { + write(node); + indent--; + writeCR(); + } else { + write(node); + } + } else if (ctx instanceof ANTLRv4Parser.LabeledAltContext) { + if (node.getSymbol().getType() == ANTLRv4Lexer.LPAREN) { + parenthcount++; + } else if (node.getSymbol().getType() == ANTLRv4Lexer.RPAREN) { + parenthcount--; + } + if (node.getSymbol().getType() == ANTLRv4Lexer.SEMI) { + writeCR(); + write(node); + indent--; + writeCR(); + } else if (node.getSymbol().getType() == ANTLRv4Lexer.OR) { + if (parenthcount == 0) { + writeCR(); + } + write(node); + } else { + if (node.getSymbol().getType() != Recognizer.EOF) { + write(node); + } + } + } + /* + * rule indenting + */ + else if ((ctx instanceof ANTLRv4Parser.RuleSpecContext) || (ctx instanceof ANTLRv4Parser.LexerRuleSpecContext)) { + parenthcount = 0; + if (node.getSymbol().getType() == ANTLRv4Lexer.COLON) { + indent++; + writeCR(); + write(node); + } else if (node.getSymbol().getType() == ANTLRv4Lexer.SEMI) { + writeCR(); + write(node); + indent--; + writeCR(); + } else if (node.getSymbol().getType() == ANTLRv4Lexer.DOC_COMMENT) { + write(node); + writeCR(); + } else { + if (node.getSymbol().getType() != Recognizer.EOF) { + write(node); + } + } + } + /* + * grammar spec + */ + else if (ctx instanceof ANTLRv4Parser.GrammarSpecContext) { + if (node.getSymbol().getType() == ANTLRv4Lexer.SEMI) { + write(node); + writeCR(); + } else if (node.getSymbol().getType() == ANTLRv4Lexer.DOC_COMMENT) { + write(node); + writeCR(); + } else { + write(node); + } + } + /* + * grammar spec + */ + else if (ctx instanceof ANTLRv4Parser.ActionBlockContext) { + if (node.getSymbol().getType() == ANTLRv4Lexer.SEMI) { + write(node); + writeCR(); + } else if (node.getSymbol().getType() == ANTLRv4Lexer.DOC_COMMENT) { + write(node); + writeCR(); + } else if (node.getSymbol().getType() == ANTLRv4Lexer.AT) { + write(node); + writeCR(); + } else { + write(node); + } + } + /* + * grammar spec + */ + else { + write(node); + } + } - @Override - public void visitErrorNode(ErrorNode node) {} + /** + * write to the output stream + */ + private void write(String str) { + /* + * print token + */ + writeSimple(str); + if (true == newline) { + newline = false; + } + /* + * save the previous + */ + previousToken = str; + } - @Override - public void visitTerminal(TerminalNode node) { - /* - * log the rule - */ - LOG.debug(ctx.getClass().getSimpleName() + " : " + node.getText()); - /* - * options indenting - */ - if ((ctx instanceof ANTLRv4Parser.OptionsSpecContext) || (ctx instanceof ANTLRv4Parser.ModeSpecContext) || (ctx instanceof ANTLRv4Parser.TokensSpecContext) - || (ctx instanceof ANTLRv4Parser.ChannelsSpecContext)) { - if (node.getSymbol().getType() == ANTLRv4Lexer.LBRACE) { - indent++; - CR(); - write(node); - } else if (node.getSymbol().getType() == ANTLRv4Lexer.RBRACE) { - write(node); - indent--; - CR(); - } else { - write(node); - } - } else if (ctx instanceof ANTLRv4Parser.LabeledAltContext) { - if (node.getSymbol().getType() == ANTLRv4Lexer.LPAREN) { - parenthcount++; - } else if (node.getSymbol().getType() == ANTLRv4Lexer.RPAREN) { - parenthcount--; - } - if (node.getSymbol().getType() == ANTLRv4Lexer.SEMI) { - CR(); - write(node); - indent--; - CR(); - } else if (node.getSymbol().getType() == ANTLRv4Lexer.OR) { - if (parenthcount == 0) { - CR(); - } - write(node); - } else { - if (node.getSymbol().getType() != Recognizer.EOF) { - write(node); - } - } - } - /* - * rule indenting - */ - else if ((ctx instanceof ANTLRv4Parser.RuleSpecContext) || (ctx instanceof ANTLRv4Parser.LexerRuleSpecContext)) { - parenthcount = 0; - if (node.getSymbol().getType() == ANTLRv4Lexer.COLON) { - indent++; - CR(); - write(node); - } else if (node.getSymbol().getType() == ANTLRv4Lexer.SEMI) { - CR(); - write(node); - indent--; - CR(); - } else if (node.getSymbol().getType() == ANTLRv4Lexer.DOC_COMMENT) { - write(node); - CR(); - } else { - if (node.getSymbol().getType() != Recognizer.EOF) { - write(node); - } - } - } - /* - * grammar spec - */ - else if (ctx instanceof ANTLRv4Parser.GrammarSpecContext) { - if (node.getSymbol().getType() == ANTLRv4Lexer.SEMI) { - write(node); - CR(); - } else if (node.getSymbol().getType() == ANTLRv4Lexer.DOC_COMMENT) { - write(node); - CR(); - } else { - write(node); - } - } - /* - * grammar spec - */ - else if (ctx instanceof ANTLRv4Parser.ActionBlockContext) { - if (node.getSymbol().getType() == ANTLRv4Lexer.SEMI) { - write(node); - CR(); - } else if (node.getSymbol().getType() == ANTLRv4Lexer.DOC_COMMENT) { - write(node); - CR(); - } else if (node.getSymbol().getType() == ANTLRv4Lexer.AT) { - write(node); - CR(); - } else { - write(node); - } - } - /* - * grammar spec - */ - else { - write(node); - } - } + /** + * write to the output stream + */ + private void write(TerminalNode node) { + LOG.debug("Writing: '" + node.getText() + "'"); + /* + * space before the output + */ + if (false == (ctx instanceof ANTLRv4Parser.ActionBlockContext)) { + if ((false == newline) && (false == noSpacingAfterTokens.contains(previousToken)) && (false == noSpacingBeforeTokens.contains(node.getText()))) { + writeSimple(" "); + } + } + /* + * print token + */ + writeSimple(node.getText()); + if (true == newline) { + newline = false; + } + /* + * save the previous + */ + previousToken = node.getText(); + } - private void writeSimple(String string) { - try { - writer.write(string); - } catch (IOException e) { - throw new RuntimeException("Could not write to writer", e); - } - } + /** + * write a CR + */ + private void writeCR() { + writeSimple("\n"); + writeSimple(buildIndent(indent)); + newline = true; + } - /** - * write to the output stream - */ - void write(String str) { - /* - * print token - */ - writeSimple(str); - if (true == newline) { - newline = false; - } - /* - * save the previous - */ - previousToken = str; - - } - - /** - * write to the output stream - */ - void write(TerminalNode node) { - LOG.debug("Writing: '" + node.getText() + "'"); - /* - * space before the output - */ - if (false == (ctx instanceof ANTLRv4Parser.ActionBlockContext)) { - if ((false == newline) && (false == noSpacingAfterTokens.contains(previousToken)) && (false == noSpacingBeforeTokens.contains(node.getText()))) { - writeSimple(" "); - } - } - /* - * print token - */ - writeSimple(node.getText()); - if (true == newline) { - newline = false; - } - /* - * save the previous - */ - previousToken = node.getText(); - } + private void writeSimple(String string) { + try { + writer.write(string); + } catch (final IOException e) { + throw new RuntimeException("Could not write to writer", e); + } + } } diff --git a/antlr4-formatter/src/main/java/org/antlr/parser/antlr4/LexerAdaptor.java b/antlr4-formatter/src/main/java/org/antlr/parser/antlr4/LexerAdaptor.java index 0ac2b74..416a6f8 100644 --- a/antlr4-formatter/src/main/java/org/antlr/parser/antlr4/LexerAdaptor.java +++ b/antlr4-formatter/src/main/java/org/antlr/parser/antlr4/LexerAdaptor.java @@ -6,68 +6,68 @@ import org.antlr.v4.runtime.misc.Interval; public abstract class LexerAdaptor extends Lexer { - /** - * Track whether we are inside of a rule and whether it is lexical parser. currentRuleType==Token.INVALID_TYPE means that we are outside of a rule. At the first sign of a rule name reference and - * currentRuleType==invalid, we can assume that we are starting a parser rule. Similarly, seeing a token reference when not already in rule means starting a token rule. The terminating ';' of a - * rule, flips this back to invalid type. This is not perfect logic but works. For example, "grammar T;" means that we start and stop a lexical rule for the "T;". Dangerous but works. The whole - * point of this state information is to distinguish between [..arg actions..] and [charsets]. Char sets can only occur in lexical rules and arg actions cannot occur. - */ - private int currentRuleType = Token.INVALID_TYPE; + /** + * Track whether we are inside of a rule and whether it is lexical parser. currentRuleType==Token.INVALID_TYPE means that we are outside of a rule. At the first sign of a rule name reference and + * currentRuleType==invalid, we can assume that we are starting a parser rule. Similarly, seeing a token reference when not already in rule means starting a token rule. The terminating ';' of a + * rule, flips this back to invalid type. This is not perfect logic but works. For example, "grammar T;" means that we start and stop a lexical rule for the "T;". Dangerous but works. The whole + * point of this state information is to distinguish between [..arg actions..] and [charsets]. Char sets can only occur in lexical rules and arg actions cannot occur. + */ + private int currentRuleType = Token.INVALID_TYPE; - public LexerAdaptor(CharStream input) { - super(input); - } + public LexerAdaptor(CharStream input) { + super(input); + } - @Override - public Token emit() { - if (_type == ANTLRv4Lexer.ID) { - final String firstChar = _input.getText(Interval.of(_tokenStartCharIndex, _tokenStartCharIndex)); - if (Character.isUpperCase(firstChar.charAt(0))) { - _type = ANTLRv4Lexer.TOKEN_REF; - } else { - _type = ANTLRv4Lexer.RULE_REF; - } - if (currentRuleType == Token.INVALID_TYPE) { // if outside of rule def - currentRuleType = _type; // set to inside lexer or parser rule - } - } else if (_type == ANTLRv4Lexer.SEMI) { // exit rule def - currentRuleType = Token.INVALID_TYPE; - } - return super.emit(); - } + @Override + public Token emit() { + if (_type == ANTLRv4Lexer.ID) { + final String firstChar = _input.getText(Interval.of(_tokenStartCharIndex, _tokenStartCharIndex)); + if (Character.isUpperCase(firstChar.charAt(0))) { + _type = ANTLRv4Lexer.TOKEN_REF; + } else { + _type = ANTLRv4Lexer.RULE_REF; + } + if (currentRuleType == Token.INVALID_TYPE) { // if outside of rule def + currentRuleType = _type; // set to inside lexer or parser rule + } + } else if (_type == ANTLRv4Lexer.SEMI) { // exit rule def + currentRuleType = Token.INVALID_TYPE; + } + return super.emit(); + } - public int getCurrentRuleType() { - return currentRuleType; - } + public int getCurrentRuleType() { + return currentRuleType; + } - protected void handleBeginArgument() { - if (inLexerRule()) { - pushMode(ANTLRv4Lexer.LexerCharSet); - more(); - } else { - pushMode(ANTLRv4Lexer.Argument); - } - } + protected void handleBeginArgument() { + if (inLexerRule()) { + pushMode(ANTLRv4Lexer.LexerCharSet); + more(); + } else { + pushMode(ANTLRv4Lexer.Argument); + } + } - protected void handleEndAction() { - popMode(); - if (_modeStack.size() > 0) { - setType(ANTLRv4Lexer.ACTION_CONTENT); - } - } + protected void handleEndAction() { + popMode(); + if (_modeStack.size() > 0) { + setType(ANTLRv4Lexer.ACTION_CONTENT); + } + } - protected void handleEndArgument() { - popMode(); - if (_modeStack.size() > 0) { - setType(ANTLRv4Lexer.ARGUMENT_CONTENT); - } - } + protected void handleEndArgument() { + popMode(); + if (_modeStack.size() > 0) { + setType(ANTLRv4Lexer.ARGUMENT_CONTENT); + } + } - private boolean inLexerRule() { - return currentRuleType == ANTLRv4Lexer.TOKEN_REF; - } + private boolean inLexerRule() { + return currentRuleType == ANTLRv4Lexer.TOKEN_REF; + } - public void setCurrentRuleType(int ruleType) { - currentRuleType = ruleType; - } + public void setCurrentRuleType(int ruleType) { + currentRuleType = ruleType; + } } diff --git a/antlr4-formatter/src/test/java/com/khubla/antlr4formatter/Antlr4FormatterTest.java b/antlr4-formatter/src/test/java/com/khubla/antlr4formatter/Antlr4FormatterTest.java index abf5713..2635230 100644 --- a/antlr4-formatter/src/test/java/com/khubla/antlr4formatter/Antlr4FormatterTest.java +++ b/antlr4-formatter/src/test/java/com/khubla/antlr4formatter/Antlr4FormatterTest.java @@ -13,28 +13,24 @@ import org.junit.Test; public class Antlr4FormatterTest { - - @Test - public void formatString() throws IOException { - // given - String unformattedGrammar = readFileAsUtf8ToString("Hello.unformatted.g4"); - String formattedGrammar = readFileAsUtf8ToString("Hello.formatted.g4"); - - // when - String result = Antlr4Formatter.format(unformattedGrammar); - - // then - assertThat(result).isEqualTo(formattedGrammar); - } - - private String readFileAsUtf8ToString(String fileName) throws IOException { - try { - URI uri = Antlr4FormatterTest.class.getClassLoader().getResource(fileName).toURI(); - Path path = Paths.get(uri); - return new String(Files.readAllBytes(path), StandardCharsets.UTF_8); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - + @Test + public void formatString() throws Antlr4FormatterException, IOException { + // given + String unformattedGrammar = readFileAsUtf8ToString("Hello.unformatted.g4"); + String formattedGrammar = readFileAsUtf8ToString("Hello.formatted.g4"); + // when + String result = Antlr4Formatter.format(unformattedGrammar); + // then + assertThat(result).isEqualTo(formattedGrammar); + } + + private String readFileAsUtf8ToString(String fileName) throws IOException, Antlr4FormatterException { + try { + URI uri = Antlr4FormatterTest.class.getClassLoader().getResource(fileName).toURI(); + Path path = Paths.get(uri); + return new String(Files.readAllBytes(path), StandardCharsets.UTF_8); + } catch (URISyntaxException e) { + throw new Antlr4FormatterException(e); + } + } }