/* Logic.java * Program to process description of a logic circuit * author Douglas W. Jones * version 2017-09-27 * adapted from RoadNetwork.java Version 2017-09-14 * * Bug notices in the code indicate unsolved problems */ import java.util.LinkedList; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; /** Error reporting package * provides standard prefix and behavior for messages */ class Errors { // error messages are counted. private static int errorCount = 0; /** Allow public read-only access to the count of error messages * @return the count */ public static int count() { return errorCount; } /** Report nonfatal errors, output a message and return * @arg message the message to output */ public static void warn( String message ) { System.err.println( "Logic: " + message ); errorCount = errorCount + 1; } /** Report fatal errors, output a message and exit, never to return * @arg message the message to output */ public static void fatal( String message ) { warn( message ); System.exit( 1 ); } } /** Wires join Gates * @see Gate */ class Wire { // constructors may throw this when an error prevents construction public static class ConstructorFailure extends Exception {} // fields of a gate private float delay; // measured in seconds private Gate source; // where this wire comes from private String srcPin; // what pin of source private Gate destination; // where this wire goes private String dstPin; // what pin of the destination // name of a wire is source-srcpin-destination-dstpin /** construct a new wire by scanning its description from the source file */ public Wire( Scanner sc ) throws ConstructorFailure { String sourceName = sc.next(); srcPin = sc.next(); String dstName = sc.next(); dstPin = sc.next(); //Bug: In the above, what if there are no next inputs? source = Logic.findGate( sourceName ); destination = Logic.findGate( dstName ); if (source == null) { Errors.warn( "No such source gate: Wire " + sourceName + " " + srcPin + " " + dstName + " " + dstPin ); sc.nextLine(); throw new ConstructorFailure(); } if (destination == null) { Errors.warn( "No such destination gate: Wire " + sourceName + " " + srcPin + " " + dstName + " " + dstPin ); sc.nextLine(); throw new ConstructorFailure(); } // take care of source and destination pins // Bug: This is a start, but in the long run, it might not be right source.registerOutput( srcPin ); destination.registerInput( dstPin ); if (sc.hasNextFloat()) { delay = sc.nextFloat(); if (delay < 0.0F) { Errors.warn( "Negative delay: Wire " + sourceName + " " + srcPin + " " + dstName + " " + dstPin + " " + delay ); delay = 99999.0F; // no failure needed, use bogus value } } else { Errors.warn( "Floating point delay expected: Wire " + sourceName + " " + srcPin + " " + dstName + " " + dstPin + " " ); delay = 99999.0F; // no failure needed, use bogus value } String skip = sc.nextLine(); } /** output the wire in a form like that used for input * @return the textual form */ public String toString() { // Bug: Can source be null for a badly constructed wire? // Bug: Can destination be null for a badly constructed wire? // Bug: Can the name fields be null for either gate? return "wire " + source.name + " " + srcPin + " " + destination.name + " " + dstPin + " " + delay; } } /** Gates process inputs from Wires and deliver outputs to Wires * @see Wire */ class Gate { // constructors may throw this when an error prevents construction public static class ConstructorFailure extends Exception {} // fields of a gate public String name; // textual name of gate, never null! private String kind; // textual name of gate's type, never null! private float delay; // the delay of this gate, in seconds private LinkedList outgoing; // set of all wires out of this gate private LinkedList incoming; // set of all wires in to this gate // Bug: Is incoming really needed? // Bug: When are the above ever set to anything? // names of all inputs and outputs for this gate private LinkedList inputs = new LinkedList (); private LinkedList outputs = new LinkedList (); // Bug: The need for the above really depends on the gate kind Gate( Scanner sc ) throws ConstructorFailure { name = sc.next(); kind = sc.next(); // Bug: in the above, what if there was no next if (Logic.findGate( name ) != null) { Errors.warn( "Gate redefined: " + name ); sc.nextLine(); throw new ConstructorFailure(); } if (sc.hasNextFloat()) { delay = sc.nextFloat(); if (delay < 0.0F) { Errors.warn( "Negative delay: Gate " + name + " " + kind + " " + delay ); delay = 99999.0F; // no failure needed, use bogus value } } else { Errors.warn( "Floating point delay expected: Gate " + name + " " + kind ); delay = 99999.0F; // no failure needed, use bogus value } String skip = sc.nextLine(); } /** tell the gate that one of its input pins is in use * @param pinName */ public void registerInput( String pinName ) { //Bug: all we do is prevent inputs from being used twice, more later for (String s: inputs) { if (s.equals( pinName )) { Errors.warn( "Input reused: " + name + " " + pinName ); } } inputs.add( pinName ); } /** tell the gate that one of its output pins is in use * @param pinName */ public void registerOutput( String pinName ) { //Bug: we do nothing about this here, it'll get more fun later } /** output the gate in a form like that used for input * @return the textual form */ public String toString() { // Bug: Can name be null because of a bad definition? return "gate " + name + " " + kind + " " + delay; } } public class Logic { // the sets of all wires and all gates private static LinkedList wires = new LinkedList (); private static LinkedList gates = new LinkedList (); /** Find a gate by textual name in the set gates * @param s name of a gate * @return the gate named s or null if none */ public static Gate findGate( String s ) { // quick and dirty implementation for (Gate i: gates) { if (i.name.equals( s )) { return i; } } return null; } /** Initialize this logic circuit by scanning its description */ private static void readCircuit( Scanner sc ) { while (sc.hasNext()) { String command = sc.next(); if ("gate".equals( command )) { try { gates.add( new Gate( sc ) ); } catch (Gate.ConstructorFailure e) { // do nothing, the constructor already reported the error } } else if ("wire".equals( command )) { try { wires.add( new Wire( sc ) ); } catch (Wire.ConstructorFailure e) { // do nothing, the constructor already reported the error } } else if ("--".equals( command )) { sc.nextLine(); } else { Errors.warn( "unknown command: " + command ); sc.nextLine(); } } } /** Print out the wire network to system.out */ private static void printCircuit() { for (Gate i: gates) { System.out.println( i.toString() ); } for (Wire r: wires) { System.out.println( r.toString() ); } } /** Main program */ public static void main( String[] args ) { if (args.length < 1) { Errors.fatal( "Missing file name argument" ); } else if (args.length > 1) { Errors.fatal( "Too many arguments" ); } else try { readCircuit( new Scanner( new File( args[0] ) ) ); if (Errors.count() == 0) printCircuit(); } catch (FileNotFoundException e) { Errors.fatal( "Can't open the file" ); } } }