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 “.sml”. *)
- (* #!/usr/bin/env smlnj-script *) (* This is better 1st line if smlnj-script is in your PATH. *)
- (* Author of template portion: Joe Wells *)
- (**********************************************************************
- * INSTRUCTIONS FOR USING THIS FILE:
- *
- * 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 “Xyzzy”.
- *
- * 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 “chmod a+rx
- * Xyzzy” (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 “#!/usr/bin/env smlnj-script”, then
- * “smlnj-script” 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
- * “#!/directory/path/to/smlnj-script”, then the path
- * “/directory/path/to/smlnj-script” 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 “git clone
- * ssh://MACHINE.macs.hw.ac.uk/~jbw/smlnj-script-git/smlnj-script”
- * (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 “*SKALPEL-” 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.sml *);
- (* structure U = SmlnjScriptUtils; *) (* Already in effect; uncomment to list what is available. *)
- (* val () = U.raisePrintingLimitsToMax (); *) (* Uncomment to see into “...” in compiler message. *)
- (* val () = U.interact (); *) (* Uncomment where you want to enter an interactive compiling mode. *)
- val () = silenceCompiler ();
- (* 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 causes difficulty with
- pattern matching, the sortJsonFields function is used to sort the JSON object fields. This also
- will help with detecting two fields with the same name (which I suspect the JSON structure
- allows). *)
- fun sortJsonFields fields =
- 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 =
- 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", "set-difference", "union"]),
- (SOME 4, ["diagonalize"]),
- (NONE, ["set"])]);
- (* Enforces the rules for valid variable names and converts them to integers. *)
- fun parseVariableName varName =
- 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. The “extra” parameter is
- way to get information to the 3 functions that might be different for this expression; this is an
- alternative to using mutable references or generating fresh functions. *)
- fun ('extra,'result)
- caseJsonMathExp
- {intFn : {extra : 'extra, int : IntInf.int} -> 'result,
- opFn : {extra : 'extra, op_name : string, arguments : J.value list} -> 'result,
- varFn : {extra : 'extra, var_num : IntInf.int} -> 'result}
- extra
- j =
- case j
- of (J.INT i) => intFn {extra = extra, int = i}
- | (J.OBJECT fields) =>
- (case sortJsonFields fields
- of [("variable", J.STRING varName)] =>
- varFn {extra = extra, var_num = 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 = length args
- val maybeArgSpec =
- 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 {extra = extra, 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 calls the function it is supplied with the extracted pieces of the definition *)
- fun 'result caseJsonMathDec (decFn : {var_num : IntInf.int, expression : J.value} -> 'result)
- (j as J.OBJECT fields) =
- (case sortJsonFields fields
- of [("declared-variable", J.STRING var),
- ("value", exp)] =>
- decFn {var_num = parseVariableName var, expression = exp}
- | _ => raise (BadJsonMath (j, ["JSON object does not represent a math declaration: ",
- "wrong fields or declared-variable not a string"])))
- | caseJsonMathDec _ 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 definition
- and if it is calls the function it is supplied with the list of definitions. *)
- fun 'result caseJsonMathDecList (decListFn : J.value list -> 'result)
- (J.OBJECT [("declaration-list", J.ARRAY decs)]) =
- decListFn decs
- | caseJsonMathDecList _ j =
- raise (BadJsonMath (j, ["JSON value does not represent a math declaration list: ",
- "not an object or object with wrong fields"]));
- (* *************************************************************************** *
- * Code implementation of Part 1 by Nasr Herzallah * *
- * *************************************************************************** *)
- (* Open output file *)
- val outfile = TextIO.openOut "output.txt";
- (* output helpers *)
- fun printErr s = TextIO.output (TextIO.stdErr, s);
- fun printOut s = TextIO.output (outfile, s);
- fun closeFile _ = TextIO.closeOut outfile;
- fun printList (h::t) = let
- val () = printOut h;
- val () = printList t;
- in () end
- | printList [] = ();
- (* Program Exit helpers *)
- fun exitCode true = OS.Process.failure
- | exitCode false = OS.Process.success;
- fun exit error = let
- val () = closeFile ();
- val () = OS.Process.exit(exitCode error);
- in () end;
- (* Functions related to exception handling *)
- fun badInput m = let
- val () = printOut "Error: ";
- val () = printList m;
- val () = printOut "\n";
- in () end;
- fun badInitialInput (j,m) = let
- val () = badInput m;
- val () = printErr "Error: See output file.\n";
- val () = exit true;
- in [j] end;
- fun badVar m = let
- val () = badInput m;
- val () = printErr "Error: See output file.\n";
- in () end;
- fun badLaterInput (j,m) = let
- val () = badInput m;
- val () = printErr "Error: See output file.\n";
- in () end;
- fun badImplementation m = let
- val () = printOut "Error: ";
- val () = printList m;
- val () = printOut "\n";
- val () = printErr "Error: See output file.\n";
- in (exit true) end;
- datatype EXP =
- INT of IntInf.int
- | PAIR of (EXP * EXP)
- | SET of EXP list
- (* type to hold variable, which is a pair containg a string variable name, and
- * an associated expression. *)
- datatype VAR = VAR of (IntInf.int * EXP)
- exception EXPException of string list;
- (* a couple function that can be used to print out an EXP. *)
- fun printEXP (INT i) = printInt i
- | printEXP (PAIR p) = printPair p
- | printEXP (SET s) = let
- val () = printOut "{";
- val () = printSet s;
- val () = printOut "}";
- in () end
- and printInt i = let
- val () = if i<0 then printOut "-" else ();
- val j = IntInf.abs i;
- in printOut (IntInf.toString j) end
- and printPair (f, l) = let
- val () = printOut "(";
- val () = printEXP f;
- val () = printOut ", ";
- val () = printEXP l;
- val () = printOut ")";
- in () end
- and printSet (hd::m::tl) = let
- val () = printEXP hd;
- val () = printOut ", ";
- val () = printSet (m::tl);
- in () end
- | printSet (hd::tl) = let
- val () = printEXP hd;
- in () end
- | printSet [] = ();
- fun printVarDec (VAR(i:IntInf.int, e:EXP)) = let
- val () = printOut "Let x";
- val () = printInt i;
- val () = printOut " be ";
- val () = printEXP e;
- in () end;
- fun printEvaluation (VAR(i:IntInf.int, e:EXP)) = let
- val () = printOut "\n\twhich is ";
- val () = printEXP e;
- val () = printOut ".\n"
- in () end;
- fun orderInt (INT i, INT j) = if i=j then
- EQUAL else if i>j then
- GREATER else
- LESS
- | orderInt _ = raise EXPException ["Not required for part-1"];
- fun evaluateSet e = let
- val sorted = ListMergeSort.uniqueSort orderInt e;
- in sorted end;
- fun evaluateEXP (INT i) = (INT i)
- | evaluateEXP (SET s) = SET(evaluateSet s)
- | evaluateEXP e = e;
- fun evaluateVariable (VAR(i:IntInf.int, e:EXP)) = let
- val eval = evaluateEXP e;
- in VAR(i, eval) end;
- (* ********************************
- * Translate from JSON to EXP/VAR *
- * ********************************)
- fun translateExpression vars expression = let
- val exp = caseJsonMathExp
- { intFn = translateInt, opFn = translateOp, varFn = translateVar }
- vars expression;
- in exp end
- and translateInt {extra, int} = INT int
- and translateOp {extra, op_name, arguments} = let
- val exp = (case op_name
- of "set" => translateSet { extra = extra, arguments = arguments }
- | "pair" => translatePair { extra = extra, arguments = arguments }
- | _ => raise EXPException ["Not required for part-1"]);
- in exp end
- and translateVar {extra, var_num} = let
- val () = raise EXPException ["Not required for part-1"];
- in INT 42 end
- and translatePair {extra, arguments} = (case arguments
- of [i, j] => PAIR((translateExpression extra i),(translateExpression extra j))
- | _ => raise (BadVariableName ["Variable name does not exist!"]))
- and translateSet {extra, arguments} = let
- fun loop vars (h::t) = (translateExpression vars h)::(loop vars t)
- | loop valList [] = [];
- val set = loop extra arguments;
- in (SET(set)) end;
- fun translateDeclaration vars {var_num, expression} = let
- val exp = translateExpression vars expression;
- in VAR(var_num, exp) end;
- (* *************************************************************************** *
- * Main Loop Function === NASR REWRITE THIS ===
- === MAKE SURE YOU UNDERSTAND WHAT THE CODE DOES === *
- * *
- * Function iterates over each JSON variable declaration. Each declaration is *
- * translated into a VAR and then printed to output, first in its *
- * 'declared' form, and then in it 'evaluated' form, the function also stores *
- * a list of all previously evaluated VARs in case they need to be *
- * referenced in the future. *
- * *
- * If JSON is not correctly formatted, exceptions are raised through the *
- * `caseJsonMathDec`, `caseJsonMathExp`, or `parseVariableName` functions, *
- * they are caught and that declaration is skipped. This error is also *
- * recorded so that the program can exit with an appropriate error code. *
- * *************************************************************************** *)
- exception LoopException of (J.value list * VAR list * bool);
- fun mainLoop [] _ error = (exit error)
- | mainLoop (h::t) vars error = let
- val var = caseJsonMathDec (translateDeclaration vars) h
- handle BadJsonMath (j,m) => let val () = badLaterInput (j,m);
- in raise (LoopException (t, vars, true)) end
- | BadVariableName m => let val () = badVar m;
- in raise (LoopException (t, vars, true)) end;
- val () = printVarDec var;
- val eval = evaluateVariable var;
- val () = printEvaluation eval;
- val variables = (eval)::vars;
- in mainLoop t variables error end
- handle LoopException (json,vars,error) => mainLoop json vars true
- | EXPException m => badImplementation m;
- (* Use JSONParser using the preliminary code given to us to parse the
- * JSON file as input *)
- val inputFile = JSONParser.parseFile "input.json";
- (* If BadJsonMath is raised here, the script exits with an error code. *)
- val declarations = caseJsonMathDecList (fn x => x) inputFile
- handle BadJsonMath (j,m) => badInitialInput (j,m);
- (* Loop over the list of variable declarations *)
- mainLoop declarations [] false;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement