/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: CIF.java * Input/output tool: CIF input * Original C CIF Parser (front end) by Robert W. Hon, Schlumberger Palo Alto Research * and its interface to C Electric (back end) by Robert Winstanley, University of Calgary. * Translated into Java by Steven M. Rubin, Sun Microsystems. * * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. * * Electric(tm) 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. * * Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.api.minarea; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.PushbackInputStream; import java.net.URL; import java.net.URLConnection; import com.sun.electric.api.minarea.geometry.Point; /** * This class reads files in CIF files. */ public class CIF { /** max value that can add extra digit */ private static final int BIGSIGNED = ((0X7FFFFFFF - 9) / 10); // specific syntax errors private static final int NOERROR = 100; private static final int NUMTOOBIG = 101; private static final int NOUNSIGNED = 102; private static final int NOSIGNED = 103; private static final int NOSEMI = 104; private static final int NOPATH = 105; private static final int BADTRANS = 106; private static final int BADUSER = 107; private static final int BADCOMMAND = 108; private static final int INTERNAL = 109; private static final int BADDEF = 110; private static final int NOLAYER = 111; private static final int BADCOMMENT = 112; private static final int BADAXIS = 113; private static final int NESTDEF = 114; private static final int NESTDD = 115; private static final int NODEFSTART = 116; private static final int NESTEND = 117; private static final int NOSPACE = 118; private static final int NONAME = 119; // error codes for reporting errors private static final int FATALINTERNAL = 0; private static final int FATALSYNTAX = 1; private static final int FATALSEMANTIC = 2; private static final int FATALOUTPUT = 3; private static final int ADVISORY = 4; // private static final int OTHER = 5; /* OTHER must be last */ /** flag for error encountered */ private boolean errorFound; /** what it was */ private int errorType; /** definition in progress flag */ private boolean isInCellDefinition; /** end command flag */ private boolean endIsSeen; /** number of chars in buffer */ private int charactersRead; /** flag to reset buffer */ private boolean resetInputBuffer; /** number of "fatal" errors */ private int numFatalErrors; /** lookahead character */ private int nextInputCharacter; /** the line being read */ private StringBuilder inputBuffer; private CIFActions c1; /** * Creates a new instance of CIF. */ CIF(CIFActions c1) { this.c1 = c1; } /** * Method to import a library from disk. * @param lib the library to fill * @param currentCells this map will be filled with currentCells in Libraries found in library file * @return the created library (null on error). */ protected boolean importALibrary() { setProgressNote("Reading CIF file"); if (initFind()) { return false; } // parse the CIF and create a listing if (interpret()) { return false; } // clean up c1.doneInterpreter(); return true; } private boolean interpret() { initParser(); c1.initInterpreter(); inFromFile(); parseFile(); // read in the CIF doneParser(); if (numFatalErrors > 0) { return true; } return false; } private boolean initFind() { return false; } private void initParser() { errorFound = false; errorType = NOERROR; isInCellDefinition = false; endIsSeen = false; initInput(); initErrors(); } private void doneParser() { if (!endIsSeen) { errorReport("missing End command", FATALSYNTAX); } } private int parseFile() { int comCount = 1; for (;;) { boolean end = parseStatement(); if (end) { break; } comCount++; } return comCount; } private void initInput() { charactersRead = 0; resetInputBuffer = true; } private void initErrors() { numFatalErrors = 0; } private void inFromFile() { try { nextInputCharacter = lineReader.read(); updateProgressDialog(1); } catch (IOException e) { nextInputCharacter = -1; } } private char getNextCharacter() { if (resetInputBuffer) { resetInputBuffer = false; inputBuffer = new StringBuilder(); charactersRead = 0; } int c = nextInputCharacter; if (c >= 0) { if (c != '\n') { charactersRead++; inputBuffer.append((char) c); } else { resetInputBuffer = true; } try { nextInputCharacter = lineReader.read(); updateProgressDialog(1); } catch (IOException e) { nextInputCharacter = -1; } } return (char) c; } private char peekNextCharacter() { return (char) nextInputCharacter; } private boolean atEndOfFile() { return nextInputCharacter < 0; } private int flushInput(char breakchar) { int c; while ((c = peekNextCharacter()) >= 0 && c != breakchar) { getNextCharacter(); } return c; } private void skipBlanks() { for (;;) { if (atEndOfFile()) { break; } int c = peekNextCharacter(); if (isAsciiDigit((char) c) || Character.isUpperCase((char) c)) { break; } if (c == '(' || c == ')' || c == ';' || c == '-') { break; } getNextCharacter(); } } private boolean parseStatement() { if (atEndOfFile()) { return true; } skipBlanks(); // flush initial junk int curChar = getNextCharacter(); int xRotate = 0, yRotate = 0, length = 0, width = 0, diameter = 0, symbolNumber = 0, multiplier = 0, divisor = 0, userCommand = 0; Point center = null, namePoint = null; String lName = null, nameText = null, userText = null; switch (curChar) { case 'P': getPath(); if (errorFound) { return reportError(); } c1.makePolygon(); break; case 'B': xRotate = 1; yRotate = 0; length = getNumber(); if (errorFound) { return reportError(); } width = getNumber(); if (errorFound) { return reportError(); } center = getPoint(); if (errorFound) { return reportError(); } skipSeparators(); if (((curChar = peekNextCharacter()) >= '0' && curChar <= '9') || curChar == '-') { xRotate = getSignedInteger(); if (errorFound) { return reportError(); } yRotate = getSignedInteger(); if (errorFound) { return reportError(); } } c1.makeBox(length, width, center, xRotate, yRotate); break; case 'R': diameter = getNumber(); if (errorFound) { return reportError(); } center = getPoint(); if (errorFound) { return reportError(); } c1.makeFlash(diameter, center); break; case 'W': width = getNumber(); if (errorFound) { return reportError(); } getPath(); if (errorFound) { return reportError(); } c1.makeWire(width); break; case 'L': skipBlanks(); StringBuilder layerName = new StringBuilder(); for (;;) // for (int i = 0; i<4; i++) { int chr = peekNextCharacter(); if (!Character.isUpperCase((char) chr) && !isAsciiDigit((char) chr)) { break; } layerName.append(getNextCharacter()); } if (layerName.length() == 0) { errorFound = true; errorType = NOLAYER; return reportError(); } lName = layerName.toString(); c1.makeLayer(lName); break; case 'D': skipBlanks(); switch (getNextCharacter()) { case 'S': symbolNumber = getNumber(); if (errorFound) { return reportError(); } skipSeparators(); multiplier = divisor = 1; if (isAsciiDigit(peekNextCharacter())) { multiplier = getNumber(); if (errorFound) { return reportError(); } divisor = getNumber(); if (errorFound) { return reportError(); } } if (isInCellDefinition) { errorFound = true; errorType = NESTDEF; return reportError(); } isInCellDefinition = true; c1.makeStartDefinition(symbolNumber, multiplier, divisor); break; case 'F': if (!isInCellDefinition) { errorFound = true; errorType = NODEFSTART; return reportError(); } isInCellDefinition = false; c1.makeEndDefinition(); break; case 'D': symbolNumber = getNumber(); if (errorFound) { return reportError(); } if (isInCellDefinition) { errorFound = true; errorType = NESTDD; return reportError(); } c1.makeDeleteDefinition(symbolNumber); break; default: errorFound = true; errorType = BADDEF; return reportError(); } break; case 'C': symbolNumber = getNumber(); if (errorFound) { return reportError(); } skipBlanks(); c1.initTransform(); for (;;) { int val = peekNextCharacter(); if (val == ';') { break; } switch (peekNextCharacter()) { case 'T': getNextCharacter(); int xt = getSignedInteger(); if (errorFound) { return reportError(); } int yt = getSignedInteger(); if (errorFound) { return reportError(); } c1.appendTranslate(xt, yt); break; case 'M': getNextCharacter(); skipBlanks(); switch (getNextCharacter()) { case 'X': c1.appendMirrorX(); break; case 'Y': c1.appendMirrorY(); break; default: errorFound = true; errorType = BADAXIS; return reportError(); } break; case 'R': getNextCharacter(); int xRot = getSignedInteger(); if (errorFound) { return reportError(); } int yRot = getSignedInteger(); if (errorFound) { return reportError(); } c1.appendRotate(xRot, yRot); break; default: errorFound = true; errorType = BADTRANS; return reportError(); } skipBlanks(); // between transformation commands } // end of while (1) loop c1.makeCall(symbolNumber, lineReader.getLineNumber()); break; case '(': { int level = 1; StringBuffer comment = new StringBuffer(); while (level != 0) { curChar = getNextCharacter(); switch (curChar) { case '(': level++; comment.append('('); break; case ')': level--; if (level != 0) { comment.append(')'); } break; case -1: errorFound = true; errorType = BADCOMMENT; return reportError(); default: comment.append(curChar); } } } // comment break; case 'E': skipBlanks(); if (isInCellDefinition) { errorFound = true; errorType = NESTEND; return reportError(); } if (!atEndOfFile()) { errorReport("more text follows end command", ADVISORY); } endIsSeen = true; c1.processEnd(); return true; case ';': return false; default: if (isAsciiDigit((char) curChar)) { userCommand = curChar - '0'; if (userCommand == 9) { curChar = peekNextCharacter(); if (curChar == ' ' || curChar == '\t' || curChar == '1' || curChar == '2' || curChar == '3') { switch (getNextCharacter()) { case ' ': case '\t': skipSpaces(); nameText = parseName(); if (errorFound) { return reportError(); } c1.makeSymbolName(nameText); break; case '1': case '2': case '3': if (!skipSpaces()) { errorFound = true; errorType = NOSPACE; return reportError(); } nameText = parseName(); if (errorFound) { return reportError(); } switch (curChar) { case '1': c1.makeInstanceName(nameText); break; case '2': { namePoint = getPoint(); if (errorFound) { return reportError(); } skipBlanks(); StringBuffer layName = new StringBuffer(); for (int i = 0; i < 4; i++) { int chr = peekNextCharacter(); if (!Character.isUpperCase((char) chr) && !isAsciiDigit((char) chr)) { break; } layName.append(getNextCharacter()); } lName = layName.toString(); c1.makeGeomName(nameText, namePoint, lName); break; } case '3': namePoint = getPoint(); if (errorFound) { return reportError(); } c1.makeLabel(nameText, namePoint); break; } break; } } else { userText = getUserText(); if (atEndOfFile()) { errorFound = true; errorType = BADUSER; return reportError(); } c1.makeUserComment(userCommand, userText); } } else if (userCommand == 4) { curChar = peekNextCharacter(); if (curChar == 'I' || curChar == 'X' || curChar == 'N') { switch (getNextCharacter()) { case 'I': case 'X': case 'N': if (!skipSpaces()) { errorFound = true; errorType = NOSPACE; return reportError(); } nameText = parseName(); if (errorFound) { return reportError(); } switch (curChar) { case 'I': c1.makeInstanceName(nameText); break; case 'X': { getSignedInteger(); // skip Index namePoint = getPoint(); if (errorFound) { return reportError(); } getUserText(); c1.makeGeomName(nameText, namePoint, null); break; } case 'N': namePoint = getPoint(); if (errorFound) { return reportError(); } c1.makeLabel(nameText, namePoint); break; } break; } } else { userText = getUserText(); if (atEndOfFile()) { errorFound = true; errorType = BADUSER; return reportError(); } c1.makeUserComment(userCommand, userText); } } else { userText = getUserText(); if (atEndOfFile()) { errorFound = true; errorType = BADUSER; return reportError(); } c1.makeUserComment(userCommand, userText); } } else { errorFound = true; errorType = BADCOMMAND; return reportError(); } } // by now we have a syntactically valid command although it might be missing a semicolon if (!skipSemicolon()) { errorFound = true; errorType = NOSEMI; return reportError(); } return false; } private String getUserText() { StringBuilder user = new StringBuilder(); for (;;) { if (atEndOfFile()) { break; } if (peekNextCharacter() == ';') { break; } user.append(getNextCharacter()); } return user.toString(); } private String parseName() { StringBuilder nText = new StringBuilder(); boolean noChar = true; for (;;) { if (atEndOfFile()) { break; } int c = peekNextCharacter(); if (c == ';' || c == ' ' || c == '\t' || c == '{' || c == '}') { break; } noChar = false; getNextCharacter(); nText.append((char) c); } if (noChar) { logIt(NONAME); } return nText.toString(); } private int getNumber() { boolean somedigit = false; int ans = 0; skipSpaces(); while (ans < BIGSIGNED && isAsciiDigit(peekNextCharacter())) { ans *= 10; ans += getNextCharacter() - '0'; somedigit = true; } if (!somedigit) { logIt(NOUNSIGNED); return 0; } if (isAsciiDigit(peekNextCharacter())) { logIt(NUMTOOBIG); return 0XFFFFFFFF; } return ans; } private boolean skipSemicolon() { boolean ans = false; skipBlanks(); if (peekNextCharacter() == ';') { getNextCharacter(); ans = true; skipBlanks(); } return ans; } private boolean skipSpaces() { boolean ans = false; for (;;) { int c = peekNextCharacter(); if (c != ' ' && c != '\t') { break; } getNextCharacter(); ans = true; } return ans; } private int getSignedInteger() { boolean sign = false; int ans = 0; skipSeparators(); if (peekNextCharacter() == '-') { sign = true; getNextCharacter(); } boolean someDigit = false; while (ans < BIGSIGNED && isAsciiDigit(peekNextCharacter())) { ans *= 10; ans += getNextCharacter() - '0'; someDigit = true; } if (!someDigit) { logIt(NOSIGNED); return 0; } if (isAsciiDigit(peekNextCharacter())) { logIt(NUMTOOBIG); return sign ? -0X7FFFFFFF : 0X7FFFFFFF; } return sign ? -ans : ans; } private void logIt(int thing) { errorFound = true; errorType = thing; } private Point getPoint() { int x = getSignedInteger(); int y = getSignedInteger(); return new Point(x, y); } private void getPath() { c1.initPath(); skipSeparators(); boolean hasPoints = false; for (;;) { int c = peekNextCharacter(); if (!isAsciiDigit((char) c) && c != '-') { break; } Point temp = getPoint(); if (errorFound) { break; } hasPoints = true; c1.appendPoint(temp); skipSeparators(); } if (!hasPoints) { logIt(NOPATH); } } private void skipSeparators() { for (;;) { int c = peekNextCharacter(); switch (c) { case '(': case ')': case ';': case '-': case -1: return; default: if (isAsciiDigit((char) c)) { return; } getNextCharacter(); } } } private boolean reportError() { switch (errorType) { case NUMTOOBIG: errorReport("number too large", FATALSYNTAX); break; case NOUNSIGNED: errorReport("unsigned integer expected", FATALSYNTAX); break; case NOSIGNED: errorReport("signed integer expected", FATALSYNTAX); break; case NOSEMI: errorReport("missing ';' inserted", FATALSYNTAX); break; case NOPATH: errorReport("no points in path", FATALSYNTAX); break; case BADTRANS: errorReport("no such transformation command", FATALSYNTAX); break; case BADUSER: errorReport("end of file inside user command", FATALSYNTAX); break; case BADCOMMAND: errorReport("unknown command encountered", FATALSYNTAX); break; case INTERNAL: errorReport("parser can't find i routine", FATALINTERNAL); break; case BADDEF: errorReport("no such define command", FATALSYNTAX); break; case NOLAYER: errorReport("layer name expected", FATALSYNTAX); break; case BADCOMMENT: errorReport("end of file inside a comment", FATALSYNTAX); break; case BADAXIS: errorReport("no such axis in mirror command", FATALSYNTAX); break; case NESTDEF: errorReport("symbol definitions can't nest", FATALSYNTAX); break; case NODEFSTART: errorReport("DF without DS", FATALSYNTAX); break; case NESTDD: errorReport("DD can't appear inside symbol definition", FATALSYNTAX); break; case NOSPACE: errorReport("missing space in name command", FATALSYNTAX); break; case NONAME: errorReport("no name in name command", FATALSYNTAX); break; case NESTEND: errorReport("End command inside symbol definition", FATALSYNTAX); break; case NOERROR: errorReport("error signaled but not reported", FATALINTERNAL); break; default: errorReport("uncaught error", FATALSYNTAX); } if (errorType != INTERNAL && errorType != NOSEMI && flushInput(';') < 0) { errorReport("unexpected end of input file", FATALSYNTAX); } else { skipBlanks(); } errorFound = false; errorType = NOERROR; return false/*SYNTAXERROR*/; } private void errorReport(String mess, int kind) { if (charactersRead > 0) { System.out.println("line " + (lineReader.getLineNumber() + (resetInputBuffer ? 0 : 1)) + ": " + inputBuffer.toString()); } if (kind == FATALINTERNAL || kind == FATALINTERNAL || kind == FATALSEMANTIC || kind == FATALOUTPUT) { numFatalErrors++; } switch (kind) { case FATALINTERNAL: System.out.println("Fatal internal error: " + mess); break; case FATALSYNTAX: System.out.println("Syntax error: " + mess); break; case FATALSEMANTIC: System.out.println("Error: " + mess); break; case FATALOUTPUT: System.out.println("Output error: " + mess); break; case ADVISORY: System.out.println("Warning: " + mess); break; default: System.out.println(mess); break; } } // From class com.sun.electric.tool.io.input.Input protected static final int READ_BUFFER_SIZE = 65536; /** Name of the file being input. */ protected String filePath; /** The raw input stream. */ protected InputStream inputStream; /** The line number reader (text only). */ protected LineNumberReader lineReader; /** The input stream. */ protected PushbackInputStream pushbackInputStream; /** The input stream. */ protected DataInputStream dataInputStream; /** The length of the file. */ protected long fileLength; /** the number of bytes of data read so far */ protected long byteCount; protected boolean openTextInput(URL fileURL) { if (openBinaryInput(fileURL)) { return true; } InputStreamReader is = new InputStreamReader(inputStream); lineReader = new LineNumberReader(is); return false; } protected boolean openBinaryInput(URL fileURL) { filePath = fileURL.getFile(); try { URLConnection urlCon = fileURL.openConnection(); String contentLength = urlCon.getHeaderField("content-length"); fileLength = -1; try { fileLength = Long.parseLong(contentLength); } catch (Exception e) { } inputStream = urlCon.getInputStream(); } catch (IOException e) { System.out.println("Could not find file: " + filePath); return true; } byteCount = 0; BufferedInputStream bufStrm = new BufferedInputStream(inputStream, READ_BUFFER_SIZE); pushbackInputStream = new PushbackInputStream(bufStrm); dataInputStream = new DataInputStream(pushbackInputStream); return false; } protected void closeInput() { try { dataInputStream = null; if (lineReader != null) { lineReader.close(); lineReader = null; } if (inputStream != null) { inputStream.close(); inputStream = null; } } catch (IOException e) { } } protected static void setProgressNote(String msg) { } protected void updateProgressDialog(int bytesRead) { } private static boolean isAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } static interface CIFActions { void initInterpreter(); void makeWire(int width/*, path*/); void makeStartDefinition(int symbol, int mtl, int div); void makeEndDefinition(); void makeDeleteDefinition(int n); void initTransform(); void appendTranslate(int xt, int yt); void appendMirrorX(); void appendMirrorY(); void appendRotate(int xRot, int yRot); void initPath(); void appendPoint(Point p); void makeCall(int symbol, int lineNumber/*, transform*/); void makeLayer(String lName); void makeFlash(int diameter, Point center); void makePolygon(/*path*/); void makeBox(int length, int width, Point center, int xr, int yr); void makeUserComment(int command, String text); void makeSymbolName(String name); void makeInstanceName(String name); void makeGeomName(String name, Point pt, String lay); void makeLabel(String name, Point pt); void processEnd(); void doneInterpreter(); } }