Submitted By: Dayanandan Natarajan
Supervisor: Joe Wells
School of Mathematical and Computer Sciences
Heriot-Watt University
Objective
Primary objective of this change is to give the programmers and developers the capability of running a SML program as a script. Also the capability to mute and unmute compiler messages. Programmers and developers can write a SML program and run the program like a script over the command prompt.
Files modified
smlnj/base/cm/main/cm-boot.sml
smlnj/base/system/smlnj/internal/boot-env-fn.sml
smlnj/base/compiler/TopLevel/interact/interact.sig
smlnj/base/compiler/TopLevel/interact/interact.sml
smlnj/base/compiler/TopLevel/backend/backend-fn.sml
smlnj/base/compiler/TopLevel/backend/backend.sig
smlnj/base/compiler/TopLevel/interact/mutecompiler.sig (New component)
smlnj/base/compiler/TopLevel/interact/mutecompiler.sml (New component)
smlnj/base/compiler/INDEX
smlnj/base/compiler/MAP
smlnj/base/compiler/core.cm
Change details
interact.sig & interact.sml
A new function (useScriptFile) is added to Backend.Interact structure, which takes the file name and its content as a stream and process the stream by passing it to EvalLoop.evalStream. The compiler messages are muted and unmuted before the processing of the file. (Functions silenceCompiler, dummyfn and unsilenceCompiler will be explained later in this document)
a) New function declaration is added to interact.sig,
val useStream : TextIO.instream -> unit
val useScriptFile : string * TextIO.instream -> unit (* Addded by DAYA *)
val evalStream : TextIO.instream * Environment.environment -> Environment.environment
b) New function definition is added to interact.sml,
The following changes were made in cm-boot.sml to recognise the new command line parameter passed from script.
a) In function args, line added to recognise the new command-line parameter ‘--script’, and a new function ‘nextargscript’ is called to initiate the process of the file.
b) In function init(), the new function (useScriptFile) is added as one of the parameter passed,
fun init (bootdir, de, er, useStream, useScriptFile, useFile, errorwrap, icm) = let
c) In function procCmdLine (), new function processFileScript is added to process the script file, function will check for whether the file passed on is a script file starting with ‘#!’ thru another new function checkSharpbang, consumes the first line thru another new function eatuntilneline and pass the remaining content of the file to function useScriptFile.
(* DAYA change starts here *)
fun eatuntilnewline (instream : TextIO.instream): bool = let
val c = TextIO.input1 instream
in
case TextIO.lookahead instream of
SOME #"\n" => true
| SOME c => eatuntilnewline instream
| NONE => false
end
fun checkSharpbang (instream : TextIO.instream): bool = let
val c = TextIO.input1 instream
in
case c of
SOME #"#" => (
case TextIO.lookahead instream of
SOME #"!" => eatuntilnewline instream
| SOME c => false
| NONE => false
)
| SOME c => false
| NONE => false
end
fun processFileScript (fname) = let
val stream = TextIO.openIn fname
val isscript = checkSharpbang stream
in
if (isscript) = false
then ( Say.say [ "!* Script file doesn't start with #!. \n" ] )
else ( useScriptFile (fname, stream) )
end
(* DAYA change ends here *)
boot-env-fn.sml
In functor BootEnvF, cminit function declaration is amended to include the newly added function useScriptFile.
functor BootEnvF (datatype envrequest = AUTOLOAD | BARE
val architecture: string
val cminit : string DynamicEnv.env envrequest
({ manageImport:
Ast.dec EnvRef.envref -> unit,
managePrint:
Symbol.symbol EnvRef.envref -> unit,
getPending : unit -> Symbol.symbol list }
-> unit)
-> (unit -> unit) option
val cmbmake: string * bool -> unit) :> BOOTENV = struct
backend.sig
A new structure Mutecompiler is declared within signature BACKEND,
signature BACKEND = sig
structure Profile : PROFILE
structure Compile : COMPILE
structure Interact : INTERACT
structure Mutecompiler : MUTECOMPILER
structure Machine : MACHINE
val architecture: string
val abi_variant: string option
end
backend-fn.sml
New structure Mutecompiler is defined within functor BackendFn,
structure Mutecompiler = Mutecompiler
mutecompiler.sig
New signature MUTECOMPILER is defined with all the global variables and functions that are part of Structure Mutecompiler,
mutecompiler.sml
New structure Mutecompiler has the following core functions,
a. silencecompiler function mutes the compiler messages by saving the current printing limits in a ref cell, then set them all to zero and stashes the compiler messages by saving the value of Control.Print.out in a ref cell.
b. unsilencecompiler function unmutes the compiler messages by restoring the printing limits and value of Control.Print.out from stored ref cell.
c. printStashedCompilerOutput function prints the stashed compiler messages to the user.
d. dummyfn function which does nothing is created and invoked to preload the Mutecompiler structure before the script is passed to evalloop, this is to supress the structure auto-loading logs in the script results.
INDEX, MAP and core.cm
INDEX, MAP and core.cm are updated with definitions for signature MUTECOMPILER and structure Mutecompiler.
a. INDEX
MUTECOMPILER
TopLevel/interact/mutecompiler.sig
Mutecompiler
TopLevel/interact/mutecompiler.sml
c. core.cm
TopLevel/interact/mutecompiler.sig
TopLevel/interact/mutecompiler.sml
Writing a script
The script should start with ‘#!’ in the first line followed by the environment location, command-line parameters ‘-Ssml’ and ‘—script’, a new line and then followed by the SML code or program.
Example script named ‘sample’,
--------------beginning of the script-------------
!/usr/bin/env -Ssml –script
;(--SML--)
val () = print "Hello World\n";
--------------end of the script---------------------
Running a script
The script ‘sample’ can be executed from Linux terminal or command prompt as a regular OS script as below provided it is given execution permission,
$ ./sample
Additional functions
a. The compiler messages can be muted/suppressed by invoking the silencecompiler function as below in the script,
val _ = Backend.Mutecompiler.silenceCompiler ();
b. The compiler messages can be unmuted by invoking the unsilencecompiler function as below in the script,
val _ = Backend.Mutecompiler.unsilenceCompiler ();
c. Whenever an error is encountered in compiling, by default only last 5 lines of the suppressed compiler messages are printed to the user and this limit can be pre-set in script as below,
Backend.Mutecompiler.printlineLimit := 10;
d. Whenever compiler messages are muted by calling silenceCompiler function, variable declarations are stashed with ‘#’ in suppressed compiler messages to save memory. To see the original content in case of debugging, this can be retrieved by amending the Control print parameters and restoring the print limits by increasing the string depth and calling the restorePrintingLimits function as below in the script,
Control.Print.stringDepth := 999;
val _ = Backend.Mutecompiler.restorePrintingLimits ();
SML/NJ Version used
Our development and testing is based on Standard ML of New Jersey (32-bit) v110.99.3 on an Intel based macOS 10.13.16.
Test Details
Test Script #1
!/usr/bin/env -Ssml --script
;( SML code starts here )
val x = "Hello World x\n";
val () = print x;
val y = "Hello World y\n";
val () = print y;
val z = "Hello World z\n";
val () = print z;
Test Result #1
$ ./exml07
Standard ML of New Jersey (32-bit) v110.99.3 [built: Mon Apr 10 18:03:19 2023]
val x = "Hello World x\n" : string
Hello World x
val y = "Hello World y\n" : string
Hello World y
val z = "Hello World z\n" : string
Hello World z
$
Test Script #2
!/usr/bin/env -Ssml --script
;( SML code starts here )
val _ = Backend.Mutecompiler.silenceCompiler ();
val x = "Hello World x\n";
val () = print x;
val y = "Hello World y\n";
val () = print y;
val z = "Hello World z\n";
val () = print z;
Test Result #2
$ ./exml07
Standard ML of New Jersey (32-bit) v110.99.3 [built: Mon Apr 10 18:03:19 2023]
Hello World x
Hello World y
Hello World z
$
Test Script #3
!/usr/bin/env -Ssml --script
;(--SML--)
val x = "Hello World x\n";
val () = print x;
val = Backend.Mutecompiler.silenceCompiler ();
val y = "Hello World y\n";
val () = print y;
val = Backend.Mutecompiler.unsilenceCompiler ();
val z = "Hello World z\n";
val () = print z;
Test Result #3
$ ./sample
Standard ML of New Jersey (32-bit) v110.99.3 [built: Mon Apr 10 18:03:19 2023]
val x = "Hello World x\n" : string
Hello World x
Hello World y
val z = "Hello World z\n" : string
Hello World z
$
Test Script #4
!/usr/bin/env -Ssml --script
;(--SML--)
val x = "Hello World x\n";
val () = print x;
val = Backend.Mutecompiler.silenceCompiler ();
Control.Print.stringDepth := 999;
val = Backend.Mutecompiler.restorePrintingLimits ();
val y = "Hello World y\n";
val () == print y;
val _ = Backend.Mutecompiler.unsilenceCompiler ();
val z = "Hello World z\n";
val () = print z;
Test Result #4
$ ./sample
Standard ML of New Jersey (32-bit) v110.99.3 [built: Mon Apr 10 18:03:19 2023]
val x = "Hello World x\n" : string
Hello World x
The last 5 lines 31 through 35 of suppressed compiler messages are:
[library $SMLNJ-MLRISC/IA32.cm is stable]
[autoloading done]
val it = # : unit
val y = "Hello World y\n" : string
./exml07:8.18-9.4 Error: syntax error: deleting SEMICOLON VAL
_____End of suppressed compiler messages.__
Script execution made simple with SML/NJ
Submitted By: Dayanandan Natarajan Supervisor: Joe Wells School of Mathematical and Computer Sciences Heriot-Watt University
Objective
Primary objective of this change is to give the programmers and developers the capability of running a SML program as a script. Also the capability to mute and unmute compiler messages. Programmers and developers can write a SML program and run the program like a script over the command prompt.
Files modified
smlnj/base/cm/main/cm-boot.sml smlnj/base/system/smlnj/internal/boot-env-fn.sml smlnj/base/compiler/TopLevel/interact/interact.sig smlnj/base/compiler/TopLevel/interact/interact.sml smlnj/base/compiler/TopLevel/backend/backend-fn.sml smlnj/base/compiler/TopLevel/backend/backend.sig smlnj/base/compiler/TopLevel/interact/mutecompiler.sig (New component) smlnj/base/compiler/TopLevel/interact/mutecompiler.sml (New component) smlnj/base/compiler/INDEX smlnj/base/compiler/MAP smlnj/base/compiler/core.cm
Change details
A new function (useScriptFile) is added to Backend.Interact structure, which takes the file name and its content as a stream and process the stream by passing it to EvalLoop.evalStream. The compiler messages are muted and unmuted before the processing of the file. (Functions silenceCompiler, dummyfn and unsilenceCompiler will be explained later in this document)
a) New function declaration is added to interact.sig,
b) New function definition is added to interact.sml,
The following changes were made in cm-boot.sml to recognise the new command line parameter passed from script.
a) In function args, line added to recognise the new command-line parameter ‘--script’, and a new function ‘nextargscript’ is called to initiate the process of the file.
b) In function init(), the new function (useScriptFile) is added as one of the parameter passed,
fun init (bootdir, de, er, useStream, useScriptFile, useFile, errorwrap, icm) = let
c) In function procCmdLine (), new function processFileScript is added to process the script file, function will check for whether the file passed on is a script file starting with ‘#!’ thru another new function checkSharpbang, consumes the first line thru another new function eatuntilneline and pass the remaining content of the file to function useScriptFile.
In functor BootEnvF, cminit function declaration is amended to include the newly added function useScriptFile.
functor BootEnvF (datatype envrequest = AUTOLOAD | BARE val architecture: string val cminit : string DynamicEnv.env envrequest
signature BACKEND = sig structure Profile : PROFILE structure Compile : COMPILE structure Interact : INTERACT structure Mutecompiler : MUTECOMPILER structure Machine : MACHINE val architecture: string val abi_variant: string option end
structure Mutecompiler = Mutecompiler
mutecompiler.sig New signature MUTECOMPILER is defined with all the global variables and functions that are part of Structure Mutecompiler,
mutecompiler.sml New structure Mutecompiler has the following core functions,
a. silencecompiler function mutes the compiler messages by saving the current printing limits in a ref cell, then set them all to zero and stashes the compiler messages by saving the value of Control.Print.out in a ref cell.
b. unsilencecompiler function unmutes the compiler messages by restoring the printing limits and value of Control.Print.out from stored ref cell.
c. printStashedCompilerOutput function prints the stashed compiler messages to the user.
d. dummyfn function which does nothing is created and invoked to preload the Mutecompiler structure before the script is passed to evalloop, this is to supress the structure auto-loading logs in the script results.
a. INDEX MUTECOMPILER TopLevel/interact/mutecompiler.sig Mutecompiler TopLevel/interact/mutecompiler.sml
b. MAP interact/ envref.sml supports top-level environment management defs: ENVREF, EnvRef : ENVREF evalloop.sig,sml top-level read-eval-print loop defs: EVALLOOP, EvalLoopF: TOP_COMPILE => EVALLOOP interact.sig,sml creating top-level loops defs: INTERACT, Interact: EVALLOOP => INTERACT mutecompiler.sig,sml allow compiler silencing defs: MUTECOMPILER, Mutecompiler
c. core.cm TopLevel/interact/mutecompiler.sig TopLevel/interact/mutecompiler.sml
Writing a script
The script should start with ‘#!’ in the first line followed by the environment location, command-line parameters ‘-Ssml’ and ‘—script’, a new line and then followed by the SML code or program.
Example script named ‘sample’, --------------beginning of the script-------------
!/usr/bin/env -Ssml –script
;(--SML--) val () = print "Hello World\n"; --------------end of the script---------------------
Running a script
Additional functions
a. The compiler messages can be muted/suppressed by invoking the silencecompiler function as below in the script,
val _ = Backend.Mutecompiler.silenceCompiler ();
b. The compiler messages can be unmuted by invoking the unsilencecompiler function as below in the script,
val _ = Backend.Mutecompiler.unsilenceCompiler ();
c. Whenever an error is encountered in compiling, by default only last 5 lines of the suppressed compiler messages are printed to the user and this limit can be pre-set in script as below,
Backend.Mutecompiler.printlineLimit := 10;
d. Whenever compiler messages are muted by calling silenceCompiler function, variable declarations are stashed with ‘#’ in suppressed compiler messages to save memory. To see the original content in case of debugging, this can be retrieved by amending the Control print parameters and restoring the print limits by increasing the string depth and calling the restorePrintingLimits function as below in the script,
Control.Print.stringDepth := 999; val _ = Backend.Mutecompiler.restorePrintingLimits ();
SML/NJ Version used
Our development and testing is based on Standard ML of New Jersey (32-bit) v110.99.3 on an Intel based macOS 10.13.16.
Test Details
Test Script #1
!/usr/bin/env -Ssml --script
;( SML code starts here ) val x = "Hello World x\n"; val () = print x; val y = "Hello World y\n"; val () = print y; val z = "Hello World z\n"; val () = print z;
Test Result #1
$ ./exml07 Standard ML of New Jersey (32-bit) v110.99.3 [built: Mon Apr 10 18:03:19 2023] val x = "Hello World x\n" : string Hello World x val y = "Hello World y\n" : string Hello World y val z = "Hello World z\n" : string Hello World z $
Test Script #2
!/usr/bin/env -Ssml --script
;( SML code starts here ) val _ = Backend.Mutecompiler.silenceCompiler (); val x = "Hello World x\n"; val () = print x; val y = "Hello World y\n"; val () = print y; val z = "Hello World z\n"; val () = print z;
Test Result #2
$ ./exml07 Standard ML of New Jersey (32-bit) v110.99.3 [built: Mon Apr 10 18:03:19 2023] Hello World x Hello World y Hello World z $
Test Script #3
!/usr/bin/env -Ssml --script
;(--SML--) val x = "Hello World x\n"; val () = print x; val = Backend.Mutecompiler.silenceCompiler (); val y = "Hello World y\n"; val () = print y; val = Backend.Mutecompiler.unsilenceCompiler (); val z = "Hello World z\n"; val () = print z;
Test Result #3
$ ./sample Standard ML of New Jersey (32-bit) v110.99.3 [built: Mon Apr 10 18:03:19 2023] val x = "Hello World x\n" : string Hello World x Hello World y val z = "Hello World z\n" : string Hello World z $
Test Script #4
!/usr/bin/env -Ssml --script
;(--SML--) val x = "Hello World x\n"; val () = print x; val = Backend.Mutecompiler.silenceCompiler (); Control.Print.stringDepth := 999; val = Backend.Mutecompiler.restorePrintingLimits (); val y = "Hello World y\n"; val () == print y; val _ = Backend.Mutecompiler.unsilenceCompiler (); val z = "Hello World z\n"; val () = print z;
Test Result #4 $ ./sample Standard ML of New Jersey (32-bit) v110.99.3 [built: Mon Apr 10 18:03:19 2023] val x = "Hello World x\n" : string Hello World x
The last 5 lines 31 through 35 of suppressed compiler messages are: [library $SMLNJ-MLRISC/IA32.cm is stable] [autoloading done] val it = # : unit val y = "Hello World y\n" : string ./exml07:8.18-9.4 Error: syntax error: deleting SEMICOLON VAL _____End of suppressed compiler messages.__
uncaught exception Compile [Compile: "syntax error"] raised at: ../compiler/Parse/main/smlfile.sml:19.24-19.46 ../compiler/TopLevel/interact/evalloop.sml:45.54 ../compiler/TopLevel/interact/evalloop.sml:306.20-306.23$