# 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