# You may have to edit this file to delete header lines produced by # mailers or news systems from this file (all lines before these shell # comments you are currently reading). # Shell archive made by dwjones on Fri Apr 21 10:07:24 CDT 2017 # To install this software on a UNIX system: # 1) create a directory (e.g. with the shell command mkdir mp6) # 2) change to that directory (e.g. with the command cd mp6), # 3) direct the remainder of this text to sh (e.g. sh < ../thisfile). # This will make sh create files in the new directory; it will do # nothing else (if you're paranoid, you should scan the following text # to verify this before you follow these directions). Then read README # in the new directory for additional instructions. cat > README <<\xxxxxxxxxx README a ternary logic simulator with graphical output in the style required by MP5. This has been entirely rewritten and tested using a new simulation framework, and then the file Gate.java has been removed. Contents: README -- this file classes -- a list of all the class.java files in this program *.java -- the source code testfile -- a file of input formatted for this code To build this project, convert Gate.java to the new framework and then use the following command line: javac @classes To run this project on the test file use this command line: java TernaryLogic testfile 1 16 xxxxxxxxxx cat > classes <<\xxxxxxxxxx Errors.java ScanSupport.java Simulation.java Wire.java Gate.java MaxGate.java MinGate.java NegGate.java IsFGate.java IsTGate.java IsUGate.java TernaryLogic.java xxxxxxxxxx cat > Errors.java <<\xxxxxxxxxx /* Errors.java -- error reporting support package */ /** Utility package for error handling. * * General purpose error reporting package for command-line applications. * It allows reporting fatal errors and warnings to the user. * * @author Douglas Jones * @version 2017-04-05 * this code is ripped from RoadNetwork.java version 2017-03-31. */ public class Errors { private Errors(){}; // you may never instantiate this class private static int count = 0; // warning count, really public read only /** Provide public read only access to the count of warnings. * @return the count of the non-fatal warnings */ public static int count() { return count; } /** Warn of non fatal errors with a message on system.err * @param message the string to output as an error message. */ public static void warn( String message ) { System.err.println( "Warning: " + message ); count = count + 1; } /** Report fatal errors with a message on system.err * and then exit the application. * @param message the string to output as an error message. */ public static void fatal( String message ) { System.err.println( "Fatal error: " + message ); System.exit( -1 ); } } xxxxxxxxxx cat > IsFGate.java <<\xxxxxxxxxx /* IsFGate.java -- implements the is-false subclass of gates */ import java.util.Scanner; /** the is-false gate, a kind of gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class IsFGate extends Gate { /** initializer scans and processes one is-false gate * @param sc the Scanner from which gate description is read * @param myName the value to be put in the name field */ IsFGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); inputs = 1; // it is a one-input gate this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " isfalse " + delay ); } // ***** Logic Simulation for IsFGate ***** /** Sanity check for IsFGate */ public void check() { super.check(); // now change the output from unknown to false Simulation.schedule( new Gate.OutputChangeEvent( delay, this, 1, 0 ) ); output = 0; } /** Return the new logic value, false unless the input is false. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { int newOutput = 0; if (inputCounts[0] != 0) newOutput = 2; return newOutput; } } xxxxxxxxxx cat > IsTGate.java <<\xxxxxxxxxx /* IsTGate.java -- implements the is-true subclass of gates */ import java.util.Scanner; /** the is-true gate, a kind of gate * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class IsTGate extends Gate { /** initializer scans and processes one is-true gate * @parame sc Scanner from which gate description is read * @param myName the value to be put in the name field */ IsTGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); inputs = 1; // it is a one-input gate this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " istrue " + delay ); } // ***** Logic Simulation for IsTGate ***** /** Sanity check for IsTGate */ public void check() { super.check(); // now change the output from unknown to false Simulation.schedule( new Gate.OutputChangeEvent( delay, this, 1, 0 ) ); output = 0; } /** Return the new logic value, false unless the input is true. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { int newOutput = 0; if (inputCounts[2] != 0) newOutput = 2; return newOutput; } } xxxxxxxxxx cat > IsUGate.java <<\xxxxxxxxxx /* IsUGate.java -- implements the is-undefined subclass of gates */ import java.util.Scanner; /** the is-undefined gate, a kind of gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class IsUGate extends Gate { /** initializer scans and processes one is-undefined gate * @param sc Scanner from which gate description is read * @param myName the value to be put in the name field */ IsUGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); inputs = 1; // it is a one-input gate this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " isundefined " + delay ); } // ***** Logic Simulation for IsUGate ***** /** Sanity check for IsUGate */ public void check() { super.check(); // now change the output from unknown to true Simulation.schedule( new Gate.OutputChangeEvent( delay, this, 1, 2 ) ); output = 2; } /** Return the new logic value, false unless the input is undefined. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { int newOutput = 0; if (inputCounts[1] != 0) newOutput = 2; return newOutput; } } xxxxxxxxxx cat > MaxGate.java <<\xxxxxxxxxx /* MaxGate.java -- a subclass of gates that computes the max of its inputs */ import java.util.Scanner; /** the max operator, a fundamental kind of ternary logic gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class MaxGate extends Gate { /** initializer scans and processes one max gate * @param sc Scanner from which gate description is read * @param myName the value to be put in the name field */ MaxGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); // get inputs if (sc.hasNextInt()) { inputs = sc.nextInt(); } else { Errors.warn( this.myString() + " max -- has no input count" ); } this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " max " + inputs + " " + delay ); } // ***** Logic Simulation for MaxGate ***** /** Return the new logic value, the max of the input values. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { // find the maximum of all the inputs int newOutput = 2; while (inputCounts[newOutput] == 0) newOutput--; return newOutput; } } xxxxxxxxxx cat > MinGate.java <<\xxxxxxxxxx /* MinGate.java -- a subclass of gates that computes the min of its inputs */ import java.util.Scanner; /** the min operator, a fundamental kind of ternary logic gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class MinGate extends Gate { /** initializer scans and processes one min gate * @param sc Scanner from which gate description is read * @param myName the value to be put in the name field */ MinGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); // get inputs if (sc.hasNextInt()) { inputs = sc.nextInt(); } else { Errors.warn( this.myString() + " min -- has no input count" ); } this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " min " + inputs + " " + delay ); } // ***** Logic Simulation for MinGate ***** /** Return the new logic value, the min of the input values. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { // find the minimum of all the inputs int newOutput = 0; while (inputCounts[newOutput] == 0) newOutput++; return newOutput; } } xxxxxxxxxx cat > NegGate.java <<\xxxxxxxxxx /* NegGate.java -- implements logical negation gates, a ternary operator */ import java.util.Scanner; /** the negate gate, a fundamental ternary logic gate. * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * @see Gate */ public class NegGate extends Gate { /** initializer scans and processes one neg gate * @param sc Scanner from which gate description is read * @param myName the value to be put in the name field */ NegGate( Scanner sc, String myName ) { // the text "gate myName min" has already been scanned super( myName ); inputs = 1; // it is a one-input gate this.finishGate( sc ); } /** get a representation for this Gate in the form used for input * @return the representation as a string */ public String toString() { return( this.myString() + " neg " + " " + delay ); } // ***** Logic Simulation for NegGate ***** /** Return the new logic value, 2 minus the input value. * Every subclass of gate must define this. * @return the new logic value, a function of inputCounts; */ protected int logicValue() { // Warning this is mildly tricky code int newOutput = 2; while (inputCounts[2 - newOutput] == 0) newOutput--; return newOutput; } } xxxxxxxxxx cat > ScanSupport.java <<\xxxxxxxxxx /* ScanSupport.java -- package of support methods for scanning */ import java.util.Scanner; import java.util.regex.Pattern; /** Support methods for scanning input files. * @author Douglas Jones * @version 2017-04-05 * This is ripped from RoadNetwork.java version 2017-03-31 with a change: * nextFloat method is added based on the homework and the comments are * somewhat improved. * @see Errors */ public class ScanSupport { /** Pattern for recognizing identifers */ public static final Pattern name // letter followed by alphanumeric = Pattern.compile( "[a-zA-Z][a-zA-Z0-9_]*|" ); /** Pattern for recognizing floating point numbers */ public static final Pattern numb // Digits.Digits or .Digits or nothing = Pattern.compile( "[0-9]+\\.?[0-9]*|\\.[0-9]+|" ); /** Pattern for recognzing whitespace excluding newlines */ public static final Pattern whitespace = Pattern.compile( "[ \t]*" ); /** Get next name without skipping to next line (unlike sc.Next()). * @param sc the scanner from which end of line is scanned. * @return the name, if there was one, or an empty string. */ public static String nextName( Scanner sc ) { sc.skip( whitespace ); // the following is weird code, it skips the name // and then returns the string that matched what was skipped sc.skip( name ); return sc.match().group(); } /** Get next float without skipping lines (unlike sc.nextFloat()). * @param sc the scanner from which end of line is scanned. * @return the name, if there was one, or NaN if not. */ public static Float nextFloat( Scanner sc ) { sc.skip( whitespace ); // the following is weird code, it skips the name // and then returns the string that matched what was skipped sc.skip( numb ); String f = sc.match().group(); // now convert what we can or return NaN if (!"".equals( f )) { return Float.parseFloat( f ); } else { return Float.NaN; } } /** Class used only for deferred evaluation of lambda expressions * passed to lineEnd. */ public interface EndMessage { /** Method to compute the error message text * @return the text the error message */ public abstract String myString(); } /** Advance to next line and complain if there is junk at the line end; * call this when all useful content has been consumed from the line * it skips optional line-end comments and complains about anything * it finds while advancing to the next line. * @see Errors * @see EndMessage * @param sc the scanner from which end of line is scanned. * @param message will be evaluated only when there is an error; * it is typically passed as a lambda expression, for example, * {@code ScanSupport.lineEnd( sc, () -> "this " + x + " that" );} */ public static void lineEnd( Scanner sc, EndMessage message ) { sc.skip( whitespace ); String lineEnd = sc.nextLine(); if ( (!lineEnd.equals( "" )) && (!lineEnd.startsWith( "--" )) ) { Errors.warn( "" + message.myString() + " followed unexpected by '" + lineEnd + "'" ); } } } xxxxxxxxxx cat > Simulation.java <<\xxxxxxxxxx /* Simulation.java -- discrete event simulation framework */ import java.util.PriorityQueue; /** Discrete event simulation support framework * @author Douglas Jones * @version 2017-04-17 * A new simulation framework based on lecture from Apr. 17. */ class Simulation { /** Events are the core of the control structure of the simulation. */ public static abstract class Event { /** Each event has a time */ float time; /** Construct and initialize a new Event. * @param t the time of the event. */ Event( float t ) { time = t; }; /** Trigger the event. * Every subclass of Event must provide a trigger method */ public abstract void trigger(); } /** Events are queued for {@code run} retrieve in chronological order. */ private static final PriorityQueue eventSet = new PriorityQueue ( (Event e1, Event e2)->Float.compare( e1.time, e2.time ) ); /** Users call schedule to schedule an event at its inherent time. * usually a later time but possibly the current time. * @param e specifies when the event should occur. */ public static void schedule( Event e ) { eventSet.add( e ); } /** the main program should build the model, * this inolves scheduling some initial events * and then, just once, it should call {@code run}. */ public static void run() { while (!eventSet.isEmpty()) { Event e = eventSet.remove(); e.trigger(); } } } xxxxxxxxxx cat > TernaryLogic.java <<\xxxxxxxxxx /* TernaryLogic.java -- main class of a ternary logic simulator */ import java.util.LinkedList; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; /** TernaryLogic -- The main class of a ternary logic simulator. * * This reads a description of a ternary logic circuit, builds a model, * and if that model passes sanity checks, runs a simulation of that * circuit. * * It runs from the command line, with three command line arguments, * the input file name, the interval between successive outputs, and the * total duration of the simulation. * * @author Douglas Jones * @version 2017-04-20 * This code is largely ripped from Version 2017-04-06, with comment changes. * * @see Wire * @see Gate * @see Errors * @see Simulation * @see ScanSupport * @see #main */ public class TernaryLogic { // lists of roads and intersectins static final LinkedList wires = new LinkedList (); static final LinkedList gates = new LinkedList (); /** utility method to look up an gate by name * @param s is the name of the gate, a string * @return is the Gate object with that name */ public static Gate findGate( String s ) { for ( Gate g: gates ) { if (g.name.equals( s )) return g; } return null; } /** read a ternary logic system. * @param sc the scanner from which the system is read. */ public static void initializeTernary( Scanner sc ) { while (sc.hasNext()) { // until we hit the end of the file String command = ScanSupport.nextName( sc ); if ("gate".equals( command )) { Gate g = Gate.newGate( sc ); if (g != null) gates.add( g ); } else if ("wire".equals( command )) { wires.add( new Wire( sc ) ); } else if ("".equals( command )) { // blank or comment // line holding -- ends up here! ScanSupport.lineEnd( sc, () -> "Line" ); } else { Errors.warn( "Command '" + command + "' is not gate or wire" ); sc.nextLine(); // skip the rest of the error } } } /** Check the sanity of the network. * @see Gate#check */ public static void checkNetwork() { for ( Gate g: gates ) { g.check(); } // we could also go through the wires, // but there's nothing to check there. } /** write out a ternary logic system */ public static void writeTernary() { for ( Gate g: gates ) { System.out.println( g.toString() ); } for ( Wire w: wires ) { System.out.println( w.toString() ); } } /** output headline for logic output * @param i the interval between successive outputs */ public static void initPrint( float i ) { Simulation.schedule( new PrintEvent( 0.0f, i ) ); for( Gate g: gates ) { System.out.print( " " + g.name ); } System.out.println(); } /** Output print event */ private static final class PrintEvent extends Simulation.Event { private final float printInterval; /** Construct a print event * @param time the time at which to print. * @param i the interval between print events. */ public PrintEvent( float time, float i ) { super( time ); printInterval = i; } /** Every event must provide a trigger method */ public void trigger() { for( Gate g: gates ) { System.out.print( " " + g.printValue() ); } System.out.println(); Simulation.schedule( new PrintEvent( time + printInterval, printInterval ) ); } } /** Terminate Simulation Event */ private static class ExitEvent extends Simulation.Event { public ExitEvent( float t ) { super( t ); } /** Every event must provide a trigger method */ public void trigger() { System.exit( 0 ); } } /** main program that reads and writes a road network * @param args the command line arguments must hold one file name */ public static void main( String[] args ) { // verify that the argument exists. if (args.length < 1) { Errors.fatal( "Missing file name on command line" ); } else if (args.length < 2) { Errors.fatal( "Missing interval on command line" ); } else if (args.length < 3) { Errors.fatal( "Missing time limit on command line" ); } else if (args.length > 3) { Errors.fatal( "Unexpected command line args" ); } else try { initializeTernary( new Scanner( new File( args[0] ) ) ); checkNetwork(); if (Errors.count() > 0) { writeTernary(); } else try { initPrint( Float.parseFloat( args[1] ) ); Simulation.schedule( new ExitEvent( Float.parseFloat( args[2] ) ) ); Simulation.run(); } catch (NumberFormatException e) { // Bug: The error message is wrong for args[2] Errors.fatal( "'" + args[1] + "' is not an floating print interval" ); } } catch (FileNotFoundException e) { Errors.fatal( "Could not read '" + args[0] + "'" ); } } } xxxxxxxxxx cat > Wire.java <<\xxxxxxxxxx /* Wire.java -- class defining the properties of wires */ import java.util.Scanner; /** Wires are link by gates. * * @author Douglas Jones * @version 2017-04-17 * * @see Gate * @see Errors * @see TernaryLogic#findGate(String) */ class Wire { private final float delay; // time delay of this wire private final Gate destination; // where wire goes, or null private final Gate source; // source of wire, or null // Wire name is the source-destination names /** Initializer scans and processes one wire definition. * @param sc The scanner from which the definition is read. */ public Wire( Scanner sc ) { // textual names of source and dest String srcName = ScanSupport.nextName( sc ); String dstName = ScanSupport.nextName( sc ); // if there are no next names on this line, these are "" // therefore, the findGate calls below will fail // lookup names of source and dest source = TernaryLogic.findGate( srcName ); if (source == null) { Errors.warn( "Wire '" + srcName + "' '" + dstName + "' source undefined." ); } destination = TernaryLogic.findGate( dstName ); if (destination == null) { Errors.warn( "Wire '" + srcName + "' '" + dstName + "' destination undefined." ); } delay = ScanSupport.nextFloat( sc ); if (delay != delay) { // really asks if delay == NaN Errors.warn( "Wire '" + srcName + "' '" + dstName + "' has no delay." ); } else if (delay < 0.0f) { Errors.warn( "Wire '" + srcName + "' '" + dstName + "' '" + delay + "' has negative delay." ); } ScanSupport.lineEnd( sc, () -> this.toString() ); // Now, tell the gates that they've been wired together if (destination != null) destination.addIncoming( this ); if (source != null) source.addOutgoing( this ); } /** Convert this wire to a format like that used for input * @return The textual description of the wire */ public String toString() { String srcName; String dstName; if (source == null) { srcName = "???"; } else { srcName = source.name; } if (destination == null) { dstName = "???"; } else { dstName = destination.name; } return( "wire " + srcName + " " + dstName + " " + delay ); } // ***** Logic Simulation ***** /** simulation class for an input change to this wire */ public static final class InputChangeEvent extends Simulation.Event { private final Wire w; // the wire with an input that changes private final int oldv; // the former value on w private final int newv; // the new value on w /** Construct an input change event * @param time the time at which the input changes. * @param w the wire where the input changes. * @param ov the previous logic value carried over w. * @param nv the new logic value carried over w. */ public InputChangeEvent( float time, Wire w, int ov, int nv) { super( time ); this.w = w; this.oldv = ov; this.newv = nv; } /** Every event must provide a trigger method */ public void trigger() { Simulation.schedule( new OutputChangeEvent( time + w.delay, w, oldv, newv ) ); } }; /** simulation class for an output change to this wire */ public static final class OutputChangeEvent extends Simulation.Event { private final Wire w; // the wire with an output that changes private final int oldv; // the former value on w private final int newv; // the new value on w /** Construct an output change event * @param time the time at which the output changes. * @param w the wire where the output changes. * @param ov the previous logic value carried over w. * @param nv the new logic value carried over w. */ public OutputChangeEvent( float time, Wire w, int ov, int nv ) { super( time ); this.w = w; this.oldv = ov; this.newv = nv; } /** Every event must provide a trigger method */ public void trigger() { Simulation.schedule( new Gate.InputChangeEvent( time, w.destination, oldv, newv ) ); } } } xxxxxxxxxx