/******************************************************************************* * PSHDL is a library and (trans-)compiler for PSHDL input. It generates * output suitable for implementation or simulation of it. * * Copyright (C) 2013 Karsten Becker (feedback (at) pshdl (dot) org) * * 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 <http://www.gnu.org/licenses/>. * * This License does not grant permission to use the trade names, trademarks, * service marks, or product names of the Licensor, except as required for * reasonable and customary use in describing the origin of the Work. * * Contributors: * Karsten Becker - initial API and implementation ******************************************************************************/ package org.pshdl.model.parser; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.antlr.v4.runtime.ANTLRErrorListener; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.BaseErrorListener; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.FailedPredicateException; import org.antlr.v4.runtime.InputMismatchException; import org.antlr.v4.runtime.LexerNoViableAltException; import org.antlr.v4.runtime.NoViableAltException; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.misc.Nullable; import org.pshdl.model.HDLExpression; import org.pshdl.model.HDLPackage; import org.pshdl.model.parser.PSHDLLangParser.PsExpressionContext; import org.pshdl.model.parser.PSHDLLangParser.PsModelContext; import org.pshdl.model.utils.HDLLibrary; import org.pshdl.model.utils.services.IHDLValidator.IErrorCode; import org.pshdl.model.validation.Problem; import org.pshdl.model.validation.Problem.ProblemSeverity; public class PSHDLParser { public static String[] getKeywords() throws Exception { final InputStream resourceAsStream = PSHDLParser.class.getResourceAsStream("PSHDLLangLexer.tokens"); try (final BufferedReader reader = new BufferedReader(new InputStreamReader(resourceAsStream, StandardCharsets.UTF_8))) { String line = null; final List<String> keywords = new ArrayList<String>(); while ((line = reader.readLine()) != null) if (line.charAt(0) == '\'') { final String keyWord = line.substring(1, line.lastIndexOf('\'')); if (keyWord.matches("[a-z]+")) { keywords.add(keyWord); } } return keywords.toArray(new String[keywords.size()]); } } public static HDLPackage parse(File file, String libURI, Set<Problem> syntaxProblems) throws IOException, FileNotFoundException { try (final FileInputStream fis = new FileInputStream(file)) { final HDLPackage hdl = parseStream(new ANTLRInputStream(fis), libURI, syntaxProblems, file.getAbsolutePath()); return hdl; } } public static final class SyntaxErrorCollector extends BaseErrorListener { private final Set<Problem> syntaxProblems; private final CommonTokenStream ts; private int lineOffset = 0; public SyntaxErrorCollector(CommonTokenStream ts, Set<Problem> syntaxProblems) { this.syntaxProblems = syntaxProblems; this.ts = ts; } public SyntaxErrorCollector(CommonTokenStream tokens, Set<Problem> problems, int lineOffset) { this(tokens, problems); this.lineOffset = lineOffset; } @Override public void syntaxError(Recognizer<?, ?> recognizer, @Nullable Object offendingSymbol, int line, int charPositionInLine, String msg, @Nullable RecognitionException e) { int length = -1; int totalOffset = -1; SyntaxErrors error = SyntaxErrors.OtherException; if ((e == null) && PSHDLLangParser.MISSING_SEMI.equals(msg)) { error = SyntaxErrors.MissingSemicolon; msg = "Missing ';'"; } if ((e == null) && PSHDLLangParser.WRONG_ORDER.equals(msg)) { error = SyntaxErrors.WrongOrder; msg = "The order for variable declarations is «direction?» «type» «register?» «name»"; } if ((e == null) && PSHDLLangParser.MISSING_WIDTH.equals(msg)) { error = SyntaxErrors.MissingWidth; msg = "The empty width <> is only allowed in function declarations"; } if ((e == null) && PSHDLLangParser.MISSING_TYPE.equals(msg)) { error = SyntaxErrors.MissingType; msg = "The variable declaration is missing its type"; } if ((e == null) && PSHDLLangParser.MISSING_IFPAREN.equals(msg)) { error = SyntaxErrors.MissingIfParen; msg = "The syntax for if-statements is 'if («expression») { «thenStatements*» } else { «elseStatements*» }"; } if (offendingSymbol instanceof Token) { Token t = (Token) offendingSymbol; if (error == SyntaxErrors.MissingSemicolon) { do { t = ts.get(t.getTokenIndex() - 1); } while (t.getChannel() != 0); line = t.getLine(); charPositionInLine = t.getCharPositionInLine(); } totalOffset = t.getStartIndex(); final String text = t.getText(); if (text != null) { length = text.length(); } } if (e instanceof NoViableAltException) { final NoViableAltException noVi = (NoViableAltException) e; error = SyntaxErrors.NoViableAlternative; final Token t = noVi.getStartToken(); charPositionInLine = t.getCharPositionInLine(); line = t.getLine(); totalOffset = t.getStartIndex(); final String text = t.getText(); if (text != null) { length = text.length(); } } if (e instanceof LexerNoViableAltException) { error = SyntaxErrors.LexerNoViableAlternative; } if (e instanceof InputMismatchException) { error = SyntaxErrors.InputMismatch; } if (e instanceof FailedPredicateException) { error = SyntaxErrors.FailedPredicate; } line += lineOffset; syntaxProblems.add(new Problem(error, msg, line, charPositionInLine, length, totalOffset)); } } public static enum SyntaxErrors implements IErrorCode { FailedPredicate, NoViableAlternative, LexerNoViableAlternative, InputMismatch, OtherException, MissingSemicolon, MissingType, WrongOrder, MissingWidth, MissingIfParen; @Override public ProblemSeverity getSeverity() { return ProblemSeverity.ERROR; } } /** * Parses the given input String and generates a output {@link HDLPackage} * if it succeed * * @param input * the String to parse and convert * @param libURI * the library URI to retrieve a registered {@link HDLLibrary} * @param syntaxProblems * a HashSet where syntax problems will be added to * @param src * the resource from which this String was derived * @return a {@link HDLPackage} if successful, <code>null</code>l otherwise */ public static HDLPackage parseString(String input, String libURI, final Set<Problem> syntaxProblems, String src) { return parseStream(new ANTLRInputStream(input), libURI, syntaxProblems, src); } private static HDLPackage parseStream(ANTLRInputStream input, String libURI, final Set<Problem> syntaxProblems, String src) { final PSHDLLangLexer lexer = new PSHDLLangLexer(input); final CommonTokenStream tokens = new CommonTokenStream(lexer); final PSHDLLangParser parser = new PSHDLLangParser(tokens); final ANTLRErrorListener listener = new SyntaxErrorCollector(tokens, syntaxProblems); lexer.removeErrorListeners(); lexer.addErrorListener(listener); parser.removeErrorListeners(); parser.addErrorListener(listener); final PsModelContext psModel = parser.psModel(); if (syntaxProblems.size() == 0) { final HDLPackage hdl = ParserToModelExtension.toHDL(tokens, psModel, libURI, src); return hdl; } return null; } /** * Parses the given input String and generates a output {@link HDLPackage} * if it succeed * * @param input * the String to parse and convert * @param libURI * the library URI to retrieve a registered {@link HDLLibrary} * @param syntaxProblems * a HashSet where syntax problems will be added to * @return a {@link HDLPackage} if successful, <code>null</code>l otherwise */ public static HDLExpression parseExpressionString(String input, final Set<Problem> syntaxProblems) { return parseExpressionStream(new ANTLRInputStream(input), syntaxProblems); } private static HDLExpression parseExpressionStream(ANTLRInputStream input, final Set<Problem> syntaxProblems) { final PSHDLLangLexer lexer = new PSHDLLangLexer(input); final CommonTokenStream tokens = new CommonTokenStream(lexer); final ANTLRErrorListener parserListener = new SyntaxErrorCollector(tokens, syntaxProblems); final PSHDLLangParser parser = new PSHDLLangParser(tokens); parser.removeErrorListeners(); parser.addErrorListener(parserListener); lexer.removeErrorListeners(); lexer.addErrorListener(parserListener); final PsExpressionContext psExpression = parser.psExpression(); if (syntaxProblems.size() == 0) { final HDLExpression hdl = ParserToModelExtension.toHDLExpression(tokens, psExpression); return hdl; } return null; } }