Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/home/jbw/jbw-cvs/bin/smlnj-script
- (*-*-SML-*-*) (* Tell Emacs to use SML mode even though name does not end with \u201c.sml\u201d. *)
- (* #!/usr/bin/env smlnj-script *) (* This is better 1st line if smlnj-script is in your PATH. *)
- (* Author of template portion: Joe Wells *)
- (* STUDENT'S SOURCE CODE MUST HAVE A STATEMENT (INCLUDING THEIR NAME) OF WHAT PORTION THEY HAVE AUTHORED!!! *)
- (**********************************************************************
- * INSTRUCTIONS FOR USING THIS FILE
- * (DELETE THIS COMMENT AFTER UNDERSTANDING IT.)
- *
- * These instructions assume this file is named Xyzzy. When reading
- * these instructions, replace Xyzzy in your mind by the actual file
- * name.
- *
- * This is an SML/NJ _*script*_. You do not need to compile it. It
- * should be runnable as is. This means if this file is in the
- * current directory, you should be able to type ./Xyzzy at a shell
- * command prompt and this program will run. If you put this file in
- * one of the directories listed in your PATH environment variable,
- * then you can run this by just typing \u201cXyzzy\u201d.
- *
- * There are a few things that can go wrong, and here is a short guide
- * to how to fix them:
- *
- * 1. The permissions on this file might be wrong. Do \u201cchmod a+rx
- * Xyzzy\u201d (assuming you own the file).
- *
- * 2. smlnj-script might not be installed anywhere on the computer you
- * are using. Install it.
- *
- * 3. If the first line reads \u201c#!/usr/bin/env smlnj-script\u201d, then
- * \u201csmlnj-script\u201d might not be in any of the directories found via
- * your PATH environment variable. Fix your PATH.
- *
- * Alternatively, if the first line is of the form
- * \u201c#!/directory/path/to/smlnj-script\u201d, then the path
- * \u201c/directory/path/to/smlnj-script\u201d might be the wrong path to
- * smlnj-script. Fix it.
- *
- * Fuller instructions for smlnj-script can be found in the README
- * file in the smlnj-script git repository. As of 2016-01, you can
- * find a copy of this repository at
- * /home/jbw/smlnj-script-git/smlnj-script on the UNIX NFS file
- * systems of the Heriot-Watt University Computer Science department.
- * Do \u201cgit clone
- * ssh://MACHINE.macs.hw.ac.uk/~jbw/smlnj-script-git/smlnj-script\u201d
- * (where you must replace MACHINE by the name of some machine that
- * works, e.g., linux01 worked for me) to get your own copy of this
- * repository.
- **********************************************************************)
- (* *************************)
- (*** support for debugging *)
- (* *************************)
- (* Comments that begin with \u201c*SKALPEL-\u201d help Skalpel, a tool for SML type errors. *)
- (* Semicolons after Skalpel magic comments are needed by Skalpel. *)
- (**SKALPEL-USE-FILE /home/jbw/smlnj-script-git/smlnj-script/smlnj-script-for-skalpel.sml *);
- val () = U.raisePrintingLimitsToMax (); (* Comment to hide details in compiler messages. *)
- (* val () = U.interact (); *) (* Move this to where you want to enter interactive mode and uncomment. *)
- val () = silenceCompiler (); (* Because next 3 function calls print a lot. *)
- (* These two help you avoid shooting yourself in the foot. *)
- val () = Student.strongerPatternErrorsAndWarnings ();
- val () = Student.disableBadPartialFunctions ();
- (* This helps you avoid getting a lower mark by violating the prohibition on string building. *)
- val () = Student.disableStringBuilding ();
- val () = U.clearCompilerOutputStash ();
- val () = unsilenceCompiler (); (* Comment when your script is finally completely working. *)
- (* *********************************************************************)
- (*** utilities for processing mathematical definitions encoded in JSON *)
- (* *********************************************************************)
- structure J = JSON;
- (* The JSON structure represents JSON objects as association lists. Parse results are not
- guaranteed to have object fields in any particular order. Because this makes pattern matching
- tedious, the sortJsonFields function is used to sort the JSON object fields. This also will help
- with detecting bad JSON having two fields with the same name (which I suspect the JSON structure
- allows). *)
- fun 'value sortJsonFields (fields : (string * 'value) list) : (string * 'value) list =
- ListMergeSort.sort (fn ((key1,value1),(key2,value2)) =>
- U.isGreater (String.compare (key1,key2)))
- fields;
- (* Error exceptions have (string list) rather than string so as to abide by the prohibition of
- run-time string building *)
- exception BadJsonMath of J.value * (string list);
- exception BadVariableName of string list;
- (* Identifies which operators are allowed and how many arguments each expects. *)
- val operatorArgSpecs : (string * (int option)) list =
- List.concat
- (map (fn (argSpec, operList) =>
- (map (fn oper => (oper, argSpec))
- operList))
- [(SOME 1, ["domain", "inverse", "is-function"]),
- (SOME 2, ["pair", "apply-function", "equal", "member", "function-space", "union"]),
- (SOME 4, ["diagonalize"]),
- (NONE, ["set"])]);
- (* Enforces the rules for valid variable names and converts them to integers. *)
- fun parseVariableName (varName : string) : IntInf.int =
- case String.explode varName
- of #"x" :: #"0" :: _ :: _ =>
- raise (BadVariableName ["purported variable name contains character(s) after x0: ",
- varName])
- | #"x" :: digits =>
- foldl (fn (c as (#"0"|(#"1")|(#"2")|(#"3")|(#"4")|(#"5")|(#"6")|(#"7")|(#"8")|(#"9")),
- num : IntInf.int)
- => (num * 10) + (IntInf.fromInt (Char.ord c - Char.ord #"0"))
- | _ =>
- raise (BadVariableName
- ["purported variable name contains non-digit after the first position: ",
- varName]))
- (0 : IntInf.int)
- digits
- | _ :: _ => raise (BadVariableName ["purported variable name does not start with x: ",
- varName])
- | nil => raise (BadVariableName ["purported variable name is empty string"]);
- (* Detects whether a JSON.value is a valid representation of a mathematical expression and if it
- is calls one of the 3 functions it is supplied for the different cases. *)
- fun ('result)
- caseJsonMathExp
- {intFn : IntInf.int -> 'result,
- opFn : {op_name : string, arguments : J.value list} -> 'result,
- varFn : IntInf.int -> 'result,
- j : J.value}
- : 'result =
- case j
- of (J.INT i) => intFn i
- | (J.OBJECT fields) =>
- (case sortJsonFields fields
- of [("variable", J.STRING varName)] =>
- varFn (parseVariableName varName)
- | [("variable", _)] =>
- raise (BadJsonMath (j, ["JSON object does not represent a variable reference: ",
- "the contents of its field is not a string"]))
- | [("arguments", J.ARRAY args),
- ("operator", J.STRING operName)] => let
- val numArgs : int = length args
- val maybeArgSpec : int option option =
- Option.map (# 2) (List.find (fn (operName2, argSpec) =>
- operName2 = operName)
- operatorArgSpecs)
- val () =
- case maybeArgSpec
- of NONE =>
- raise (BadJsonMath (j, ["JSON object does not represent a math expression: ",
- "bad operator name: ", operName]))
- | SOME NONE => ()
- | SOME (SOME requiredNumArgs) =>
- if requiredNumArgs = numArgs
- then ()
- else raise (BadJsonMath
- (j, ["JSON object does not represent a math expression: ",
- "operator ",operName," requires ",
- Int.toString requiredNumArgs,
- " arguments but got ",Int.toString numArgs]))
- in opFn {op_name = operName, arguments = args} end
- | [("arguments", J.ARRAY _),
- ("operator", _)] =>
- raise (BadJsonMath (j, ["JSON object does not represent an operation: ",
- "the contents of its operator field is not a string"]))
- | [("arguments", _),
- ("operator", J.STRING _)] =>
- raise (BadJsonMath (j, ["JSON object does not represent an operation: ",
- "the contents of its arguments field is not an array"]))
- | _ => raise (BadJsonMath (j, ["JSON object does not represent a math expression: ",
- "wrong fields"])))
- | _ => raise (BadJsonMath (j, ["JSON value does not represent a math expression: ",
- "neither integer nor object"]));
- (* Detects whether a JSON.value is a valid representation of a mathematical definition and if it
- is extracts the variable and the JSON representing what the variable is being defined to be. *)
- fun extractJsonMathDec (j as J.OBJECT fields) =
- (case sortJsonFields fields
- of [("declared-variable", J.STRING var),
- ("value", exp)] =>
- (parseVariableName var, exp)
- | _ => raise (BadJsonMath (j, ["JSON object does not represent a math declaration: ",
- "wrong fields or declared-variable not a string"])))
- | extractJsonMathDec j =
- raise (BadJsonMath (j, ["JSON value does not represent a math declaration: not an object"]));
- (* Detects whether a JSON.value is a valid representation of a list of mathematical definitions
- and if it is extracts the list. *)
- fun extractJsonMathDecList (J.OBJECT [("declaration-list", J.ARRAY decs)]) = decs
- | extractJsonMathDecList j =
- raise (BadJsonMath (j, ["JSON value does not represent a math declaration list: ",
- "not an object or object with wrong fields"]));
- (* *************************************************************************** *
- * BEGIN STUDENT WORK *
- * Code implementation of Part 1 by Nasr Herzallah - H00201083 * *
- * *************************************************************************** *)
- val outFile = TextIO.openOut "output.txt"; (* Open output file *)
- fun printToFile s = TextIO.output (outFile, s); (* Print to output file *)
- fun printToFileAndConsole s = (printToFile s; print s); (* Print to output file and console *)
- fun printError s = TextIO.output (TextIO.stdErr, s); (* Print error stream *)
- (* Create tree data structure for representing the mathematical expressions
- of integers, pairs of integers and sets of integers *)
- datatype DATA = INT of IntInf.int | VAR of (IntInf.int * DATA) | PAIR of (DATA * DATA) | SET of DATA list;
- (* Create error exception for 'BAD INPUT:' statements*)
- exception DataTreeException of string;
- (* function to print tree data structure to the output file
- - covers all parts of tree to avoid errors *)
- fun printDataTree (INT i) = printInt i
- | printDataTree (VAR v) = printVar v
- | printDataTree (PAIR p) = printPair p
- | printDataTree (SET s) = let
- val () = printToFile "{";
- val () = printSet s;
- val () = printToFile "}";
- in () end
- and printInt i = let
- val () = if i < 0 then printToFile "-" else (); (* Check if number is negative *)
- val j = IntInf.abs i; (* Get the absolute value of the integer (positive) *)
- in printToFile (IntInf.toString j) end
- and printVar (i, d) = let
- val () = printToFile "Let x";
- val () = printToFile (IntInf.toString i); (* Print xi, where i is the variable number *)
- val () = printToFile " be ";
- val () = printDataTree d;
- in () end
- and printPair (d1, d2) = let
- val () = printToFile "(";
- val () = printDataTree d1; (* Print first element of pair *)
- val () = printToFile ", ";
- val () = printDataTree d2; (* Print last element of pair *)
- val () = printToFile ")";
- in () end
- and printSet (hd::snd::tl) = let
- val () = printDataTree hd; (* Print first element of the set *)
- val () = printToFile ", ";
- val () = printSet (snd::tl); (* Print the other elements in the set *)
- in () end
- | printSet (hd::tl) = let
- val () = printDataTree hd; (* Loop the rest of the set's elements *)
- in () end
- | printSet [] = ();
- (* Function to parse the JSON input file into the tree data structure elements *)
- fun JSONToDataTree (data : J.value, node : int) = let
- val result = caseJsonMathExp {intFn = JSONToINT, varFn = JSONToVAR, opFn = JSONToOperator, j = data};
- in result end
- and JSONToINT (i : IntInf.int) = INT i (* Parse integer from JSON to INT in datatype *)
- (* Variables (VAR) not required for part 1 *)
- and JSONToVAR (varNum : IntInf.int) = let (* Parse variable from JSON to VAR in datatype *)
- val () = raise DataTreeException "Not required for part-1";
- in INT 0 end
- and JSONToOperator {op_name : string, arguments : J.value list} = let
- val result = (case op_name of (* Check if operator is pair or set *)
- "pair" => JSONToPAIR arguments |
- "set" => JSONToSET arguments |
- _ => raise DataTreeException "Not required for part-1" (* Other operators not required for part 1 *)
- );
- in result end
- and JSONToPAIR arguments = (case arguments (* Parse pair from JSON to PAIR in datatype *)
- of [i, j] => PAIR((JSONToDataTree (i, 0)), (JSONToDataTree (j, 0))) (* Create pair *)
- | _ => raise DataTreeException "Bad pair!"
- )
- and JSONToSET arguments = let (* Parse set from JSON to SET in datatype *)
- fun loop (h::t) = (JSONToDataTree (h, 0))::(loop t)
- | loop [] = [];
- val set = loop arguments;
- in (SET(set)) end;
- (* Traverse/loop the JSON object from the JSON object list *)
- fun traverseJSONObj (dec : J.value, node : int) = let
- val (var_num : IntInf.int, exp : J.value) = extractJsonMathDec dec;
- val () = printToFile "Let x";
- val () = printToFile (IntInf.toString var_num)
- val () = printToFile " be ";
- val n2 = JSONToDataTree (exp, node + 1)
- val () = printDataTree n2;
- val () = printToFile " which is ";
- val () = printDataTree n2;
- val () = printToFile ".\n";
- in 2 end; (* random int return *)
- (* Traverse/loop the JSON list from the parsed json input file *)
- fun traverseJSONList list = let
- fun loop ((dec : J.value) :: list, node : int) : unit =
- loop (list, traverseJSONObj (dec, node))
- | loop ([], _) = ();
- val () = loop (list, 2);
- in () end;
- (* Load JSON input file, parse it and then traverse it *)
- val jsonInput = JSONParser.parseFile "input.json";
- val json = extractJsonMathDecList jsonInput;
- val () = traverseJSONList json;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement