001    package ca.ryerson.kclowes.digsim.ui;
002    import ca.ryerson.kclowes.digsim.*;
003    import java.io.*;
004    import java.util.*;
005    
006    /**
007    * Preprocess a circuit description interpreting/expanding macros.
008    *
009    * <p>
010    * A circuit description to preprocess contains (BNF description):
011    *
012    *  <pre>
013    *    description  = defBlock | circuitBlock
014    *    defBlock     = 'defBlock' blockName blockArgs '\n' body 'enddef'
015    *    blockName    = <i>sequence of printable characters EXCEPT whitespace</i>
016    *    blockArgs    = arg*
017    *    arg          = <i>formal name</i>
018    *    body         = circuitBlock*
019    *    circuitBlock = <i>DEFINED IN</i> CircuitParser <i>(as block)</i>
020    * </pre>
021    *  <p> A comment can be added with '//'; the rest of the line is ignored.
022    *
023    * @author Ken Clowes (kclowes@ee.ryerson.ca)
024    * @version 1.0 (27 February 2004)
025    */
026    public class CircuitPreprocessor {
027        private static CircuitPreprocessor INSTANCE = null;
028        private LineNumberReader lineReader;
029        private Writer writer;
030        
031        private CircuitPreprocessor() {}
032        
033        public static CircuitPreprocessor getInstance()
034        {
035            if (INSTANCE == null) {
036                INSTANCE = new CircuitPreprocessor();
037            }
038            return INSTANCE;
039        }
040        /**
041        * Expand a circuit description.
042        *
043        * @param in The source of the circuit description.
044        * @param out Where the expansion is written.
045        */
046        public void expand(Reader in, Writer out)
047        {
048            writer = out;
049            String line = null;
050            lineReader = new LineNumberReader(in);
051            try {
052                while ((line = lineReader.readLine()) != null) {
053                    int commentStart = -1;
054                    if ((commentStart = line.indexOf("//")) != -1) {
055                        String comment = line.substring(commentStart);
056                        line = line.substring(0, commentStart) + "\n";
057                    }
058                    line = line.trim();
059                    if (line.equals("")) continue;
060                    String [] words = line.split("  *", 2);
061                    String firstWord = words[0];
062                    String rest = words[1];
063                    if (line.startsWith("defBlock ")) {
064                        registerBlockDef(line);
065                        continue;
066                    }
067                    if (isPrimitiveBlock(firstWord)) {
068                        writer.write(line + "\n");
069                        writer.flush();
070                        continue;
071                    }
072                    if (MacroDefinition.containsMacro(firstWord)) {
073                        MacroDefinition macro = MacroDefinition.getMacroNamed(firstWord);
074                        String [] nodeNames = rest.split("  *");
075                        String expansion = macro.parse(nodeNames);
076                        writer.write(expansion);
077                        writer.flush();
078                        continue;
079                    }
080                }
081            } catch (IOException e) {
082                System.err.println("Unexpected: " + e );
083                System.exit(1);
084            }
085        }
086        
087        private void registerBlockDef(String line)
088        {
089            line = removeComments(line);
090            String [] words = line.split("  *", 3);
091            String blockName = words[1];
092            String [] nodeNames = words[2].split("  *");
093            Vector macroLines = new Vector();
094            try {
095                while ((line = lineReader.readLine()) != null) {
096                    line = removeComments(line);
097                    if (line.equals("")) continue;
098                    if (line.startsWith("enddef")) {
099                        break;
100                    }
101                    macroLines.add(line);
102                }
103            } catch (IOException e) {
104                System.err.println("Unexpected: " + e );
105                System.exit(1);
106            }
107            String [] bodyStrings = new String[0];
108            bodyStrings = (String []) macroLines.toArray(bodyStrings);
109            MacroDefinition macro = new MacroDefinition(blockName, nodeNames, bodyStrings);
110        }
111        
112        private boolean isPrimitiveBlock(String b)
113        {
114            return b.equals("Nand");
115        }
116        
117        private String removeComments(String l)
118        {
119            int commentStart = -1;
120            if ((commentStart = l.indexOf("//")) != -1) {
121                String comment = l.substring(commentStart);
122                l = l.substring(0, commentStart) + "\n";
123            }
124            l = l.trim();
125            return l;
126        }
127        
128        /**
129        * The <tt>main</tt> method allows the <tt>CircuitPreprocessor</tt> to be used
130        * as an application.
131        *
132        * <p>With no arguments, it works as a filter: a circuit description is read from
133        * <i>stdin</i> and its expansion is written to <i>stdout</i>.
134        */
135        public static void main(String [] args)
136        {
137            CircuitPreprocessor p = CircuitPreprocessor.getInstance();
138            Reader r = new InputStreamReader(System.in);
139            Writer w = new BufferedWriter(new OutputStreamWriter(System.out));
140            p.expand(r, w);
141            System.exit(0);
142        }
143    }