/* ** file: graphics.mashenv ** created: Thu Jun 9 12:50:33 EST 2011 ** authors: Andrew Rock & Jolon Faichney */ environment graphics { purpose <# This environment supports drawing in a window, and responding to mouse and keyboard events. It also supports console input and output. #> private class { purpose <# The code must be wrapped in a class declaration. #><# There are no imports. Imports are BAD, as they cause name collisions with student program names that drive the students and tutors crazy trying to figure out. #><# The class extends a \verb+$SUPER+ class that defines methods that {\it might} be overidden. These include the default handlers for mouse and keyboard events. #> prelude {# public class #PROGRAM_NAME# extends #PROGRAM_NAME#$SUPER { #} postlude {# } // end of the program class class #PROGRAM_NAME#$SUPER { protected static void mash_graphics_onMouseClicked(int x, int y) { } protected static void mash_graphics_onMouseEntered(int x, int y) { } protected static void mash_graphics_onMouseExited(int x, int y) { } protected static void mash_graphics_onMousePressed(int x, int y) { } protected static void mash_graphics_onMouseReleased(int x, int y) { } protected static void mash_graphics_onKeyPressed(int code) { } protected static void mash_graphics_onKeyReleased(int code) { } protected static void mash_graphics_onKeyTyped(char c) { } } // end of superclass #} } // end environment class public mandatory rewrite void main() { purpose <# A graphics program must have a {\tt main} method (a procedure with no arguments). This will be the first method to execute. {\tt mashc} automatically rewrites this method to conform to standard Java. #><# In the {\tt main} method, a program must at least call {\tt setFrameVisible(true)} to make the window visible. #><# The {\tt main} method, and any method called by {\tt main} may not call any of the drawing methods provided by this environment. #><# The {\tt main} method may call {\tt animate(int)} just once to start the automatic redrawing of the window for animation. #> prelude {# public static void main(java.lang.String[] mash_args_param) throws java.lang.Exception { mash_graphics_setUpFrame(); #OPEN_DEBUGGER# #} postlude {# #CLOSE_DEBUGGER# } #} } public section <#Setting up the window#> { public purpose <# Use these methods to set up the window into which all drawing is done. #><# In Java, the window is known as a {\tt Frame}. #> private member { purpose <# \verb"mash_graphics_frame" is the window. #> inline {# private static javax.swing.JFrame mash_graphics_frame = new javax.swing.JFrame(); #} } private member { purpose <# \verb"mash_graphics_component" is the component added to the frame that we draw into. #> inline {# private static mash_graphics_Component mash_graphics_component = new mash_graphics_Component(); #} } private member { purpose <#\verb"mash_graphics_setUpFrame()" initialises the frame. Called by the {\tt main} rewrite. #> inline {# public static void mash_graphics_setUpFrame() throws java.lang.Exception { mash_graphics_frame.setSize(300, 300); mash_graphics_frame.setTitle("#PROGRAM_NAME#"); mash_graphics_frame.setDefaultCloseOperation( javax.swing.JFrame.EXIT_ON_CLOSE); mash_graphics_frame.add(mash_graphics_component); } #} } public void setFrameSize(int width, int height) { purpose <# Sets the size of the window with {\tt width} and {\tt height}, both in pixels. Call this before making the window visible. #> inline {# mash_graphics_frame.setSize(#width#, #height#) #} } public void setFrameTitle(String title) { purpose <# Set the {\tt title} of the window. Call this before making the window visible. #> inline {# mash_graphics_frame.setTitle(#title#) #} } public void setFrameVisible(boolean visibility) { purpose <# Set the {\tt visibility} of the window. {\tt true} means visible. #> inline {# mash_graphics_frame.setVisible(#visibility#) #} } } // section: Setting up the window public mandatory rewrite void paintWindow() { purpose <# This is the method called to draw the graphics. Every graphics program must define this procedure. The drawing methods provided by this environment may only be called from this method or from a method called from this method. #><# Do not call this method directly from {\tt main}. It will be called when the window is made visible and then any time the window needs to be redrawn. #> prelude {# private static class mash_graphics_Component extends javax.swing.JComponent { public mash_graphics_Component() { super(); addMouseListener(new java.awt.event.MouseListener() { public void mouseClicked(java.awt.event.MouseEvent e) { #PROGRAM_NAME#.mash_graphics_onMouseClicked( e.getX(), e.getY()); } public void mouseEntered(java.awt.event.MouseEvent e) { #PROGRAM_NAME#.mash_graphics_onMouseEntered( e.getX(), e.getY()); } public void mouseExited(java.awt.event.MouseEvent e) { #PROGRAM_NAME#.mash_graphics_onMouseExited( e.getX(), e.getY()); } public void mousePressed(java.awt.event.MouseEvent e) { #PROGRAM_NAME#.mash_graphics_onMousePressed( e.getX(), e.getY()); } public void mouseReleased(java.awt.event.MouseEvent e) { #PROGRAM_NAME#.mash_graphics_onMouseReleased( e.getX(), e.getY()); } }); addKeyListener(new java.awt.event.KeyListener() { public void keyPressed(java.awt.event.KeyEvent e) { #PROGRAM_NAME#.mash_graphics_onKeyPressed( e.getKeyCode()); } public void keyReleased(java.awt.event.KeyEvent e) { #PROGRAM_NAME#.mash_graphics_onKeyReleased( e.getKeyCode()); } public void keyTyped(java.awt.event.KeyEvent e) { #PROGRAM_NAME#.mash_graphics_onKeyTyped( e.getKeyChar()); } }); } public void paintComponent(java.awt.Graphics mash_graphics_g1) { super.paintComponent(mash_graphics_g1); mash_graphics_g = mash_graphics_g1; mash_graphics_g2 = (java.awt.Graphics2D) mash_graphics_g1; mash_graphics_g2.setRenderingHint( java.awt.RenderingHints.KEY_ANTIALIASING, java.awt.RenderingHints.VALUE_ANTIALIAS_ON); mash_graphics_component.requestFocusInWindow(); #SUSPEND_DEBUGGER# #} postlude {# #RESUME_DEBUGGER# } // method: paintComponent } // class: mash_graphics_Component #} } // rewrite: paintWindow public section <#Content area dimensions#> { public purpose <# The size of the window, set with {\tt setFrameSize(int, int)}, is the size of the whole window including its title bar and resizing control areas. The actual size of the content area into which we can draw is platform dependent, and the as the user resizes the window may change. These methods allow the program to find out the true current dimensions of the content area. #> public int contentWidth() { purpose <# This returns the current width of the content area of the frame in pixels. #> inline {# mash_graphics_component.getBounds().width #} } public int contentHeight() { purpose <# This returns the current height of the content area of the frame in pixels. #> inline {# mash_graphics_component.getBounds().height #} } } // section: Content area diemensions public section <#Drawing#> { public purpose <# Use these methods draw in the window. #><# These methods may only be called by the {\tt paintWindow()} method or by any method called by {\tt paintWindow()}. #><# All lengths are in pixels. All angles are in degrees. #><# All rectangles are specified by {\tt (x, y, width, height)}, where {\tt (x,y)} are the coordinates of the top, left corner of the rectangle. #> private member { purpose <# The graphics object. This will be assigned every time paintWindow gets called. #> inline {# private static java.awt.Graphics mash_graphics_g = null; #} } private member { purpose <# The 2D graphics object. This will be assigned every time paintWindow gets called. #> inline {# private static java.awt.Graphics2D mash_graphics_g2 = null; #} } public void clearRect( int x, int y, int width, int height) { purpose <# Clear the rectangle by filling it with the background surface. #> inline {# mash_graphics_g2.clearRect(#x#, #y#, #width#, #height#) #} } public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { purpose <# Draws the outline of an eliptical arc inscribed in the specified rectangle, starting at {\tt startAngle} degrees (0 is 3 o'clock), for {\tt arcAngle} degrees (positive is anticlockwise). #> inline {# mash_graphics_g2.drawArc(#x#, #y#, #width#, #height#, #startAngle#, #arcAngle#) #} } public void drawLine(int x1, int y1, int x2, int y2) { purpose <# Draw a line from {\tt (x1, y1)} to {\tt (x2, y2)}. #> inline {# mash_graphics_g2.drawLine(#x1#, #y1#, #x2#, #y2#) #} } public void drawOval(int x, int y, int width, int height) { purpose <# Draws the outline of an oval inscribed in the specified rectangle. #> inline {# mash_graphics_g2.drawOval(#x#, #y#, #width#, #height#) #} } public void drawPolygon(int[] xpoints, int[] ypoints, int npoints) { purpose <# Draws the outline of a closed polygon with {\tt npoints} vertices with coodinates ({\tt xpoints[i]}, {\tt ypoints[i]}). #> inline {# mash_graphics_g2.drawPolygon(#xpoints#, #ypoints#, #npoints#) #} } public void drawPolyline(int[] xpoints, int[] ypoints, int npoints) { purpose <# Draws a sequence of {\tt npoints} $-\,1$ connected lines with {\tt npoints} end points at coodinates ({\tt xpoints[i]}, {\tt ypoints[i]}). #> inline {# mash_graphics_g2.drawPolyline(#xpoints#, #ypoints#, #npoints#) #} } public void drawRect(int x, int y, int width, int height) { purpose <# Draws the outline of the specified rectangle. #> inline {# mash_graphics_g2.drawRect(#x#, #y#, #width#, #height#) #} } public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { purpose <# Draws the outline of the specified rectangle with rounded corners made with arcs with width {\tt arcWidth} and height {\tt arcHeight}. #> inline {# mash_graphics_g2.drawRoundRect(#x#, #y#, #width#, #height#, #arcWidth#, #arcHeight#) #} } public void drawString(String s, int x, int y) { purpose <# Draw string {\tt s} with $(x,y)$ being the coordinates of the bottom-left corner of the first character. #> inline {# mash_graphics_g2.drawString(#s#, #x#, #y#) #} } public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { purpose <# Fills an eliptical arc inscribed in the specified rectangle, starting at {\tt startAngle} degrees (0 is 3 o'clock), for {\tt arcAngle} degrees (positive is anticlockwise). #> inline {# mash_graphics_g2.fillArc(#x#, #y#, #width#, #height#, #startAngle#, #arcAngle#) #} } public void fillOval(int x, int y, int width, int height) { purpose <# Fills an oval inscribed in the specified rectangle. #> inline {# mash_graphics_g2.fillOval(#x#, #y#, #width#, #height#) #} } public void fillPolygon(int[] xpoints, int[] ypoints, int npoints) { purpose <# Fills a closed polygon with {\tt npoints} vertices with coodinates ({\tt xpoints[i]}, {\tt ypoints[i]}). #> inline {# mash_graphics_g2.fillPolygon(#xpoints#, #ypoints#, #npoints#) #} } public void fillRect(int x, int y, int width, int height) { purpose <# Fills the specified rectangle. #> inline {# mash_graphics_g2.fillRect(#x#, #y#, #width#, #height#) #} } public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { purpose <# Fills the specified rectangle with rounded corners made with arcs with width {\tt arcWidth} and height {\tt arcHeight}. #> inline {# mash_graphics_g2.fillRoundRect(#x#, #y#, #width#, #height#, #arcWidth#, #arcHeight#) #} } public void setColor(int red, int green, int blue) { purpose <# Set the color to draw with by defining the amounts of ({\tt red}, {\tt green}, {\tt blue}). $(0,0,0)$ is black. $(255,255,255)$ is white. #> precondition <# $0 \le$ {\tt red} $\le 255$; $0 \le$ {\tt green} $\le 255$; and $0 \le$ {\tt blue} $\le 255$. #> inline {# mash_graphics_g2.setColor( new java.awt.Color(#red#, #green#, #blue#)) #} } public void drawImage(int imageID, int x, int y) { purpose <# Draw the image selected by {\tt imageID} with $(x,y)$ being the coordinates of its top-left corner. #> precondition <# {\tt imageID} must refer to an image that has been preloaded with {\tt getImage(String)}. #> inline {# mash_graphics_g.drawImage(mash_graphics_images[#imageID#], #x#, #y#, null) #} } } // section: Drawing public section <#Loading images#> { private member { purpose <# Array of loaded images, and a count. #> inline {# private static java.awt.Image[] mash_graphics_images = new java.awt.Image[10000]; private static int mash_graphics_imageCount = 0; private static boolean mash_image_load_complete = false; #} } public int getImage(String path) { purpose <# Load an image from the file at {\tt path} and return its new ID. If the ID returned is negative, the load failed. #> inline {# mash_graphics_getImage(#path#) #} } private member { purpose <# Load an image from the {\tt path} and return its new ID. If the ID returned is negative, the load failed. #> inline {# private static int mash_graphics_getImage(String path) { try { mash_image_load_complete = false; java.lang.String ext = ""; int i = path.length() -1; while (i >= 0) { char c = path.charAt(i); if (c == '.') { i = -1; } else { ext = c + ext; i--; } } java.io.FileInputStream fin = new java.io.FileInputStream(path); java.util.Iterator readers = javax.imageio.ImageIO.getImageReadersBySuffix(ext); javax.imageio.ImageReader ir = (javax.imageio.ImageReader) readers.next(); javax.imageio.stream.ImageInputStream iis = javax.imageio.ImageIO.createImageInputStream(fin); ir.setInput(iis, false); ir.addIIOReadProgressListener( new javax.imageio.event.IIOReadProgressListener() { public void imageComplete( javax.imageio.ImageReader source) { mash_image_load_complete = true; } public void imageProgress( javax.imageio.ImageReader source, float percentageDone) { } public void imageStarted( javax.imageio.ImageReader source, int imageIndex) { } public void readAborted( javax.imageio.ImageReader source) { } public void sequenceComplete( javax.imageio.ImageReader source) { } public void sequenceStarted( javax.imageio.ImageReader source, int minIndex) { } public void thumbnailComplete( javax.imageio.ImageReader source) { } public void thumbnailProgress( javax.imageio.ImageReader source, float percentageDone) { } public void thumbnailStarted( javax.imageio.ImageReader source, int imageIndex, int thumbnailIndex) { } }); mash_graphics_images[mash_graphics_imageCount++] = ir.read(0); while (!mash_image_load_complete) { } return mash_graphics_imageCount - 1; } catch (java.lang.Exception e) { java.lang.System.out.println("Could not read image file: " + path); java.lang.System.out.println(e); return -1; } } #} } } // section: Loading images public section <#Animation#> { private member { purpose <# Timer object. #> inline {# private static java.util.Timer mash_graphics_timer = new java.util.Timer(); #} } private member { purpose <# TimerTask object. #> inline {# private static java.util.TimerTask mash_graphics_task = new java.util.TimerTask() { public void run() { #UPDATE_DEBUGGER# mash_graphics_component.repaint(); } }; #} } public void repaint() { purpose <# Causes the window to be repainted. #> inline {# mash_graphics_component.repaint() #} } public void animate(int fps) { purpose <# Causes the window to be repainted at {\tt fps} frames per second. Only call this method \emph{once}. #> precondition <# {\tt fps} $> 0$. #> inline {# mash_graphics_timer.schedule(mash_graphics_task, 1000 / #fps#, 1000 / #fps#); #} } } // section: Animation public section <#Sounds#> { public purpose <# Like images, sound files can be loaded and played many times. File formats include {\tt .wav} and {\tt .aif}. Others may work. Loaded sounds are identified by an ID number. #><# A sound once loaded, can be played or looped. If it is played again, before it is finished, it will start from the beginning again. Multiple sounds may be played at the same time. A single sound file loaded multiple times may be played at the same time. #> private member { purpose <# Array of loaded clips and a count. #> inline {# private static java.applet.AudioClip[] mash_graphics_clips = new java.applet.AudioClip[10000]; private static int mash_graphics_clipCount = 0; #} } public int getSound(String url) { purpose <# Load a sound clip from the file at {\tt url} and return its new ID. If the ID returned is negative, the load failed. #> precondition <# {\tt url} is a URL, not just a file name. For example, {\tt file:sound.wav}. #> inline {# mash_graphics_getSound(#url#) #} } private member { purpose <# Load an image from the {\tt path} and return its new ID. If the ID returned is negative, the load failed. #> inline {# private static int mash_graphics_getSound(String url) { try { mash_graphics_clips[mash_graphics_clipCount] = java.applet.Applet.newAudioClip(new java.net.URL(url)); mash_graphics_clipCount++; return mash_graphics_clipCount - 1; } catch (java.lang.Exception e) { java.lang.System.out.println("Could not read sound file: " + url); java.lang.System.out.println(e); return -1; } } #} } public void playSound(int soundID) { purpose <# Play the sound selected by {\tt soundID}. #> precondition <# {\tt soundID} must refer to a sound that has been preloaded with {\tt getSound(String)}. #> inline {# mash_graphics_clips[#soundID#].play(); #} } public void loopSound(int soundID) { purpose <# Loop the sound selected by {\tt soundID}. #> precondition <# {\tt soundID} must refer to a sound that has been preloaded with {\tt getSound(String)}. #> inline {# mash_graphics_clips[#soundID#].loop(); #} } public void stopSound(int soundID) { purpose <# Stop the sound selected by {\tt soundID}. #> precondition <# {\tt soundID} must refer to a sound that has been preloaded with {\tt getSound(String)}. #> inline {# mash_graphics_clips[#soundID#].stop(); #} } } // section: Sounds public section <#Using the mouse#> { public purpose <# Implement the rewrite methods to handle mouse events that occur within the content area of the frame. The functions provide the mouse position at any time. #> public rewrite void onMouseClicked(int x, int y) { purpose <# Implement this method to handle mouse clicks in the content area of the frame. #><# $(x,y)$ will be the passed the coordinates of the mouse relative to the content area of the frame at the time the event occured. #> prelude{# protected static void mash_graphics_onMouseClicked( int #x#, int #y#) { #SUSPEND_DEBUGGER# #} postlude{# #RESUME_DEBUGGER# } #} } public rewrite void onMouseEntered(int x, int y) { purpose <# Implement this method to handle the mouse entering the content area of the frame. #><# $(x,y)$ will be the passed the coordinates of the mouse relative to the content area of the frame at the time the event occured. #> prelude{# protected static void mash_graphics_onMouseEntered( int #x#, int #y#) { #SUSPEND_DEBUGGER# #} postlude{# #RESUME_DEBUGGER# } #} } public rewrite void onMouseExited(int x, int y) { purpose <# Implement this method to handle the mouse exiting the content area of the frame. #><# $(x,y)$ will be the passed the coordinates of the mouse relative to the content area of the frame at the time the event occured. #> prelude{# protected static void mash_graphics_onMouseExited( int #x#, int #y#) { #SUSPEND_DEBUGGER# #} postlude{# #RESUME_DEBUGGER# } #} } public rewrite void onMousePressed(int x, int y) { purpose <# Implement this method to handle the mouse being pressed within the content area of the frame. #><# $(x,y)$ will be the passed the coordinates of the mouse relative to the content area of the frame at the time the event occured. #> prelude{# protected static void mash_graphics_onMousePressed( int #x#, int #y#) { #SUSPEND_DEBUGGER# #} postlude{# #RESUME_DEBUGGER# } #} } public rewrite void onMouseReleased(int x, int y) { purpose <# Implement this method to handle the mouse being released within the content area of the frame. #><# $(x,y)$ will be the passed the coordinates of the mouse relative to the content area of the frame at the time the event occured. #> prelude{# protected static void mash_graphics_onMouseReleased( int #x#, int #y#) { #SUSPEND_DEBUGGER# #} postlude{# #RESUME_DEBUGGER# } #} } public int mouseX() { purpose <# Returns the current horizontal ordinate $x$ of the mouse pointer relative to the content area of the frame. #> inline {# (java.awt.MouseInfo.getPointerInfo().getLocation().x - mash_graphics_frame.getBounds().x - mash_graphics_frame.getRootPane().getBounds().x) #} } public int mouseY() { purpose <# Returns the current vertical ordinate $y$ of the mouse pointer relative to the content area of the frame. #> inline {# (java.awt.MouseInfo.getPointerInfo().getLocation().y - mash_graphics_frame.getBounds().y - mash_graphics_frame.getRootPane().getBounds().y) #} } } // section: Using the mouse public section <#Keyboard events#> { public purpose <# Implement the rewrite methods to handle keyboard events when the frame is in focus. #><# The codes that identify keys may be discovered by experiment. (Write handlers that print the codes to the console.) Some handy codes are those for the arrow keys: left = 37; up = 38; right = 39; and down = 40. #> public rewrite void onKeyPressed(int code) { purpose <# Implement this method to handle a key being pressed while the frame is in focus. #><# {\tt code} will be the passed a code that identifies the key. #> prelude{# protected static void mash_graphics_onKeyPressed(int #code#) { #SUSPEND_DEBUGGER# #} postlude{# #RESUME_DEBUGGER# } #} } public rewrite void onKeyReleased(int code) { purpose <# Implement this method to handle a key being released while the frame is in focus. #><# {\tt code} will be the passed a code that identifies the key. #> prelude{# protected static void mash_graphics_onKeyReleased(int #code#) { #SUSPEND_DEBUGGER# #} postlude{# #RESUME_DEBUGGER# } #} } public rewrite void onKeyTyped(char c) { purpose <# Implement this method to handle a key being typed while the frame is in focus. #><# {\tt c} will be the passed the character that was typed. #> prelude{# protected static void mash_graphics_onKeyTyped(char #c#) { #SUSPEND_DEBUGGER# #} postlude{# #RESUME_DEBUGGER# } #} } } // section: keyboard events public section <#Waiting for fixed times#> { private member { purpose <# Provides a binding to {\tt Thread.sleep} that won't throw exceptions. #> inline {# private static void mash_sleep(int ms) { try { java.lang.Thread.sleep(ms); } catch (java.lang.Exception e) { } } #} } public void sleep(int ms) { purpose <# Makes the program wait for a requested number of {\tt ms} (miliseconds). #> inline {# mash_sleep(#ms#) #} } } // end section: Waiting for fixed times public section <#Console input#> { public purpose <# The following are methods for reading from standard input (which is usually the keyboard) and for checking to see if there is more input to read. #> private member { purpose <# Scanner object for reading from standard input. #> inline {# private static java.util.Scanner mash_console_scanner = new java.util.Scanner(System.in); #} } public int readInt() { purpose <# Returns the next integer from standard input. #> precondition <# Will cause a run time error unless there is an integer in standard input to read. Use {\tt isNextInt()} to check first. #> inline {# mash_console_scanner.nextInt() #} } public long readLong() { purpose <# Returns the next long from standard input. #> precondition <# Will cause a run time error unless there is a long in standard input to read. Use {\tt isNextLong()} to check first. #> inline {# mash_console_scanner.nextLong() #} } public boolean readBoolean() { purpose <# Returns the next boolean from standard input. #> precondition <# Will cause a run time error unless there is a boolean in standard input to read. Use {\tt isNextBoolean()} to check first. #> inline {# mash_console_scanner.nextBoolean() #} } public double readDouble() { purpose <# Returns the next double from standard input. #> precondition <# Will cause a run time error unless there is a double in standard input to read. Use {\tt isNextDouble()} to check first. #> inline {# mash_console_scanner.nextDouble() #} } public float readFloat() { purpose <# Returns the next float from standard input. #> precondition <# Will cause a run time error unless there is a float in standard input to read. Use {\tt isNextFloat()} to check first. #> inline {# mash_console_scanner.nextFloat() #} } public String readWord() { purpose <# Returns the next word as a {\tt String}. A ``word'' is a sequence of one-or-more non-whitespace characters. #> precondition <# Will cause a run time error unless there is a word in standard input to read. Use {\tt isNextWord()} to check first. #> inline {# mash_console_scanner.next() #} } public String readLine() { purpose <# Returns the next line of text as a {\tt String}. A line is a sequence of zero-or-more characters terminated by the end of line, which is not returned as part of the line. #> precondition <# Will cause a run time error unless there is a line in standard input to read. Use {\tt isNextLine()} to check first. #> inline {# mash_console_scanner.nextLine() #} } public boolean isNextInt() { purpose <# Returns {\tt true} if and only if there is an integer in standard input available to read, that is {\tt readInt()} would succeed. #> inline {# mash_console_scanner.hasNextInt() #} } public boolean isNextLong() { purpose <# Returns {\tt true} if and only if there is a long in standard input available to read, that is {\tt readLong()} would succeed. #> inline {# mash_console_scanner.hasNextLong() #} } public boolean isNextBoolean() { purpose <# Returns {\tt true} if and only if there is an boolean in standard input available to read, that is {\tt readBoolean()} would succeed. #> inline {# mash_console_scanner.hasNextBoolean() #} } public boolean isNextDouble() { purpose <# Returns {\tt true} if and only if there is a double in standard input available to read, that is {\tt readDouble()} would succeed. #> inline {# mash_console_scanner.hasNextDouble() #} } public boolean isNextFloat() { purpose <# Returns {\tt true} if and only if there is a float in standard input available to read, that is {\tt readFloat()} would succeed. #> inline {# mash_console_scanner.hasNextFloat() #} } public boolean isNextWord() { purpose <# Returns {\tt true} if and only if there is a word in standard input available to read, that is {\tt readWord()} would succeed. #> inline {# mash_console_scanner.hasNext() #} } public boolean isNextLine() { purpose <# Returns {\tt true} if and only if there is a line in standard input available to read, that is {\tt readLine()} would succeed. #> inline {# mash_console_scanner.hasNextLine() #} } } public section <#Console output#> { public purpose <# The following are methods for printing to standard output (which is usually the terminal window). #> public void print(char c) { purpose <# Writes character {\tt c} to standard output. #> inline {# java.lang.System.out.print(#c#) #} } public void print(String s) { purpose <# Writes string {\tt s} to standard output. #> inline {# java.lang.System.out.print(#s#) #} } public void print(long i) { purpose <# Writes integral {\tt i} to standard output. #> inline {# java.lang.System.out.print(#i#) #} } public void print(boolean p) { purpose <# Writes boolean {\tt p} to standard output. #> inline {# java.lang.System.out.print(#p#) #} } public void print(double x) { purpose <# Writes floating point number {\tt x} to standard output. #> inline {# java.lang.System.out.print(#x#) #} } public void println(char c) { purpose <# Writes character {\tt c} and then a newline to standard output. #> inline {# java.lang.System.out.println(#c#) #} } public void println() { purpose <# Writes a newline to standard output. #> inline {# java.lang.System.out.println() #} } public void println(String s) { purpose <# Writes string {\tt s} and then a newline to standard output. #> inline {# java.lang.System.out.println(#s#) #} } public void println(long i) { purpose <# Writes integral {\tt i} and then a newline to standard output. #> inline {# java.lang.System.out.println(#i#) #} } public void println(boolean p) { purpose <# Writes boolean {\tt p} and then a newline to standard output. #> inline {# java.lang.System.out.println(#p#) #} } public void println(double x) { purpose <# Writes floating point number {\tt x} and then a newline to standard output. #> inline {# java.lang.System.out.println(#x#) #} } } public section <#Math#> { public purpose <# The following are some commonly used numeric constants and functions. #> public final int MAX_INT { purpose <# A constant holding the maximum value an {\tt int} can have, $2^{31}-1$. #> inline {# java.lang.Integer.MAX_VALUE #} } public final int MIN_INT { purpose <# A constant holding the minimum value an {\tt int} can have, $- 2^{31}$. #> inline {# java.lang.Integer.MIN_VALUE #} } public final long MAX_LONG { purpose <# A constant holding the maximum value a {\tt long} can have, $2^{63}-1$. #> inline {# java.lang.Long.MAX_VALUE #} } public final long MIN_LONG { purpose <# A constant holding the minimum value a {\tt long} can have, $- 2^{63}$. #> inline {# java.lang.Long.MIN_VALUE #} } public final double PI { purpose <# The closest {\tt double} approximation to $\pi$. #> inline {# java.lang.Math.PI #} } public double abs(double a) { purpose <# Returns the absolute value of {\tt a}. #> inline {# java.lang.Math.abs(#a#) #} } public float abs(float a) { purpose <# Returns the absolute value of {\tt a}. #> inline {# java.lang.Math.abs(#a#) #} } public long abs(long a) { purpose <# Returns the absolute value of {\tt a}. #> inline {# java.lang.Math.abs(#a#) #} } public int abs(int a) { purpose <# Returns the absolute value of {\tt a}. #> inline {# java.lang.Math.abs(#a#) #} } public double ceil(double a) { purpose <# Returns the least double value that is greater than or equal to {\tt a} and equal to an integer. #> inline {# java.lang.Math.ceil(#a#) #} } public double exp(double x) { purpose <# Returns $e^x$, that is Euler's constant $e$ raised to power $x$. #> inline {# java.lang.Math.exp(#x#) #} } public double floor(double a) { purpose <# Returns the greatest double value that is less than or equal to {\tt a} and equal to an integer. #> inline {# java.lang.Math.floor(#a#) #} } public double log(double x) { purpose <# Returns the natural logarithm of $x$. #> inline {# java.lang.Math.log(#x#) #} } public long round(double a) { purpose <# Returns the closest {\tt long} to {\tt a}. #> inline {# java.lang.Math.round(#a#) #} } public int round(float a) { purpose <# Returns the closest {\tt int} to {\tt a}. #> inline {# java.lang.Math.round(#a#) #} } public double sqrt(double a) { purpose <# Returns the square root of {\tt a}. #> precondition <# {\tt a} $\ge 0.0$. #> inline {# java.lang.Math.sqrt(#a#) #} } public double pow(double a, double b) { purpose <# Returns {\tt a} raised to the power {\tt b}, $a^b$. #> inline {# java.lang.Math.pow(#a#, #b#) #} } public double sin(double a) { purpose <# Returns the trigonometric sine of {\tt a} radians. #> inline {# java.lang.Math.sin(#a#) #} } public double cos(double a) { purpose <# Returns the trigonometric cosine of {\tt a} radians. #> inline {# java.lang.Math.cos(#a#) #} } public double tan(double a) { purpose <# Returns the trigonometric tangent of {\tt a} radians. #> inline {# java.lang.Math.tan(#a#) #} } public double asin(double a) { purpose <# Returns the trigonometric arc sine of {\tt a} in radians. #> inline {# java.lang.Math.asin(#a#) #} } public double acos(double a) { purpose <# Returns the trigonometric arc cosine of {\tt a} in radians. #> inline {# java.lang.Math.acos(#a#) #} } public double atan(double a) { purpose <# Returns the trigonometric arc tangent of {\tt a} in radians. #> inline {# java.lang.Math.atan(#a#) #} } public double max(double a, double b) { purpose <# Returns the greater of {\tt a} and {\tt b}. #> inline {# java.lang.Math.max(#a#, #b#) #} } public float max(float a, float b) { purpose <# Returns the greater of {\tt a} and {\tt b}. #> inline {# java.lang.Math.max(#a#, #b#) #} } public int max(int a, int b) { purpose <# Returns the greater of {\tt a} and {\tt b}. #> inline {# java.lang.Math.max(#a#, #b#) #} } public long max(long a, long b) { purpose <# Returns the greater of {\tt a} and {\tt b}. #> inline {# java.lang.Math.max(#a#, #b#) #} } public double min(double a, double b) { purpose <# Returns the lesser of {\tt a} and {\tt b}. #> inline {# java.lang.Math.min(#a#, #b#) #} } public float min(float a, float b) { purpose <# Returns the lesser of {\tt a} and {\tt b}. #> inline {# java.lang.Math.min(#a#, #b#) #} } public int min(int a, int b) { purpose <# Returns the lesser of {\tt a} and {\tt b}. #> inline {# java.lang.Math.min(#a#, #b#) #} } public long min(long a, long b) { purpose <# Returns the lesser of {\tt a} and {\tt b}. #> inline {# java.lang.Math.min(#a#, #b#) #} } public double random() { purpose <# Returns a random value $x$ such that $0.0 \le x < 1.0$. #> inline {# java.lang.Math.random() #} } } // end section: Math public section <#Strings#> { public purpose <# The following are methods for working with {\tt String}s. #> public int length(String s) { purpose <# Returns the length of {\tt s}. #> inline {# #s#.length() #} } public char charAt(String s, int i) { purpose <# Returns the character at position {\tt i} in {\tt s}. #> precondition <# $0 \le$ {\tt i} $<$ {\tt length(s)}. #> inline {# #s#.charAt(#i#) #} } public boolean equals(String a, String b) { purpose <# Returns {\tt true} if and only if {\tt a} contains the same sequence of characters as in {\tt b}. #> inline {# #a#.equals(#b#) #} } public String format(boolean p, int w) { purpose <# Returns {\tt p} converted to a string, padded with spaces to a minimum width \textbar{\tt w}\textbar. If {\tt w} is negative, the result is left-justified, otherwise right-justified. #><# Examples:\\ {\tt format(true, 10)} returns {\tt "~~~~~~true"}; and\\ {\tt format(true, -10)} returns {\tt "true~~~~~~"}. #> inline {# java.lang.String.format("%" + (#w#) + "b", new java.lang.Boolean(#p#)) #} } public String format(char c, int w) { purpose <# Returns {\tt c} converted to a string, padded with spaces to a minimum width \textbar{\tt w}\textbar. If {\tt w} is negative, the result is left-justified, otherwise right-justified. #><# Examples:\\ {\tt format('a', 3)} returns {\tt "~~a"}; and \\ {\tt format('a', -3)} returns {\tt "a~~"}. #> inline {# java.lang.String.format("%" + (#w#) + "c", new java.lang.Character(#c#)) #} } public String format(double d, int w, char f, int p) { purpose <# Returns {\tt d} converted to a string, padded with spaces to a minimum width \textbar {\tt w}\textbar. If {\tt w} is negative, the result is left-justified, otherwise right-justified. {\tt f} controls the format: {\tt 'e'} selects scientific notation; {\tt 'f'} selects fixed point; or {\tt 'g'} selects the best format depending on the number and {\tt p}. For {\tt 'e'} and {\tt 'f'}, {\tt p} is the number of decimal digits to display after the decimal point, but for {\tt 'g'} it is the total number of digits. #><# Examples: \\ {\tt format(1234.56789, 12, 'e', 4)} returns {\tt "~~1.2346e+03"};\\ {\tt format(1234.56789, 12, 'f', 4)} returns {\tt "~~~1234.5679"};\\ {\tt format(1234.56789, 12, 'g', 4)} returns {\tt "~~~~~~~~1235"};\\ {\tt format(0.000001234567, 12, 'e', 4)} returns {\tt "~~1.2346e-06"};\\ {\tt format(0.000001234567, 12, 'f', 4)} returns {\tt "~~~~~~0.0000"}; and\\ {\tt format(0.000001234567, 12, 'g', 4)} returns {\tt "~~~1.235e-06"}. #> inline {# java.lang.String.format("%" + (#w#) + "." + (#p#) + (#f#), new Double(#d#)) #} } public String format(long l, int w) { purpose <# Returns {\tt l} converted to a string, padded with spaces to a minimum width \textbar{\tt w}\textbar. If {\tt w} is negative, the result is left-justified, otherwise right-justified. #><# Examples: \\ {\tt format(42, 5)} returns {\tt "~~~42"}; and \\ {\tt format(42, -5)} returns {\tt "42~~~"}. #> inline {# java.lang.String.format("%" + (#w#) + "d", new java.lang.Long(#l#)) #} } public String format(String s, int w) { purpose <# Returns {\tt s} padded with spaces to a minimum width \textbar{\tt w}\textbar. If {\tt w} is negative, the result is left-justified, otherwise right-justified. #><# Examples: \\ {\tt format("aaa", 5)} returns {\tt "~~aaa"}; and \\ {\tt format("aaa", -5)} returns {\tt "aaa~~"}. #> inline {# java.lang.String.format("%" + (#w#) + "s", #s#) #} } public boolean parseBoolean(String s) { purpose <# Returns {\tt s} converted to a {\tt boolean}. #> inline {# java.lang.Boolean.parseBoolean(#s#) #} } public int parseInt(String s) { purpose <# Returns {\tt s} converted to an {\tt int}. #> inline {# java.lang.Integer.parseInt(#s#) #} } public long parseLong(String s) { purpose <# Returns {\tt s} converted to a {\tt long}. #> inline {# java.lang.Long.parseLong(#s#) #} } public float parseFloat(String s) { purpose <# Returns {\tt s} converted to a {\tt float}. #> inline {# java.lang.Float.parseFloat(#s#) #} } public double parseDouble(String s) { purpose <# Returns {\tt s} converted to a {\tt double}. #> inline {# java.lang.Double.parseDouble(#s#) #} } } // end section: Strings public section <#Terminating a program#> { public void exit() { purpose <# Terminates the program. #> inline {# java.lang.System.exit(0) #} } } // end section: Terminating a program public section <#Debugger#> { public purpose <# This environment supports a debugger with a graphical user interface. It allows the user to slow down the execution of a program, so that the flow of control through the program may be observed and the values in variables monitored. #><# To activate the debugger, compile your program with the \verb"mashc" \verb"+debug" option. #><# While the program is running under the control of the debugger, the original MaSH program's source code is displayed on the left, with a pointer showing the statement that is about to execute. Sometimes it will point to a closing brace, indicating that the current method is about to be exited, or a loop guard is about to be tested again, depending on the context. #><# The contents of all the program's variables are displayed on the right in a table. The entries in the {\it scope} column, consist of a number and a word. The number indicates which method invocation the variable belongs to. For no-method MaSH programs the number is always 0. For method MaSH programs, 0 indicates a global variable, 1 indicates the \verb"main" method, 2 indicates a method called from \verb"main", 3 indicates a method called from 2, etc. The word is blank for variables in a no-method program, unless the variable is declared in a block, which makes it {\it local}. In method programs, the word can be: {\it global}; {\it local}; or {\it param}. Variables are added to the table as they come into scope and removed again when their enclosing block exits. Anything changed on the last program step is displayed in green. #><# Limitations: The debugger can not display values of arrays with 3 or more dimensions. The debugger can not step through methods that run in the programs event dispatch thread (for example, the \verb"paintWindow" method in the \verb"graphics" environment). This GUI debugger can not work in environments without a big screen (for example, Lego Mindstorms). #> private debug member { purpose <# This object is a stack that maintains a model of the program's runtime environment. It presents the interface for the code generated by the \verb"+debug" option. #><# {\tt mash\verb"_"debugger.open()} opens the debugger window (program is about to start). #><# {\tt mash\verb"_"debugger.close()} closes the debugger window (program is about to terminate). #><# {\tt mash\verb"_"debugger.push(String name, String kind, String type, String value)} adds a new variable. #><# {\tt mash\verb"_"debugger.push()} notes we have entered a new method (start a new stack frame). #><# {\tt mash\verb"_"debugger.pop(int n)} remove $n$ variables. #><# {\tt mash\verb"_"debugger.pop()} remove the current stack frame (we are exiting a method). #><# {\tt mash\verb"_"debugger.set(String name, value)} updates the value in the named variable. #><# {\tt mash\verb"_"debugger.checkpoint(int line, int column)} allows the debugger window to update and control execution timing. #><# {\tt mash\verb"_"debugger.testpoint(int line, int column, boolean p)} allows the debugger window to update and control execution timing, returning p. #><# {\tt mash\verb"_"debugger.checkpoint()} a checkpoint where the location is the very end of the program (it's about to terminate). #><# {\tt mash\verb"_"debugger.toString(value)} convert a java value to a string. #><# {\tt mash\verb"_"debugger.suspend()} turn the debugger off tempoparily (e.g. to stop the event dispatcher thread blocking). #><# {\tt mash\verb"_"debugger.suspend()} turn the debugger on again. #><# {\tt mash\verb"_"debugger.update()} update the debugger display (after some asyncronous event). #> inline {# // Interface for creating anonymous classes for wrapping // Array Initialisers private interface MaSHDebuggerArrayInit { } // Debugger stack class private static class MaSHDebuggerStack { // One entry on the debugger's stack private class MaSHDebuggerVar { public int stackFrame; // 0 = global, 1, 2, ... per method public java.lang.String name; // "x" public java.lang.String kind; // "global", ... public java.lang.String type; // "int", ... public java.lang.String value; // "3" public int freshness; // 0 = old, 1 = new value; 2 = new var public MaSHDebuggerVar(int stackFrame, java.lang.String name, java.lang.String kind, java.lang.String type, java.lang.String value) { this.stackFrame = stackFrame; this.name = name; this.kind = kind; this.type = type; this.value = value; freshness = 2; } public void set(java.lang.String value) { this.value = value; freshness = 1; } public void reset() { freshness = 0; } } // end class: MaSHDebuggerVar // the stack of program variables to display private java.util.Stack stack = new java.util.Stack(); // what is the current stackFrame number, starts 0 = global private int stackFrame = 0; // start a new stack frame public void push() { if (!suspended) { stackFrame++; } } // add a new variable public void push(java.lang.String name, java.lang.String kind, java.lang.String type, java.lang.String value) { if (!suspended) { stack.push(new MaSHDebuggerVar(stackFrame, name, kind, type, value)); } } // remove n variables public void pop(int n) { if (!suspended) { for (int i = 0; i < n; i++) { stack.pop(); } } } // pop the current stack frame public void pop() { if (!suspended) { while (stack.size() > 0 && stack.elementAt(stack.size() - 1).stackFrame == stackFrame) { stack.pop(); } stackFrame--; } } // find a variable, either a local in the current stack frame, // or a global private MaSHDebuggerVar lookup(java.lang.String name) { for (int i = stack.size() - 1; i >= 0; i--) { MaSHDebuggerVar v = stack.elementAt(i); if (name.equals(v.name) && (v.stackFrame == stackFrame || v.stackFrame == 0)) { return v; } } return null; } // update a value (generic version, after the fact) public void set(java.lang.String name, java.lang.String value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(value); } } // update a value (inline versions) public boolean iset(java.lang.String name, boolean value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public byte iset(java.lang.String name, byte value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public double iset(java.lang.String name, double value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public float iset(java.lang.String name, float value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public int iset(java.lang.String name, int value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public long iset(java.lang.String name, long value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public char iset(java.lang.String name, char value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public java.lang.String iset(java.lang.String name, java.lang.String value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public boolean[] iset(java.lang.String name, boolean[] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public byte[] iset(java.lang.String name, byte[] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public double[] iset(java.lang.String name, double[] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public float[] iset(java.lang.String name, float[] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public int[] iset(java.lang.String name, int[] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public long[] iset(java.lang.String name, long[] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public char[] iset(java.lang.String name, char[] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public java.lang.String[] iset(java.lang.String name, java.lang.String[] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public boolean[][] iset(java.lang.String name, boolean[][] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public byte[][] iset(java.lang.String name, byte[][] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public double[][] iset(java.lang.String name, double[][] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public float[][] iset(java.lang.String name, float[][] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public int[][] iset(java.lang.String name, int[][] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public long[][] iset(java.lang.String name, long[][] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public char[][] iset(java.lang.String name, char[][] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } public java.lang.String[][] iset(java.lang.String name, java.lang.String[][] value) { MaSHDebuggerVar v = lookup(name); if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(value)); } return value; } // update a value (inline array element versions) public boolean[] aiset(java.lang.String name, boolean[] array, int i, boolean value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public byte[] aiset(java.lang.String name, byte[] array, int i, byte value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public double[] aiset(java.lang.String name, double[] array, int i, double value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public float[] aiset(java.lang.String name, float[] array, int i, float value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public int[] aiset(java.lang.String name, int[] array, int i, int value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public long[] aiset(java.lang.String name, long[] array, int i, long value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public char[] aiset(java.lang.String name, char[] array, int i, char value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public java.lang.String[] aiset(java.lang.String name, java.lang.String[] array, int i, java.lang.String value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public boolean[][] aiset(java.lang.String name, boolean[][] array, int i, boolean[] value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public byte[][] aiset(java.lang.String name, byte[][] array, int i, byte[] value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public double[][] aiset(java.lang.String name, double[][] array, int i, double[] value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public float[][] aiset(java.lang.String name, float[][] array, int i, float[] value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public int[][] aiset(java.lang.String name, int[][] array, int i, int[] value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public long[][] aiset(java.lang.String name, long[][] array, int i, long[] value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public char[][] aiset(java.lang.String name, char[][] array, int i, char[] value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public java.lang.String[][] aiset(java.lang.String name, java.lang.String[][] array, int i, java.lang.String[] value) { MaSHDebuggerVar v = lookup(name); array[i] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public boolean[][] aaiset(java.lang.String name, boolean[][] array, int i, int j, boolean value) { MaSHDebuggerVar v = lookup(name); array[i][j] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public byte[][] aaiset(java.lang.String name, byte[][] array, int i, int j, byte value) { MaSHDebuggerVar v = lookup(name); array[i][j] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public double[][] aaiset(java.lang.String name, double[][] array, int i, int j, double value) { MaSHDebuggerVar v = lookup(name); array[i][j] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public float[][] aaiset(java.lang.String name, float[][] array, int i, int j, float value) { MaSHDebuggerVar v = lookup(name); array[i][j] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public int[][] aaiset(java.lang.String name, int[][] array, int i, int j, int value) { MaSHDebuggerVar v = lookup(name); array[i][j] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public long[][] aaiset(java.lang.String name, long[][] array, int i, int j, long value) { MaSHDebuggerVar v = lookup(name); array[i][j] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public char[][] aaiset(java.lang.String name, char[][] array, int i, int j, char value) { MaSHDebuggerVar v = lookup(name); array[i][j] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } public java.lang.String[][] aaiset(java.lang.String name, java.lang.String[][] array, int i, int j, java.lang.String value) { MaSHDebuggerVar v = lookup(name); array[i][j] = value; if (v != null && (!suspended || v.stackFrame == 0)) { v.set(toString(array)); } return array; } // escape a special character private java.lang.String escape(char c) { switch (c) { case '\n': return "\\n"; case '\t': return "\\t"; case '\'': return "\\'"; case '\"': return "\\\""; case '\\': return "\\\\"; default: return "" + c; } } // convert SIMPLE values to strings public java.lang.String toString(boolean value) { return "" + value; } public java.lang.String toString(byte value) { return "" + value; } public java.lang.String toString(double value) { return "" + value; } public java.lang.String toString(float value) { return "" + value; } public java.lang.String toString(int value) { return "" + value; } public java.lang.String toString(long value) { return "" + value; } public java.lang.String toString(char value) { return "'" + escape(value) + "'"; } public java.lang.String toString(java.lang.String value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder(); sb.append("\""); for (int i = 0; i < value.length(); i++) { sb.append(escape(value.charAt(i))); } return sb.append("\"").toString(); } } // convert a ARRAYs to strings public java.lang.String toString(boolean[] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(byte[] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(int[] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(long[] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(float[] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(double[] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(char[] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(java.lang.String[] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(boolean[][] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(byte[][] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(int[][] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(long[][] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(float[][] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(double[][] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(char[][] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } public java.lang.String toString(java.lang.String[][] value) { if (value == null) { return "null"; } else { java.lang.StringBuilder sb = new java.lang.StringBuilder("{"); for (int i = 0; i < value.length; i++) { sb.append(toString(value[i])); if (i < value.length - 1) { sb.append(','); } } return sb.append("}").toString(); } } // The MaSH program source code private java.util.List source = new java.util.ArrayList(); // Read the MaSH program source code private void readSource() { if (source.size() == 0) { // so we don't do it twice try { java.util.Scanner sc = new java.util.Scanner( new java.io.File("#PROGRAM_NAME#.mash")); while (sc.hasNextLine()) { source.add(expandTabs(sc.nextLine())); } sc.close(); } catch (Exception e) { java.lang.System.err.println( "MaSH debugger could not read #PROGRAM_NAME#.mash."); java.lang.System.exit(1); } } } // expand tabs private java.lang.String expandTabs(java.lang.String s) { java.lang.StringBuilder sb = new java.lang.StringBuilder(); int w = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '\t') { do { sb.append(' '); w++; } while (w % 8 != 0); } else { sb.append(c); w++; } } return sb.toString(); } // The frame class private class DebugFrame extends javax.swing.JFrame { // Boxes within which visible components laid out private javax.swing.Box mainBox = new javax.swing.Box( javax.swing.BoxLayout.Y_AXIS); private javax.swing.Box topBox = new javax.swing.Box( javax.swing.BoxLayout.X_AXIS); private javax.swing.Box botBox = new javax.swing.Box( javax.swing.BoxLayout.X_AXIS); // Display components private javax.swing.JPanel srcPanel = new javax.swing.JPanel(); private javax.swing.JScrollPane srcScrollPane = new javax.swing.JScrollPane(); private javax.swing.JEditorPane srcEditorPane = new javax.swing.JEditorPane("text/html", null); private javax.swing.JPanel varPanel = new javax.swing.JPanel(); private javax.swing.JScrollPane varScrollPane = new javax.swing.JScrollPane(); private javax.swing.JEditorPane varEditorPane = new javax.swing.JEditorPane("text/html", null); // Control components private javax.swing.JButton stepButton = new javax.swing.JButton("Step"); private javax.swing.JLabel slowLabel = new javax.swing.JLabel("Slow"); private javax.swing.JSlider speedSlider = new javax.swing.JSlider(1, 100); private javax.swing.JLabel fastLabel = new javax.swing.JLabel("Fast"); private javax.swing.JButton runButton = new javax.swing.JButton("Run/Pause"); private javax.swing.JButton quitButton = new javax.swing.JButton("Quit"); public DebugFrame() { super(); // layout boxes getContentPane().add(mainBox); mainBox.add(topBox); mainBox.add(botBox); // source code display topBox.add(srcPanel); srcPanel.add(srcScrollPane); srcScrollPane.setPreferredSize( new java.awt.Dimension(400, 400)); srcScrollPane.setViewportView(srcEditorPane); srcEditorPane.setEditable(false); // variables display topBox.add(varPanel); varPanel.add(varScrollPane); varScrollPane.setPreferredSize( new java.awt.Dimension(400, 400)); varScrollPane.setViewportView(varEditorPane); varEditorPane.setEditable(false); // controls //stepButton.setDefaultCapable(false); //stepButton.setFocusPainted(false); //quitButton.setDefaultCapable(false); botBox.add(stepButton); stepButton.addActionListener( new java.awt.event.ActionListener() { public void actionPerformed( java.awt.event.ActionEvent e) { step(); } }); botBox.add(slowLabel); botBox.add(speedSlider); speedSlider.setValue(5); // 2s setDelay(speedSlider.getValue()); speedSlider.addChangeListener( new javax.swing.event.ChangeListener() { public void stateChanged( javax.swing.event.ChangeEvent e) { setDelay(speedSlider.getValue()); } }); botBox.add(fastLabel); botBox.add(runButton); runButton.addActionListener( new java.awt.event.ActionListener() { public void actionPerformed( java.awt.event.ActionEvent e) { runPause(); } }); botBox.add(quitButton); quitButton.addActionListener( new java.awt.event.ActionListener() { public void actionPerformed( java.awt.event.ActionEvent e) { java.lang.System.exit(0); } }); // frame setTitle("MaSH Debugger: #PROGRAM_NAME#"); setDefaultCloseOperation(EXIT_ON_CLOSE); setResizable(false); pack(); } // end constructor // display source, with a marker at (line, col). // (-1,_) means at end of a no-method program. // tag is a string so display at the marker, e.g. // true/false at a testpoint. It is usually null. public void displaySource(int line, int col, java.lang.String tag) { int nominalWidth = 40; java.lang.String marker = ""; if (tag == null) { marker = ""; } else { marker = "" + tag + "►"; } int count = 0; // source chars int markerPos = 0; java.lang.StringBuilder sb = new java.lang.StringBuilder() .append("
");
                     for (int i = 0; i < source.size(); i++) {
                        java.lang.String s = source.get(i);
                        if (i == line) {
                           appendHTMLSafe(sb, s.substring(0,col));
                           sb.append(marker);
                           count += col + 1;
                           if (s.length() < nominalWidth 
                              || col <= nominalWidth / 3) {
                              markerPos = count - col;
                           } else {
                              markerPos = count + 
                                 Math.min(nominalWidth, s.length() - col);
                           }
                           appendHTMLSafe(sb, s.substring(col));
                           count += s.length() - col;
                        } else {
                           appendHTMLSafe(sb, s);
                           count += s.length();
                        }
                        sb.append('\n');
                        count++;
                     }
                     if (line == -1) {
                        sb.append(marker);
                        count++;
                        markerPos = count;
                     } 
                     sb.append("
"); srcEditorPane.setText(sb.toString()); srcEditorPane.setCaretPosition(markerPos); lastLine = line; lastCol = col; } // display source, with a marker at (line, col). // (-1,_) means at end of a no-method program. // p is a boolean value to tag the marker. public void displaySource(int line, int col, boolean p) { displaySource(line, col, "" + p); } // display source, with a marker at (line, col). // (-1,_) means at end of a no-method program. public void displaySource(int line, int col) { displaySource(line, col, null); } // display variables stack public void displayVars() { java.lang.StringBuilder sb = new java.lang.StringBuilder().append("" + "" + "" + ""); for (int i = 0; i < stack.size(); i++) { MaSHDebuggerVar v = stack.elementAt(i); sb.append(""); v.reset(); } sb.append("
scopenametypevalue
"); if (v.freshness >= 2) sb.append(""); sb.append(v.stackFrame); sb.append('-'); sb.append(v.kind); if (v.freshness >= 2) sb.append(""); sb.append(""); if (v.freshness >= 2) sb.append(""); sb.append(""); sb.append(v.name); sb.append(""); if (v.freshness >= 2) sb.append(""); sb.append(""); if (v.freshness >= 2) sb.append(""); sb.append(""); sb.append(v.type); sb.append(""); if (v.freshness >= 2) sb.append(""); sb.append(""); if (v.value.equals("undefined")) sb.append(""); else if (v.freshness >= 1) sb.append(""); sb.append(""); appendHTMLSafe(sb, v.value); sb.append(""); if (v.freshness >= 1 || v.value.equals("undefined")) sb.append(""); sb.append("
"); varEditorPane.setText(sb.toString()); } // update the controls to those normally available public void updateControls() { stepButton.setEnabled(execMode < 2); speedSlider.setEnabled(execMode < 2); runButton.setEnabled(execMode < 2); quitButton.setEnabled(true); } // disable the controls that should not be available when the // host program is running (e.g. a read)( public void disableStepRun() { stepButton.setEnabled(false); runButton.setEnabled(false); } // encode html specials as you add a string to a builder // returns actual # chars appended private void appendHTMLSafe(java.lang.StringBuilder sb, java.lang.String s) { for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '&': sb.append("&"); break; case '"': sb.append("""); break; default : sb.append(c); } } } } // end class: DebugFrame // The debugger window private DebugFrame window = new DebugFrame(); // run speed private final int MAX_DELAY = 10000; // 10s private int delay; // MAX_DELAY / [1..100] = [10000..100] // update delay from slider value private void setDelay(int sliderValue) { delay = MAX_DELAY / sliderValue; } // execution mode // 0 = single step // 1 = running // 2 = program is finished private int execMode = 0; // true = don't proceed if in single step mode private boolean block = true; // do one step or revert to step mode private void step() { if (execMode == 0) { block = false; } else { execMode = 0; block = true; } } // toggle run/Pause private void runPause() { execMode = 1 - execMode; block = true; } // wait until allowed to proceed by stepping or delay private void hold() { if (!java.lang.Thread.currentThread().getName().equals("main")) return; switch (execMode) { case 0 : while (block && execMode < 1) { java.lang.Thread.yield(); } block = true; break; case 1 : try { java.lang.Thread.sleep(delay); } catch (Exception e) { } break; default : return; } window.disableStepRun(); } // suspend flag private boolean suspended = false; // suspend public void suspend() { suspended = true; } // unsuspend public void resume() { suspended = false; } // the last position drawn private int lastLine = -2, lastCol = -2; // open the debugger window public void open() { if (!suspended) { readSource(); window.setVisible(true); } } // update the debugger display and wait for step or delay public void checkpoint(int line, int column) { if (!suspended) { window.displaySource(line, column); window.displayVars(); window.updateControls(); hold(); } } // update the debugger display and wait for step or delay // in a condition public boolean testpoint(int line, int column, boolean p) { if (!suspended) { window.displaySource(line, column, p); window.displayVars(); window.updateControls(); hold(); } return p; } // checkpoint where the loction is after the last character in // the program public void checkpoint() { if (!suspended) { window.displaySource(-1, -1, "FINISHED"); window.displayVars(); window.updateControls(); } } // the program is finished. public void close() { if (!suspended) { execMode = 2; window.displaySource(lastLine, lastCol, "FINISHED"); window.updateControls(); } } // the program is finished. public void update() { if (!suspended) { //window.displaySource(lastLine, lastCol, "FINISHED"); window.displayVars(); //window.updateControls(); } } } // end class: MaSHDebuggerStack // The instance of the debugger stack private static MaSHDebuggerStack mash_debugger = new MaSHDebuggerStack(); #} } // end debug member } // end section: Debugger }