/******************************************************************************* * This file is part of logisim-evolution. * * logisim-evolution 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. * * logisim-evolution 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 logisim-evolution. If not, see <http://www.gnu.org/licenses/>. * * Original code by Carl Burch (http://www.cburch.com), 2011. * Subsequent modifications by : * + Haute École Spécialisée Bernoise * http://www.bfh.ch * + Haute École du paysage, d'ingénierie et d'architecture de Genève * http://hepia.hesge.ch/ * + Haute École d'Ingénierie et de Gestion du Canton de Vaud * http://www.heig-vd.ch/ * The project is currently maintained by : * + REDS Institute - HEIG-VD * Yverdon-les-Bains, Switzerland * http://reds.heig-vd.ch *******************************************************************************/ package com.cburch.logisim.std.hdl; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.cburch.logisim.data.BitWidth; import com.cburch.logisim.instance.Port; public class VhdlParser { public static class IllegalVhdlContentException extends Exception { private static final long serialVersionUID = 1L; public IllegalVhdlContentException() { super(); } public IllegalVhdlContentException(String message) { super(message); } public IllegalVhdlContentException(String message, Throwable cause) { super(message, cause); } public IllegalVhdlContentException(Throwable cause) { super(cause); } } public static class PortDescription { private String name; private String type; private BitWidth width; public PortDescription(String name, String type, int width) { this.name = name; this.type = type; this.width = BitWidth.create(width); } public String getName() { return this.name; } public String getType() { return this.type; } public BitWidth getWidth() { return this.width; } } private static final String ENTITY_PATTERN = "\\s*entity\\s+(\\w+)\\s+is\\s+(.*?end)\\s+(\\w+)\\s*;"; private static final String ARCH_PATTERN = "\\s*architecture.*"; private static final String LIBRARY_PATTERN = "\\s*library\\s+\\w+\\s*;"; private static final String USING_PATTERN = "\\s*use\\s+\\S+\\s*;"; private static final String PORTS_PATTERN = "\\s*port\\s*[(](.*)[)]\\s*;\\s*end"; private static final String PORT_PATTERN = "\\s*(\\w+)\\s*"; private static final String LINE_PATTERN = ":\\s*(\\w+)\\s+std_logic"; private static final String VECTOR_PATTERN = ":\\s*(\\w+)\\s+std_logic_vector\\s*[(]\\s*(\\d+)\\s+downto\\s+(\\d+)\\s*[)]"; private List<PortDescription> inputs; private List<PortDescription> outputs; private String source; private String name; private String libraries; private String architecture; public VhdlParser(String source) { this.source = source; this.inputs = new ArrayList<PortDescription>(); this.outputs = new ArrayList<PortDescription>(); } public String getArchitecture() { return architecture; } private int getEOLIndex(String input, int from) { int index; index = input.indexOf("\n", from); if (index != -1) return index; index = input.indexOf("\r\n", from); if (index != -1) return index; index = input.indexOf("\r", from); if (index != -1) return index; return input.length(); } public List<PortDescription> getInputs() { return inputs; } public String getLibraries() { return libraries; } public String getName() { return name; } public List<PortDescription> getOutputs() { return outputs; } private String getType(String type) throws IllegalVhdlContentException { if (type.equals("in")) return Port.INPUT; if (type.equals("out")) return Port.OUTPUT; if (type.equals("inout")) return Port.INOUT; throw new IllegalVhdlContentException( Strings.get("invalidTypeException")); } public void parse() throws IllegalVhdlContentException { String input = removeComments(); Pattern pattern = Pattern.compile(ENTITY_PATTERN, Pattern.DOTALL | Pattern.CASE_INSENSITIVE); String[] parts = pattern.split(input); Matcher matcher = pattern.matcher(input); if (parts.length > 2) { throw new IllegalVhdlContentException( Strings.get("duplicatedEntityException")); } if (!matcher.find() || matcher.groupCount() != 3 || !matcher.group(1).equals(matcher.group(3))) { throw new IllegalVhdlContentException( Strings.get("CannotFindEntityException")); } name = matcher.group(1); parsePorts(matcher.group(2)); parseLibraries(parts[0]); parseContent(parts.length == 2 ? parts[1] : ""); } private void parseContent(String input) throws IllegalVhdlContentException { Matcher matcher = Pattern.compile(ARCH_PATTERN, Pattern.DOTALL | Pattern.CASE_INSENSITIVE).matcher(input); if (matcher.find()) { architecture = matcher.group().trim(); } else { architecture = ""; } } private void parseLibraries(String input) throws IllegalVhdlContentException { StringBuilder result = new StringBuilder(); Matcher library = Pattern.compile(LIBRARY_PATTERN, Pattern.CASE_INSENSITIVE).matcher(input); while (library.find()) { result.append(library.group().trim().replaceAll("\\s+", " ")); result.append(System.getProperty("line.separator")); } Matcher using = Pattern .compile(USING_PATTERN, Pattern.CASE_INSENSITIVE) .matcher(input); while (using.find()) { result.append(using.group().trim().replaceAll("\\s+", " ")); result.append(System.getProperty("line.separator")); } libraries = result.toString(); } private int parseLine(Scanner scanner, StringBuilder type) throws IllegalVhdlContentException { if (scanner.findWithinHorizon( Pattern.compile(LINE_PATTERN, Pattern.CASE_INSENSITIVE), 0) == null) throw new IllegalVhdlContentException( Strings.get("lineDeclarationException")); MatchResult result = scanner.match(); if (result.groupCount() != 1) throw new IllegalVhdlContentException( Strings.get("lineDeclarationException")); type.append(getType(result.group(1).toLowerCase())); return 1; } private void parseMultiplePorts(String line) throws IllegalVhdlContentException { int index = line.indexOf(':'); if (index == -1) throw new IllegalVhdlContentException( Strings.get("multiplePortsDeclarationException")); Scanner local = new Scanner(line.substring(0, index)); local.useDelimiter(","); List<String> names = new ArrayList<String>(); while (local.hasNext()) names.add(local.next().trim()); local.close(); local = new Scanner(line); int width; StringBuilder type = new StringBuilder(); if (line.toLowerCase().contains("std_logic_vector")) width = parseVector(local, type); else width = parseLine(local, type); for (String name : names) { if (type.toString().equals(Port.INPUT)) inputs.add(new PortDescription(name, type.toString(), width)); else outputs.add(new PortDescription(name, type.toString(), width)); } local.close(); } private void parsePort(String line) throws IllegalVhdlContentException { Scanner local = new Scanner(line); if (local.findWithinHorizon( Pattern.compile(PORT_PATTERN, Pattern.CASE_INSENSITIVE), 0) == null) { local.close(); throw new IllegalVhdlContentException( Strings.get("portDeclarationException")); } String name = local.match().group().trim(); int width; StringBuilder type = new StringBuilder(); if (line.toLowerCase().contains("std_logic_vector")) width = parseVector(local, type); else width = parseLine(local, type); if (type.toString().equals(Port.INPUT)) inputs.add(new PortDescription(name, type.toString(), width)); else outputs.add(new PortDescription(name, type.toString(), width)); local.close(); } private void parsePorts(String input) throws IllegalVhdlContentException { Matcher matcher = Pattern.compile(PORTS_PATTERN, Pattern.DOTALL | Pattern.CASE_INSENSITIVE).matcher(input); if (!matcher.find() || matcher.groupCount() != 1) return; String ports = matcher.group(1); Scanner scanner = new Scanner(ports); scanner.useDelimiter(";"); while (scanner.hasNext()) { String statement = scanner.next(); if (statement.contains(",")) parseMultiplePorts(statement.trim()); else parsePort(statement.trim()); } scanner.close(); } private int parseVector(Scanner scanner, StringBuilder type) throws IllegalVhdlContentException { if (scanner.findWithinHorizon( Pattern.compile(VECTOR_PATTERN, Pattern.CASE_INSENSITIVE), 0) == null) throw new IllegalVhdlContentException( Strings.get("vectorDeclarationException")); MatchResult result = scanner.match(); if (result.groupCount() != 3) throw new IllegalVhdlContentException( Strings.get("vectorDeclarationException")); type.append(getType(result.group(1).toLowerCase())); return Integer.parseInt(result.group(2)) - Integer.parseInt(result.group(3)) + 1; } private String removeComments() throws IllegalVhdlContentException { StringBuffer input; try { input = new StringBuffer(source); } catch (NullPointerException ex) { throw new IllegalVhdlContentException( Strings.get("emptySourceException")); } int from; while ((from = input.indexOf("--")) != -1) { int to = getEOLIndex(input.toString(), from); input.delete(from, to); } return input.toString().trim(); } }