├── README.md ├── discussions ├── d10_eval │ ├── CFGs and Parsing.pdf │ ├── OpSem.pdf │ ├── OpSem_sols.pdf │ ├── README.md │ └── src │ │ ├── .merlin │ │ ├── dune │ │ ├── interpreter.ml │ │ ├── interpreter.mli │ │ ├── interpreter.skeleton.ml │ │ ├── lexer.ml │ │ ├── lexer.mli │ │ ├── lexer.skeleton.ml │ │ ├── lexer_cameron.ml │ │ ├── parser.ml │ │ ├── parser.mli │ │ └── parser.skeleton.ml ├── d11_lambda │ ├── Lambda Calc.pdf │ ├── README.md │ └── src │ │ ├── .merlin │ │ ├── dune │ │ ├── interpreter.ml │ │ ├── interpreter.mli │ │ ├── interpreter.skeleton.ml │ │ ├── lexer.ml │ │ ├── lexer.mli │ │ ├── lexer.skeleton.ml │ │ ├── lexer_cameron.ml │ │ ├── parser.ml │ │ ├── parser.mli │ │ └── parser.skeleton.ml ├── d12_intro_rust │ ├── README.md │ ├── d12_lambda_calc.pdf │ ├── d12_lambda_calc_sols.pdf │ └── src │ │ ├── correct.rs │ │ └── error.rs ├── d13_rust_memory │ └── README.md ├── d14_security │ └── README.md ├── d1_intro_ruby │ ├── README.md │ └── src │ │ ├── disc1.rb │ │ └── disc1_soln.rb ├── d2_regex │ ├── README.md │ └── src │ │ ├── disc2.rb │ │ └── disc2_soln.rb ├── d3_intro_ocaml │ ├── README.md │ └── src │ │ ├── disc3.ml │ │ └── disc3_solutions.ml ├── d4_map_fold │ ├── README.md │ └── src │ │ ├── disc4.ml │ │ └── disc4_sol.ml ├── d5_typing │ ├── README.md │ ├── disc5.ml │ └── disc5_sol.ml ├── d6_functions │ ├── README.md │ └── examples ├── d7_nfa_dfa │ ├── Disc 7 - Automata Algorithms.pdf │ ├── README.md │ ├── Worksheet.pdf │ ├── nfa.png │ ├── nfa2dfa.png │ └── nfa2dfa_alt.jpg └── d8_parsing │ ├── README.md │ ├── cfg.jpg │ ├── nfa.png │ ├── nfa2dfa.png │ └── src │ ├── .merlin │ ├── dune │ ├── interpreter.ml │ ├── interpreter.mli │ ├── interpreter.skeleton.ml │ ├── lexer.ml │ ├── lexer.mli │ ├── lexer.skeleton.ml │ ├── lexer_cameron.ml │ ├── parser.ml │ ├── parser.mli │ └── parser.skeleton.ml ├── project0 ├── .gitignore ├── .submit ├── README.md └── test │ └── public │ └── public.rb ├── project1a ├── .submit ├── README.md ├── src │ ├── phonebook.rb │ └── warmup.rb └── test │ └── public │ └── public.rb ├── project1b ├── .submit ├── README.md ├── src │ ├── controllers │ │ ├── game_controller.rb │ │ └── input_controller.rb │ ├── main.rb │ └── models │ │ ├── game_board.rb │ │ ├── position.rb │ │ └── ship.rb └── test │ └── public │ ├── inputs │ ├── bad_ships.txt │ ├── correct_ships_p1.txt │ ├── correct_ships_p2.txt │ ├── correct_strat_p1.txt │ ├── correct_strat_p2.txt │ ├── perfect_strat_p1.txt │ ├── perfect_strat_p2.txt │ └── player1.txt │ └── public.rb ├── project2a ├── .gitignore ├── .submit ├── README.md ├── dune-project ├── src │ ├── basics.ml │ ├── basics.mli │ └── dune └── test │ ├── property-based-test │ ├── dune │ └── pbt.ml │ ├── public │ ├── dune │ └── public.ml │ └── student │ ├── dune │ └── student.ml ├── project2b ├── .ocamlinit ├── .submit ├── 3WST.png ├── README.md ├── dune-project ├── src │ ├── data.ml │ ├── data.mli │ ├── dune │ ├── funs.ml │ ├── funs.mli │ ├── higher.ml │ └── higher.mli └── test │ ├── dune │ ├── pbt │ ├── dune │ └── pbt.ml │ ├── public │ ├── dune │ └── public.ml │ ├── student │ ├── dune │ └── student.ml │ └── testUtils.ml ├── project3 ├── .ocamlinit ├── .submit ├── README.md ├── SETS.md ├── bin │ ├── dune │ └── viz.ml ├── dune-project ├── images │ ├── m_viz.png │ └── n_viz.png ├── ocaml_version.sh ├── src │ ├── dune │ ├── nfa.ml │ ├── nfa.mli │ ├── regexp.ml │ ├── regexp.mli │ ├── sets.ml │ └── sets.mli ├── test.sh ├── test │ ├── dune │ ├── pbt │ │ ├── #pbt.ml# │ │ ├── dune │ │ └── pbt.ml │ ├── public │ │ ├── dune │ │ └── public.ml │ ├── student │ │ └── student.ml │ └── testUtils.ml ├── utop.sh └── viz.sh ├── project4a ├── .ocamlinit ├── .submit ├── README.md ├── dune-project ├── src │ ├── dune │ ├── lexer.ml │ ├── lexer.mli │ ├── microCamlTypes.ml │ ├── parser.ml │ ├── parser.mli │ ├── tokenTypes.ml │ └── utils.ml └── test │ ├── dune │ ├── pbt │ ├── dune │ └── pbt.ml │ ├── public │ ├── dune │ └── public.ml │ └── testUtils.ml ├── project4b ├── .ocamlinit ├── .submit ├── README.md ├── assets │ └── ex.gif ├── bin │ ├── dune │ └── mutop.ml ├── dune-project ├── dune-workspace ├── microcaml-opsem.pdf ├── mutop.sh ├── src │ ├── dune │ ├── eval.ml │ ├── eval.mli │ ├── lexer.ml │ ├── lexer.mli │ ├── microCamlTypes.ml │ ├── parser.ml │ ├── parser.mli │ ├── tokenTypes.ml │ └── utils.ml └── test │ ├── dune │ ├── pbt │ ├── dune │ └── pbt.ml │ ├── public │ ├── dune │ └── public.ml │ └── testUtils.ml └── project5 ├── .gitignore ├── .submit ├── Cargo.toml ├── Gc.adoc ├── README.md ├── src ├── basics.rs ├── communicator.rs ├── lib.rs └── linkedlist.rs └── tests ├── public └── mod.rs ├── student └── mod.rs └── tests.rs /README.md: -------------------------------------------------------------------------------- 1 | # CMSC330, Spring 22 2 | 3 | ## Projects 4 | 5 | * [Project 0 - Setup](./project0) 6 | * [Project 1a - Ruby Warmup](./project1a) 7 | * [Project 1b - Battleship Game](./project1b) 8 | * [Project 2a - OCaml Warmup](./project2a) 9 | * [Project 2b - OCaml Higher Order Functions and Data](./project2b) 10 | * [Project 3 - Regular Expression Engine](./project3) 11 | * [Project 4a - MicroCaml Lexer and Parser](./project4a) 12 | * [Project 4b - MicroCaml Interpreter](./project4b) 13 | * [Project 5 - Stark Suit Repair](./project5) 14 | 15 | ## Discussion Exercises 16 | 17 | * [Discussion 1 - Introduction to Ruby](./discussions/d1_intro_ruby) 18 | * [Discussion 2 - Codeblocks and RegEx](./discussions/d2_regex) 19 | * [Discussion 3 - Introduction to OCaml](./discussions/d3_intro_ocaml) 20 | * [Discussion 4 - Map and Fold](./discussions/d4_map_fold) 21 | * [Discussion 5 - Types and Currying](./discussions/d5_typing) 22 | * [Discussion 6 - Closures and Imperative OCaml](./discussions/d6_functions) 23 | * [Discussion 7 - NFAs and DFAs!](./discussions/d7_nfa_dfa) 24 | * [Discussion 8 - CFGs and Parsing](./discussions/d8_parsing) 25 | * [Discussion 10 - Operational Semantics and Parsing (cont)](./discussions/d10_eval) 26 | * [Discussion 11 - Lambda Calculus](./discussions/d11_lambda) 27 | * [Discussion 12 - Intro to Rust](./discussions/d12_intro_rust) 28 | * [Discussion 13 - Rust Memory](./discussions/d13_rust_memory) 29 | * [Discussion 14 - Lambda Calc + Final Exam Review](./discussions/d14_security) 30 | -------------------------------------------------------------------------------- /discussions/d10_eval/CFGs and Parsing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d10_eval/CFGs and Parsing.pdf -------------------------------------------------------------------------------- /discussions/d10_eval/OpSem.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d10_eval/OpSem.pdf -------------------------------------------------------------------------------- /discussions/d10_eval/OpSem_sols.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d10_eval/OpSem_sols.pdf -------------------------------------------------------------------------------- /discussions/d10_eval/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 10 - Friday, April 1st 2 | 3 | ## Coding Excercise 4 | * To go from source code to a running program, there are 3 steps (at least for our purposes): 5 | * Tokenizing/Lexing (separating text into smaller tokens) 6 | * Parsing (generating something meaningful from the tokens - an AST) 7 | * Interpreting (evaluating the result of the AST) 8 | 9 | * Consider the following grammar: 10 | * S -> M + S | M 11 | * M -> N * M | N 12 | * N -> n | (S) 13 | * where n is any integer 14 | 15 | * This grammar is right associative/recursive (Why did we provide a right associative grammar? What would you do if we didn't?). 16 | 17 | * What is the relative precedence of the + and \* operators here? How is it determined? How can we use CFGs to enforce precedence? 18 | 19 | ### Lexer 20 | * Open `lexer.skeleton.ml`. 21 | * Answer key in `lexer.ml` 22 | * Note the variant type `token` we have defined. 23 | * Keep an index that keeps track of where we are in the string, and move forward as we keep tokenizing. 24 | * In P5, you will have to worry about the order in which you have `if/else` ... `if/else` statements (certain regexs should be checked before others). 25 | * It's probably also a good idea to just define all the regex's and store in variables at the top. 26 | 27 | ### Parser 28 | * Open `parser.skeleton.ml`. 29 | * Answer key in `parser.ml` 30 | * Note the variant type `expr` that we have defined 31 | * Note: Use `let rec ...` and to write mutually recursive functions. 32 | * Note: `lookahead` just returns the head of the list. 33 | * Note: `match` just "consumes" the head of the list (provided that the token and head of the list match). 34 | * IMPORTANT: 35 | * We're going to write a function named `parse_X` for each nonterminal `X` in our grammar. 36 | * Each of these functions will parse (consume) some tokens, and return (1) the unparsed tokens and (2) the AST which corresponds to the parsed tokens. 37 | 38 | NOTE: We have also provided a worksheet on CFGs and Parsing for more practice if you want 39 | 40 | ## Operational Semantics Explanation 41 | Basically, we're trying to systematically understand how programs evaluate parsed statements 42 | 43 | ## Operational Semantics Problems 44 | Worksheet on operational semantics - ignore the evaluator problem, we'll cover that next week 45 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/.merlin: -------------------------------------------------------------------------------- 1 | B /home/subomi/.opam/4.07.0/lib/ocaml 2 | B ../_build/default/src/.disc.objs 3 | S /home/subomi/.opam/4.07.0/lib/ocaml 4 | S . 5 | FLG -open Disc -w -40 6 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name disc) 3 | (modules lexer parser interpreter) 4 | (libraries str)) 5 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/interpreter.ml: -------------------------------------------------------------------------------- 1 | open Parser 2 | 3 | (* Evaluater *) 4 | 5 | let rec eval (ast : expr) : int = 6 | match ast with 7 | | Int x -> x 8 | | Mult (x, y) -> let x' = eval x in 9 | let y' = eval y in 10 | x' * y' 11 | | Plus (x, y) -> let x' = eval x in 12 | let y' = eval y in 13 | x' + y' 14 | 15 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/interpreter.mli: -------------------------------------------------------------------------------- 1 | val eval : Parser.expr -> int 2 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/interpreter.skeleton.ml: -------------------------------------------------------------------------------- 1 | open Parser 2 | 3 | (* Evaluater *) 4 | 5 | let rec eval (ast : expr) : int = 6 | failwith "unimplemented" 7 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/lexer.ml: -------------------------------------------------------------------------------- 1 | (* Type *) 2 | type token = 3 | | Tok_Int of int 4 | | Tok_Mult 5 | | Tok_Plus 6 | | Tok_LParen 7 | | Tok_RParen 8 | | Tok_EOF 9 | 10 | let string_of_token tok = match tok with 11 | | Tok_Int(i) -> string_of_int i 12 | | Tok_Mult -> "*" 13 | | Tok_Plus -> "+" 14 | | Tok_LParen -> "(" 15 | | Tok_RParen -> ")" 16 | | Tok_EOF -> "" 17 | 18 | 19 | let rec string_of_list conv lst = 20 | match lst with 21 | | [] -> "" 22 | | h::[] -> conv h 23 | | h::t -> (conv h) ^ " " ^ (string_of_list conv t) 24 | 25 | (* Given source code returns a token list. *) 26 | let rec lexer (input : string) : token list = 27 | let length = String.length input in 28 | 29 | let rec tok pos = 30 | if pos >= length then 31 | [Tok_EOF] 32 | 33 | else if Str.string_match (Str.regexp "(") input pos then 34 | Tok_LParen::(tok (pos + 1)) 35 | 36 | else if Str.string_match (Str.regexp ")") input pos then 37 | Tok_RParen::(tok (pos + 1)) 38 | 39 | else if Str.string_match (Str.regexp "\\+") input pos then 40 | Tok_Plus::(tok (pos + 1)) 41 | 42 | else if Str.string_match (Str.regexp "\\*") input pos then 43 | Tok_Mult::(tok (pos + 1)) 44 | 45 | else if Str.string_match (Str.regexp "-?[0-9]+") input pos then 46 | let value = Str.matched_string input in 47 | Tok_Int(int_of_string value)::(tok (pos + String.length value)) 48 | 49 | else 50 | tok (pos + 1) 51 | 52 | in tok 0;; 53 | 54 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/lexer.mli: -------------------------------------------------------------------------------- 1 | type token = 2 | | Tok_Int of int 3 | | Tok_Mult 4 | | Tok_Plus 5 | | Tok_LParen 6 | | Tok_RParen 7 | | Tok_EOF 8 | 9 | val lexer : string -> token list 10 | 11 | val string_of_token : token -> string 12 | 13 | val string_of_list : ('a -> string) -> 'a list -> string 14 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/lexer.skeleton.ml: -------------------------------------------------------------------------------- 1 | (* Type *) 2 | type token = 3 | | Tok_Int of int 4 | | Tok_Mult 5 | | Tok_Plus 6 | | Tok_LParen 7 | | Tok_RParen 8 | | Tok_EOF 9 | 10 | let string_of_token tok = match tok with 11 | | Tok_Int(i) -> string_of_int i 12 | | Tok_Mult -> "*" 13 | | Tok_Plus -> "+" 14 | | Tok_LParen -> "(" 15 | | Tok_RParen -> ")" 16 | | Tok_EOF -> "" 17 | 18 | let rec string_of_list conv lst = 19 | match lst with 20 | | [] -> "" 21 | | h::[] -> conv h 22 | | h::t -> (conv h) ^ " " ^ (string_of_list conv t) 23 | 24 | (* Given source code returns a token list. *) 25 | let rec lexer (input : string) : token list = 26 | failwith "unimplemented" 27 | 28 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/lexer_cameron.ml: -------------------------------------------------------------------------------- 1 | module L = List 2 | module S = String 3 | module R = Str 4 | 5 | (* Type *) 6 | type token = 7 | | Tok_Int of int 8 | | Tok_Mult 9 | | Tok_Plus 10 | | Tok_LParen 11 | | Tok_RParen 12 | | Tok_EOF 13 | 14 | (* Regular expressions and the tokens they generate. *) 15 | let re = [ 16 | (R.regexp "[0-9]+" , fun x -> [Tok_Int (int_of_string x)]) ; 17 | (R.regexp "\\+" , fun _ -> [Tok_Plus]) ; 18 | (R.regexp "\\*" , fun _ -> [Tok_Mult]) ; 19 | (R.regexp "(" , fun _ -> [Tok_LParen]) ; 20 | (R.regexp ")" , fun _ -> [Tok_RParen]) ; 21 | (R.regexp " " , fun _ -> []) 22 | ] 23 | 24 | (* Given source code returns a token list. *) 25 | let rec lexer (s : string) : token list = 26 | lexer' s 0 27 | 28 | (* Helper for lexer takes in a position offset. *) 29 | and lexer' (s : string) (pos : int) : token list = 30 | if pos >= S.length s then [Tok_EOF] 31 | else 32 | let (_, f) = L.find (fun (re, _) -> R.string_match re s pos) re in 33 | let s' = R.matched_string s in 34 | (f s') @ (lexer' s (pos + (S.length s'))) 35 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/parser.ml: -------------------------------------------------------------------------------- 1 | open Lexer 2 | 3 | (* Types *) 4 | type expr = 5 | | Int of int 6 | | Plus of expr * expr 7 | | Mult of expr * expr 8 | 9 | (* Provided helper function - takes a token list and an exprected token. 10 | * Handles error cases and returns the tail of the list *) 11 | let match_token (toks : token list) (tok : token) : token list = 12 | match toks with 13 | | [] -> raise (Failure(string_of_token tok)) 14 | | h::t when h = tok -> t 15 | | h::_ -> raise (Failure( 16 | Printf.sprintf "Expected %s from input %s, got %s" 17 | (string_of_token tok) 18 | (string_of_list string_of_token toks) 19 | (string_of_token h) 20 | )) 21 | 22 | let lookahead toks = match toks with 23 | h::t -> h 24 | | _ -> raise (Failure("Empty input to lookahead")) 25 | 26 | 27 | (* Parses a token list. *) 28 | let rec parser (toks : token list) : expr = 29 | let (t, exp) = parse_S toks in 30 | if t <> [Tok_EOF] then 31 | raise (Failure "did not reach EOF") 32 | else 33 | exp 34 | 35 | (* Parses the S rule. *) 36 | and parse_S toks = 37 | let (t, m) = parse_M toks in 38 | match lookahead t with 39 | | Tok_Plus -> let t' = match_token t Tok_Plus in 40 | let (t'', s) = parse_S t' in 41 | (t'', Plus (m, s)) 42 | | _ -> t, m 43 | 44 | (* Parses the M rule. *) 45 | and parse_M toks = 46 | let (t, n) = parse_N toks in 47 | match lookahead t with 48 | | Tok_Mult -> let t' = match_token t Tok_Mult in 49 | let (t'', m) = parse_M t' in 50 | (t'', Mult (n, m)) 51 | | _ -> t, n 52 | 53 | (* Parses the N rule. *) 54 | and parse_N toks = 55 | match lookahead toks with 56 | | Tok_Int i -> let t = match_token toks (Tok_Int i) in 57 | (t, Int i) 58 | | Tok_LParen -> let t = match_token toks Tok_LParen in 59 | let (t', s) = parse_S t in 60 | let t'' = match_token t' Tok_RParen in 61 | (t'', s) 62 | | _ -> failwith "parse_N failed" 63 | -------------------------------------------------------------------------------- /discussions/d10_eval/src/parser.mli: -------------------------------------------------------------------------------- 1 | type expr = 2 | | Int of int 3 | | Plus of expr * expr 4 | | Mult of expr * expr 5 | 6 | val parser : Lexer.token list -> expr 7 | val parse_S : Lexer.token list -> Lexer.token list * expr 8 | val parse_M : Lexer.token list -> Lexer.token list * expr 9 | val parse_N : Lexer.token list -> Lexer.token list * expr -------------------------------------------------------------------------------- /discussions/d10_eval/src/parser.skeleton.ml: -------------------------------------------------------------------------------- 1 | open Lexer 2 | 3 | (* Types *) 4 | type expr = 5 | | Int of int 6 | | Plus of expr * expr 7 | | Mult of expr * expr 8 | 9 | (* Provided helper function - takes a token list and an exprected token. 10 | * Handles error cases and returns the tail of the list *) 11 | let match_token (toks : token list) (tok : token) : token list = 12 | match toks with 13 | | [] -> raise (Failure(string_of_token tok)) 14 | | h::t when h = tok -> t 15 | | h::_ -> raise (Failure( 16 | Printf.sprintf "Expected %s from input %s, got %s" 17 | (string_of_token tok) 18 | (string_of_list string_of_token toks) 19 | (string_of_token h) 20 | )) 21 | 22 | let lookahead toks = match toks with 23 | h::t -> h 24 | | _ -> raise (Failure("Empty input to lookahead")) 25 | 26 | 27 | 28 | (* Parses a token list. *) 29 | let rec parser (toks : token list) : expr = 30 | failwith "unimplemented" 31 | 32 | (* Parses the S rule. *) 33 | and parse_S (toks : token list) : (token list * expr) = 34 | failwith "unimplemented" 35 | 36 | (* Parses the M rule. *) 37 | and parse_M (toks : token list) : (token list * expr) = 38 | failwith "unimplemented" 39 | 40 | (* Parses the N rule. *) 41 | and parse_N (toks : token list) : (token list * expr) = 42 | failwith "unimplemented" 43 | -------------------------------------------------------------------------------- /discussions/d11_lambda/Lambda Calc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d11_lambda/Lambda Calc.pdf -------------------------------------------------------------------------------- /discussions/d11_lambda/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 11 - Friday, April 8th 2 | 3 | ## Operational Semantics 4 | Reviewing Operational Semantics from last week - ask any questions! 5 | 6 | ## Coding Excercise 7 | * Recall from last week, to go from source code to a running program, there are 3 steps (at least for our purposes): 8 | * Tokenizing/Lexing (separating text into smaller tokens) 9 | * Parsing (generating something meaningful from the tokens - an AST) 10 | * Interpreting (evaluating the result of the AST) 11 | 12 | * Recall the following grammar: 13 | * S -> M + S | M 14 | * M -> N * M | N 15 | * N -> n | (S) 16 | * where n is any integer 17 | 18 | ## Interpreter 19 | * Open `interpreter.skeleton.ml`. 20 | * Answer key in `interpreter.ml` 21 | * General Approach: Make recursive calls to evaluate subexpressions 22 | 23 | ## Intro to Lambda Calculus - Explanation + Practice 24 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/.merlin: -------------------------------------------------------------------------------- 1 | B /home/subomi/.opam/4.07.0/lib/ocaml 2 | B ../_build/default/src/.disc.objs 3 | S /home/subomi/.opam/4.07.0/lib/ocaml 4 | S . 5 | FLG -open Disc -w -40 6 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name disc) 3 | (modules lexer parser interpreter) 4 | (libraries str)) 5 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/interpreter.ml: -------------------------------------------------------------------------------- 1 | open Parser 2 | 3 | (* Evaluater *) 4 | 5 | let rec eval (ast : expr) : int = 6 | match ast with 7 | | Int x -> x 8 | | Mult (x, y) -> let x' = eval x in 9 | let y' = eval y in 10 | x' * y' 11 | | Plus (x, y) -> let x' = eval x in 12 | let y' = eval y in 13 | x' + y' 14 | 15 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/interpreter.mli: -------------------------------------------------------------------------------- 1 | val eval : Parser.expr -> int 2 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/interpreter.skeleton.ml: -------------------------------------------------------------------------------- 1 | open Parser 2 | 3 | (* Evaluater *) 4 | 5 | let rec eval (ast : expr) : int = 6 | failwith "unimplemented" 7 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/lexer.ml: -------------------------------------------------------------------------------- 1 | (* Type *) 2 | type token = 3 | | Tok_Int of int 4 | | Tok_Mult 5 | | Tok_Plus 6 | | Tok_LParen 7 | | Tok_RParen 8 | | Tok_EOF 9 | 10 | let string_of_token tok = match tok with 11 | | Tok_Int(i) -> string_of_int i 12 | | Tok_Mult -> "*" 13 | | Tok_Plus -> "+" 14 | | Tok_LParen -> "(" 15 | | Tok_RParen -> ")" 16 | | Tok_EOF -> "" 17 | 18 | 19 | let rec string_of_list conv lst = 20 | match lst with 21 | | [] -> "" 22 | | h::[] -> conv h 23 | | h::t -> (conv h) ^ " " ^ (string_of_list conv t) 24 | 25 | (* Given source code returns a token list. *) 26 | let rec lexer (input : string) : token list = 27 | let length = String.length input in 28 | 29 | let rec tok pos = 30 | if pos >= length then 31 | [Tok_EOF] 32 | 33 | else if Str.string_match (Str.regexp "(") input pos then 34 | Tok_LParen::(tok (pos + 1)) 35 | 36 | else if Str.string_match (Str.regexp ")") input pos then 37 | Tok_RParen::(tok (pos + 1)) 38 | 39 | else if Str.string_match (Str.regexp "\\+") input pos then 40 | Tok_Plus::(tok (pos + 1)) 41 | 42 | else if Str.string_match (Str.regexp "\\*") input pos then 43 | Tok_Mult::(tok (pos + 1)) 44 | 45 | else if Str.string_match (Str.regexp "-?[0-9]+") input pos then 46 | let value = Str.matched_string input in 47 | Tok_Int(int_of_string value)::(tok (pos + String.length value)) 48 | 49 | else 50 | tok (pos + 1) 51 | 52 | in tok 0;; 53 | 54 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/lexer.mli: -------------------------------------------------------------------------------- 1 | type token = 2 | | Tok_Int of int 3 | | Tok_Mult 4 | | Tok_Plus 5 | | Tok_LParen 6 | | Tok_RParen 7 | | Tok_EOF 8 | 9 | val lexer : string -> token list 10 | 11 | val string_of_token : token -> string 12 | 13 | val string_of_list : ('a -> string) -> 'a list -> string 14 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/lexer.skeleton.ml: -------------------------------------------------------------------------------- 1 | (* Type *) 2 | type token = 3 | | Tok_Int of int 4 | | Tok_Mult 5 | | Tok_Plus 6 | | Tok_LParen 7 | | Tok_RParen 8 | | Tok_EOF 9 | 10 | let string_of_token tok = match tok with 11 | | Tok_Int(i) -> string_of_int i 12 | | Tok_Mult -> "*" 13 | | Tok_Plus -> "+" 14 | | Tok_LParen -> "(" 15 | | Tok_RParen -> ")" 16 | | Tok_EOF -> "" 17 | 18 | let rec string_of_list conv lst = 19 | match lst with 20 | | [] -> "" 21 | | h::[] -> conv h 22 | | h::t -> (conv h) ^ " " ^ (string_of_list conv t) 23 | 24 | (* Given source code returns a token list. *) 25 | let rec lexer (input : string) : token list = 26 | failwith "unimplemented" 27 | 28 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/lexer_cameron.ml: -------------------------------------------------------------------------------- 1 | module L = List 2 | module S = String 3 | module R = Str 4 | 5 | (* Type *) 6 | type token = 7 | | Tok_Int of int 8 | | Tok_Mult 9 | | Tok_Plus 10 | | Tok_LParen 11 | | Tok_RParen 12 | | Tok_EOF 13 | 14 | (* Regular expressions and the tokens they generate. *) 15 | let re = [ 16 | (R.regexp "[0-9]+" , fun x -> [Tok_Int (int_of_string x)]) ; 17 | (R.regexp "\\+" , fun _ -> [Tok_Plus]) ; 18 | (R.regexp "\\*" , fun _ -> [Tok_Mult]) ; 19 | (R.regexp "(" , fun _ -> [Tok_LParen]) ; 20 | (R.regexp ")" , fun _ -> [Tok_RParen]) ; 21 | (R.regexp " " , fun _ -> []) 22 | ] 23 | 24 | (* Given source code returns a token list. *) 25 | let rec lexer (s : string) : token list = 26 | lexer' s 0 27 | 28 | (* Helper for lexer takes in a position offset. *) 29 | and lexer' (s : string) (pos : int) : token list = 30 | if pos >= S.length s then [Tok_EOF] 31 | else 32 | let (_, f) = L.find (fun (re, _) -> R.string_match re s pos) re in 33 | let s' = R.matched_string s in 34 | (f s') @ (lexer' s (pos + (S.length s'))) 35 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/parser.ml: -------------------------------------------------------------------------------- 1 | open Lexer 2 | 3 | (* Types *) 4 | type expr = 5 | | Int of int 6 | | Plus of expr * expr 7 | | Mult of expr * expr 8 | 9 | (* Provided helper function - takes a token list and an exprected token. 10 | * Handles error cases and returns the tail of the list *) 11 | let match_token (toks : token list) (tok : token) : token list = 12 | match toks with 13 | | [] -> raise (Failure(string_of_token tok)) 14 | | h::t when h = tok -> t 15 | | h::_ -> raise (Failure( 16 | Printf.sprintf "Expected %s from input %s, got %s" 17 | (string_of_token tok) 18 | (string_of_list string_of_token toks) 19 | (string_of_token h) 20 | )) 21 | 22 | let lookahead toks = match toks with 23 | h::t -> h 24 | | _ -> raise (Failure("Empty input to lookahead")) 25 | 26 | 27 | (* Parses a token list. *) 28 | let rec parser (toks : token list) : expr = 29 | let (t, exp) = parse_S toks in 30 | if t <> [Tok_EOF] then 31 | raise (Failure "did not reach EOF") 32 | else 33 | exp 34 | 35 | (* Parses the S rule. *) 36 | and parse_S toks = 37 | let (t, m) = parse_M toks in 38 | match lookahead t with 39 | | Tok_Plus -> let t' = match_token t Tok_Plus in 40 | let (t'', s) = parse_S t' in 41 | (t'', Plus (m, s)) 42 | | _ -> t, m 43 | 44 | (* Parses the M rule. *) 45 | and parse_M toks = 46 | let (t, n) = parse_N toks in 47 | match lookahead t with 48 | | Tok_Mult -> let t' = match_token t Tok_Mult in 49 | let (t'', m) = parse_M t' in 50 | (t'', Mult (n, m)) 51 | | _ -> t, n 52 | 53 | (* Parses the N rule. *) 54 | and parse_N toks = 55 | match lookahead toks with 56 | | Tok_Int i -> let t = match_token toks (Tok_Int i) in 57 | (t, Int i) 58 | | Tok_LParen -> let t = match_token toks Tok_LParen in 59 | let (t', s) = parse_S t in 60 | let t'' = match_token t' Tok_RParen in 61 | (t'', s) 62 | | _ -> failwith "parse_N failed" 63 | -------------------------------------------------------------------------------- /discussions/d11_lambda/src/parser.mli: -------------------------------------------------------------------------------- 1 | type expr = 2 | | Int of int 3 | | Plus of expr * expr 4 | | Mult of expr * expr 5 | 6 | val parser : Lexer.token list -> expr 7 | val parse_S : Lexer.token list -> Lexer.token list * expr 8 | val parse_M : Lexer.token list -> Lexer.token list * expr 9 | val parse_N : Lexer.token list -> Lexer.token list * expr -------------------------------------------------------------------------------- /discussions/d11_lambda/src/parser.skeleton.ml: -------------------------------------------------------------------------------- 1 | open Lexer 2 | 3 | (* Types *) 4 | type expr = 5 | | Int of int 6 | | Plus of expr * expr 7 | | Mult of expr * expr 8 | 9 | (* Provided helper function - takes a token list and an exprected token. 10 | * Handles error cases and returns the tail of the list *) 11 | let match_token (toks : token list) (tok : token) : token list = 12 | match toks with 13 | | [] -> raise (Failure(string_of_token tok)) 14 | | h::t when h = tok -> t 15 | | h::_ -> raise (Failure( 16 | Printf.sprintf "Expected %s from input %s, got %s" 17 | (string_of_token tok) 18 | (string_of_list string_of_token toks) 19 | (string_of_token h) 20 | )) 21 | 22 | let lookahead toks = match toks with 23 | h::t -> h 24 | | _ -> raise (Failure("Empty input to lookahead")) 25 | 26 | 27 | 28 | (* Parses a token list. *) 29 | let rec parser (toks : token list) : expr = 30 | failwith "unimplemented" 31 | 32 | (* Parses the S rule. *) 33 | and parse_S (toks : token list) : (token list * expr) = 34 | failwith "unimplemented" 35 | 36 | (* Parses the M rule. *) 37 | and parse_M (toks : token list) : (token list * expr) = 38 | failwith "unimplemented" 39 | 40 | (* Parses the N rule. *) 41 | and parse_N (toks : token list) : (token list * expr) = 42 | failwith "unimplemented" 43 | -------------------------------------------------------------------------------- /discussions/d12_intro_rust/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 12 - Friday, April 15th 2 | 3 | ## What is Rust? 4 | 5 | 6 | 7 | Modern language designed for concurrency; it's similar to C++ and OCaml, but does it in a fast + safe manner 8 | * How does it achieve speed and safety? It avoids Garbage Collection, and instead uses rules for memory management 9 | 10 | `let mut x = String::from("hello");` <- Mutability 11 | 12 | `do_something(&mut x);` <- Borrowing, avoids ownership change 13 | 14 | `println("{}",x);` 15 | 16 | 17 | 18 | There's three concepts that we need to keep in mind when developing in Rust: Mutability, Ownership, and Borrowing 19 | * _Mutability:_ Variables can either be declared as mutable (`mut`) or immutable 20 | * Similar to `const` in C 21 | * Mutable means the variable can be reassigned, e.g. `let mut x = 0; x = 5;` 22 | * _Ownership:_ Technique used to automatically free variables 23 | * Essentially, each piece of data has one owner, and when the owner goes out of scope, the variable is removed 24 | * Note that this only matters for non-primitives, as primitives like integers, booleans, etc. implement the `Copy` trait, so their value can simply be copied to new variables 25 | * E.g. 26 | 27 | `let s1 = String::from("hello")` <- s1 is owner 28 | 29 | `let s2 = s1;` <- Now s2 is owner, s1 goes out of scope 30 | 31 | `println!("{}",s1)` <- Not allowed, as s2 is out of scope 32 | 33 | * Ownership changes through two operations 34 | * Variable aliasing 35 | * `let s1 = String::from("hello"); s2 = s1` 36 | * Now s2 is owner of the String, s1 is out of scope 37 | * Function call 38 | * When function is passed in a variable, ownership is transferred to the function, and goes out of scope after function is run 39 | * `let s1 = String::from("hello"); do_something(s1);` 40 | * s1 is now out of scope 41 | * _Borrowing/References_ 42 | * We can use references to get around ownership issues 43 | * Borrowing is similar to points in C, two types 44 | * Mutable ref, `&mut` - Can edit variable 45 | * Immutable ref, `&` - Can't edit 46 | * Allows us to pass in references to functions, preventing ownership from going out of scope 47 | * Also used for things like iteration; when looping through list, get a series of references to elements in list 48 | * One limitation: Can only either have 1 mutable ref or many immutable refs 49 | * This prevents weird write errors 50 | * Exemplified by String class; `&str` is a read-only pointer to String, whereas String class is similar to array of chars 51 | 52 | Example with .iter(): 53 | 54 | ```ocaml 55 | let mut arr = [1,2,3]; 56 | for &i in arr.iter() { 57 | println!("{}",i); 58 | } 59 | ``` 60 | 61 | What does this mean when you write programs? 62 | 63 | 64 | 65 | 1. Make sure you know which variables are mutable and immutable 66 | 2. Be aware of who owns certain variables, and pass around references to make sure ownership doesn't go out 67 | 68 | Some common errors are 69 | 70 | 71 | 72 | 1. Variable mutability 73 | 2. Using variable after it goes out of scope 74 | 3. Passing in regular variable instead of reference 75 | 4. Failing to return correct value from function 76 | 5. Incorrect types (reference when regular variables should be used, etc.) 77 | 78 | ## Debugging Problems 79 | 80 | Each of the functions in error.rs has an error; fix the error 81 | 82 | Solutions in correct.rs 83 | 84 | ## Advanced Lambda Calculus 85 | 86 | Problems in d12_lambda_calc, solutions in the solutions file 87 | -------------------------------------------------------------------------------- /discussions/d12_intro_rust/d12_lambda_calc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d12_intro_rust/d12_lambda_calc.pdf -------------------------------------------------------------------------------- /discussions/d12_intro_rust/d12_lambda_calc_sols.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d12_intro_rust/d12_lambda_calc_sols.pdf -------------------------------------------------------------------------------- /discussions/d12_intro_rust/src/correct.rs: -------------------------------------------------------------------------------- 1 | pub fn sum_evens(i: i32, j: i32) -> i32 { 2 | 3 | let mut sum = 0; // Change sum to mut 4 | 5 | for k in i..j { 6 | 7 | if k % 2 == 0 { 8 | 9 | sum += k; 10 | 11 | } 12 | 13 | } 14 | 15 | sum 16 | 17 | } 18 | 19 | pub fn distance((ax, ay): (f64, f64), (bx, by): (f64, f64)) -> f64 { 20 | 21 | ((bx - ax).powi(2) + (by - ay).powi(2)).sqrt() // should be powi; i is for integer function, f is for float) 22 | 23 | } 24 | 25 | pub fn raise_1(arr: &mut [i32]) { 26 | 27 | for i in arr { 28 | 29 | *i += 1; // i is a reference, so should be *i) 30 | 31 | } 32 | 33 | } 34 | 35 | pub fn add_hello(a: &mut String) { // Make it &mut 36 | 37 | a.push_str("hello"); 38 | 39 | } 40 | 41 | pub fn create_hello_world()->String { 42 | 43 | let mut s = String::from(""); 44 | 45 | add_hello(&mut s); // &mut s and make add_hello(a: &mut String)) 46 | 47 | s.push_str("world"); 48 | 49 | return s; 50 | 51 | } 52 | 53 | pub fn get_first_elem(a: &Vec) -> u32 { 54 | 55 | if(a.len() == 0) { 56 | 57 | return 0; 58 | 59 | } 60 | 61 | return *a.get(0).unwrap(); //Unwrap the some and dereference 62 | 63 | } 64 | -------------------------------------------------------------------------------- /discussions/d12_intro_rust/src/error.rs: -------------------------------------------------------------------------------- 1 | pub fn sum_evens(i: i32, j: i32) -> i32 { 2 | 3 | let sum = 0; 4 | 5 | for k in i..j { 6 | 7 | if k % 2 == 0 { 8 | 9 | sum += k; 10 | 11 | } 12 | 13 | } 14 | 15 | sum 16 | 17 | } 18 | 19 | pub fn distance((ax, ay): (f64, f64), (bx, by): (f64, f64)) -> f64 { 20 | 21 | ((bx - ax).powf(2) + (by - ay).powf(2)).sqrt() 22 | 23 | } 24 | 25 | pub fn raise_1(arr: &mut [i32]) { 26 | 27 | for i in arr { 28 | 29 | i += 1; _ 30 | 31 | } 32 | 33 | } 34 | 35 | 36 | pub fn add_hello(a: String) { 37 | 38 | a.push_str("hello"); 39 | 40 | } 41 | 42 | pub fn create_hello_world()->String { 43 | 44 | let mut s = String::from(""); 45 | 46 | add_hello(s); 47 | 48 | s.push_str("world"); 49 | 50 | return s; 51 | 52 | } 53 | 54 | 55 | pub fn get_first_elem(a: &Vec) -> u32 { 56 | 57 | if(a.len() == 0) { 58 | 59 | return 0; 60 | 61 | } 62 | 63 | return a.get(0); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /discussions/d13_rust_memory/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 13 - Friday, April 22nd 2 | 3 | ## What are the problems with Rust? 4 | 5 | * As noted last week, Rust has two primary features: ownerships and mutability 6 | * However (as you might have noticed) dealing with these is tough, especially for larger programs 7 | 8 | ### Ownership Issues 9 | 10 | * What if we want multiple people to have ownership over a single data point? 11 | * E.g. A graph, where multiple nodes have ownership over a common neighbor 12 | * Note we can't just use references here; once we have a reference, we can't edit the object till the reference goes out of scope 13 | 14 | ``` rust 15 | let mut a = String::from("hello") 16 | let b = &a; 17 | a.push_str("b"); // Not allowed 18 | ``` 19 | 20 | 21 | 22 | * What if we want ownership over dynamically sized objects? 23 | * What if we want some passed in variable to guarantee some property? 24 | * We need some type of wrapper over Trait 25 | 26 | ### Mutability Issues 27 | 28 | * What if we want multiple editors for an object? 29 | 30 | ### How do we fix these problems? 31 | 32 | ## Box, Rc, and RefCell 33 | 34 | ### Issue 1: 35 | Can we store dynamically sized elements, say for example, LinkedLists? 36 | 37 | * This should be done on the heap (where dynamically sized things are stored) 38 | * We can use Box, which basically wraps around variables, and allows them to reside on the heap 39 | * `E.g. Box::new(String::from("hello"))` 40 | * Then we can treat this like a normal variable, apply operations, etc. 41 | * Note that any need for dereferencing is largely handled by the compiler 42 | * Why is this useful? Two things: 43 | * Recursive types: 44 | 45 | ``` rust 46 | LinkedList { 47 | Cons(i32,Box) 48 | } 49 | ``` 50 | * If we don't have the box, then compiler won't allow it, as it's unknown how large LinkedList is 51 | * It's similar to how we use pointers for LinkedLists in C 52 | * Basically we create a pointer, which is of constant size, rather than LinkedList itself 53 | * Traits: 54 | * `fn hello(Box)` 55 | * Basically allows us to force input types to have certain properties + methods 56 | 57 | 58 | ### Issue 2: 59 | What if we want variables to have multiple owners? 60 | 61 | * This is possible through Rc or reference count 62 | * `let a = Rc::new(String::from("hello"))` 63 | * `let b = a.clone()` 64 | * Now a and b can view String 65 | 66 | ### Issue 3: 67 | What if we want variable to have multiple owners, and the variable is editable? 68 | 69 | * We can use RefCell, which allows for on-the-fly mutability 70 | * What does this mean? 71 | * `let a = Rc::new(RefCell::new("Hello"))` 72 | * `let b = a.clone()` 73 | * `a.borrow_mut().push_str("hi")` 74 | * Updates everywhere 75 | * Rc let's us have multiple copies, and RefCell allows interior mutability/changes to whatever is referenced 76 | 77 | ### In summary 78 | * `Box` - Wrapper so variable property/size are standardized 79 | * `Rc` - Shares object by counting references, no one owner 80 | * `Refcell` - Allows for editing whatever is referenced by Rc 81 | 82 | ## Graph Exercise 83 | 84 | Say we want to make a simple Node class with left child, right child, and data (i32), and parent properties, AND be able to do something along the following: 85 | 86 | ``` rust 87 | let a = Node { 88 | data: 1, 89 | parent: None, 90 | left: None, 91 | right: None 92 | }; 93 | 94 | let b = Node { 95 | data: 0, 96 | parent: None, 97 | left: None, 98 | right: None 99 | }; 100 | 101 | a.left = b; 102 | b.parent = a; 103 | ``` 104 | 105 | What types do we use? 106 | 107 | ``` rust 108 | struct Node { 109 | data: i32, 110 | left: Node, 111 | right: Node, 112 | parent: Node, 113 | } 114 | ``` 115 | 116 | Nope, can't do this, we can't define Node with Node 117 | 118 | ``` rust 119 | struct Node { 120 | data: i32, 121 | left: Box, 122 | right: Box, 123 | parent: Box, 124 | } 125 | ``` 126 | 127 | Nope, can't do this, ownership issue, as `a` owns `b`, and `b` owns `a` 128 | 129 | ``` rust 130 | struct Node { 131 | data: i32, 132 | left: Box, 133 | right: Box, 134 | parent: Box<&Node>, 135 | } 136 | ``` 137 | 138 | Nope, then the parent can never be modified 139 | 140 | ``` rust 141 | struct Node { 142 | data: i32, 143 | left: Rc, 144 | right: Rc, 145 | parent: Rc, 146 | } 147 | ``` 148 | 149 | Then the node is static, and so won't update as the Node updates 150 | 151 | ``` rust 152 | struct Node { 153 | data: i32, 154 | left: Rc>, 155 | right: Rc>, 156 | parent: Rc>, 157 | } 158 | ``` 159 | 160 | Yay! This works. 161 | 162 | ### What's the big takeaway? 163 | 164 | Box, Rc, and RefCell can help us develop tools for complex data structures, such as Graphs, allowing us to get around some of the complex ownership and mutability issues safely 165 | -------------------------------------------------------------------------------- /discussions/d14_security/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 14 - Friday, April 29th 2 | 3 | 4 | ## Recap 5 | 6 | Last week's discussion: Box, Rc, Refcell 7 | 8 | ## Lambda Calculus Review 9 | 10 | Lambda calculus problems located on the 330 webpage 11 | 12 | Quiz is next week! 13 | 14 | ## Final Exam Review 15 | 16 | Practice final exams located on the 330 webpage 17 | -------------------------------------------------------------------------------- /discussions/d1_intro_ruby/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 1 - Friday, January 28th 2 | 3 | 4 | ## Introduction 5 | 6 | We created a Ruby exercise which has you work with some of the features you learned about in class. The focus here is on the problem solving process itself, and particularly the following steps: 7 | 8 | 9 | 1. Problem Understanding: can you break down a set of instructions into manageable tasks 10 | 2. Algorithmic Planning: can you develop pseudocode or a sketch of the code 11 | 3. Implementation and information retrieval: can you implement the solution, and more importantly, find resources to help you (documentation) when stuck 12 | 13 | We'll walk you through some of these steps for some parts of the problem, and have you work your own way through others. 14 | 15 | ## Instructions 16 | 17 | We will be implementing a simple database using Ruby data structures to store the data. A database is used to store data in an ordered manner, and an example is shown below. A database consists of a set of columns (in this case name and age), and some number of tuples (in this case, 4 tuples). Each tuple has a value for each column; for example, the first tuple in the database, (A,22), corresponds to a datapoint with name A and age 22. In particular, we plan to implement features for the database so users can read and write data. 18 | 19 | An example of a table is below: 20 | 21 | | Name | Age | 22 | |------|------| 23 | | A | 22 | 24 | | B | 23 | 25 | | C | 24 | 26 | | D | 21 | 27 | 28 | ## Part 1: `Tuple` 29 | 30 | A `Tuple` represents a single entry, in a table. The methods below will be implemented in the `Tuple` class in [disc1.rb](src/disc1.rb). 31 | 32 | #### `initialize(data)` 33 | 34 | - **Type**: `(Array) -> _` 35 | - **Description**: Given an array of values for the tuple, store them in the `Tuple` object in any way you would like. You should perform any initialization steps for the `Tuple` instance here. The return value of this function does not matter. 36 | - **Examples**: 37 | ```ruby 38 | t = Tuple.new(["a", 1, "b", 2]) 39 | t = Tuple.new([]) # Tuples may be empty 40 | ``` 41 | 42 | #### `getData(index)` 43 | 44 | - **Type**: `(Integer) -> Object` 45 | - **Description**: Return the data at a particular index of a `Tuple` (indexed starting at 0). If the provided index exceeds the largest index in the tuple, return `nil`. 46 | - **Assumptions**: `index` is non-negative. 47 | - **Examples**: 48 | ```ruby 49 | t = Tuple.new(["a", 1, "b", 2]) 50 | t.getData(0) # Returns "a" 51 | t.getData(4) # Returns nil 52 | ``` 53 | 54 | #### `self.getNumTuples(n)` 55 | 56 | - **Type**: `(Integer) -> Integer` 57 | - **Description**: Return the number of `Tuple`s of size `n` that have ever been created. Hint: you should use a static variable to keep track of this. 58 | - **Examples**: 59 | ```ruby 60 | Tuple.getNumTuples(3) # Returns 0 61 | t = Tuple.new(["a", 1, "b"]) 62 | t = Tuple.new(["a", 1, "b"]) 63 | Tuple.getNumTuples(3) # Returns 2 64 | t = Tuple.new([3]) 65 | Tuple.getNumTuples(3) # Returns 2 66 | ``` 67 | 68 | ## Part 2: `Table` 69 | A `Table` represents a collection of tuples. The methods below will be implemented in the `Table` class in [disc1.rb](src/disc1.rb). 70 | 71 | #### `initialize(column_names)` 72 | 73 | - **Type**: `(Array) -> _` 74 | - **Description**: Given an array of column names for the `Table`, store them in the object in any way you would like. You should perform any initialization steps for the `Table` instance here. The return value of this function does not matter. 75 | - **Assumptions**: The elements in `column_names` will be unique. 76 | - **Examples**: 77 | ```ruby 78 | t = Table.new(["c0", "c1", "c2"]) 79 | t = Table.new([]) 80 | ``` 81 | 82 | #### `insertTuple(tuple)` 83 | 84 | - **Type**: `(Tuple) -> boolean` 85 | - **Description**: Insert a `Tuple` into the `Table`. Note that the number of entries in the `Tuple` must match the number of columns in the `Table`. If this is not the case, make no changes to the `Table` and return `false`. If the sizes match, insert the `Tuple` and return `true`. 86 | - **Examples**: 87 | ```ruby 88 | table = Table.new(["a", "b"]) 89 | x = Tuple.new([0, 1]) 90 | y = Tuple.new([3, "y"]) 91 | z = Tuple.new([1, 2, 3]) 92 | 93 | table.insertTuple(x) # Returns true 94 | table.insertTuple(y) # Returns true 95 | table.insertTuple(z) # Returns false (sizes do not match) 96 | ``` 97 | 98 | #### `numRowsWhere` 99 | 100 | - **Type**: `(String,'t) -> Integer` 101 | - **Description**: Given a column name and a value, find the number of rows where the value for the column matches the given value. 102 | - **Examples** 103 | ```ruby 104 | table = Table.new(["a", "b"]) 105 | x = Tuple.new([0, 1]) 106 | y = Tuple.new([3,,1]) 107 | z = Tuple.new([3,,4]) 108 | table.insertTuple(x) 109 | table.insertTuple(y) 110 | table.insertTuple(z) 111 | table.numRowsWhere("b", 1) # 2 112 | ``` 113 | 114 | -------------------------------------------------------------------------------- /discussions/d1_intro_ruby/src/disc1.rb: -------------------------------------------------------------------------------- 1 | # We will be implimenting a simple database table using Ruby data structures to store the data. 2 | # The class Tuple represents an entry in a table. 3 | # The class Table represents a collection of tuples. 4 | 5 | class Tuple 6 | 7 | # data is an array of values for the tuple 8 | def initialize(data) 9 | raise "unimplemented" 10 | end 11 | 12 | # This method returns the data at a particular index of a tuple (0 indexing) 13 | # If the provided index exceeds the largest index in the tuple, nil should be returned. 14 | # index is an Integer representing a valid index in the tuple. 15 | def getData(index) 16 | raise "unimplemented" 17 | end 18 | 19 | # This method should return the number of tuples of size n that have ever been created 20 | # hint: you should use a static variable 21 | # hint2: a hash can be helpful (though not strictly necessary!) 22 | def self.getNumTuples(n) 23 | raise "unimplemented" 24 | end 25 | end 26 | 27 | class Table 28 | # column_names is an Array of Strings 29 | def initialize(column_names) 30 | raise "unimplemented" 31 | end 32 | 33 | # This method inserts a tuple into the table. 34 | # Note that tuples inserted into the table must have the right number of entries 35 | # I.e., the tuple should be the size of column_names 36 | # If the tuple is the correct size, insert it and return true 37 | # otherwise, DO NOT insert the tuple and return false instead. 38 | # tuple is an instance of class Tuple declared above. 39 | def insertTuple(tuple) 40 | raise "unimplemented" 41 | end 42 | 43 | # Given a column name and a value, this method finds the number of rows where the value 44 | # for the column matches the given value. 45 | def numRowsWhere(column,value) 46 | raise "unimplemented" 47 | end 48 | 49 | end -------------------------------------------------------------------------------- /discussions/d1_intro_ruby/src/disc1_soln.rb: -------------------------------------------------------------------------------- 1 | # We will be implementing a simple database using Ruby data structures to store the data. 2 | # A database can contain an arbitrary number of tables. Each table will contain tuples of size n, where n is the number of columns in 3 | # the table. 4 | # Through a series of discussion exercises each week, we will improve upon our simple database. 5 | # This week we will create a Database representation that will implement some basic functionality. 6 | # The class Tuple represents and entry in a table. 7 | # The class Table represents a collection of tuples. 8 | 9 | class Tuple 10 | @@tupleHash = Hash.new(0) 11 | 12 | def initialize(data) 13 | @data = data; 14 | @@tupleHash[data.size] += 1 15 | end 16 | 17 | # This method returns the data at a particular index of a tuple (0 indexing) 18 | # If the provided index exceeds the largest index in the tuple, nil should be returned. 19 | # index is an Integer representing a valid index in the tuple. 20 | def getData(index) 21 | if index > (getSize() - 1) then 22 | nil 23 | else 24 | @data[index] 25 | end 26 | end 27 | 28 | # This method should return the number of tuples of size n that have ever been created 29 | def self.getNumTuples(n) 30 | @@tupleHash[n] 31 | end 32 | end 33 | 34 | class Table 35 | # column_names is an Array of Strings 36 | def initialize(column_names) 37 | @column_names = column_names; 38 | @tuples = []; 39 | end 40 | 41 | # This method inserts a tuple into the table. 42 | # Note that tuples inserted into the table must have the right number of entries 43 | # I.e., the tuple should be the size of column_names 44 | # If the tuple is the correct size, insert it and return true 45 | # otherwise, DO NOT insert the tuple and return false instead. 46 | # tuple is an instance of class Tuple declared above. 47 | def insertTuple(tuple) 48 | col_length = @column_names.length(); 49 | tuple_size = tuple.getSize; 50 | 51 | if tuple_size != col_length then 52 | false 53 | else 54 | @tuples.push(tuple); 55 | true 56 | end 57 | end 58 | 59 | def numRowsWhere(column,value) 60 | column_index = @column_names.index(column) 61 | result = 0 62 | 63 | for tuple in @tuples do 64 | if tuple[column_index] == value then 65 | results+=1 66 | 67 | end 68 | end 69 | result 70 | end 71 | end -------------------------------------------------------------------------------- /discussions/d2_regex/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 2 - Friday, February 4th 2 | 3 | ## What are Codeblocks and Regex? 4 | 5 | Codeblocks: Package code and pass it in as a variable 6 | The fundamental idea is passing code as data 7 | 8 | `a = [1,2,3,4]` 9 | 10 | `a.each do **|x| puts x end**` 11 | 12 | `a.each {|x| puts x}` 13 | 14 | Internally, this is done through yield commands, which run passed in code block 15 | 16 | 17 | Regular Expression: Used to match strings and extract information 18 | 19 | For example, `"abc1" =~ /(\w+)(\d)/` matches the letters and numbers separately 20 | 21 | `/(\w+)(\d)/` generally matches one or more letters followed by one number 22 | 23 | The first part, `"abc"`, is stored in variable `$1`, and `"1"` is stored in `$2` (backreferencing) 24 | 25 | Internally, this is done through automata, which we'll implement later 26 | 27 | To test out Regex, use: [https://rubular.com/](https://rubular.com/) (note this uses Ruby-specific regex; other languages, like OCaml, might have small differences) 28 | 29 | ## Codeblock Problems 30 | 31 | 1) Waiting Time problem: [http://www.cs.umd.edu/~anwar/cmsc330_tests/final-fall19.pdf](http://www.cs.umd.edu/~anwar/cmsc330_tests/final-fall19.pdf) 32 | 33 | 34 | 2) Ducks problem: [http://www.cs.umd.edu/~anwar/cmsc330_tests/final-fall18.pdf](http://www.cs.umd.edu/~anwar/cmsc330_tests/final-fall18.pdf) 35 | -------------------------------------------------------------------------------- /discussions/d2_regex/src/disc2.rb: -------------------------------------------------------------------------------- 1 | class WaitingTime 2 | def initialize(filename) 3 | raise "unimplemented" 4 | end 5 | 6 | def student_waited_for(student_name) 7 | raise "unimplemented" 8 | end 9 | 10 | def total_wait_time() 11 | raise "unimplemented" 12 | end 13 | end 14 | 15 | class DuckSorter 16 | def initialize(filename) 17 | IO.foreach(filename) { |line| 18 | raise "unimplemented" 19 | } 20 | end 21 | 22 | def get_attribute(name) 23 | raise "unimplemented" 24 | end 25 | 26 | def search(attribute) 27 | raise "unimplemented" 28 | end 29 | end -------------------------------------------------------------------------------- /discussions/d2_regex/src/disc2_soln.rb: -------------------------------------------------------------------------------- 1 | # Solution from Fall 2019 Final Exam 2 | # (http://www.cs.umd.edu/~anwar/cmsc330_tests/final-soln-fall19.pdf) 3 | class WaitingTime 4 | def initialize(filename) 5 | @wait_time = {} 6 | File.foreach(filename) do |line| 7 | if line=~/([A-Z][a-z]+), ([A-Z][a-z]+), (\d+):(\d\d)/ 8 | if @wait_time[$2+" "+$1] == nil 9 | @wait_time[$2+" "+$1] = 0 10 | end 11 | 12 | @wait_time[$2+" "+$1]+=$3.to_i*60+$4.to_i 13 | end 14 | end 15 | end 16 | 17 | def student_waited_for(student_name) 18 | if @wait_time[student_name] != nil 19 | return @wait_time[student_name] 20 | else 21 | return 0 22 | end 23 | end 24 | 25 | def total_wait_time() 26 | S = 0 27 | @wait_time.each do |k,v| 28 | s+=v 29 | end 30 | return s 31 | end 32 | end 33 | 34 | # Solution from Fall 2018 Final Exam 35 | # (http://www.cs.umd.edu/~anwar/cmsc330_tests/final-soln-fall2018.pdf) 36 | class DuckSorter 37 | def initialize(filename) 38 | @ducks = Hash.new [] 39 | IO.foreach(filename) { |line| 40 | if line =~ /name:([A-Z][a-z]+), attributes:([a-z]+(, [a-z]+)*)/ 41 | @ducks[$1] += $2.split(", ") 42 | @ducks[$1].uniq! 43 | end 44 | } 45 | end 46 | 47 | def get_attribute(name) 48 | @ducks[name] 49 | end 50 | 51 | def search(attribute) 52 | arr = [] 53 | @ducks.each{ |k, v| 54 | if v.include?(attribute) 55 | arr.push(k) 56 | end 57 | } 58 | arr 59 | end 60 | end -------------------------------------------------------------------------------- /discussions/d3_intro_ocaml/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 3 - Friday, February 11th 2 | 3 | ## OCaml Introduction 4 | 5 | ## Problems 6 | 7 | **Number Positivity:** Given an integer, return whether the number is positive, negative, or zero. 8 | 9 | _Type:_ int -> string 10 | 11 | **Double a number:** Given an integer, find 2*the integer 12 | 13 | _Type:_ int -> int 14 | 15 | **Fizz Buzz:** Similar to the common fizz buzz problem, return "fizz buzz" if a number is divisible by 15, "fizz" if a number is divisible by 3, and "buzz" if a number is divisible by 5. Return the empty string if a number isn't divisible by any. 16 | 17 | _Type:_ int -> string 18 | 19 | ## OCaml Pattern Matching 20 | 21 | **Types:** 22 | 23 | _List:_ 1::(2::(3::[]))) 24 | 25 | _Tuple:_ (1,"abc") 26 | 27 | ## Problems: 28 | 29 | **First element List:** Find the first element in an int list; if there is no element, return 0 30 | 31 | _Type:_ int list -> int 32 | 33 | **Sum of List:** Sum up the elements of a linked list 34 | 35 | _Type:_ int list -> int 36 | 37 | 38 | **Max of List:** Find the maximum element of a list, if list is empty, return 0 39 | 40 | _Type:_ int list -> int 41 | 42 | **Sum of three numbers in tuple:** Given a tuple with three integers, find the sum 43 | 44 | _Type:_ (int * int * int) -> int 45 | 46 | 47 | ## Multi-function problems 48 | 49 | **Problems:** 50 | 51 | **Largest List:** Given a list of int lists, find the maximal product amongst the lists; all numbers are >= 0 52 | 53 | Ex: [[1,2],[3,4]] -> 12 54 | 55 | _Type:_ int list list -> int 56 | 57 | **Check Matrix:** Given a list of lists, verify that all lists have the same length 58 | 59 | _Type:_ int list list -> boolean 60 | 61 | -------------------------------------------------------------------------------- /discussions/d3_intro_ocaml/src/disc3.ml: -------------------------------------------------------------------------------- 1 | (* Introduction *) 2 | 3 | let positive n = failwith "Not implemented" 4 | 5 | let double x = failwith "Not implemented" 6 | 7 | let fizz n = failwith "Not implemented" 8 | 9 | (* Problems *) 10 | 11 | let first_elem lst = failwith "Not implemented" 12 | 13 | let rec sum lst = failwith "Not implemented" 14 | 15 | let max_list lst = failwith "Not implemented" 16 | 17 | let sum_tuple t = failwith "Not implemented" 18 | 19 | (* Multi-Function Problems *) 20 | 21 | let rec product lst = failwith "Not implemented" 22 | 23 | let rec max_product lst = failwith "Not implemented" 24 | 25 | let rec length l = failwith "Not implemented" 26 | 27 | let rec check_matrix_aux lst len = failwith "Not implemented" 28 | 29 | let check_matrix lst = failwith "Not implemented" 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /discussions/d3_intro_ocaml/src/disc3_solutions.ml: -------------------------------------------------------------------------------- 1 | let positive n = if n > 0 then "positive" else if n < 0 then "negative" else "zero" 2 | 3 | let double x = 2*x 4 | 5 | let fizz n= if n mod 15 = 0 then "fizz buzz" else if n mod 3 = 0 then "fizz" else if n mod 5 = 0 then "buzz" else "" 6 | 7 | let first_elem lst = match lst with 8 | | h::t -> h 9 | | [] -> 0 10 | 11 | let rec sum lst = match lst with 12 | | [] -> 0 13 | | h::t -> h+sum t 14 | 15 | let rec max_list lst = match lst with 16 | | [] -> 0 17 | | h::t -> max h (max_list t) 18 | 19 | let sum_tuple t = match t with 20 | | (a,b,c) -> a+b+c 21 | 22 | let rec product lst = match lst with 23 | | [] -> 1 24 | | h::t -> h * (product t) 25 | 26 | let rec max_product lst = match lst with 27 | | [] -> 0 28 | | h::t -> max (product h) (max_product t) 29 | 30 | let rec length l = match l with 31 | | [] -> 0 32 | | h::t -> 1+length t 33 | 34 | let rec check_matrix_aux lst len = match lst with 35 | | [] -> true 36 | | h::t -> if length h = len then check_matrix_aux t len else false 37 | 38 | let check_matrix lst = match lst with 39 | | h::t -> check_matrix_aux lst (length h) 40 | | [] -> true 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /discussions/d4_map_fold/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 4 - Friday, February 18th 2 | 3 | ## Map and Fold Explained 4 | 5 | * Map and fold are higher order functions that allow you to manipulate lists easily 6 | * Map applies some function to each element of a list 7 | * map (fun x -> x*2) [1;2;3;4] -> [2;4;6;8] 8 | * map (fun x -> x^"a") ["a";"b";"c";"d"] -> ["aa";"ba";"ca";"da"] 9 | * Fold - Loop through a list, and aggregate data from list 10 | * fold (fun a x -> a+x) 0 [1;2;3;4] -> 1+2+3+4 -> 10 11 | * fold (fun a x -> x::a) [] [1;2;3;4] -> 4::(3::(2::(1::[]))) -> [4;3;2;1] 12 | * We can do fold from the left side (fold fun a lst) or from the right side (fold fun lst a) 13 | * In most situations they're the same, but for non-commutative things like list appending, which one you select matters 14 | 15 | ## Problems 16 | 17 | **Tuple concatenation** - Given a list of tuple pairs consisting of strings, create a list of strings 18 | 19 | _Type:_ (string*string) list -> string list 20 | 21 | _Example:_ [("ab","cd"); ("hello ","world")] -> ["abcd";"hello world"] 22 | 23 | **Average** - Given a list of integers, find the average (rounded to the nearest integer) 24 | 25 | _Type:_ int list -> int 26 | 27 | **Sentence Formation** - Given a list of tuples with strings, combine them into a sentence 28 | 29 | _Type:_ (string,string) list -> string 30 | 31 | _Example:_ [("ab","cd "); ("hello ","world")] -> "abcd hello world" 32 | 33 | **Index** - Given a list of integers, return the element at that index 34 | 35 | _Type:_ integer list -> integer -> integer 36 | 37 | **Zip** - Given two lists, combine these lists into one, with each element consisting of a tuple from each list 38 | 39 | _Type:_ integer list -> integer list -> (integer*integer) list 40 | 41 | _Example:_ [4;5;6], [1;2;3] -> [(4,1);(5,2);(6,3)] 42 | 43 | **List Difference** - Given two lists of integers, find the difference of lists 44 | 45 | _Type:_ integer list -> integer list -> integer list 46 | 47 | _Example:_ [4;5;6], [1;2;3] -> [3,3,3] 48 | -------------------------------------------------------------------------------- /discussions/d4_map_fold/src/disc4.ml: -------------------------------------------------------------------------------- 1 | let concat lst = failwith "Not implemented" 2 | 3 | (* Tuple concatenation *) 4 | 5 | let concat lst = failwith "Not implemented" 6 | 7 | (* Average *) 8 | 9 | let length lst = failwith "Not implemented" 10 | 11 | let sum lst = failwith "Not implemented" 12 | 13 | let average lst = failwith "Not implemented" 14 | 15 | (* Sentence formation *) 16 | 17 | let sentence lst = failwith "Not implemented" 18 | 19 | (* Index *) 20 | 21 | let index lst elem = failwith "Not implemented" 22 | 23 | (* Zip *) 24 | 25 | let get_nums lst = failwith "Not implemented" 26 | 27 | let zip a b = failwith "Not implemented" 28 | 29 | (* List Difference *) 30 | 31 | let diff lsta lstb = failwith "Not implemented" -------------------------------------------------------------------------------- /discussions/d4_map_fold/src/disc4_sol.ml: -------------------------------------------------------------------------------- 1 | (* Tuple concatenation *) 2 | 3 | let concat lst = 4 | map (fun x -> match x with 5 | | (y,z) -> y^z) lst 6 | 7 | (* Average *) 8 | 9 | let length lst = 10 | fold (fun a x -> a+1) 0 lst 11 | 12 | let sum lst = 13 | fold (a x -> a+x) 0 lst 14 | 15 | let average lst = 16 | (sum lst) / (length lst) 17 | 18 | (* Sentence formation *) 19 | 20 | let sentence lst = 21 | fold (fun a x -> a^x) "" (concat lst) 22 | 23 | (* Index *) 24 | 25 | let index lst elem = 26 | let value = 27 | fold (fun a x -> match a with 28 | | (a,b) -> if elem = b then (x,b+1) else (a,b+1)) 29 | (0,0) lst in 30 | match value with 31 | | (a,b) -> a 32 | 33 | (* Zip *) 34 | 35 | let get_nums lst = fold (a x -> a @ length a) [] lst 36 | 37 | let zip a b = map (fun x -> (index a x, index b x)) (get_nums a) 38 | 39 | (* List Difference *) 40 | 41 | let diff lsta lstb = 42 | map (fun a x-> match a with 43 | | (b,c) -> b-c)) 44 | (zip lsta lstb) -------------------------------------------------------------------------------- /discussions/d5_typing/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 5 - Friday, February 25th 2 | 3 | ## OCaml Typing / Currying 4 | 5 | 6 | 7 | How do OCaml functions work internally? 8 | * When we have a function `let f x = x*x`, this is stored internally as: `let f = fun x -> x*x` 9 | * The type for this is `int -> int`; the first argument taken in is an integer, and it returns an integer (the return type being the very last type in the function type) 10 | * With multiple arguments, say `let f x y = x*y`, this is internally as: `let f = fun x -> fun y -> x*y` 11 | * The type for this is `int -> int -> int` 12 | * Two ways to understand this: function takes in two arguments (two ints) and returns an int 13 | * Or function takes an int, and returns a function with type `int -> int` 14 | * This is the key idea of **Currying**, we turn multi-argument function into multiple single argument ones 15 | * With typing, if type is ambiguous, use 'a, 'b, etc., like `let f x = x` `(type: 'a -> 'a)` 16 | 17 | Why is all this important? 18 | * Helps us understand what happens under the hood of OCaml, and how languages function 19 | 20 | ## Typing Practice 21 | 22 | 1. 23 | `let f x y = x + y` 24 | 25 | 26 | 2. 27 | `let f x y = [x; y]` 28 | 29 | 30 | 3. 31 | ```ocaml 32 | let f a = 33 | if a then 1 else "hi" 34 | ``` 35 | 36 | 37 | 4. 38 | ```ocaml 39 | let f a b = 40 | if a then 0 else b 41 | ``` 42 | 43 | 44 | 5. 45 | ```ocaml 46 | let f a b c = 47 | let d = "hi" ^ a ^ b ^ c in 48 | d == "hello" 49 | ``` 50 | 51 | **Creating functions** 52 | 53 | 1. 54 | `float -> float -> float` 55 | 56 | 57 | 2. 58 | `'a -> 'b -> 'b list` 59 | 60 | 61 | 3. 62 | `int -> float -> int` 63 | 64 | 65 | 4. 66 | `'a -> 'a -> 'a list` 67 | 68 | 69 | **Composite Types + custom Types** 70 | 71 | 72 | 73 | * In addition to basic types (int, string, etc.) we can define our own types 74 | * `type hello = (str * int)` or `type coin = Heads | Tails` 75 | * `let a: hello = ("a",5)`, `let b: coin = Heads` 76 | * Can do pattern matching with this 77 | * Other types include 78 | * Records: `type date = {month: string; day: int}` 79 | * `let today = {day=16; year=2017}` 80 | * To access fields, do `today.day` 81 | 82 | ## Examples 83 | Tuples 84 | 85 | `let x = (1, "hello")` 86 | 87 | Records 88 | ```ocaml 89 | type student_information = 90 | { 91 | name : string; 92 | age : int; 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /discussions/d5_typing/disc5.ml: -------------------------------------------------------------------------------- 1 | (* Typing Practice *) 2 | 3 | 4 | 1) let f x y = x + y 5 | 6 | 2) let f x y = [x; y] 7 | 8 | 3) 9 | let f a = 10 | if a then 1 else "hi" 11 | 12 | 4) 13 | let f a b = 14 | if a then 0 else b 15 | 16 | 5) 17 | let f a b c = 18 | let d = "hi" ^ a ^ b ^ c in 19 | d == "hello" 20 | 21 | 22 | (* Creating functions *) 23 | 24 | 25 | 1) float -> float -> float 26 | 27 | 2) 'a -> 'b -> 'b list 28 | 29 | 3) int -> float -> int 30 | 31 | 4) 'a -> 'a -> 'a list 32 | 33 | 34 | (* Records and New Types *) 35 | 36 | 37 | 1) 38 | type hello = (str * int) 39 | 40 | let a: hello = ("a",5) 41 | 42 | 43 | 2) 44 | type Coin = Heads | Tails 45 | 46 | let b: Coin = Heads 47 | 48 | 3) 49 | type date = {month: string, day: int} 50 | 51 | let today = {day=16; year=2017} 52 | 53 | today.day -------------------------------------------------------------------------------- /discussions/d5_typing/disc5_sol.ml: -------------------------------------------------------------------------------- 1 | (* Typing Practice *) 2 | 3 | 4 | 1) let f x y = x + y 5 | 6 | int -> int -> int 7 | 8 | 9 | 10 | 2) let f x y = [x; y] 11 | 12 | 'a -> 'a -> 'a list 13 | 14 | 15 | 16 | 3) 17 | let f a = 18 | if a then 1 else "hi" 19 | 20 | INVALID - Can't have two different return types 21 | 22 | 23 | 24 | 4) 25 | let f a b = 26 | if a then 0 else b 27 | 28 | bool -> int -> int 29 | 30 | 31 | 32 | 5) 33 | let f a b c = 34 | let d = "hi" ^ a ^ b ^ c in 35 | d == "hello" 36 | 37 | String -> String -> String -> bool 38 | 39 | 40 | 41 | (* Creating functions *) 42 | 43 | 44 | 1) float -> float -> float 45 | 46 | Ex. 47 | let f a b = a +. b 48 | 49 | 50 | 51 | 2) 'a -> 'b -> 'b list 52 | 53 | Ex. 54 | let f a b = [b] 55 | 56 | 57 | 58 | 3) int -> float -> int 59 | 60 | Ex. 61 | let f a b = if b = 0.1 then a else 1 62 | 63 | 64 | 65 | 4) 'a -> 'a -> 'a list 66 | 67 | Ex. 68 | let f a b = [a; b] 69 | 70 | 71 | 72 | (* Records and New Types *) 73 | 74 | 75 | 1) 76 | type hello = (str * int) 77 | 78 | let a: hello = ("a",5) 79 | 80 | 81 | 2) 82 | type Coin = Heads | Tails 83 | 84 | let b: Coin = Heads 85 | 86 | 3) 87 | type date = {month: string, day: int} 88 | 89 | let today = {day=16; year=2017} 90 | 91 | today.day -------------------------------------------------------------------------------- /discussions/d6_functions/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 6 - Friday, March 4th 2 | 3 | ## Closures 4 | 5 | 6 | 7 | * How do functions work? For example, if `let f x y = x+y`, what happens when we call `f 2`? 8 | * Internally, what happens is, a closure is created, which binds x -> 2, and stores the code/function `fun y -> x+y` 9 | * Understanding closures and variable bindings is important to understand how functions work, and are key to concepts like partial applications 10 | * Closures are especially important for recursive functions: 11 | * Say we have: `let rec f x = if x = 0 then 1 else x*(f (x-1))` 12 | * When this is run, OCaml sees the keyword `rec` and initially creates a variable, f->0 13 | * Then it will store the inner part of the function as a closure, with no variables 14 | * Finally, `let f ->` this closure, and return it 15 | * When the function is run, we add a variable to the environment and evaluate the code 16 | * The concept of closures will be a key idea in later projects 17 | 18 | **Examples and Expalanations (Covered during discussion)** 19 | 20 | 1) 21 | * What will `f 3` return? 22 | * In f, x is bound to the number 2 within the function closure statically and locally 23 | * When f is called, it uses this closure binding rather than the most recent or global version of x 24 | * Thus, `f 3` will use `x = 2` and return 5 25 | 26 | 2) 27 | * Closures are good for nested functions or partial applications 28 | * Partial application and currying in OCaml means OCaml functions really only take one parameter 29 | * The example represents `int -> (string -> bool)`, meaning if you only pass in one argument to the above function, it returns another function `(string -> bool)` 30 | * Since OCaml functions are closures, when you pass in one argument, it binds that argument in the closure and returns a new function with that binding in it 31 | 32 | 3) 33 | * Can’t use recursion, so automatically think map or fold 34 | * Since we are operating on every element, we’ll use map 35 | * The + operator and other operators are functions in OCaml 36 | * `+: int -> int -> int` 37 | * This means `(+) 3` returns a function of type `(int -> int)` 38 | * Map the + function using n to the list 39 | * Answer: `add_n n lst = List.map ((+) n) lst` 40 | * Try it with an example: `add_n 3 [1; 2; 3] -> [4; 5; 6]` 41 | 42 | 43 | ## Imperative OCaml and Mutability 44 | 45 | 46 | We always discuss how everything in OCaml is immutable. Sometimes it is useful for values to change, like for a counter or an array 47 | 48 | **Examples and Expalanations (Covered during discussion)** 49 | 50 | 1) 51 | * References in OCaml are like pointers in C 52 | * Use the ref function to allocate a reference to some space in memory 53 | * In the example, z is 3, and x points to a space in memory with value 3 54 | * Note that the binding of the variable is immutable. We can’t change x, only the value that x is pointing to - ie, it's a constant pointer to the variable 55 | 56 | 2) 57 | * `!` is a function used to dereference a reference 58 | * Using the variables from 1), `!x` returns 3 59 | 60 | 3) 61 | * `:=` is a function used to change the contents of a reference 62 | * Using the variables from 1), `x := 4` sets the memory space x refers to to 4 63 | * Does this change the value of z? 64 | * No, x and z are disjoint 65 | 66 | 4) 67 | * What does `!y` return? 68 | * Using the variables from 1), when we do `let y = x`, we bind y to the same memory location as x, so x and y become aliases 69 | * This is an example of aliasing, using a reference to change the value of another reference 70 | * Thus, when we set x to 4, we change the memory location for both x and y 71 | * Thus, `!y` returns 4 72 | * Note that z is still 3 73 | 74 | 5) 75 | * Recall that `;;` is how we end an OCaml expression in Utop or a program 76 | * A single semicolon operates like the comma operator in C 77 | * It evaluates all the expressions separated by a semicolon, then returns the last expression 78 | * In this example, it evaluates both print statements, then returns 0 79 | * This prints “Hello” and “World”, and returns `number = 0`, so the final output of the second statement is 0 + 1 = 1 80 | * One recommendation: might want to group the semicolon expressions with `begin … end` or `()` for clarity in scoping 81 | 82 | 6) 83 | * We can also make the fields of a record mutable, alongside these individual variables 84 | * We declare the field as mutable if we want to be able to change them 85 | * In this example, color is a mutable field, so we can change it even after setting it 86 | * `p.color <- “white”` is valid because color is a mutable field 87 | * `p.x <- 1` is invalid, though, because x is immutable 88 | -------------------------------------------------------------------------------- /discussions/d6_functions/examples: -------------------------------------------------------------------------------- 1 | ----------------------------------------------------- 2 | Closures, Currying, and Partial Application 3 | ----------------------------------------------------- 4 | 5 | 1. 6 | let x = 2;; 7 | let f y = x + y;; 8 | let x = 4;; 9 | f 3;; 10 | 11 | 12 | 2. 13 | int -> string -> bool 14 | 15 | 16 | 3. Write a function 'add_n n lst' that adds 'n' to every element of the list without using recursion 17 | 18 | 19 | -------------------------------------------------------- 20 | Imperative OCaml and Mutability 21 | -------------------------------------------------------- 22 | 23 | 1. 24 | let z = 3;; 25 | let x = ref z;; 26 | 27 | 28 | 2. !x 29 | 30 | 31 | 3. x := 4 32 | 33 | 34 | 4. 35 | let y = x;; 36 | x := 4;; 37 | !y;; 38 | 39 | 40 | 5. 41 | let print_both (s, t) = 42 | print_string s; 43 | print_string t; 44 | 0 45 | ;; 46 | let number = print_both ("Hello", "World") in 47 | number + 1 48 | ;; 49 | 50 | 51 | 6. 52 | type point = { x : int; y : int; mutable color : string };; 53 | let p = { x = 0; y = 0; color = "red" };; 54 | p.color <- "white";; 55 | p.x <- 1;; 56 | -------------------------------------------------------------------------------- /discussions/d7_nfa_dfa/Disc 7 - Automata Algorithms.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d7_nfa_dfa/Disc 7 - Automata Algorithms.pdf -------------------------------------------------------------------------------- /discussions/d7_nfa_dfa/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 7 - Friday, March 11th 2 | 3 | ## NFA, DFA, and Regex Explanation + Practice 4 | 5 | Worksheet.pdf, Disc 7 - Automata Algorithms.pdf (extra practice) 6 | 7 | ## NFA Accept 8 | * NFAs vs. DFAs: What's the difference? 9 | * NFAs can have e transitions. 10 | * NFAs can have more than 1 transition on the same character from a state. 11 | 12 | ### Question 1 13 | ![nfa](nfa.png) 14 | * Does the NFA accept "abab"? 15 | * Here's how we process things: 16 | * For DFAs, as we read characters, we track which state we're on. 17 | * For NFAs, as we read characters, we track which states (PLURAL!) we COULD be on. 18 | * Let's demonstrate: 19 | * Draw a stick figure on the start state. Let's call him Naruto. 20 | * Let's start w/ an e-closure. 21 | * Even before we read a character, we COULD be on any state reachable w/ e transitions. 22 | * So, Naruto clones himself and puts 1 clone on each state reachable w/ e transitions. 23 | * (draw the clones) 24 | * Let's read a character: 'a' 25 | * Each clone takes an 'a' transition. 26 | * If more than 1 'a' transitions exist, the clone clones himself and takes both transitions. 27 | * If no 'a' transitions exist, the clone dies. 28 | * (draw and erase the clones) 29 | * Then, we do an e-closure. 30 | * We do an e-closure after each character we read, cloning for each e-closure. 31 | * Read the rest of the characters: 'b', 'a', 'b'. 32 | * We ACCEPT if ANY clone is on ANY final state. 33 | * A clone on a final state shows that after reading "abab", we COULD be on that state. (The clone DID get to that state w/ "abab", after all.) 34 | * As we can see, there is a clone on state 6. We ACCEPT the string! 35 | * *As humans:* We can examine the NFA and find a path to a final state, like we just saw with this example. 36 | * *But for a computer?* Our process is hard to code. (Who knows what our brains are doing!) Later we'll see it would be easier to code after converting to a DFA. 37 | 38 | ### Hints on NFA accept 39 | - NFA accept is essentially e-closure, move, e-closure, move, ....e-closure. In other words, e-closure, (move, e-closure,)* e-closure. 40 | - This will be key for future projects 41 | 42 | ## RE -> NFA conversion 43 | * 3 basic operators: concatenation, union, kleene closure 44 | 45 | ### Question 2 46 | * Convert the following regular expressions to NFAs. 47 | * ab*|a*|c* 48 | * c(a|b)* 49 | * (abc)+ (equivalent to abc(abc)*) 50 | -------------------------------------------------------------------------------- /discussions/d7_nfa_dfa/Worksheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d7_nfa_dfa/Worksheet.pdf -------------------------------------------------------------------------------- /discussions/d7_nfa_dfa/nfa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d7_nfa_dfa/nfa.png -------------------------------------------------------------------------------- /discussions/d7_nfa_dfa/nfa2dfa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d7_nfa_dfa/nfa2dfa.png -------------------------------------------------------------------------------- /discussions/d7_nfa_dfa/nfa2dfa_alt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d7_nfa_dfa/nfa2dfa_alt.jpg -------------------------------------------------------------------------------- /discussions/d8_parsing/README.md: -------------------------------------------------------------------------------- 1 | # Discussion 8 - Friday, March 18th 2 | 3 | ## NFA -> DFA conversion 4 | ![nfa](nfa.png) 5 | * We will convert the NFA above to a DFA. 6 | * Here's an idea: Let's create a DFA which has a state for each clone formation! In other words: 7 | * For an NFA w/ states 1, 2, 3, 4, 5, and 6: 8 | * The DFA has a state for each SUBSET of states we COULD be on. 9 | * But aren't there 2^n subsets!? 10 | * No problem! We just have to account for subsets which represent possible (reachable) formations. 11 | * For example, our DFA doesn't need a state for {1}. If we COULD be at state 1, we COULD be at state 2, also (via e transition). 12 | * So, let's start at the first subset {1, 2} (e-closure of the start state) and do transitions over the alphabet and see what subsets actually show up! 13 | 14 | ![nfa2dfa](nfa2dfa.png) 15 | 16 | ## Parsing and Grammars, Explanation 17 | ### 1) Terminals, non-terminals and productions 18 | ### 2) Practice designing grammars 19 | ![cfg](cfg.jpg) 20 | ### 3) Practice derivations 21 | Grammar: S -> S + S | 1 | 2 | 3 22 | 23 | Leftmost derivation of 1 + 2 + 3 24 | * Start w/ S and use the production rules on the LEFTMOST nonterminal ONE AT A TIME. (For a rightmost derivation, use the productions on the RIGHTMOST nonterminal.) 25 | * ONE NONTERMINAL AT A TIME!!!! DON'T COMBINE STEPS!!!! 26 | * S -> S + S -> S + S + S -> 1 + S + S -> 1 + 2 + S -> 1 + 2 + 3 27 | * S -> S + S -> 1 + S -> 1 + S + S -> 1 + 2 + S -> 1 + 2 + 3 works too 28 | 29 | Note: If there are 2 derivations for the same string, what does that mean? The grammar is ambiguous. 30 | * To show that a grammar is ambiguous, show 2 different derivations for the same string. 31 | * It's hard to know whether a grammar is ambiguous or not (it's an undecidable problem afaik). But be suspicious if you see something along the lines of S -> SS, S -> SSS, S -> S+S, etc. 32 | ### 4) Parsing with a computer 33 | The exercise below goes into further detail 34 | 35 | 36 | ## Coding Excercise (Optional) 37 | * To go from source code to a running program, there are 3 steps (at least for our purposes): 38 | * Tokenizing/Lexing (separating text into smaller tokens) 39 | * Parsing (generating something meaningful from the tokens - an AST) 40 | * Interpreting (evaluating the result of the AST) 41 | 42 | * Consider the following grammar: 43 | * S -> M + S | M 44 | * M -> N * M | N 45 | * N -> n | (S) 46 | * where n is any integer 47 | 48 | * This grammar is right associative/recursive (Why did we provide a right associative grammar? What would you do if we didn't?) 49 | 50 | * What is the relative precedence of the + and \* operators here? How is it determined? How can we use CFGs to enforce precedence? 51 | 52 | ### Lexer 53 | * Open `lexer.skeleton.ml`. 54 | * Answer key in `lexer.ml` 55 | * Variant type `token` 56 | * Maintain an index that keeps track of where we are in the string, and move forward as we keep tokenizing. 57 | * In P4, you will have to worry about the order in which they have their `if/else` ... `if/else` (certain regexs should be checked before others). 58 | * It's probably also a good idea to just define all the regex's and store in variables at the top. 59 | 60 | ### Parser 61 | * Open `parser.skeleton.ml`. 62 | * Answer key in `parser.ml` 63 | * Variant type `expr` 64 | * Note: Use `let rec ...` and to write mutually recursive functions. 65 | * Note: `lookahead` just returns the head of the list. 66 | * Note: `match` just "consumes" the head of the list (provided that the token and head of the list match). 67 | * IMPORTANT: 68 | * We're going to write a function named `parse_X` for each nonterminal `X` in our grammar. 69 | * Each of these functions will parse (consume) some tokens, and return (1) the unparsed tokens and (2) the AST which corresponds to the parsed tokens. 70 | 71 | ### Interpreter 72 | -------------------------------------------------------------------------------- /discussions/d8_parsing/cfg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d8_parsing/cfg.jpg -------------------------------------------------------------------------------- /discussions/d8_parsing/nfa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d8_parsing/nfa.png -------------------------------------------------------------------------------- /discussions/d8_parsing/nfa2dfa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/discussions/d8_parsing/nfa2dfa.png -------------------------------------------------------------------------------- /discussions/d8_parsing/src/.merlin: -------------------------------------------------------------------------------- 1 | B /home/subomi/.opam/4.07.0/lib/ocaml 2 | B ../_build/default/src/.disc.objs 3 | S /home/subomi/.opam/4.07.0/lib/ocaml 4 | S . 5 | FLG -open Disc -w -40 6 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name disc) 3 | (modules lexer parser interpreter) 4 | (libraries str)) 5 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/interpreter.ml: -------------------------------------------------------------------------------- 1 | open Parser 2 | 3 | (* Evaluater *) 4 | 5 | let rec eval (ast : expr) : int = 6 | match ast with 7 | | Int x -> x 8 | | Mult (x, y) -> let x' = eval x in 9 | let y' = eval y in 10 | x' * y' 11 | | Plus (x, y) -> let x' = eval x in 12 | let y' = eval y in 13 | x' + y' 14 | 15 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/interpreter.mli: -------------------------------------------------------------------------------- 1 | val eval : Parser.expr -> int 2 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/interpreter.skeleton.ml: -------------------------------------------------------------------------------- 1 | open Parser 2 | 3 | (* Evaluater *) 4 | 5 | let rec eval (ast : expr) : int = 6 | failwith "unimplemented" 7 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/lexer.ml: -------------------------------------------------------------------------------- 1 | (* Type *) 2 | type token = 3 | | Tok_Int of int 4 | | Tok_Mult 5 | | Tok_Plus 6 | | Tok_LParen 7 | | Tok_RParen 8 | | Tok_EOF 9 | 10 | let string_of_token tok = match tok with 11 | | Tok_Int(i) -> string_of_int i 12 | | Tok_Mult -> "*" 13 | | Tok_Plus -> "+" 14 | | Tok_LParen -> "(" 15 | | Tok_RParen -> ")" 16 | | Tok_EOF -> "" 17 | 18 | 19 | let rec string_of_list conv lst = 20 | match lst with 21 | | [] -> "" 22 | | h::[] -> conv h 23 | | h::t -> (conv h) ^ " " ^ (string_of_list conv t) 24 | 25 | (* Given source code returns a token list. *) 26 | let rec lexer (input : string) : token list = 27 | let length = String.length input in 28 | 29 | let rec tok pos = 30 | if pos >= length then 31 | [Tok_EOF] 32 | 33 | else if Str.string_match (Str.regexp "(") input pos then 34 | Tok_LParen::(tok (pos + 1)) 35 | 36 | else if Str.string_match (Str.regexp ")") input pos then 37 | Tok_RParen::(tok (pos + 1)) 38 | 39 | else if Str.string_match (Str.regexp "\\+") input pos then 40 | Tok_Plus::(tok (pos + 1)) 41 | 42 | else if Str.string_match (Str.regexp "\\*") input pos then 43 | Tok_Mult::(tok (pos + 1)) 44 | 45 | else if Str.string_match (Str.regexp "-?[0-9]+") input pos then 46 | let value = Str.matched_string input in 47 | Tok_Int(int_of_string value)::(tok (pos + String.length value)) 48 | 49 | else 50 | tok (pos + 1) 51 | 52 | in tok 0;; 53 | 54 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/lexer.mli: -------------------------------------------------------------------------------- 1 | type token = 2 | | Tok_Int of int 3 | | Tok_Mult 4 | | Tok_Plus 5 | | Tok_LParen 6 | | Tok_RParen 7 | | Tok_EOF 8 | 9 | val lexer : string -> token list 10 | 11 | val string_of_token : token -> string 12 | 13 | val string_of_list : ('a -> string) -> 'a list -> string 14 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/lexer.skeleton.ml: -------------------------------------------------------------------------------- 1 | (* Type *) 2 | type token = 3 | | Tok_Int of int 4 | | Tok_Mult 5 | | Tok_Plus 6 | | Tok_LParen 7 | | Tok_RParen 8 | | Tok_EOF 9 | 10 | let string_of_token tok = match tok with 11 | | Tok_Int(i) -> string_of_int i 12 | | Tok_Mult -> "*" 13 | | Tok_Plus -> "+" 14 | | Tok_LParen -> "(" 15 | | Tok_RParen -> ")" 16 | | Tok_EOF -> "" 17 | 18 | let rec string_of_list conv lst = 19 | match lst with 20 | | [] -> "" 21 | | h::[] -> conv h 22 | | h::t -> (conv h) ^ " " ^ (string_of_list conv t) 23 | 24 | (* Given source code returns a token list. *) 25 | let rec lexer (input : string) : token list = 26 | failwith "unimplemented" 27 | 28 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/lexer_cameron.ml: -------------------------------------------------------------------------------- 1 | module L = List 2 | module S = String 3 | module R = Str 4 | 5 | (* Type *) 6 | type token = 7 | | Tok_Int of int 8 | | Tok_Mult 9 | | Tok_Plus 10 | | Tok_LParen 11 | | Tok_RParen 12 | | Tok_EOF 13 | 14 | (* Regular expressions and the tokens they generate. *) 15 | let re = [ 16 | (R.regexp "[0-9]+" , fun x -> [Tok_Int (int_of_string x)]) ; 17 | (R.regexp "\\+" , fun _ -> [Tok_Plus]) ; 18 | (R.regexp "\\*" , fun _ -> [Tok_Mult]) ; 19 | (R.regexp "(" , fun _ -> [Tok_LParen]) ; 20 | (R.regexp ")" , fun _ -> [Tok_RParen]) ; 21 | (R.regexp " " , fun _ -> []) 22 | ] 23 | 24 | (* Given source code returns a token list. *) 25 | let rec lexer (s : string) : token list = 26 | lexer' s 0 27 | 28 | (* Helper for lexer takes in a position offset. *) 29 | and lexer' (s : string) (pos : int) : token list = 30 | if pos >= S.length s then [Tok_EOF] 31 | else 32 | let (_, f) = L.find (fun (re, _) -> R.string_match re s pos) re in 33 | let s' = R.matched_string s in 34 | (f s') @ (lexer' s (pos + (S.length s'))) 35 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/parser.ml: -------------------------------------------------------------------------------- 1 | open Lexer 2 | 3 | (* Types *) 4 | type expr = 5 | | Int of int 6 | | Plus of expr * expr 7 | | Mult of expr * expr 8 | 9 | (* Provided helper function - takes a token list and an exprected token. 10 | * Handles error cases and returns the tail of the list *) 11 | let match_token (toks : token list) (tok : token) : token list = 12 | match toks with 13 | | [] -> raise (Failure(string_of_token tok)) 14 | | h::t when h = tok -> t 15 | | h::_ -> raise (Failure( 16 | Printf.sprintf "Expected %s from input %s, got %s" 17 | (string_of_token tok) 18 | (string_of_list string_of_token toks) 19 | (string_of_token h) 20 | )) 21 | 22 | let lookahead toks = match toks with 23 | h::t -> h 24 | | _ -> raise (Failure("Empty input to lookahead")) 25 | 26 | 27 | (* Parses a token list. *) 28 | let rec parser (toks : token list) : expr = 29 | let (t, exp) = parse_S toks in 30 | if t <> [Tok_EOF] then 31 | raise (Failure "did not reach EOF") 32 | else 33 | exp 34 | 35 | (* Parses the S rule. *) 36 | and parse_S toks = 37 | let (t, m) = parse_M toks in 38 | match lookahead t with 39 | | Tok_Plus -> let t' = match_token t Tok_Plus in 40 | let (t'', s) = parse_S t' in 41 | (t'', Plus (m, s)) 42 | | _ -> t, m 43 | 44 | (* Parses the M rule. *) 45 | and parse_M toks = 46 | let (t, n) = parse_N toks in 47 | match lookahead t with 48 | | Tok_Mult -> let t' = match_token t Tok_Mult in 49 | let (t'', m) = parse_M t' in 50 | (t'', Mult (n, m)) 51 | | _ -> t, n 52 | 53 | (* Parses the N rule. *) 54 | and parse_N toks = 55 | match lookahead toks with 56 | | Tok_Int i -> let t = match_token toks (Tok_Int i) in 57 | (t, Int i) 58 | | Tok_LParen -> let t = match_token toks Tok_LParen in 59 | let (t', s) = parse_S t in 60 | let t'' = match_token t' Tok_RParen in 61 | (t'', s) 62 | | _ -> failwith "parse_N failed" 63 | -------------------------------------------------------------------------------- /discussions/d8_parsing/src/parser.mli: -------------------------------------------------------------------------------- 1 | type expr = 2 | | Int of int 3 | | Plus of expr * expr 4 | | Mult of expr * expr 5 | 6 | val parser : Lexer.token list -> expr 7 | val parse_S : Lexer.token list -> Lexer.token list * expr 8 | val parse_M : Lexer.token list -> Lexer.token list * expr 9 | val parse_N : Lexer.token list -> Lexer.token list * expr -------------------------------------------------------------------------------- /discussions/d8_parsing/src/parser.skeleton.ml: -------------------------------------------------------------------------------- 1 | open Lexer 2 | 3 | (* Types *) 4 | type expr = 5 | | Int of int 6 | | Plus of expr * expr 7 | | Mult of expr * expr 8 | 9 | (* Provided helper function - takes a token list and an exprected token. 10 | * Handles error cases and returns the tail of the list *) 11 | let match_token (toks : token list) (tok : token) : token list = 12 | match toks with 13 | | [] -> raise (Failure(string_of_token tok)) 14 | | h::t when h = tok -> t 15 | | h::_ -> raise (Failure( 16 | Printf.sprintf "Expected %s from input %s, got %s" 17 | (string_of_token tok) 18 | (string_of_list string_of_token toks) 19 | (string_of_token h) 20 | )) 21 | 22 | let lookahead toks = match toks with 23 | h::t -> h 24 | | _ -> raise (Failure("Empty input to lookahead")) 25 | 26 | 27 | 28 | (* Parses a token list. *) 29 | let rec parser (toks : token list) : expr = 30 | failwith "unimplemented" 31 | 32 | (* Parses the S rule. *) 33 | and parse_S (toks : token list) : (token list * expr) = 34 | failwith "unimplemented" 35 | 36 | (* Parses the M rule. *) 37 | and parse_M (toks : token list) : (token list * expr) = 38 | failwith "unimplemented" 39 | 40 | (* Parses the N rule. *) 41 | and parse_N (toks : token list) : (token list * expr) = 42 | failwith "unimplemented" 43 | -------------------------------------------------------------------------------- /project0/.gitignore: -------------------------------------------------------------------------------- 1 | p0.report 2 | -------------------------------------------------------------------------------- /project0/.submit: -------------------------------------------------------------------------------- 1 | [course] 2 | id = 358171 3 | name = "CMSC330" 4 | term = "Spring 2022" 5 | 6 | [assignment] 7 | id = 1802451 8 | name = "Project 0 - Setup" 9 | files = [ "p0.report" ] 10 | -------------------------------------------------------------------------------- /project0/test/public/public.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require "open3" 3 | 4 | # 5 | # Constants 6 | # 7 | 8 | VERSION = /(\d+(\.\d+))/ 9 | OCAML_VERSION = "4.12" 10 | OPAM_VERSION = "2.1" 11 | 12 | # 13 | # Required Packages 14 | # 15 | 16 | class PublicTests < Minitest::Test 17 | def test_public_ocaml 18 | assert(ocaml_version, not_installed("OCaml")) 19 | assert_equal(OCAML_VERSION, ocaml_version, wrong_version("OCaml")) 20 | end 21 | 22 | def test_public_opam 23 | assert(opam_version, not_installed("OPAM")) 24 | assert_equal(OPAM_VERSION, opam_version, wrong_version("OPAM")) 25 | end 26 | 27 | def test_public_sqlite3 28 | assert(sqlite3_version, not_installed("SQLite3")) 29 | end 30 | 31 | def test_public_ruby_gems 32 | assert(gem_version("minitest"), not_installed("MiniTest gem")) 33 | assert(gem_version("sinatra"), not_installed("Sinatra gem")) 34 | assert(gem_version("sqlite3"), not_installed("SQLite3 gem")) 35 | end 36 | 37 | def test_public_ocaml_pkgs 38 | assert(ocaml_pkg_version("ounit"), not_installed("OUnit pkg")) 39 | assert(ocaml_pkg_version("dune"), not_installed("dune pkg")) 40 | assert(ocaml_pkg_version("utop"), not_installed("utop pkg")) 41 | end 42 | 43 | def test_public_graphviz 44 | assert(graphviz_version, not_installed("Graphviz")) 45 | end 46 | end 47 | 48 | # 49 | # Helpers 50 | # 51 | 52 | def not_installed(name) 53 | "#{name} is not installed" 54 | end 55 | 56 | def wrong_version(name) 57 | "The wrong version of #{name} is installed" 58 | end 59 | 60 | def optional_warn(msg) 61 | puts "OPTIONAL: #{msg}" 62 | end 63 | 64 | def match_version(cmd) 65 | stdout, stderr, _ = Open3.capture3("#{cmd};") 66 | (stdout =~ VERSION or stderr =~ VERSION) and $1 67 | end 68 | 69 | # 70 | # Version Checkers 71 | # 72 | 73 | def ocaml_version 74 | match_version("ocaml -version") 75 | end 76 | 77 | def opam_version 78 | match_version("opam --version") 79 | end 80 | 81 | def graphviz_version 82 | match_version("dot -V") 83 | end 84 | 85 | def sqlite3_version 86 | match_version("sqlite3 -version") 87 | end 88 | 89 | def gem_version(name) 90 | spec = Gem::Specification.find { |s| s.name == name } 91 | spec and spec.version.version 92 | end 93 | 94 | def ocaml_pkg_version(name) 95 | `opam info #{name}`.encode("UTF-8", "UTF-8") =~ /all-installed-versions/ 96 | end 97 | 98 | # 99 | # Results Reporter 100 | # 101 | 102 | module Minitest 103 | class SubmitReporter < AbstractReporter 104 | attr_accessor :results 105 | 106 | def initialize(options) 107 | self.results = [] 108 | end 109 | 110 | def record result 111 | self.results << result 112 | end 113 | 114 | def report 115 | result_hashes = results.map do |result| 116 | { :name => result.name, 117 | :assertions => result.assertions, 118 | :failures => result.failures 119 | } 120 | end 121 | 122 | File.open("p0.report", "w") do |f| 123 | Marshal.dump(result_hashes, f) 124 | end 125 | end 126 | end 127 | 128 | def self.plugin_submit_init(options) 129 | self.reporter << SubmitReporter.new(options) 130 | end 131 | 132 | self.extensions << "submit" 133 | end 134 | -------------------------------------------------------------------------------- /project1a/.submit: -------------------------------------------------------------------------------- 1 | [course] 2 | id = 358171 3 | name = "CMSC330" 4 | term = "Spring 2022" 5 | 6 | [assignment] 7 | id = 1802991 8 | name = "Project 1a - Ruby Warmup" 9 | files = [ "src/warmup.rb", "src/phonebook.rb" ] 10 | -------------------------------------------------------------------------------- /project1a/src/phonebook.rb: -------------------------------------------------------------------------------- 1 | class PhoneBook 2 | def initialize 3 | end 4 | 5 | def add(name, number, is_listed) 6 | raise Exception, "Not implemented" 7 | end 8 | 9 | def lookup(name) 10 | raise Exception, "Not implemented" 11 | end 12 | 13 | def lookupByNum(number) 14 | raise Exception, "Not implemented" 15 | end 16 | 17 | def namesByAc(areacode) 18 | raise Exception, "Not implemented" 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /project1a/src/warmup.rb: -------------------------------------------------------------------------------- 1 | def fib(n) 2 | raise Exception, "Not Implemented" 3 | end 4 | 5 | def isPalindrome(n) 6 | raise Exception, "Not Implemented" 7 | end 8 | 9 | def nthmax(n, a) 10 | raise Exception, "Not Implemented" 11 | end 12 | 13 | def freq(s) 14 | raise Exception, "Not Implemented" 15 | end 16 | 17 | def zipHash(arr1, arr2) 18 | raise Exception, "Not Implemented" 19 | end 20 | 21 | def hashToArray(hash) 22 | raise Exception, "Not Implemented" 23 | end 24 | -------------------------------------------------------------------------------- /project1a/test/public/public.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require_relative "../../src/warmup.rb" 3 | require_relative "../../src/phonebook.rb" 4 | 5 | class PublicTests < MiniTest::Test 6 | def setup 7 | @phonebook = PhoneBook.new 8 | end 9 | 10 | def test_public_fib 11 | assert_equal([], fib(0)) 12 | assert_equal([0], fib(1)) 13 | assert_equal([0, 1], fib(2)) 14 | assert_equal([0, 1, 1], fib(3)) 15 | assert_equal([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], fib(10)) 16 | end 17 | 18 | def test_public_ispalindrome 19 | assert_equal(true, isPalindrome(0)) 20 | assert_equal(true, isPalindrome(1)) 21 | assert_equal(false, isPalindrome(10)) 22 | assert_equal(true, isPalindrome(101)) 23 | assert_equal(false, isPalindrome(120210)) 24 | end 25 | 26 | def test_public_nthmax 27 | assert_equal(3, nthmax(0, [1,2,3,0])) 28 | assert_equal(2, nthmax(1, [3,2,1,0])) 29 | assert_equal(4, nthmax(2, [7,3,4,5])) 30 | assert_nil(nthmax(5, [1,2,3])) 31 | end 32 | 33 | def test_public_freq 34 | assert_equal("", freq("")) 35 | assert_equal("a", freq("aaabb")) 36 | assert_equal("a", freq("bbaaa")) 37 | assert_equal("s", freq("ssabcd")) 38 | assert_equal("x", freq("a12xxxxxyyyxyxyxy")) 39 | end 40 | 41 | def test_public_ziphash 42 | assert_equal({}, zipHash([], [])) 43 | assert_equal({1 => 2}, zipHash([1], [2])) 44 | assert_equal({1 => 2, 5 => 4}, zipHash([1, 5], [2, 4])) 45 | assert_nil(zipHash([1], [2,3])) 46 | assert_equal({"Mamat" => "prof", "Hicks" => "prof", "Vinnie" => "TA"}, 47 | zipHash(["Mamat", "Hicks", "Vinnie"], ["prof", "prof", "TA"])) 48 | end 49 | 50 | def test_public_hashtoarray 51 | assert_equal([], hashToArray({})) 52 | assert_equal([["a", "b"]], hashToArray({"a" => "b"})) 53 | assert_equal([["a", "b"], [1, 2]], hashToArray({"a" => "b", 1 => 2})) 54 | assert_equal([["x", "v"], ["y", "w"], ["z", "u"]], hashToArray({"x" => "v", "y" => "w", "z" => "u"})) 55 | end 56 | 57 | def test_public_phonebook_add 58 | assert_equal(true, @phonebook.add("John", "110-192-1862", false)) 59 | assert_equal(true, @phonebook.add("Jane", "220-134-1312", false)) 60 | assert_equal(false, @phonebook.add("John", "110-192-1862", false)) 61 | end 62 | 63 | def test_public_phonebook_lookup 64 | assert_equal(true, @phonebook.add("John", "110-192-1862", false)) 65 | assert_equal(true, @phonebook.add("Jane", "220-134-1312", true)) 66 | assert_equal(true, @phonebook.add("Jack", "114-192-1862", false)) 67 | assert_equal(true, @phonebook.add("Jessie", "410-124-1131", true)) 68 | assert_nil(@phonebook.lookup("John")) 69 | assert_nil(@phonebook.lookup("Jack")) 70 | assert_equal("220-134-1312", @phonebook.lookup("Jane")) 71 | assert_equal("410-124-1131", @phonebook.lookup("Jessie")) 72 | end 73 | 74 | def test_public_phonebook_lookup_by_num 75 | assert_equal(true, @phonebook.add("John", "110-192-1862", false)) 76 | assert_equal(true, @phonebook.add("Jane", "220-134-1312", true)) 77 | assert_equal(true, @phonebook.add("Jack", "114-192-1862", false)) 78 | assert_equal(true, @phonebook.add("Jessie", "410-124-1131", true)) 79 | assert_nil(@phonebook.lookupByNum("110-192-1862")) 80 | assert_nil(@phonebook.lookupByNum("114-192-1862")) 81 | assert_equal("Jane", @phonebook.lookupByNum("220-134-1312")) 82 | assert_equal("Jessie", @phonebook.lookupByNum("410-124-1131")) 83 | end 84 | 85 | def test_public_names_by_ac 86 | assert_equal(true, @phonebook.add("John", "110-192-1862", false)) 87 | assert_equal(true, @phonebook.add("Jane", "110-134-1312", true)) 88 | assert_equal(true, @phonebook.add("Jack", "114-192-1862", false)) 89 | assert_equal(true, @phonebook.add("Jessie", "110-124-1131", true)) 90 | assert_equal(["John", "Jane", "Jessie"].sort, @phonebook.namesByAc("110").sort) 91 | assert_equal(["Jack"], @phonebook.namesByAc("114")) 92 | assert_equal([], @phonebook.namesByAc("111")) 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /project1b/.submit: -------------------------------------------------------------------------------- 1 | [course] 2 | id = 358171 3 | name = "CMSC330" 4 | term = "Spring 2022" 5 | 6 | [assignment] 7 | id = 1822607 8 | name = "Project 1b - Battleship game" 9 | files = [ "src/models/game_board.rb", "src/controllers/input_controller.rb" ] 10 | -------------------------------------------------------------------------------- /project1b/src/controllers/game_controller.rb: -------------------------------------------------------------------------------- 1 | require_relative 'input_controller' 2 | require_relative '../models/game_board' 3 | 4 | # =========================================== 5 | # =====DON'T modify the following code======= 6 | # =========================================== 7 | 8 | PLAYER_ONE = 1 9 | PLAYER_TWO = 2 10 | RANDOM_ATTACK_SIZE = 35 11 | 12 | # GameController responsible for the game logic 13 | # Reads files by calling input_controller and take 14 | class GameController 15 | def initialize(players) 16 | @players = players 17 | @game_board = nil, nil 18 | @attacks = [], [] 19 | end 20 | 21 | # setup and start the game 22 | def start_game 23 | @game_board = load_ships(@players.game_board_1, PLAYER_ONE), load_ships(@players.game_board_2, PLAYER_TWO) 24 | @attacks = load_attacks(@players.attack_1, PLAYER_ONE), load_attacks(@players.attack_2, PLAYER_TWO) 25 | puts "" 26 | play_game 27 | end 28 | 29 | # load the GameBoard for the player # (calls input_controller function) 30 | def load_ships(game_board_file, player_number) 31 | player_game_board = read_ships_file(game_board_file) 32 | unless player_game_board 33 | raise "Input Error\nError loading Player #{player_number}'s ships file. " + 34 | "Make sure the ship file provided exists and the format is correct (i.e, 5 ships per player)." 35 | end 36 | player_game_board 37 | end 38 | 39 | # load the attack strategy file for the player # 40 | # Creates random attack moves if file is not provided. 41 | def load_attacks(attack_file, player) 42 | if attack_file.nil? 43 | puts "No attack file found for Player #{player}. Using random generator instead." 44 | return generate_random_attacks RANDOM_ATTACK_SIZE 45 | end 46 | 47 | attacks = read_attacks_file(attack_file) 48 | unless attacks 49 | raise "Input Error\nError reading Player #{player}'s attack file. " + 50 | "Make sure the attack strategy file provided exists and the format is correct." 51 | end 52 | 53 | attacks 54 | end 55 | 56 | # take turns and perform attacks 57 | def play_game 58 | winner = -1 59 | if @attacks[0].empty? || @attacks[1].empty? 60 | raise "Each player must have AT LEAST one attack position. " + 61 | "Player-#{@attacks[0].empty? ? 1 : 2} has 0 attacks." 62 | end 63 | 64 | while true 65 | winner = take_turn_check_winner PLAYER_ONE 66 | break if winner != -1 67 | 68 | winner = take_turn_check_winner PLAYER_TWO 69 | break if winner != -1 70 | end 71 | 72 | #print end of game message 73 | 74 | msg = '˗ ˏ ˋ ˎ ˊ ˗ ' * 2 75 | puts "\n#{msg}\nYoo-hoooo!\nPlayer #{winner} has won the game!\n#{msg}\n\n" 76 | puts "#{'=' * 20}\nPlayer-1 GameBoard" 77 | puts @game_board[PLAYER_ONE - 1] 78 | puts "#{'=' * 20}\nPlayer-2 GameBoard" 79 | puts @game_board[PLAYER_TWO - 1] 80 | end 81 | 82 | # player takes turn and performs attack. 83 | # checks for winner and return the winner number if there is one. 84 | # If no winner, returns -1 85 | def take_turn_check_winner(player) 86 | other_player = player == PLAYER_ONE ? PLAYER_TWO : PLAYER_ONE 87 | 88 | # skip invalid attacks 89 | did_attack = false 90 | until did_attack 91 | did_attack = perform_attack_on(player, other_player) 92 | end 93 | 94 | # if one player ran out of attack moves, stop the game and announce the winner 95 | if player == PLAYER_TWO 96 | unless has_valid_attack?(PLAYER_ONE) && has_valid_attack?(PLAYER_TWO) 97 | p_success = @game_board[other_player - 1].num_successful_attacks 98 | other_p_success = @game_board[player - 1].num_successful_attacks 99 | 100 | puts "Game result:\nP#{other_player} success: #{other_p_success}, P#{player} success: #{p_success}" 101 | if p_success > other_p_success 102 | return player 103 | else 104 | return other_player 105 | end 106 | end 107 | end 108 | 109 | if @game_board[other_player - 1].all_sunk? 110 | p_success = @game_board[other_player - 1].num_successful_attacks 111 | other_p_success = @game_board[player - 1].num_successful_attacks 112 | 113 | puts "Game result:\nP#{player} success: #{p_success}, P#{other_player} success: #{other_p_success}" 114 | puts "All player #{other_player} ships sunk!" 115 | return player 116 | end 117 | -1 118 | end 119 | 120 | # Perform a single attack from to its opponent 121 | def perform_attack_on(player, other_player) 122 | player_attack_strategy = @attacks[player - 1] 123 | # player attacks other player 124 | result = @game_board[other_player - 1].attack_pos(player_attack_strategy.shift) 125 | 126 | result != nil 127 | end 128 | 129 | # generate a list of random attack positions 130 | def generate_random_attacks(size) 131 | attacks = [] 132 | rng = Random.new 133 | for _ in 0...size 134 | attacks.append Position.new(rng.rand(1..@game_board[0].max_row), rng.rand(1..@game_board[0].max_column)) 135 | end 136 | attacks 137 | end 138 | 139 | def has_valid_attack?(player) 140 | player_attack_strategy = @attacks[player - 1] 141 | player_attack_strategy.each do |position| 142 | return true if position_valid? position 143 | end 144 | false 145 | end 146 | 147 | def position_valid?(position) 148 | is_valid = Proc.new { |x, max| x <= max && x >= 1 } 149 | 150 | is_valid.call(position.row, @game_board[0].max_row) && is_valid.call(position.column, @game_board[0].max_column) 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /project1b/src/controllers/input_controller.rb: -------------------------------------------------------------------------------- 1 | require_relative '../models/game_board' 2 | require_relative '../models/ship' 3 | require_relative '../models/position' 4 | 5 | # return a populated GameBoard or nil 6 | # Return nil on any error (validation error or file opening error) 7 | # If 5 valid ships added, return GameBoard; return nil otherwise 8 | def read_ships_file(path) 9 | GameBoard.new 10, 10 10 | end 11 | 12 | 13 | # return Array of Position or nil 14 | # Returns nil on file open error 15 | def read_attacks_file(path) 16 | [Position.new(1, 1)] 17 | end 18 | 19 | 20 | # =========================================== 21 | # =====DON'T modify the following code======= 22 | # =========================================== 23 | # Use this code for reading files 24 | # Pass a code block that would accept a file line 25 | # and does something with it 26 | # Returns True on successfully opening the file 27 | # Returns False if file doesn't exist 28 | def read_file_lines(path) 29 | return false unless File.exist? path 30 | if block_given? 31 | File.open(path).each do |line| 32 | yield line 33 | end 34 | end 35 | 36 | true 37 | end 38 | -------------------------------------------------------------------------------- /project1b/src/main.rb: -------------------------------------------------------------------------------- 1 | require_relative 'controllers/game_controller' 2 | 3 | # =========================================== 4 | # =====DON'T modify the following code======= 5 | # =========================================== 6 | 7 | Players = Struct.new(:game_board_1, :game_board_2, :attack_1, :attack_2) 8 | 9 | def parser(args) 10 | players = Players.new(nil, nil) 11 | raise ArgumentError unless args.size >= 2 12 | players.game_board_1 = args[0] 13 | players.game_board_2 = args[1] 14 | players.attack_1 = args[2] if args.size >= 3 15 | players.attack_2 = args[3] if args.size >= 4 16 | 17 | players 18 | end 19 | 20 | def main(files=ARGV) 21 | begin 22 | players = parser(files) 23 | game_controller = GameController.new(players) 24 | game_controller.start_game 25 | rescue ArgumentError 26 | puts "Invalid number of arguments." 27 | puts "Usage: main.rb " 28 | exit(1) 29 | end 30 | end 31 | 32 | main 33 | -------------------------------------------------------------------------------- /project1b/src/models/game_board.rb: -------------------------------------------------------------------------------- 1 | class GameBoard 2 | # @max_row is an `Integer` 3 | # @max_column is an `Integer` 4 | attr_reader :max_row, :max_column 5 | 6 | def initialize(max_row, max_column) 7 | @max_row = max_row 8 | @max_column = max_column 9 | end 10 | 11 | # adds a Ship object to the GameBoard 12 | # returns Boolean 13 | # Returns true on successfully added the ship, false otherwise 14 | # Note that Position pair starts from 1 to max_row/max_column 15 | def add_ship(ship) 16 | true 17 | end 18 | 19 | # return Boolean on whether attack was successful or not (hit a ship?) 20 | # return nil if Position is invalid (out of the boundary defined) 21 | def attack_pos(position) 22 | # check position 23 | 24 | # update your grid 25 | 26 | # return whether the attack was successful or not 27 | true 28 | end 29 | 30 | # Number of successful attacks made by the "opponent" on this player GameBoard 31 | def num_successful_attacks 32 | 0 33 | end 34 | 35 | # returns Boolean 36 | # returns True if all the ships are sunk. 37 | # Return false if at least one ship hasn't sunk. 38 | def all_sunk? 39 | true 40 | end 41 | 42 | 43 | # String representation of GameBoard (optional but recommended) 44 | def to_s 45 | "STRING METHOD IS NOT IMPLEMENTED" 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /project1b/src/models/position.rb: -------------------------------------------------------------------------------- 1 | # =========================================== 2 | # =====DON'T modify the following code======= 3 | # =========================================== 4 | 5 | 6 | class Position 7 | # @row is an `Integer` 8 | # @column is an `Integer` 9 | attr_reader :row, :column 10 | 11 | def initialize(row, column) 12 | @row = row 13 | @column = column 14 | end 15 | 16 | def to_s 17 | "(#{row}, #{column})" 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /project1b/src/models/ship.rb: -------------------------------------------------------------------------------- 1 | # =========================================== 2 | # =====DON'T modify the following code======= 3 | # =========================================== 4 | 5 | class Ship 6 | # @start_position is a `Position` 7 | # @orientation is one of following `String`s 8 | # - "Up" | "Down" | "Left" | "Right" 9 | # @size is an `Integer` 10 | attr_reader :start_position, :orientation, :size 11 | 12 | def initialize(start_position, orientation, size) 13 | @start_position = start_position 14 | @orientation = orientation 15 | @size = size 16 | end 17 | 18 | def to_s 19 | "Ship: #{@start_position}, #{orientation}, #{@size}" 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /project1b/test/public/inputs/bad_ships.txt: -------------------------------------------------------------------------------- 1 | (2,2), Right, 3 2 | (2,2), Right, 2 3 | (5,2), Right, 4 4 | (6,2), Right, 6 5 | (7,2), Right, 5 6 | -------------------------------------------------------------------------------- /project1b/test/public/inputs/correct_ships_p1.txt: -------------------------------------------------------------------------------- 1 | (2,2), Right, 3 2 | (4,2), Right, 2 3 | (5,2), Right, 4 4 | (6,2), Right, 5 5 | (7,2), Right, 5 6 | -------------------------------------------------------------------------------- /project1b/test/public/inputs/correct_ships_p2.txt: -------------------------------------------------------------------------------- 1 | (2,2), Right, 2 2 | (9,5), Up, 3 3 | (6,9), Left, 3 4 | (7,2), Down, 2 5 | (7,3), Down, 2 6 | -------------------------------------------------------------------------------- /project1b/test/public/inputs/correct_strat_p1.txt: -------------------------------------------------------------------------------- 1 | (2,2) 2 | ALL NON CONFORMING LINES SHOULD BE IGNORED 3 | (3,2) 4 | (4,2) 5 | (5,2) 6 | LIKE THIS ONE 7 | (6,2) 8 | AND THIS ONE 9 | (7,2) 10 | (2,2) 11 | (2,4) 12 | (5,8) 13 | (7,7) 14 | (4,9) 15 | (8,3) 16 | (3,3) 17 | (1,3) 18 | (8,9) 19 | (9,9) 20 | -------------------------------------------------------------------------------- /project1b/test/public/inputs/correct_strat_p2.txt: -------------------------------------------------------------------------------- 1 | (2,2) 2 | THIS IS A NON CONFORMING LINE 3 | (9,5) 4 | (6,9) 5 | (7,2) 6 | DO NOT WORRY 7 | (7,3) 8 | (2,2) 9 | (2,3) 10 | (4,5) 11 | (5,5) 12 | (6,5) 13 | (7,5) 14 | (8,5) 15 | (9,5) 16 | (7,2) 17 | (7,2) 18 | (7,3) 19 | (8,2) 20 | (8,3) 21 | (6,7) 22 | (6,8) 23 | (6,9) 24 | I HAVE A SINKING FEELING THIS IS A VALID FILE 25 | -------------------------------------------------------------------------------- /project1b/test/public/inputs/perfect_strat_p1.txt: -------------------------------------------------------------------------------- 1 | (2,2) 2 | (4,2) 3 | (5,2) 4 | (6,2) 5 | (7,2) 6 | -------------------------------------------------------------------------------- /project1b/test/public/inputs/perfect_strat_p2.txt: -------------------------------------------------------------------------------- 1 | (2,2) 2 | (2,3) 3 | 4 | (9,5) 5 | (8,5) 6 | (7,5) 7 | (6,5) 8 | (5,5) 9 | (4,5) 10 | 11 | (6,9) 12 | (6,8) 13 | (6,7) 14 | 15 | (7,2) 16 | (8,2) 17 | 18 | (7,3) 19 | (8,3) 20 | -------------------------------------------------------------------------------- /project1b/test/public/inputs/player1.txt: -------------------------------------------------------------------------------- 1 | (2,2), Right, 3 2 | (4,2), Right, 2 3 | (5,2), Right, 4 4 | (6,2), Right, 5 5 | (7,2), Right, 5 6 | -------------------------------------------------------------------------------- /project1b/test/public/public.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require_relative "../../src/controllers/input_controller.rb" 3 | require_relative "../../src/controllers/game_controller.rb" 4 | require_relative "../../src/models/game_board.rb" 5 | require_relative "../../src/models/position.rb" 6 | require_relative "../../src/models/ship.rb" 7 | 8 | # The ship coordinates for p1, p2 9 | SHIPS_P1 = "#{__dir__}/inputs/correct_ships_p1.txt" 10 | SHIPS_P2 = "#{__dir__}/inputs/correct_ships_p2.txt" 11 | 12 | # The attack coordinates against p1, p2 13 | ATTACK_P1 = "#{__dir__}/inputs/correct_strat_p1.txt" 14 | ATTACK_P2 = "#{__dir__}/inputs/correct_strat_p2.txt" 15 | 16 | # The perfect attack coordinates against p1, p2 17 | PERF_ATK_P1 = "#{__dir__}/inputs/perfect_strat_p1.txt" 18 | PERF_ATK_P2 = "#{__dir__}/inputs/perfect_strat_p2.txt" 19 | 20 | # A bad ships file 21 | BAD_SHIPS = "#{__dir__}/inputs/bad_ships.txt" 22 | 23 | class PublicTests < MiniTest::Test 24 | def setup 25 | @p1_ships = [] 26 | @p1_perf_atk = [] 27 | @p2_ships = [] 28 | @p2_perf_atk = [] 29 | for i, size in [1,2,3,4].zip([4,5,3,2]) 30 | pos0 = Position.new(i, i) 31 | pos1 = Position.new(i + 4, i + 4) 32 | @p1_ships << Ship.new(pos0, "Right", size) 33 | @p2_ships << Ship.new(pos1, "Right", size) 34 | for j in 0..(size - 1) 35 | @p2_perf_atk << Position.new(i, i + j) 36 | @p1_perf_atk << Position.new(i + 4, i + j + 4) 37 | end 38 | end 39 | end 40 | 41 | def test_public_gameboard_1 42 | test_board = GameBoard.new 10, 10 43 | 44 | # Property: A ship can be added in the bounds on an empty game_board 45 | sngl_test_ret = test_board.add_ship(@p1_ships[0]) 46 | assert(sngl_test_ret, "Ship in bounds should be added without error") 47 | for shp in @p1_ships[1..] 48 | add_shp_ret = test_board.add_ship(shp) 49 | assert(add_shp_ret, "A valid ship was not added") 50 | end 51 | 52 | # Property: A ship will be hit if attacked 53 | for i in @p2_perf_atk 54 | assert(test_board.attack_pos(i), "Attack that should hit did not") 55 | end 56 | 57 | # Property: Nothing will change for a miss 58 | refute(test_board.attack_pos(Position.new(2, 1)), "Attack should have missed but hit") 59 | end 60 | 61 | def test_public_gameboard_2 62 | # Property: (add a ship & attack the length of the ship) => no. of attacks on the ship == nm_successful_attacks 63 | test_board = GameBoard.new(10, 10) 64 | for shp in @p2_ships 65 | add_shp_ret = test_board.add_ship(shp) 66 | assert(add_shp_ret, "A valid ship was not added") 67 | end 68 | refute(test_board.all_sunk?, "There is atleast one ship standing, but board says they're sunk") 69 | for i in @p1_perf_atk 70 | refute(test_board.all_sunk?, "All the ships have not sunk but board thinks they have") 71 | assert(test_board.attack_pos(i), "Attack that should hit did not") 72 | end 73 | assert(test_board.all_sunk?, "All the ships have sunk but board thinks they have not") 74 | assert_equal(@p1_perf_atk.length, test_board.num_successful_attacks, "The successful attacks must be the same as the number of ship slots") 75 | end 76 | 77 | def test_public_test_controller_1 78 | # This test just reads a correct file 79 | # and performs some tests to check if 80 | # The file was read correctly 81 | 82 | # Property: Correct files, does not yield errors 83 | assert(read_ships_file(SHIPS_P1), "#{SHIPS_P1} Should read correctly") 84 | assert(read_ships_file(SHIPS_P2), "#{SHIPS_P2} Should read correctly") 85 | assert(read_attacks_file(PERF_ATK_P1), "#{PERF_ATK_P1} Should read correctly") 86 | assert(read_attacks_file(PERF_ATK_P1), "#{PERF_ATK_P1} Should read correctly") 87 | 88 | game = GameController.new(2) 89 | p1_brd = game.load_ships(SHIPS_P1, 1) 90 | p1_atk = game.load_attacks(PERF_ATK_P1, 1) 91 | p2_brd = game.load_ships(SHIPS_P2, 2) 92 | p2_atk = game.load_attacks(PERF_ATK_P2, 1) 93 | 94 | pos0 = Position.new(2,1) 95 | pos1 = Position.new(2,2) 96 | pos2 = Position.new(2,3) 97 | pos3 = Position.new(2,4) 98 | 99 | # Property: A structure read from a valid file and has the ships attacked 100 | # will register the attack and return the values according to guidelines 101 | ret = p1_brd.attack_pos(pos1) 102 | ret = p1_brd.attack_pos(pos2) && ret 103 | ret = p1_brd.attack_pos(pos3) && ret 104 | 105 | p1_brd.attack_pos(pos0) 106 | assert(ret, "A boat is expected to be attacked but is not") 107 | assert_equal(3, p1_brd.num_successful_attacks, "The number of hits needs to be correct") 108 | end 109 | 110 | def test_public_test_controller_2 111 | # This is a rudimentary test to check if the 112 | # Guide lines mentioned 113 | assert(read_ships_file(SHIPS_P1), "#{SHIPS_P1} Should read correctly") 114 | assert(read_ships_file(SHIPS_P2), "#{SHIPS_P2} Should read correctly") 115 | assert(read_attacks_file(ATTACK_P1 ), "#{ATTACK_P1} Should read correctly") 116 | assert(read_attacks_file(PERF_ATK_P2), "#{PERF_ATK_P2} Should read correctly") 117 | 118 | board_p1 = read_ships_file(SHIPS_P1) 119 | board_p2 = read_ships_file(SHIPS_P2) 120 | 121 | p1_moves = read_attacks_file(PERF_ATK_P2) 122 | p2_moves = read_attacks_file(ATTACK_P1) 123 | 124 | board_p2.attack_pos(p1_moves[0]) 125 | 126 | for i in 1..([p1_moves.length, p2_moves.length].min - 1) 127 | refute(board_p1.all_sunk? || board_p2.all_sunk?, "The game is over too early") 128 | board_p2.attack_pos(p1_moves[i]) 129 | board_p1.attack_pos(p2_moves[i]) 130 | end 131 | 132 | assert(board_p2.all_sunk?, "P1 should have sunk all P2 Boats") 133 | refute(board_p1.all_sunk?, "P2 should not have sunk all P1 Boats") 134 | end 135 | 136 | def test_public_test_failure 137 | refute(read_ships_file(BAD_SHIPS), "#{BAD_SHIPS} Should Not read correctly") 138 | end 139 | end 140 | 141 | -------------------------------------------------------------------------------- /project2a/.gitignore: -------------------------------------------------------------------------------- 1 | *.annot 2 | *.cmo 3 | *.cma 4 | *.cmi 5 | *.a 6 | *.o 7 | *.cmx 8 | *.cmxs 9 | *.cmxa 10 | 11 | # ocamlbuild working directory 12 | _build/ 13 | 14 | # ocamlbuild targets 15 | *.byte 16 | *.native 17 | 18 | # oasis generated files 19 | setup.data 20 | setup.log 21 | 22 | # Merlin configuring file for Vim and Emacs 23 | .merlin 24 | 25 | # Dune generated files 26 | *.install 27 | 28 | # Local OPAM switch 29 | _opam/ 30 | -------------------------------------------------------------------------------- /project2a/.submit: -------------------------------------------------------------------------------- 1 | [course] 2 | id = 358171 3 | name = "CMSC330" 4 | term = "Spring 2022" 5 | 6 | [assignment] 7 | id = 1841722 8 | name = "Project 2a - OCaml Basics" 9 | files = [ "src/basics.ml"] 10 | -------------------------------------------------------------------------------- /project2a/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.3) 2 | -------------------------------------------------------------------------------- /project2a/src/basics.ml: -------------------------------------------------------------------------------- 1 | (***********************************) 2 | (* Part 1: Non-Recursive Functions *) 3 | (***********************************) 4 | 5 | let rev_tup tup = failwith "unimplemented" 6 | 7 | let is_odd x = failwith "unimplemented" 8 | 9 | let area x y = failwith "unimplemented" 10 | 11 | let volume x y = failwith "unimplemented" 12 | 13 | (*******************************) 14 | (* Part 2: Recursive Functions *) 15 | (*******************************) 16 | 17 | let rec fibonacci n = failwith "unimplemented" 18 | 19 | let rec pow x y = failwith "unimplemented" 20 | 21 | let rec log x y = failwith "unimplemented" 22 | 23 | let rec gcf x y = failwith "unimplemented" 24 | 25 | let rec is_prime x = failwith "unimplemented" 26 | 27 | (*****************) 28 | (* Part 3: Lists *) 29 | (*****************) 30 | 31 | let rec get idx lst = failwith "unimplemented" 32 | 33 | let larger lst1 lst2 = failwith "unimplemented" 34 | 35 | let reverse lst = failwith "unimplemented" 36 | 37 | let rec combine lst1 lst2 = failwith "unimplemented" 38 | 39 | let rec merge lst1 lst2 = failwith "unimplemented" 40 | 41 | let rec rotate shift lst = failwith "unimplemented" 42 | 43 | let rec is_palindrome lst = failwith "unimplemented" -------------------------------------------------------------------------------- /project2a/src/basics.mli: -------------------------------------------------------------------------------- 1 | val rev_tup : 'a * 'b * 'c -> 'c * 'b * 'a 2 | val is_odd : int -> bool 3 | val area : int * int -> int * int -> int 4 | val volume : int * int * int -> int * int * int -> int 5 | val fibonacci : int -> int 6 | val pow : int -> int -> int 7 | val log : int -> int -> int 8 | val gcf : int -> int -> int 9 | val is_prime : int -> bool 10 | val get : int -> 'a list -> 'a 11 | val larger : 'a list -> 'a list -> 'a list 12 | val reverse : 'a list -> 'a list 13 | val combine : 'a list -> 'a list -> 'a list 14 | val merge : 'a list -> 'a list -> 'a list 15 | val rotate : int -> 'a list -> 'a list 16 | val is_palindrome : 'a list -> bool 17 | -------------------------------------------------------------------------------- /project2a/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name basics)) 3 | (env 4 | (dev 5 | (flags (:standard -w -27-39)))) 6 | -------------------------------------------------------------------------------- /project2a/test/property-based-test/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names pbt) 3 | (libraries basics oUnit qcheck)) 4 | -------------------------------------------------------------------------------- /project2a/test/public/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names public) 3 | (libraries basics oUnit)) 4 | -------------------------------------------------------------------------------- /project2a/test/public/public.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | open Basics 3 | 4 | let test_rev_tup _ = 5 | assert_equal (1, 2, 3) (rev_tup (3, 2, 1)) ~msg:"rev_tup (1)"; 6 | assert_equal (3, 2, 1) (rev_tup (1, 2, 3)) ~msg:"rev_tup (2)"; 7 | assert_equal (3, 1, 1) (rev_tup (1, 1, 3)) ~msg:"rev_tup (3)"; 8 | assert_equal (1, 1, 1) (rev_tup (1, 1, 1)) ~msg:"rev_tup (4)" 9 | 10 | let test_is_odd _ = 11 | assert_equal true (is_odd 1) ~msg:"is_odd (1)"; 12 | assert_equal true (is_odd (-1)) ~msg:"is_odd (2)"; 13 | assert_equal false (is_odd 12) ~msg:"is_odd (3)"; 14 | assert_equal false (is_odd 0) ~msg:"is_odd (4)" 15 | 16 | let test_area _ = 17 | assert_equal 1 (area (1, 1) (2, 2)) ~msg:"area (1)"; 18 | assert_equal 2 (area (1, 1) (2, 3)) ~msg:"area (2)"; 19 | assert_equal 2 (area (1, 1) (3, 2)) ~msg:"area (3)"; 20 | assert_equal 4 (area (1, 1) (3, 3)) ~msg:"area (4)" 21 | 22 | let test_volume _ = 23 | assert_equal 1 (volume (1, 1, 1) (2, 2, 2)) ~msg:"volume (1)"; 24 | assert_equal 4 (volume (1, 1, 1) (2, 3, 3)) ~msg:"volume (2)"; 25 | assert_equal 4 (volume (1, 1, 1) (3, 2, 3)) ~msg:"volume (3)"; 26 | assert_equal 4 (volume (1, 1, 1) (3, 3, 2)) ~msg:"volume (4)"; 27 | assert_equal 8 (volume (1, 1, 1) (3, 3, 3)) ~msg:"volume (5)" 28 | 29 | let test_fibonacci _ = 30 | assert_equal 1 (fibonacci 1) ~msg:"fibonacci (1)"; 31 | assert_equal 1 (fibonacci 2) ~msg:"fibonacci (2)"; 32 | assert_equal 8 (fibonacci 6) ~msg:"fibonacci (3)"; 33 | assert_equal 144 (fibonacci 12) ~msg:"fibonacci (4)" 34 | 35 | let test_pow _ = 36 | assert_equal 2 (pow 2 1) ~msg:"pow (1)"; 37 | assert_equal 4 (pow 2 2) ~msg:"pow (2)"; 38 | assert_equal 3 (pow 3 1) ~msg:"pow (3)"; 39 | assert_equal 27 (pow 3 3) ~msg:"pow (4)"; 40 | assert_equal 625 (pow 5 4) ~msg:"pow (5)"; 41 | assert_equal (-27) (pow (-3) 3) ~msg:"pow (6)" 42 | 43 | let test_log _ = 44 | assert_equal 1 (log 4 4) ~msg:"log (1)"; 45 | assert_equal 2 (log 4 16) ~msg:"log (2)"; 46 | assert_equal 1 (log 4 15) ~msg:"log (3)"; 47 | assert_equal 3 (log 4 64) ~msg:"log (4)" 48 | 49 | let test_gcf _ = 50 | assert_equal 0 (gcf 0 0) ~msg:"gcf (1)"; 51 | assert_equal 3 (gcf 3 0) ~msg:"gcf (2)"; 52 | assert_equal 4 (gcf 12 8) ~msg:"gcf (3)"; 53 | assert_equal 6 (gcf 24 6) ~msg:"gcf (4)"; 54 | assert_equal 1 (gcf 27 10) ~msg:"gcf (3)"; 55 | assert_equal 13 (gcf 13 13) ~msg:"gcf (4)"; 56 | assert_equal 32 (gcf 128 96) ~msg:"gcf (5)" 57 | 58 | let test_is_prime _ = 59 | assert_equal false (is_prime 1) ~msg:"is_prime (1)"; 60 | assert_equal true (is_prime 2) ~msg:"is_prime (2)"; 61 | assert_equal true (is_prime 3) ~msg:"is_prime (3)"; 62 | assert_equal false (is_prime 4) ~msg:"is_prime (4)"; 63 | assert_equal true (is_prime 5) ~msg:"is_prime (5)"; 64 | assert_equal false (is_prime 60) ~msg:"is_prime (6)"; 65 | assert_equal true (is_prime 61) ~msg:"is_prime (7)" 66 | 67 | let test_get _ = 68 | assert_equal 26 (get 0 [26; 11; 99]) ~msg:"get (1)"; 69 | assert_equal 11 (get 1 [26; 11; 99]) ~msg:"get (2)"; 70 | assert_equal 99 (get 2 [26; 11; 99]) ~msg:"get (3)"; 71 | assert_raises (Failure ("Out of bounds")) (fun () -> get 3 [26; 11; 99]) ~msg:"get (4)" 72 | 73 | let test_larger _ = 74 | assert_equal [1; 2; 3] (larger [1; 2; 3] [5; 6]) ~msg:"larger (1)"; 75 | assert_equal [1; 2; 3] (larger [5; 6] [1; 2; 3]) ~msg:"larger (2)"; 76 | assert_equal [1; 2; 3] (larger [] [1; 2; 3]) ~msg:"larger (3)"; 77 | assert_equal [1; 2; 3] (larger [1; 2; 3] []) ~msg:"larger (4)"; 78 | assert_equal [1] (larger [1] []) ~msg:"larger (5)" 79 | 80 | let test_reverse _ = 81 | assert_equal [1] (reverse [1]) ~msg:"reverse (1)"; 82 | assert_equal [3; 2; 1] (reverse [1; 2; 3]) ~msg:"reverse (2)" 83 | 84 | let test_combine _ = 85 | assert_equal [1; 2] (combine [1] [2]) ~msg:"combine (1)"; 86 | assert_equal [1; 2; 3] (combine [1] [2; 3]) ~msg:"combine (2)"; 87 | assert_equal [1; 2; 3] (combine [1; 2] [3]) ~msg:"combine (3)"; 88 | assert_equal [1; 2; 3; 4] (combine [1; 2] [3; 4]) ~msg:"combine (4)" 89 | 90 | let test_merge _ = 91 | assert_equal [1; 2] (merge [1] [2]) ~msg:"merge (1)"; 92 | assert_equal [] (merge [] []) ~msg:"merge (2)"; 93 | assert_equal [1; 2; 3; 4] (merge [1; 4] [2; 3]) ~msg:"merge (3)"; 94 | assert_equal [0; 1] (merge [1] [0]) ~msg:"merge (4)" 95 | 96 | let test_is_palindrome _ = 97 | assert_equal true (is_palindrome [1; 2; 3; 2; 1]) ~msg:"is_palindrome (1)"; 98 | assert_equal true (is_palindrome ["a"; "n"; "n"; "a"]) ~msg:"is_palindrome (2)"; 99 | assert_equal false (is_palindrome ["N"; "o"; "o"; "n"]) ~msg:"is_palindrome (3)"; 100 | assert_equal false (is_palindrome ["O"; "C"; "A"; "M"; "L"]) ~msg:"is_palindrome (4)" 101 | 102 | let suite = 103 | "public" >::: [ 104 | "rev_tup" >:: test_rev_tup; 105 | "is_odd" >:: test_is_odd; 106 | "area" >:: test_area; 107 | "volume" >:: test_volume; 108 | "fibonacci" >:: test_fibonacci; 109 | "pow" >:: test_pow; 110 | "log" >:: test_log; 111 | "gcf" >:: test_gcf; 112 | "is_prime" >:: test_is_prime; 113 | "get" >:: test_get; 114 | "larger" >:: test_larger; 115 | "reverse" >:: test_reverse; 116 | "combine" >:: test_combine; 117 | "merge" >:: test_merge; 118 | "is_palindrome" >:: test_is_palindrome 119 | ] 120 | 121 | let _ = run_test_tt_main suite 122 | -------------------------------------------------------------------------------- /project2a/test/student/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names student) 3 | (libraries basics oUnit)) 4 | (env 5 | (dev 6 | (flags (:standard -w -33)))) 7 | -------------------------------------------------------------------------------- /project2a/test/student/student.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | open Basics 3 | 4 | let test_sanity _ = 5 | assert_equal 1 1 ~msg:"Custom error message" 6 | 7 | let suite = 8 | "student" >::: [ 9 | "sanity" >:: test_sanity 10 | ] 11 | 12 | let _ = run_test_tt_main suite 13 | -------------------------------------------------------------------------------- /project2b/.ocamlinit: -------------------------------------------------------------------------------- 1 | open P2b 2 | open Funs 3 | open Data 4 | open Higher 5 | -------------------------------------------------------------------------------- /project2b/.submit: -------------------------------------------------------------------------------- 1 | [course] 2 | id = 358171 3 | name = "CMSC330" 4 | term = "Spring 2022" 5 | 6 | [assignment] 7 | id = 1867851 8 | name = "Project 2b - OCaml Higher Order Functions and Data" 9 | files = [ "src/data.ml", "src/higher.ml" ] -------------------------------------------------------------------------------- /project2b/3WST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/project2b/3WST.png -------------------------------------------------------------------------------- /project2b/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.3) 2 | -------------------------------------------------------------------------------- /project2b/src/data.ml: -------------------------------------------------------------------------------- 1 | open Funs 2 | 3 | (*************************************) 4 | (* Part 2: Three-Way Search Tree *) 5 | (*************************************) 6 | 7 | type int_tree = 8 | | IntLeaf 9 | | IntNode of int * int option * int_tree * int_tree * int_tree 10 | 11 | let empty_int_tree = IntLeaf 12 | 13 | let rec int_insert x t = 14 | failwith "unimplemented" 15 | 16 | let rec int_mem x t = 17 | failwith "unimplemented" 18 | 19 | let rec int_size t = 20 | failwith "unimplemented" 21 | 22 | let rec int_max t = 23 | failwith "unimplemented" 24 | 25 | (*******************************) 26 | (* Part 3: Three-Way Search Tree-Based Map *) 27 | (*******************************) 28 | 29 | type 'a tree_map = 30 | | MapLeaf 31 | | MapNode of (int * 'a) * (int * 'a) option * 'a tree_map * 'a tree_map * 'a tree_map 32 | 33 | let empty_tree_map = MapLeaf 34 | 35 | let rec map_put k v t = 36 | failwith "unimplemented" 37 | 38 | let rec map_contains k t = 39 | failwith "unimplemented" 40 | 41 | let rec map_get k t = 42 | failwith "unimplemented" 43 | 44 | (***************************) 45 | (* Part 4: Variable Lookup *) 46 | (***************************) 47 | 48 | (* Modify the next line to your intended type *) 49 | type lookup_table = unit 50 | 51 | let empty_table : lookup_table = () 52 | 53 | let push_scope (table : lookup_table) : lookup_table = 54 | failwith "unimplemented" 55 | 56 | let pop_scope (table : lookup_table) : lookup_table = 57 | failwith "unimplemented" 58 | 59 | let add_var name value (table : lookup_table) : lookup_table = 60 | failwith "unimplemented" 61 | 62 | let rec lookup name (table : lookup_table) = 63 | failwith "unimplemented" -------------------------------------------------------------------------------- /project2b/src/data.mli: -------------------------------------------------------------------------------- 1 | type int_tree = 2 | | IntLeaf 3 | | IntNode of int * int option * int_tree * int_tree * int_tree 4 | val empty_int_tree: int_tree 5 | val int_insert: int -> int_tree -> int_tree 6 | val int_mem: int -> int_tree -> bool 7 | val int_size: int_tree -> int 8 | val int_max: int_tree -> int 9 | 10 | type 'a tree_map = 11 | | MapLeaf 12 | | MapNode of (int * 'a) * (int * 'a) option * 'a tree_map * 'a tree_map * 'a tree_map 13 | val empty_tree_map: 'a tree_map 14 | val map_put: int -> 'a -> 'a tree_map -> 'a tree_map 15 | val map_contains: int -> 'a tree_map -> bool 16 | val map_get: int -> 'a tree_map -> 'a 17 | 18 | type lookup_table 19 | val empty_table : lookup_table 20 | val push_scope : lookup_table -> lookup_table 21 | val pop_scope : lookup_table -> lookup_table 22 | val add_var : string -> int -> lookup_table -> lookup_table 23 | val lookup : string -> lookup_table -> int 24 | -------------------------------------------------------------------------------- /project2b/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name p2b) 3 | (modules higher data funs) 4 | (libraries oUnit)) 5 | (env 6 | (dev 7 | (flags (:standard -w -27-39-33-32)))) 8 | -------------------------------------------------------------------------------- /project2b/src/funs.ml: -------------------------------------------------------------------------------- 1 | let rec map f xs = match xs with 2 | | [] -> [] 3 | | x :: xt -> (f x)::(map f xt) 4 | 5 | let rec fold f a xs = match xs with 6 | | [] -> a 7 | | x :: xt -> fold f (f a x) xt 8 | 9 | let rec fold_right f xs a = match xs with 10 | | [] -> a 11 | | x :: xt -> f x (fold_right f xt a) 12 | 13 | let length xs = fold (fun a _ -> succ a) 0 xs 14 | 15 | let rev xs = fold (fun a x -> x :: a) [] xs 16 | -------------------------------------------------------------------------------- /project2b/src/funs.mli: -------------------------------------------------------------------------------- 1 | val map : ('a -> 'b) -> 'a list -> 'b list 2 | val fold : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a 3 | val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b 4 | val length : 'a list -> int 5 | val rev : 'a list -> 'a list 6 | -------------------------------------------------------------------------------- /project2b/src/higher.ml: -------------------------------------------------------------------------------- 1 | open Funs 2 | 3 | (********************************) 4 | (* Part 1: High Order Functions *) 5 | (********************************) 6 | 7 | let contains_elem lst e = failwith "unimplemented" 8 | 9 | let is_present lst x = failwith "unimplemented" 10 | 11 | let count_occ lst target = failwith "unimplemented" 12 | 13 | let uniq lst = failwith "unimplemented" 14 | 15 | let assoc_list lst = failwith "unimplemented" 16 | 17 | let ap fns args = failwith "unimplemented" 18 | -------------------------------------------------------------------------------- /project2b/src/higher.mli: -------------------------------------------------------------------------------- 1 | val contains_elem: 'a list -> 'a -> bool 2 | val is_present: 'a list -> 'a -> int list 3 | val count_occ : 'a list -> 'a -> int 4 | val uniq : 'a list -> 'a list 5 | val assoc_list : 'a list -> ('a * int) list 6 | val ap : ('a -> 'b) list -> 'a list -> 'b list 7 | -------------------------------------------------------------------------------- /project2b/test/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name testUtils) 3 | (libraries p2b oUnit)) 4 | -------------------------------------------------------------------------------- /project2b/test/pbt/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names pbt) 3 | (libraries p2b oUnit qcheck testUtils)) 4 | -------------------------------------------------------------------------------- /project2b/test/public/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names public) 3 | (libraries p2b oUnit testUtils)) 4 | -------------------------------------------------------------------------------- /project2b/test/student/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names student) 3 | (libraries p2b oUnit testUtils)) 4 | (env 5 | (dev 6 | (flags (:standard -w -33)))) 7 | -------------------------------------------------------------------------------- /project2b/test/student/student.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | open P2b.Data 3 | open P2b.Funs 4 | open P2b.Higher 5 | 6 | let test_sanity _ = 7 | assert_equal 1 1 8 | 9 | let suite = 10 | "student" >::: [ 11 | "sanity" >:: test_sanity 12 | ] 13 | 14 | let _ = run_test_tt_main suite 15 | -------------------------------------------------------------------------------- /project2b/test/testUtils.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | open P2b.Data 3 | 4 | let assert_true x = assert_equal true x 5 | let assert_false x = assert_equal false x 6 | 7 | let string_of_string s = s 8 | 9 | let string_of_list f xs = 10 | "[" ^ (String.concat "; " (List.map f xs)) ^ "]" 11 | 12 | let string_of_pair f g (x, y) = 13 | "(" ^ (f x) ^ ", " ^ (g y) ^ ")" 14 | 15 | let string_of_option f o = 16 | match o with 17 | |Some v -> (f v) 18 | |None -> "None" 19 | 20 | let string_of_int_pair = string_of_pair string_of_int string_of_int 21 | let string_of_string_int_pair = string_of_pair (fun x -> x) string_of_int 22 | let string_of_bool_int_pair = string_of_pair string_of_bool string_of_int 23 | let string_of_float_int_pair = string_of_pair string_of_float string_of_int 24 | 25 | let string_of_int_triple _ _ _ (x, y, z) = 26 | "(" ^ (string_of_int x) ^ ", " ^ (string_of_int y) ^ ", " ^ (string_of_int z) ^ ")" 27 | let string_of_int_quad (x, y, z, a) = 28 | "(" ^ (string_of_int x) ^ ", " ^ (string_of_int y) ^ ", " ^ (string_of_int z) ^ ", " ^ (string_of_int a) ^ ")" 29 | 30 | let string_of_int_list = string_of_list string_of_int 31 | let string_of_int_pair_list = string_of_list string_of_int_pair 32 | let string_of_bool_list = string_of_list string_of_bool 33 | let string_of_float_list = string_of_list string_of_float 34 | let string_of_bool_int_pair_list = string_of_list string_of_bool_int_pair 35 | let string_of_string_int_pair_list = string_of_list string_of_string_int_pair 36 | let string_of_float_int_pair_list = string_of_list string_of_float_int_pair 37 | let string_of_string_list = string_of_list (fun x -> x) 38 | let string_of_string_list_list = (string_of_list (string_of_list (fun x -> x))) 39 | let string_of_string_option = string_of_option (fun x -> x) 40 | let string_of_string_list_option = string_of_option string_of_string_list 41 | 42 | let rec list_of_int_tree t = 43 | match t with 44 | |IntLeaf -> [] 45 | |IntNode (f, Some s, l, m, r) -> (list_of_int_tree l) @ [f] @ (list_of_int_tree m) @ [s] @ (list_of_int_tree r) 46 | |IntNode (f, None, l, m, r) -> (list_of_int_tree l) @ [f] @ (list_of_int_tree m) @ (list_of_int_tree r) 47 | (*|_ -> failwith "Empty node found"*) 48 | 49 | let rec list_of_tree_map_keys m = 50 | match m with 51 | | MapLeaf -> [] 52 | | MapNode ((k, _), None, l, m, r) -> (list_of_tree_map_keys l) @ [k] @ (list_of_tree_map_keys m) @ (list_of_tree_map_keys r) 53 | | MapNode ((k1, _), Some (k2, _), l, m, r) -> (list_of_tree_map_keys l) @ [k1] @ (list_of_tree_map_keys m) @ [k2] @ (list_of_tree_map_keys r) 54 | (* 55 | | MapNode ((, Some (_, _)), _, _, _) -> failwith "Unbalanced tree_map node @ tree_map_keys" 56 | | MapNode ((None, None), _, _, _) -> failwith "Empty tree_map node @ tree_map_keys" 57 | *) 58 | 59 | let rec list_of_tree_map_values m = 60 | match m with 61 | | MapLeaf -> [] 62 | | MapNode ((_, v), None, l, m, r) -> (list_of_tree_map_values l) @ [v] @ (list_of_tree_map_values m) @ (list_of_tree_map_values r) 63 | | MapNode ((_, v1), Some (_, v2), l, m, r) -> (list_of_tree_map_values l) @ [v1] @ (list_of_tree_map_values m) @ [v2] @ (list_of_tree_map_values r) 64 | (* | MapNode ((None, Some (_, _)), _, _, _) -> failwith "Unbalanced tree_map node @ tree_map_values" 65 | | MapNode ((None, None), _, _, _) -> failwith "Empty tree_map node @ tree_map_values" 66 | *) 67 | -------------------------------------------------------------------------------- /project3/.ocamlinit: -------------------------------------------------------------------------------- 1 | open P3.Nfa 2 | open P3.Regexp 3 | -------------------------------------------------------------------------------- /project3/.submit: -------------------------------------------------------------------------------- 1 | [course] 2 | id = 358171 3 | name = "CMSC330" 4 | term = "Spring 2022" 5 | 6 | [assignment] 7 | id = 1913926 8 | name = "Project 3 - Regular Expression Engine" 9 | files = [ "src/nfa.ml", "src/regexp.ml" ] 10 | -------------------------------------------------------------------------------- /project3/SETS.md: -------------------------------------------------------------------------------- 1 | # Sets module 2 | 3 | ## `elem x a` 4 | 5 | - Type: `'a -> 'a list -> bool` 6 | - Description: Returns true iff `x` is an element of the set `a`. 7 | - Examples: 8 | ```ocaml 9 | elem 2 [] = false 10 | elem 3 (insert 5 (insert 3 (insert 2 []))) = true 11 | elem 4 (insert 3 (insert 2 (insert 5 []))) = false 12 | ``` 13 | 14 | ## `insert x a` 15 | 16 | - Type: `'a -> 'a list -> 'a list` 17 | - Description: Inserts `x` into the set `a`. 18 | - Examples: 19 | ```ocaml 20 | insert 2 [] = [2] 21 | insert 3 (insert 2 []) = [3; 2] 22 | insert 3 (insert 3 (insert 2 [])) = [3; 2] 23 | ``` 24 | 25 | ## `insert_all xs a` 26 | 27 | - Type: `'a list -> 'a list -> 'a list` 28 | - Description: Inserts each element from `xs` into the set `a`. 29 | - Examples: 30 | ```ocaml 31 | insert_all [2; 3; 3] [] = [2; 3] 32 | insert_all [1; 2; 3] [4; 5; 6] = [1; 2; 3; 4; 5; 6] 33 | ``` 34 | 35 | ## `subset a b` 36 | 37 | - Type: `'a list -> 'a list -> bool` 38 | - Description: Return true iff `a` **is a** subset of `b`. Formally, A ⊆ B ⇔ ∀x(xϵA ⇒ xϵB). 39 | - Examples: 40 | ```ocaml 41 | subset (insert 2 (insert 4 [])) [] = false 42 | subset (insert 5 (insert 3 [])) (insert 3 (insert 5 (insert 2 []))) = true 43 | subset (insert 5 (insert 3 (insert 2 []))) (insert 5 (insert 3 [])) = false 44 | ``` 45 | 46 | ## `eq a b` 47 | 48 | - Type: `'a list -> 'a list -> bool` 49 | - Description: Returns true iff `a` and `b` are equal as sets. Formally, A = B ⇔ ∀x(xϵA ⇔ xϵB). (Hint: The subset relation is anti-symmetric.) 50 | - Examples: 51 | ```ocaml 52 | eq [] (insert 2 []) = false 53 | eq (insert 2 (insert 3 [])) (insert 3 []) = false 54 | eq (insert 3 (insert 2 [])) (insert 2 (insert 3 [])) = true 55 | ``` 56 | 57 | ## `remove x a` 58 | 59 | - Type: `'a -> 'a list -> 'a list` 60 | - Description: Removes `x` from the set `a`. 61 | - Examples: 62 | ```ocaml 63 | elem 3 (remove 3 (insert 2 (insert 3 []))) = false 64 | eq (remove 3 (insert 5 (insert 3 []))) (insert 5 []) = true 65 | ``` 66 | 67 | ## `diff a b` 68 | 69 | - Type: `'a list -> 'a list -> 'a list` 70 | - Description: Subtracts the set `b` from the set `a`. 71 | - Examples: 72 | ```ocaml 73 | diff [1; 2; 3] [1; 2; 3] = [] 74 | diff [1; 2; 3] [1; 4; 5] = [2; 3] 75 | diff [1; 2; 3] [4; 5; 6] = [1; 2; 3] 76 | ``` 77 | 78 | ## `union a b` 79 | 80 | - Type: `'a list -> 'a list -> 'a list` 81 | - Description: Returns the union of the sets `a` and `b`. Formally, A ∪ B = {x | xϵA ∨ xϵB}. 82 | - Examples: 83 | ```ocaml 84 | eq (union [] (insert 2 (insert 3 []))) (insert 3 (insert 2 [])) = true 85 | eq (union (insert 5 (insert 2 [])) (insert 2 (insert 3 []))) (insert 3 (insert 2 (insert 5 []))) = true 86 | eq (union (insert 2 (insert 7 [])) (insert 5 [])) (insert 5 (insert 7 (insert 2 []))) = true 87 | ``` 88 | 89 | ## `intersection a b` 90 | 91 | - Type: `'a list -> 'a list -> 'a list` 92 | - Description: Returns the intersection of sets `a` and `b`. Formally, A ∩ B = {x | xϵA ∧ xϵB}. 93 | - Examples: 94 | ```ocaml 95 | eq (intersection (insert 3 (insert 5 (insert 2 []))) []) [] = true 96 | eq (intersection (insert 5 (insert 7 (insert 3 (insert 2 [])))) (insert 6 (insert 4 []))) [] = true 97 | eq (intersection (insert 5 (insert 2 [])) (insert 4 (insert 3 (insert 5 [])))) (insert 5 []) = true 98 | ``` 99 | 100 | ## `cat x a` 101 | 102 | - Type: `'a -> 'b list -> ('a * 'b) list` 103 | - Description: Turns each element of `a` into a 2-tuple where the first element is `x`. 104 | - Examples: 105 | ```ocaml 106 | cat 1 [2; 3; 4] = [(1,2); (1,3); (1,4)] 107 | cat 3 [] = [] 108 | cat "hi" [1; 2] = [("hi", 1); ("hi", 2)] 109 | ``` 110 | -------------------------------------------------------------------------------- /project3/bin/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name viz) 3 | (libraries str p3) 4 | (modes byte exe)) 5 | -------------------------------------------------------------------------------- /project3/bin/viz.ml: -------------------------------------------------------------------------------- 1 | open P3.Nfa 2 | open P3.Regexp 3 | 4 | let string_of_int_list lst = 5 | "[" ^ String.concat ";" (List.map string_of_int lst) ^ "]" 6 | 7 | let string_of_int_list_list lst = 8 | "[" ^ String.concat ";" (List.map string_of_int_list lst) ^ "]" 9 | 10 | let init_str = 11 | "digraph G { \n rankdir=LR; " 12 | ^ string_of_int (Hashtbl.hash "-1") 13 | ^ " [style=\"invis\"]; \n" 14 | 15 | let end_str = "\n}" 16 | 17 | let nodup x lst = if List.mem x lst then lst else x :: lst 18 | 19 | let string_of_vtx _ lst = 20 | List.fold_left 21 | (fun acc (v, f) -> 22 | let shape = if f then "doublecircle" else "circle" in 23 | acc 24 | ^ Printf.sprintf "%d [label=\"%s\",shape=%s];\n" (Hashtbl.hash v) v shape 25 | ) 26 | "" lst 27 | 28 | let string_of_ed _ lst = 29 | List.fold_left 30 | (fun acc ((s1, _), c, _, (s2, _)) -> 31 | acc 32 | ^ Printf.sprintf "%d -> %d [label=\"%s\"];\n" (Hashtbl.hash s1) 33 | (Hashtbl.hash s2) c ) 34 | "" lst 35 | 36 | let write_nfa_to_graphviz (show : 'q -> string) (nfa : ('q, char) nfa_t) : bool 37 | = 38 | let name = "output.viz" in 39 | let ss, fs, ts = (nfa.q0, nfa.fs, nfa.delta) in 40 | let sv = (show ss, List.mem ss fs) in 41 | let vt, ed = 42 | List.fold_left 43 | (fun (vt, ed) (v1, c, v2) -> 44 | let v1' = (show v1, List.mem v1 fs) in 45 | let v2' = (show v2, List.mem v2 fs) in 46 | let c' = match c with None -> "ε" | Some x -> String.make 1 x in 47 | let pair = List.mem (v2, c, v1) ts in 48 | let e = (v1', c', pair, v2') in 49 | (nodup v2' (nodup v1' vt), nodup e ed) ) 50 | ([], []) ts 51 | in 52 | let ed = (("-1", false), " ", false, (show ss, List.mem ss fs)) :: ed in 53 | let dot = 54 | init_str ^ string_of_vtx show (sv :: vt) ^ string_of_ed show ed ^ end_str 55 | in 56 | let file = open_out_bin name in 57 | output_string file dot ; 58 | flush file ; 59 | Sys.command (Printf.sprintf "dot %s -Tpng -o output.png && rm %s" name name) 60 | = 0 61 | 62 | ;; 63 | print_string "Type regexp to visualize: " 64 | 65 | let line = read_line () 66 | 67 | ;; 68 | print_string "Convert to DFA (y/n)? " 69 | 70 | let line2 = read_line () 71 | 72 | let nfa = string_to_nfa line 73 | 74 | ;; 75 | if line2 = "n" then 76 | if write_nfa_to_graphviz string_of_int nfa then 77 | print_string "Success! Open 'output.png' to see your visualized NFA.\n" 78 | else 79 | print_string 80 | "Failure! Are you sure you have graphviz installed on your machine?\n" 81 | else if write_nfa_to_graphviz string_of_int_list (nfa_to_dfa nfa) then 82 | print_string "Success! Open 'output.png' to see your visualized DFA.\n" 83 | else 84 | print_string 85 | "Failure! Are you sure you have graphviz installed on your machine?\n" 86 | -------------------------------------------------------------------------------- /project3/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.3) 2 | -------------------------------------------------------------------------------- /project3/images/m_viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/project3/images/m_viz.png -------------------------------------------------------------------------------- /project3/images/n_viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/project3/images/n_viz.png -------------------------------------------------------------------------------- /project3/ocaml_version.sh: -------------------------------------------------------------------------------- 1 | OCAML_VERSION=$(ocaml --version | rev | cut -d' ' -f 1 | rev) 2 | if [ $OCAML_VERSION = '4.12.0' ] ; then 3 | export OCAMLPATH=dep 4 | else 5 | echo 'You must have OCaml version 4.12.0 for this project.' 6 | echo $OCAML_VERSION ' is not valid.' 7 | fi 8 | -------------------------------------------------------------------------------- /project3/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name p3) 3 | (modules nfa regexp sets) 4 | (libraries str)) 5 | (env 6 | (dev 7 | (flags (:standard -w -27-39-33-32)))) 8 | -------------------------------------------------------------------------------- /project3/src/nfa.ml: -------------------------------------------------------------------------------- 1 | open List 2 | open Sets 3 | 4 | (*********) 5 | (* Types *) 6 | (*********) 7 | 8 | type ('q, 's) transition = 'q * 's option * 'q 9 | 10 | type ('q, 's) nfa_t = { 11 | sigma: 's list; 12 | qs: 'q list; 13 | q0: 'q; 14 | fs: 'q list; 15 | delta: ('q, 's) transition list; 16 | } 17 | 18 | (***********) 19 | (* Utility *) 20 | (***********) 21 | 22 | (* explode converts a string to a character list *) 23 | let explode (s: string) : char list = 24 | let rec exp i l = 25 | if i < 0 then l else exp (i - 1) (s.[i] :: l) 26 | in 27 | exp (String.length s - 1) [] 28 | 29 | (****************) 30 | (* Part 1: NFAs *) 31 | (****************) 32 | 33 | let move (nfa: ('q,'s) nfa_t) (qs: 'q list) (s: 's option) : 'q list = 34 | failwith "unimplemented" 35 | 36 | let e_closure (nfa: ('q,'s) nfa_t) (qs: 'q list) : 'q list = 37 | failwith "unimplemented" 38 | 39 | let accept (nfa: ('q,char) nfa_t) (s: string) : bool = 40 | failwith "unimplemented" 41 | 42 | (*******************************) 43 | (* Part 2: Subset Construction *) 44 | (*******************************) 45 | 46 | let new_states (nfa: ('q,'s) nfa_t) (qs: 'q list) : 'q list list = 47 | failwith "unimplemented" 48 | 49 | let new_trans (nfa: ('q,'s) nfa_t) (qs: 'q list) : ('q list, 's) transition list = 50 | failwith "unimplemented" 51 | 52 | let new_finals (nfa: ('q,'s) nfa_t) (qs: 'q list) : 'q list list = 53 | failwith "unimplemented" 54 | 55 | let rec nfa_to_dfa_step (nfa: ('q,'s) nfa_t) (dfa: ('q list, 's) nfa_t) 56 | (work: 'q list list) : ('q list, 's) nfa_t = 57 | failwith "unimplemented" 58 | 59 | let nfa_to_dfa (nfa: ('q,'s) nfa_t) : ('q list, 's) nfa_t = 60 | failwith "unimplemented" 61 | -------------------------------------------------------------------------------- /project3/src/nfa.mli: -------------------------------------------------------------------------------- 1 | (* IMPORTANT: YOU MAY NOT MODIFY THIS FILE! 2 | * OUR TESTS USE THE ORIGINAL VERSION. 3 | * YOUR CODE WILL NOT COMPILE IF YOU CHANGE THIS FILE. *) 4 | 5 | (* Types *) 6 | 7 | type ('q, 's) transition = 'q * 's option * 'q 8 | 9 | type ('q, 's) nfa_t = 10 | { sigma: 's list 11 | ; qs: 'q list 12 | ; q0: 'q 13 | ; fs: 'q list 14 | ; delta: ('q, 's) transition list } 15 | 16 | (* Part 1 *) 17 | 18 | val e_closure : ('q, 's) nfa_t -> 'q list -> 'q list 19 | 20 | val move : ('q, 's) nfa_t -> 'q list -> 's option -> 'q list 21 | 22 | val accept : ('q, char) nfa_t -> string -> bool 23 | 24 | (* Part 2 *) 25 | 26 | val new_states : ('q, 's) nfa_t -> 'q list -> 'q list list 27 | 28 | val new_trans : ('q, 's) nfa_t -> 'q list -> ('q list, 's) transition list 29 | 30 | val new_finals : ('q, 's) nfa_t -> 'q list -> 'q list list 31 | 32 | val nfa_to_dfa_step : 33 | ('q, 's) nfa_t -> ('q list, 's) nfa_t -> 'q list list -> ('q list, 's) nfa_t 34 | 35 | val nfa_to_dfa : ('q, 's) nfa_t -> ('q list, 's) nfa_t 36 | -------------------------------------------------------------------------------- /project3/src/regexp.ml: -------------------------------------------------------------------------------- 1 | open List 2 | open Nfa 3 | 4 | (*********) 5 | (* Types *) 6 | (*********) 7 | 8 | type regexp_t = 9 | | Empty_String 10 | | Char of char 11 | | Union of regexp_t * regexp_t 12 | | Concat of regexp_t * regexp_t 13 | | Star of regexp_t 14 | 15 | (***********) 16 | (* Utility *) 17 | (***********) 18 | 19 | let fresh = 20 | let cntr = ref 0 in 21 | fun () -> 22 | cntr := !cntr + 1 ; 23 | !cntr 24 | 25 | (*******************************) 26 | (* Part 3: Regular Expressions *) 27 | (*******************************) 28 | 29 | let regexp_to_nfa (regexp: regexp_t) : (int, char) nfa_t = 30 | failwith "unimplemented" 31 | 32 | (*****************************************************************) 33 | (* Below this point is parser code that YOU DO NOT NEED TO TOUCH *) 34 | (*****************************************************************) 35 | 36 | exception IllegalExpression of string 37 | 38 | (* Scanner *) 39 | type token = 40 | | Tok_Char of char 41 | | Tok_Epsilon 42 | | Tok_Union 43 | | Tok_Star 44 | | Tok_LParen 45 | | Tok_RParen 46 | | Tok_END 47 | 48 | let tokenize str = 49 | let re_var = Str.regexp "[a-z]" in 50 | let re_epsilon = Str.regexp "E" in 51 | let re_union = Str.regexp "|" in 52 | let re_star = Str.regexp "*" in 53 | let re_lparen = Str.regexp "(" in 54 | let re_rparen = Str.regexp ")" in 55 | let rec tok pos s = 56 | if pos >= String.length s then [Tok_END] 57 | else if Str.string_match re_var s pos then 58 | let token = Str.matched_string s in 59 | Tok_Char token.[0] :: tok (pos + 1) s 60 | else if Str.string_match re_epsilon s pos then 61 | Tok_Epsilon :: tok (pos + 1) s 62 | else if Str.string_match re_union s pos then Tok_Union :: tok (pos + 1) s 63 | else if Str.string_match re_star s pos then Tok_Star :: tok (pos + 1) s 64 | else if Str.string_match re_lparen s pos then Tok_LParen :: tok (pos + 1) s 65 | else if Str.string_match re_rparen s pos then Tok_RParen :: tok (pos + 1) s 66 | else raise (IllegalExpression ("tokenize: " ^ s)) 67 | in 68 | tok 0 str 69 | 70 | let tok_to_str t = 71 | match t with 72 | | Tok_Char v -> Char.escaped v 73 | | Tok_Epsilon -> "E" 74 | | Tok_Union -> "|" 75 | | Tok_Star -> "*" 76 | | Tok_LParen -> "(" 77 | | Tok_RParen -> ")" 78 | | Tok_END -> "END" 79 | 80 | (* 81 | S -> A Tok_Union S | A 82 | A -> B A | B 83 | B -> C Tok_Star | C 84 | C -> Tok_Char | Tok_Epsilon | Tok_LParen S Tok_RParen 85 | 86 | FIRST(S) = Tok_Char | Tok_Epsilon | Tok_LParen 87 | FIRST(A) = Tok_Char | Tok_Epsilon | Tok_LParen 88 | FIRST(B) = Tok_Char | Tok_Epsilon | Tok_LParen 89 | FIRST(C) = Tok_Char | Tok_Epsilon | Tok_LParen 90 | *) 91 | 92 | let parse_regexp (l : token list) = 93 | let lookahead tok_list = 94 | match tok_list with 95 | | [] -> raise (IllegalExpression "lookahead") 96 | | h :: t -> (h, t) 97 | in 98 | let rec parse_S l = 99 | let a1, l1 = parse_A l in 100 | let t, n = lookahead l1 in 101 | match t with 102 | | Tok_Union -> 103 | let a2, l2 = parse_S n in 104 | (Union (a1, a2), l2) 105 | | _ -> (a1, l1) 106 | and parse_A l = 107 | let a1, l1 = parse_B l in 108 | let t, n = lookahead l1 in 109 | match t with 110 | | Tok_Char c -> 111 | let a2, l2 = parse_A l1 in 112 | (Concat (a1, a2), l2) 113 | | Tok_Epsilon -> 114 | let a2, l2 = parse_A l1 in 115 | (Concat (a1, a2), l2) 116 | | Tok_LParen -> 117 | let a2, l2 = parse_A l1 in 118 | (Concat (a1, a2), l2) 119 | | _ -> (a1, l1) 120 | and parse_B l = 121 | let a1, l1 = parse_C l in 122 | let t, n = lookahead l1 in 123 | match t with Tok_Star -> (Star a1, n) | _ -> (a1, l1) 124 | and parse_C l = 125 | let t, n = lookahead l in 126 | match t with 127 | | Tok_Char c -> (Char c, n) 128 | | Tok_Epsilon -> (Empty_String, n) 129 | | Tok_LParen -> 130 | let a1, l1 = parse_S n in 131 | let t2, n2 = lookahead l1 in 132 | if t2 = Tok_RParen then (a1, n2) 133 | else raise (IllegalExpression "parse_C 1") 134 | | _ -> raise (IllegalExpression "parse_C 2") 135 | in 136 | let rxp, toks = parse_S l in 137 | match toks with 138 | | [Tok_END] -> rxp 139 | | _ -> raise (IllegalExpression "parse didn't consume all tokens") 140 | 141 | 142 | let string_to_regexp str = parse_regexp @@ tokenize str 143 | 144 | let string_to_nfa str = regexp_to_nfa @@ string_to_regexp str 145 | -------------------------------------------------------------------------------- /project3/src/regexp.mli: -------------------------------------------------------------------------------- 1 | (* IMPORTANT: YOU MAY NOT MODIFY THIS FILE! 2 | * OUR TESTS USE THE ORIGINAL VERSION. 3 | * YOUR CODE WILL NOT COMPILE IF YOU CHANGE THIS FILE. *) 4 | 5 | (* This is the type used to describe the form of a regexp *) 6 | 7 | type regexp_t = 8 | | Empty_String 9 | | Char of char 10 | | Union of regexp_t * regexp_t 11 | | Concat of regexp_t * regexp_t 12 | | Star of regexp_t 13 | 14 | (* These are the regexp functions you must implement *) 15 | 16 | val regexp_to_nfa : regexp_t -> (int, char) Nfa.nfa_t 17 | 18 | val string_to_regexp : string -> regexp_t 19 | 20 | val string_to_nfa : string -> (int, char) Nfa.nfa_t 21 | 22 | exception IllegalExpression of string 23 | -------------------------------------------------------------------------------- /project3/src/sets.ml: -------------------------------------------------------------------------------- 1 | (* Sets Implementation for CMSC 330 Project 3 2 | * Last updated: 8 March 2022 3 | * 4 | * Refer to SETS.md for documentation and do not modify this file. 5 | *) 6 | 7 | let rec elem x a = 8 | match a with 9 | | h::t -> (h = x) || (elem x t) 10 | | [] -> false 11 | 12 | let rec insert x a = 13 | if not (elem x a) then x::a else a 14 | 15 | let insert_all xs a = 16 | List.fold_right insert xs a 17 | 18 | let rec subset a b = 19 | match a with 20 | | h::t -> (elem h b) && (subset t b) 21 | | [] -> true 22 | 23 | let rec eq a b = (subset a b) && (subset b a) 24 | 25 | let rec remove x a = 26 | match a with 27 | | h::t -> if h = x then t else h::(remove x t) 28 | | [] -> [] 29 | 30 | let rec diff a b = 31 | match b with 32 | | [] -> a 33 | | h::t -> diff (remove h a) t 34 | 35 | (* Wrapper for old P3 *) 36 | let rec minus a b = diff a b 37 | 38 | let rec union a b = 39 | match a with 40 | | h::t -> insert h (union t b) 41 | | [] -> 42 | (match b with 43 | | h::t -> insert h (union [] t) 44 | | [] -> []) 45 | 46 | let rec intersection a b = 47 | match a with 48 | | h::t -> if elem h b then insert h (intersection t b) else (intersection t b) 49 | | [] -> [] 50 | 51 | let rec product a b = 52 | let rec product_help x b = 53 | match b with 54 | | h::t -> insert (x, h) (product_help x t) 55 | | [] -> [] in 56 | match a with 57 | | h::t -> union (product_help h b) (product t b) 58 | | [] -> [] 59 | 60 | let rec cat x a = 61 | match a with 62 | | [] -> [] 63 | | h::t -> (x,h)::(cat x t) -------------------------------------------------------------------------------- /project3/src/sets.mli: -------------------------------------------------------------------------------- 1 | val elem : 'a -> 'a list -> bool 2 | val insert : 'a -> 'a list -> 'a list 3 | val insert_all : 'a list -> 'a list -> 'a list 4 | val subset : 'a list -> 'a list -> bool 5 | val eq : 'a list -> 'a list -> bool 6 | val remove : 'a -> 'a list -> 'a list 7 | val minus : 'a list -> 'a list -> 'a list 8 | val union : 'a list -> 'a list -> 'a list 9 | val intersection : 'a list -> 'a list -> 'a list 10 | val product : 'a list -> 'b list -> ('a * 'b) list 11 | val diff : 'a list -> 'a list -> 'a list 12 | val cat : 'a -> 'b list -> ('a * 'b) list 13 | -------------------------------------------------------------------------------- /project3/test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | . ./ocaml_version.sh 3 | dune runtest -f 4 | -------------------------------------------------------------------------------- /project3/test/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name testUtils) 3 | (libraries str p3 oUnit)) 4 | -------------------------------------------------------------------------------- /project3/test/pbt/#pbt.ml#: -------------------------------------------------------------------------------- 1 | (* 2 | Property based tests for Regular expression and NFA 3 | *) 4 | open QCheck 5 | open P3.Nfa 6 | open P3.Regexp 7 | open TestUtils 8 | 9 | let epsilon _x = Empty_String 10 | let symbol x = Char x 11 | let union x y = Union (x,y) 12 | let concat x y = if x = Empty_String then y 13 | else if y = Empty_String then x 14 | else Concat (x,y) 15 | let star x = Star x 16 | 17 | (* Generate a regex on dept n *) 18 | 19 | let rec regex_gen n = 20 | let open Gen in 21 | match n with 22 | 0 ->frequency [(1,map epsilon char);(9, map symbol (char_range 'a' 'z'))] 23 | |_ -> oneof [ 24 | map2 union (regex_gen (n-1)) (regex_gen (n-1)); 25 | map2 concat (regex_gen (n-1)) (regex_gen (n-1)); 26 | map star (regex_gen (n-1)) 27 | ] 28 | 29 | (* generate a string that the given regex recognizes *) 30 | 31 | let rec string_gen regex = 32 | let rec dup s n = if n <= 0 then s else s ^ (dup s (n-1)) in 33 | let open Gen in 34 | match regex with 35 | Empty_String -> return "" 36 | |Char x-> return (String.make 1 x) 37 | |Union (a,b)-> (oneof [string_gen a;string_gen b]) 38 | |Concat (a,b)-> (string_gen a) >>= fun x-> 39 | (string_gen b) >>= fun y -> 40 | return (x^y) 41 | |Star s -> (int_range 1 5) >>= fun n -> 42 | (string_gen s) >>= fun str -> 43 | return (dup str n) 44 | 45 | 46 | let arb_regex_string= 47 | make ( 48 | let open Gen in 49 | (regex_gen 5) >>= fun regex -> 50 | let str = string_gen regex in 51 | (*let () = Printf.printf "Regex: %s\n String: %s\n" (re_to_str regex) (generate1 str) in *) 52 | pair (return regex) str 53 | ) 54 | 55 | let print (a,b) = "Regex:" ^re_to_str a ^ "\nString:" ^ b 56 | let shrink (a,b) = (a, Shrink.string b) 57 | [O 58 | let arb_regex_string = set_print print arb_regex_string 59 | let arb_regex_string = set_shrink shrink arb_regex_string 60 | let test_regex_to_nfa_accept_pbt = 61 | Test.make 62 | ~name:"regex_to_nfa_accept" 63 | ~count:100 (* number of tests *) 64 | (arb_regex_string) (* a regex and string the regex recognizes *) 65 | (fun (regex, str) -> 66 | let nfa = regexp_to_nfa regex in 67 | let dfa = nfa_to_dfa nfa in 68 | accept dfa str 69 | ) 70 | ;; 71 | 72 | QCheck_runner.run_tests 73 | ~verbose:true 74 | (*?debug_shrink:(Some (Some Stdlib.stdout))*) 75 | [ 76 | test_regex_to_nfa_accept_pbt 77 | ] 78 | -------------------------------------------------------------------------------- /project3/test/pbt/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names pbt) 3 | (libraries qcheck str p3 oUnit testUtils)) 4 | -------------------------------------------------------------------------------- /project3/test/pbt/pbt.ml: -------------------------------------------------------------------------------- 1 | (* 2 | Property based tests for Regular expression and NFA 3 | *) 4 | open QCheck 5 | open P3.Nfa 6 | open P3.Regexp 7 | open TestUtils 8 | 9 | let epsilon _x = Empty_String 10 | let symbol x = Char x 11 | let union x y = Union (x,y) 12 | let concat x y = if x = Empty_String then y 13 | else if y = Empty_String then x 14 | else Concat (x,y) 15 | let star x = Star x 16 | 17 | (* Generate a regex on dept n *) 18 | 19 | let rec regex_gen n = 20 | let open Gen in 21 | match n with 22 | 0 ->frequency [(1,map epsilon char);(9, map symbol (char_range 'a' 'z'))] 23 | |_ -> oneof [ 24 | map2 union (regex_gen (n-1)) (regex_gen (n-1)); 25 | map2 concat (regex_gen (n-1)) (regex_gen (n-1)); 26 | map star (regex_gen (n-1)) 27 | ] 28 | 29 | (* generate a string that the given regex recognizes *) 30 | 31 | let rec string_gen regex = 32 | let rec dup s n = if n <= 0 then s else s ^ (dup s (n-1)) in 33 | let open Gen in 34 | match regex with 35 | Empty_String -> return "" 36 | |Char x-> return (String.make 1 x) 37 | |Union (a,b)-> (oneof [string_gen a;string_gen b]) 38 | |Concat (a,b)-> (string_gen a) >>= fun x-> 39 | (string_gen b) >>= fun y -> 40 | return (x^y) 41 | |Star s -> (int_range 1 5) >>= fun n -> 42 | (string_gen s) >>= fun str -> 43 | return (dup str n) 44 | 45 | 46 | let arb_regex_string= 47 | make ( 48 | let open Gen in 49 | (regex_gen 5) >>= fun regex -> 50 | let str = string_gen regex in 51 | (*let () = Printf.printf "Regex: %s\n String: %s\n" (re_to_str regex) (generate1 str) in *) 52 | pair (return regex) str 53 | ) 54 | 55 | let print (a,b) = "Regex:" ^re_to_str a ^ "\nString:" ^ b 56 | (*let shrink (a,b) = Iter.pair (Iter.return a) (Shrink.string b)*) 57 | 58 | let arb_regex_string = set_print print arb_regex_string 59 | (*let arb_regex_string = set_shrink shrink arb_regex_string*) 60 | 61 | let test_regex_to_nfa_accept_pbt = 62 | Test.make 63 | ~name:"regex_to_nfa_accept" 64 | ~count:100 (* number of tests *) 65 | (arb_regex_string) (* a regex and string the regex recognizes *) 66 | (fun (regex, str) -> 67 | let nfa = regexp_to_nfa regex in 68 | let dfa = nfa_to_dfa nfa in 69 | accept dfa str 70 | ) 71 | ;; 72 | 73 | QCheck_runner.run_tests 74 | ~verbose:true 75 | (*?debug_shrink:(Some (Some Stdlib.stdout))*) 76 | [ 77 | test_regex_to_nfa_accept_pbt 78 | ] 79 | -------------------------------------------------------------------------------- /project3/test/public/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names public) 3 | (libraries str p3 oUnit testUtils)) 4 | -------------------------------------------------------------------------------- /project3/test/public/public.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | open P3.Nfa 3 | open P3.Regexp 4 | open TestUtils 5 | 6 | let test_nfa_accept _ = 7 | let m1 = 8 | {qs= [0; 1]; sigma= ['a'; 'b']; delta= [(0, Some 'a', 1)]; q0= 0; fs= [1]} 9 | in 10 | assert_nfa_deny m1 "" ; 11 | assert_nfa_accept m1 "a" ; 12 | assert_nfa_deny m1 "b" ; 13 | assert_nfa_deny m1 "ba" ; 14 | let m2 = 15 | { qs= [0; 1; 2] 16 | ; sigma= ['a'; 'b'] 17 | ; delta= [(0, Some 'a', 1); (0, Some 'b', 2)] 18 | ; q0= 0 19 | ; fs= [2] } 20 | in 21 | assert_nfa_deny m2 "" ; 22 | assert_nfa_deny m2 "a" ; 23 | assert_nfa_accept m2 "b" ; 24 | assert_nfa_deny m2 "ba" 25 | 26 | let test_nfa_to_dfa _ = 27 | let m1 = 28 | { qs= [0; 1; 2; 3] 29 | ; sigma= ['a'; 'b'] 30 | ; delta= [(0, Some 'a', 1); (0, Some 'a', 2); (2, Some 'b', 3)] 31 | ; q0= 0 32 | ; fs= [1; 3] } 33 | in 34 | let m1' = nfa_to_dfa m1 in 35 | assert_dfa m1' ; 36 | assert_nfa_deny m1' "" ; 37 | assert_nfa_accept m1' "a" ; 38 | assert_nfa_accept m1' "ab" ; 39 | assert_nfa_deny m1' "b" ; 40 | assert_nfa_deny m1' "ba" ; 41 | let m2 = 42 | { qs= [0; 1; 2] 43 | ; sigma= ['a'; 'b'] 44 | ; delta= [(0, Some 'a', 1); (0, Some 'b', 2)] 45 | ; q0= 0 46 | ; fs= [2] } 47 | in 48 | let m2' = nfa_to_dfa m2 in 49 | assert_dfa m2' ; 50 | assert_nfa_deny m2' "" ; 51 | assert_nfa_deny m2' "a" ; 52 | assert_nfa_accept m2' "b" ; 53 | assert_nfa_deny m2' "ba" 54 | 55 | let test_nfa_closure _ = 56 | let m1 = 57 | {qs= [0; 1]; sigma= ['a']; delta= [(0, Some 'a', 1)]; q0= 0; fs= [1]} 58 | in 59 | assert_nfa_closure m1 [0] [0] ; 60 | assert_nfa_closure m1 [1] [1] ; 61 | let m2 = {qs= [0; 1]; sigma= []; q0= 0; delta= [(0, None, 1)]; fs= [1]} in 62 | assert_nfa_closure m2 [0] [0; 1] ; 63 | assert_nfa_closure m2 [1] [1] ; 64 | let m3 = 65 | { qs= [0; 1; 2] 66 | ; sigma= ['a'; 'b'] 67 | ; q0= 0 68 | ; fs= [2] 69 | ; delta= [(0, Some 'a', 1); (0, Some 'b', 2)] } 70 | in 71 | assert_nfa_closure m3 [0] [0] ; 72 | assert_nfa_closure m3 [1] [1] ; 73 | assert_nfa_closure m3 [2] [2] ; 74 | let m4 = 75 | { qs= [0; 1; 2] 76 | ; sigma= ['a'] 77 | ; q0= 0 78 | ; fs= [2] 79 | ; delta= [(0, None, 1); (0, None, 2)] } 80 | in 81 | assert_nfa_closure m4 [0] [0; 1; 2] ; 82 | assert_nfa_closure m4 [1] [1] ; 83 | assert_nfa_closure m4 [2] [2] 84 | 85 | let test_nfa_move _ = 86 | let m1 = 87 | {qs= [0; 1]; sigma= ['a']; delta= [(0, Some 'a', 1)]; q0= 0; fs= [1]} 88 | in 89 | assert_nfa_move m1 [0] (Some 'a') [1] ; 90 | assert_nfa_move m1 [1] (Some 'a') [] ; 91 | let m2 = {qs= [0; 1]; sigma= ['a']; delta= [(0, None, 1)]; q0= 0; fs= [1]} in 92 | assert_nfa_move m2 [0] (Some 'a') [] ; 93 | assert_nfa_move m2 [1] (Some 'a') [] ; 94 | let m3 = 95 | { qs= [0; 1; 2] 96 | ; sigma= ['a'; 'b'] 97 | ; q0= 0 98 | ; fs= [2] 99 | ; delta= [(0, Some 'a', 1); (0, Some 'b', 2)] } 100 | in 101 | assert_nfa_move m3 [0] (Some 'a') [1] ; 102 | assert_nfa_move m3 [1] (Some 'a') [] ; 103 | assert_nfa_move m3 [2] (Some 'a') [] ; 104 | assert_nfa_move m3 [0] (Some 'b') [2] ; 105 | assert_nfa_move m3 [1] (Some 'b') [] ; 106 | assert_nfa_move m3 [2] (Some 'b') [] ; 107 | let m4 = 108 | { qs= [0; 1; 2] 109 | ; sigma= ['a'; 'b'] 110 | ; q0= 0 111 | ; fs= [2] 112 | ; delta= [(0, None, 1); (0, Some 'a', 2)] } 113 | in 114 | assert_nfa_move m4 [0] (Some 'a') [2] ; 115 | assert_nfa_move m4 [1] (Some 'a') [] ; 116 | assert_nfa_move m4 [2] (Some 'a') [] ; 117 | assert_nfa_move m4 [0] (Some 'b') [] ; 118 | assert_nfa_move m4 [1] (Some 'b') [] ; 119 | assert_nfa_move m4 [2] (Some 'b') [] 120 | 121 | 122 | let test_nfa_new_states _ = 123 | let m1 = 124 | { qs= [0; 1; 2; 3; 4] 125 | ; sigma= ['a'; 'b'] 126 | ; delta= [(0, Some 'a', 1); (0, Some 'a', 2); (2, Some 'b', 3); (2, None, 4); (4, Some 'a', 4)] 127 | ; q0= 0 128 | ; fs= [1; 3] } in 129 | assert_set_set_eq [[]; []] (new_states m1 []) ; 130 | assert_set_set_eq [[1; 2; 4]; []] (new_states m1 [0]) ; 131 | assert_set_set_eq [[4]; []] (new_states m1 [3; 4]) ; 132 | assert_set_set_eq [[1; 2; 4]; [3]] (new_states m1 [0; 2]) ; 133 | assert_set_set_eq [[1; 2; 4]; [3]] (new_states m1 [0; 1; 2; 3]) 134 | 135 | 136 | let test_nfa_new_trans _ = 137 | let m1 = 138 | { qs= [0; 1; 2; 3; 4] 139 | ; sigma= ['a'; 'b'] 140 | ; delta= [(0, Some 'a', 1); (0, Some 'a', 2); (2, Some 'b', 3); (2, None, 4); (4, Some 'a', 4)] 141 | ; q0= 0 142 | ; fs= [1; 3] } in 143 | assert_trans_eq 144 | [([0], Some 'a', [1; 2; 4]); ([0], Some 'b', [])] 145 | (new_trans m1 [0]) ; 146 | assert_trans_eq 147 | [([0; 2], Some 'a', [1; 2; 4]); ([0; 2], Some 'b', [3])] 148 | (new_trans m1 [0; 2]) 149 | 150 | let test_nfa_new_finals _ = 151 | let m1 = 152 | { qs= [0; 1; 2; 3; 4] 153 | ; sigma= ['a'; 'b'] 154 | ; delta= [(0, Some 'a', 1); (0, Some 'a', 2); (2, Some 'b', 3); (2, None, 4); (4, Some 'a', 4)] 155 | ; q0= 0 156 | ; fs= [1; 3] } in 157 | assert_set_set_eq [] (new_finals m1 [0; 2]) ; 158 | assert_set_set_eq [[1]] (new_finals m1 [1]) ; 159 | assert_set_set_eq [[1; 3]] (new_finals m1 [1; 3]) 160 | 161 | let test_re_to_nfa _ = 162 | let m1 = regexp_to_nfa (Char 'a') in 163 | assert_nfa_deny m1 "" ; 164 | assert_nfa_accept m1 "a" ; 165 | assert_nfa_deny m1 "b" ; 166 | assert_nfa_deny m1 "ba" ; 167 | let m2 = regexp_to_nfa (Union (Char 'a', Char 'b')) in 168 | assert_nfa_deny m2 "" ; 169 | assert_nfa_accept m2 "a" ; 170 | assert_nfa_accept m2 "b" ; 171 | assert_nfa_deny m2 "ba" 172 | 173 | let test_str_to_nfa _ = 174 | let m1 = regexp_to_nfa @@ string_to_regexp "ab" in 175 | assert_nfa_deny m1 "a" ; 176 | assert_nfa_deny m1 "b" ; 177 | assert_nfa_accept m1 "ab" ; 178 | assert_nfa_deny m1 "bb" 179 | 180 | let test_str_to_nfa_empty _ = 181 | let m1 = regexp_to_nfa @@ string_to_regexp "((E)|(E))*" in 182 | assert_nfa_deny m1 "jujujuju" 183 | 184 | let suite = 185 | "public" 186 | >::: [ "nfa_accept" >:: test_nfa_accept 187 | ; "nfa_closure" >:: test_nfa_closure 188 | ; "nfa_move" >:: test_nfa_move 189 | ; "nfa_to_dfa" >:: test_nfa_to_dfa 190 | ; "re_to_nfa" >:: test_re_to_nfa 191 | ; "str_to_nfa" >:: test_str_to_nfa 192 | ; "nfa_new_states" >:: test_nfa_new_states 193 | ; "nfa_new_trans" >:: test_nfa_new_trans 194 | ; "nfa_new_finals" >:: test_nfa_new_finals 195 | ; "regex_to_nfa" >::test_str_to_nfa_empty] 196 | let _ = run_test_tt_main suite 197 | -------------------------------------------------------------------------------- /project3/test/student/student.ml: -------------------------------------------------------------------------------- 1 | open P3.Nfa 2 | open P3.Regexp 3 | open TestUtils 4 | open OUnit2 5 | 6 | let test_placeholder _ = 7 | assert_equal true true 8 | 9 | let suite = 10 | "student" 11 | >::: [ "nfa_new_states" >:: test_placeholder ] 12 | 13 | let _ = run_test_tt_main suite 14 | -------------------------------------------------------------------------------- /project3/test/testUtils.ml: -------------------------------------------------------------------------------- 1 | open P3.Nfa 2 | open P3.Regexp 3 | open OUnit2 4 | 5 | let re_to_str r = 6 | let surround l = ("(" :: l) @ [")"] in 7 | let rec r2str = function 8 | | Empty_String -> ["E"] 9 | | Char c -> [String.make 1 c] 10 | | Union (r1, r2) -> 11 | let l1 = surround @@ r2str r1 and l2 = surround @@ r2str r2 in 12 | l1 @ ("|" :: l2) 13 | | Concat (r1, r2) -> 14 | let l1 = surround @@ r2str r1 and l2 = surround @@ r2str r2 in 15 | l1 @ l2 16 | | Star r1 -> 17 | let l1 = surround @@ r2str r1 in 18 | l1 @ ["*"] 19 | in 20 | String.concat "" (r2str r) 21 | 22 | let assert_true x = assert_equal true x 23 | 24 | let assert_false x = assert_equal false x 25 | 26 | let assert_pass () = assert_equal true true 27 | 28 | let assert_fail () = assert_equal false false 29 | 30 | let string_of_int_list l = 31 | Printf.sprintf "[%s]" @@ String.concat "; " @@ List.map string_of_int l 32 | 33 | let string_of_int_list_list l = 34 | Printf.sprintf "[%s]" @@ String.concat "; " @@ List.map string_of_int_list l 35 | 36 | let assert_dfa m = 37 | let nondet = 38 | List.fold_left 39 | (fun res (q, c, _) -> 40 | match c with 41 | | None -> true 42 | | Some _ -> 43 | let others = 44 | List.filter (fun (q', c', _) -> q' = q && c' = c) m.delta 45 | in 46 | res || List.length others > 1 ) 47 | false m.delta 48 | in 49 | if nondet then assert_failure @@ Printf.sprintf "NFA is not DFA" 50 | 51 | (* Helpers for clearly testing the accept function *) 52 | let assert_nfa_accept nfa input = 53 | if not @@ accept nfa input then 54 | assert_failure 55 | @@ Printf.sprintf "NFA should have accept string '%s', but did not" input 56 | 57 | let assert_nfa_deny nfa input = 58 | if accept nfa input then 59 | assert_failure 60 | @@ Printf.sprintf "NFA should not have accepted string '%s', but did" input 61 | 62 | let assert_nfa_closure nfa ss es = 63 | let es = List.sort compare es in 64 | let rcv = List.sort compare @@ e_closure nfa ss in 65 | if not (es = rcv) then 66 | assert_failure 67 | @@ Printf.sprintf "Closure failure: Expected %s, received %s" 68 | (string_of_int_list es) (string_of_int_list rcv) 69 | 70 | let assert_nfa_move nfa ss mc es = 71 | let es = List.sort compare es in 72 | let rcv = List.sort compare @@ move nfa ss mc in 73 | if not (es = rcv) then 74 | assert_failure 75 | @@ Printf.sprintf "Move failure: Expected %s, received %s" 76 | (string_of_int_list es) (string_of_int_list rcv) 77 | 78 | let assert_set_set_eq lst1 lst2 = 79 | let es l = List.sort_uniq compare (List.map (List.sort compare) l) in 80 | assert_equal (es lst1) (es lst2) 81 | 82 | let assert_trans_eq lst1 lst2 = 83 | let es l = 84 | List.sort_uniq compare 85 | (List.map 86 | (fun (l1, t, l2) -> (List.sort compare l1, t, List.sort compare l2)) 87 | l) 88 | in 89 | assert_equal (es lst1) (es lst2) 90 | 91 | let assert_set_eq lst1 lst2 = 92 | let es = List.sort_uniq compare in 93 | assert_equal (es lst1) (es lst2) 94 | 95 | let assert_regex_string_equiv rxp = 96 | assert_equal rxp @@ string_to_regexp @@ re_to_str rxp 97 | -------------------------------------------------------------------------------- /project3/utop.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | . ./ocaml_version.sh 3 | dune utop src 4 | -------------------------------------------------------------------------------- /project3/viz.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | . ./ocaml_version.sh 3 | dune exec bin/viz.bc 4 | -------------------------------------------------------------------------------- /project4a/.ocamlinit: -------------------------------------------------------------------------------- 1 | open P4a 2 | open P4a.Lexer 3 | open P4a.Parser 4 | open P4a.TokenTypes 5 | open P4a.MicroCamlTypes 6 | -------------------------------------------------------------------------------- /project4a/.submit: -------------------------------------------------------------------------------- 1 | [course] 2 | id = 358171 3 | name = "CMSC330" 4 | term = "Spring 2022" 5 | 6 | [assignment] 7 | id = 1959273 8 | name = "Project 4a - MicroCaml Lexer and Parser" 9 | files = [ "src/lexer.ml", "src/parser.ml" ] 10 | -------------------------------------------------------------------------------- /project4a/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.3) 2 | -------------------------------------------------------------------------------- /project4a/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name p4a) 3 | (modules lexer microCamlTypes utils parser tokenTypes) 4 | (libraries str)) 5 | (env 6 | (dev 7 | (flags (:standard -w -27-32-33-34-39)))) 8 | -------------------------------------------------------------------------------- /project4a/src/lexer.ml: -------------------------------------------------------------------------------- 1 | open TokenTypes 2 | 3 | (* Part 1: Lexer - IMPLEMENT YOUR CODE BELOW *) 4 | 5 | let tokenize input = failwith "unimplemented" -------------------------------------------------------------------------------- /project4a/src/lexer.mli: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | open TokenTypes 3 | 4 | val tokenize : string -> token list 5 | -------------------------------------------------------------------------------- /project4a/src/microCamlTypes.ml: -------------------------------------------------------------------------------- 1 | type op = Add | Sub | Mult | Div | Concat | Greater | Less | GreaterEqual | LessEqual | Equal | NotEqual | Or | And 2 | 3 | type var = string 4 | 5 | type value = 6 | | Int of int 7 | | Bool of bool 8 | | String of string 9 | | Closure of environment * var * expr 10 | 11 | and environment = (var * value ref) list 12 | 13 | and expr = 14 | | Value of value 15 | | ID of var 16 | | Fun of var * expr 17 | | Not of expr 18 | | Binop of op * expr * expr 19 | | If of expr * expr * expr 20 | | FunctionCall of expr * expr 21 | | Let of var * bool * expr * expr 22 | 23 | type mutop = 24 | | Def of var * expr 25 | | Expr of expr 26 | | NoOp 27 | -------------------------------------------------------------------------------- /project4a/src/parser.ml: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | open Utils 3 | open TokenTypes 4 | 5 | (* Provided functions - DO NOT MODIFY *) 6 | 7 | (* Matches the next token in the list, throwing an error if it doesn't match the given token *) 8 | let match_token (toks: token list) (tok: token) = 9 | match toks with 10 | | [] -> raise (InvalidInputException(string_of_token tok)) 11 | | h::t when h = tok -> t 12 | | h::_ -> raise (InvalidInputException( 13 | Printf.sprintf "Expected %s from input %s, got %s" 14 | (string_of_token tok) 15 | (string_of_list string_of_token toks) 16 | (string_of_token h))) 17 | 18 | (* Matches a sequence of tokens given as the second list in the order in which they appear, throwing an error if they don't match *) 19 | let match_many (toks: token list) (to_match: token list) = 20 | List.fold_left match_token toks to_match 21 | 22 | (* Return the next token in the token list as an option *) 23 | let lookahead (toks: token list) = 24 | match toks with 25 | | [] -> None 26 | | h::t -> Some h 27 | 28 | (* Return the token at the nth index in the token list as an option*) 29 | let rec lookahead_many (toks: token list) (n: int) = 30 | match toks, n with 31 | | h::_, 0 -> Some h 32 | | _::t, n when n > 0 -> lookahead_many t (n-1) 33 | | _ -> None 34 | 35 | (* Part 2: Parsing expressions *) 36 | 37 | let rec parse_expr toks = failwith "unimplemented" 38 | 39 | (* Part 3: Parsing mutop *) 40 | 41 | let rec parse_mutop toks = failwith "unimplemented" -------------------------------------------------------------------------------- /project4a/src/parser.mli: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | open TokenTypes 3 | 4 | val parse_expr : token list -> (token list * expr) 5 | val parse_mutop : token list -> (token list * mutop) -------------------------------------------------------------------------------- /project4a/src/tokenTypes.ml: -------------------------------------------------------------------------------- 1 | exception InvalidInputException of string 2 | 3 | type token = 4 | | Tok_RParen 5 | | Tok_LParen 6 | | Tok_Equal 7 | | Tok_NotEqual 8 | | Tok_Greater 9 | | Tok_Less 10 | | Tok_GreaterEqual 11 | | Tok_LessEqual 12 | | Tok_Or 13 | | Tok_And 14 | | Tok_Not 15 | | Tok_If 16 | | Tok_Then 17 | | Tok_Else 18 | | Tok_Add 19 | | Tok_Sub 20 | | Tok_Mult 21 | | Tok_Div 22 | | Tok_Concat 23 | | Tok_Let 24 | | Tok_Rec 25 | | Tok_In 26 | | Tok_Def 27 | | Tok_Fun 28 | | Tok_Arrow 29 | | Tok_Int of int 30 | | Tok_Bool of bool 31 | | Tok_String of string 32 | | Tok_ID of string 33 | | Tok_DoubleSemi -------------------------------------------------------------------------------- /project4a/src/utils.ml: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | open TokenTypes 3 | 4 | let string_of_token (t : token) : string = match t with 5 | | Tok_Sub -> "Tok_Sub" 6 | | Tok_RParen -> "Tok_RParen" 7 | | Tok_Add -> "Tok_Add" 8 | | Tok_Or -> "Tok_Or" 9 | | Tok_NotEqual -> "Tok_NotEqual" 10 | | Tok_Not -> "Tok_Not" 11 | | Tok_Mult -> "Tok_Mult" 12 | | Tok_LessEqual -> "Tok_LessEqual" 13 | | Tok_Less -> "Tok_Less" 14 | | Tok_LParen -> "Tok_LParen" 15 | | Tok_Int(i) -> "Tok_Int(" ^ (string_of_int i) ^ ")" 16 | | Tok_If -> "Tok_If" 17 | | Tok_ID(id) -> "Tok_ID(\"" ^ id ^ "\")" 18 | | Tok_String(s) -> "Tok_String(\"" ^ s ^ "\")" 19 | | Tok_GreaterEqual -> "Tok_GreaterEqual" 20 | | Tok_Greater -> "Tok_Greater" 21 | | Tok_Equal -> "Tok_Equal" 22 | | Tok_Then -> "Tok_Then" 23 | | Tok_Else -> "Tok_Else" 24 | | Tok_Div -> "Tok_Div" 25 | | Tok_Bool(b) -> "Tok_Bool(" ^ (string_of_bool b) ^ ")" 26 | | Tok_And -> "Tok_And" 27 | | Tok_Concat -> "Tok_Concat" 28 | | Tok_Let -> "Tok_Let" 29 | | Tok_Def -> "Tok_Def" 30 | | Tok_In -> "Tok_In" 31 | | Tok_Rec -> "Tok_Rec" 32 | | Tok_Arrow -> "Tok_Arrow" 33 | | Tok_Fun -> "Tok_Fun" 34 | | Tok_DoubleSemi -> "Tok_DoubleSemi" 35 | 36 | let string_of_list ?newline:(newline=false) (f : 'a -> string) (l : 'a list) : string = 37 | "[" ^ (String.concat ", " @@ List.map f l) ^ "]" ^ (if newline then "\n" else "");; 38 | 39 | let rec string_of_value (v : value) : string = 40 | match v with 41 | | Int(n) -> "Int " ^ string_of_int n 42 | | Bool(b) -> "Bool " ^ string_of_bool b 43 | | String(s) -> "String \"" ^ s ^ "\"" 44 | | Closure(env, s, e) -> 45 | "Closure(" ^ string_of_list (fun (v, v') -> "(" ^ v ^ ", " ^ string_of_value (!v') ^ ")") env ^ ", " ^ s ^ ", " ^ string_of_expr e ^ ")" 46 | 47 | and string_of_expr (e : expr) : string = 48 | let unparse_two (s : string) (e1 : expr) (e2 : expr) = 49 | s ^ "(" ^ string_of_expr e1 ^ ", " ^ string_of_expr e2 ^ ")" 50 | in 51 | match e with 52 | | Value(v) -> string_of_value v 53 | | ID(s) -> "ID \"" ^ s ^ "\"" 54 | | Fun(s, e) -> "Fun(" ^ s ^ ", " ^ string_of_expr e ^ ")" 55 | 56 | | Binop(Add, e1, e2) -> unparse_two "Add" e1 e2 57 | | Binop(Sub, e1, e2) -> unparse_two "Sub" e1 e2 58 | | Binop(Mult, e1, e2) -> unparse_two "Mult" e1 e2 59 | | Binop(Div, e1, e2) -> unparse_two "Div" e1 e2 60 | 61 | | Binop(Concat, e1, e2) -> unparse_two "Concat" e1 e2 62 | 63 | | Binop(Greater, e1, e2) -> unparse_two "Greater" e1 e2 64 | | Binop(Less, e1, e2) -> unparse_two "Less" e1 e2 65 | | Binop(GreaterEqual, e1, e2) -> unparse_two "GreaterEqual" e1 e2 66 | | Binop(LessEqual, e1, e2) -> unparse_two "LessEqual" e1 e2 67 | 68 | | Binop(Equal, e1, e2) -> unparse_two "Equal" e1 e2 69 | | Binop(NotEqual, e1, e2) -> unparse_two "NotEquals" e1 e2 70 | 71 | | Binop(Or, e1, e2) -> unparse_two "Or" e1 e2 72 | | Binop(And, e1, e2) -> unparse_two "Add" e1 e2 73 | | Not(e) -> "Not(" ^ string_of_expr e ^ ")" 74 | | FunctionCall(e1, e2) -> unparse_two "FunctionCall" e1 e2 75 | 76 | | If(e1, e2, e3) -> "If(" ^ string_of_expr e1 ^ ", " ^ string_of_expr e2 ^ ", " ^ string_of_expr e3 ^ ")" 77 | | Let(s, r, e1, e2) -> "Let(" ^ s ^ ", " ^ string_of_bool r ^ "," ^ string_of_expr e1 ^ ", " ^ string_of_expr e2 ^ ")" 78 | 79 | and string_of_mutop (m: mutop) : string = 80 | match m with 81 | | Def(s, e) -> "Def(" ^ s ^ ", " ^ string_of_expr e ^ ")" 82 | | Expr(e) -> "Expr(" ^ string_of_expr e ^ ")" 83 | | NoOp -> "NoOp" 84 | 85 | (********************************** 86 | * BEGIN ACTUAL PARSE HELPER CODE * 87 | **********************************) 88 | 89 | let string_of_in_channel (ic : in_channel) : string = 90 | let lines : string list = 91 | let try_read () = 92 | try Some ((input_line ic) ^ "\n") with End_of_file -> None in 93 | let rec loop acc = match try_read () with 94 | | Some s -> loop (s :: acc) 95 | | None -> List.rev acc in 96 | loop [] 97 | in 98 | 99 | List.fold_left (fun a e -> a ^ e) "" @@ lines 100 | 101 | let tokenize_from_channel (c : in_channel) : token list = 102 | Lexer.tokenize @@ string_of_in_channel c 103 | 104 | let tokenize_from_file (filename : string) : token list = 105 | let c = open_in filename in 106 | let s = tokenize_from_channel c in 107 | close_in c; 108 | s 109 | -------------------------------------------------------------------------------- /project4a/test/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name testUtils) 3 | (libraries p4a oUnit)) 4 | -------------------------------------------------------------------------------- /project4a/test/pbt/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names pbt) 3 | (libraries p4a oUnit qcheck testUtils)) 4 | -------------------------------------------------------------------------------- /project4a/test/pbt/pbt.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | open QCheck 3 | open P4a.MicroCamlTypes 4 | open P4a.Lexer 5 | open P4a.Parser 6 | 7 | let test_simple_def = 8 | Test.make 9 | ~name:"test_simple_def" 10 | ~count:1000 11 | (small_int) 12 | (fun x -> 13 | let def_string = "def " ^ "validstring" ^ " = " ^ string_of_int x ^ ";;" in 14 | let ast = def_string |> tokenize |> parse_mutop in 15 | ast = ([], Def ("validstring", Value (Int x))) 16 | ) 17 | 18 | let test_simple_let = 19 | Test.make 20 | ~name:"test_simple_let" 21 | ~count:1000 22 | (quad small_int bool small_int small_int) 23 | (fun (w, x, y, z) -> 24 | let let_string_1 = "let validstring1 = " ^ (string_of_int w) ^ " in" in 25 | let let_string_2 = let_string_1 ^ " let validstring2 = if " ^ (string_of_bool x) in 26 | let let_string_3 = let_string_2 ^ " then " ^ (string_of_int y) ^ " else " ^ (string_of_int z) ^ " in validstring2" in 27 | let ast = let_string_3 |> tokenize |> parse_expr in 28 | ast = ([], Let ("validstring1", false, Value (Int w), 29 | Let ("validstring2", false, 30 | If (Value (Bool x), Value (Int y), Value (Int z)), ID "validstring2"))) 31 | ) 32 | 33 | let test_sum_small_ints = 34 | Test.make 35 | ~name:"test_sum_small_ints" 36 | ~count:1000 37 | (pair small_int small_int) 38 | (fun (x, y) -> 39 | let sum = (string_of_int x) ^ " + " ^ (string_of_int y) in 40 | let ast = sum |> tokenize |> parse_expr in 41 | ast = ([], Binop (Add, Value (Int x), Value (Int y))) 42 | ) 43 | 44 | let test_operator_precedence = 45 | Test.make 46 | ~name:"test_operator_precedence" 47 | ~count:1000 48 | (triple small_int small_int small_int) 49 | (fun (x, y, z) -> 50 | let lower_precedence = [(" + ", Add); (" - ", Sub)] in 51 | let higher_precedence = [(" * ", Mult); (" / ", Div)] in 52 | let lower_str, lower_tok = List.nth lower_precedence (x mod 2) in 53 | let higher_str, higher_tok = List.nth higher_precedence (y mod 2) in 54 | let generated = (string_of_int x) ^ higher_str ^ (string_of_int y) ^ lower_str ^ (string_of_int z) in 55 | let ast = generated |> tokenize |> parse_expr in 56 | ast = ([], Binop (lower_tok, Binop(higher_tok, Value (Int x), Value (Int y)), Value (Int z))) 57 | ) 58 | 59 | let suite = 60 | "pbt" >::: [ 61 | QCheck_runner.to_ounit2_test test_simple_def; 62 | QCheck_runner.to_ounit2_test test_simple_let; 63 | QCheck_runner.to_ounit2_test test_sum_small_ints; 64 | QCheck_runner.to_ounit2_test test_operator_precedence; 65 | ] 66 | 67 | let _ = run_test_tt_main suite 68 | -------------------------------------------------------------------------------- /project4a/test/public/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names public) 3 | (libraries p4a oUnit testUtils)) 4 | -------------------------------------------------------------------------------- /project4a/test/testUtils.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | open P4a.Lexer 3 | open P4a.Parser 4 | open P4a.TokenTypes 5 | 6 | (* Assertion wrappers for convenience and readability *) 7 | let assert_true b = assert_equal true b 8 | let assert_false b = assert_equal false b 9 | let assert_succeed () = assert_true true 10 | 11 | let file_to_string file = 12 | let ch = open_in file in 13 | let s = really_input_string ch (in_channel_length ch) in 14 | close_in ch; 15 | s 16 | 17 | let get_file fname = file_to_string ("data/" ^ fname) 18 | 19 | let string_to_tokens s = tokenize s 20 | 21 | let gen_ast_parse_expr file = get_file file |> tokenize |> parse_expr 22 | 23 | let gen_ast_parse_mutop file = get_file file |> tokenize |> parse_mutop 24 | 25 | let input_handler f = 26 | try 27 | let _ = f () in assert_failure "Expected InvalidInputException, none received" with 28 | | InvalidInputException(_) -> assert_succeed () 29 | | ex -> assert_failure ("Got " ^ (Printexc.to_string ex) ^ ", expected InvalidInputException") -------------------------------------------------------------------------------- /project4b/.ocamlinit: -------------------------------------------------------------------------------- 1 | open P4b 2 | open P4b.Eval 3 | open P4b.Lexer 4 | open P4b.Parser 5 | open P4b.TokenTypes 6 | open P4b.MicroCamlTypes -------------------------------------------------------------------------------- /project4b/.submit: -------------------------------------------------------------------------------- 1 | [course] 2 | id = 358171 3 | name = "CMSC330" 4 | term = "Spring 2022" 5 | 6 | [assignment] 7 | id = 1959298 8 | name = "Project 4b - MicroCaml Interpreter" 9 | files = ["src/eval.ml"] 10 | -------------------------------------------------------------------------------- /project4b/assets/ex.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/project4b/assets/ex.gif -------------------------------------------------------------------------------- /project4b/bin/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name mutop) 3 | (libraries str p4b)) 4 | -------------------------------------------------------------------------------- /project4b/bin/mutop.ml: -------------------------------------------------------------------------------- 1 | open P4b.MicroCamlTypes 2 | open P4b.Utils 3 | open P4b.Parser 4 | open P4b.Lexer 5 | open P4b.Eval 6 | 7 | let end_r = Str.string_match (Str.regexp {|.*;;$|});; 8 | let exit_r = Str.string_match (Str.regexp {|^exit;;$|});; 9 | (* Set this to false if you want to turn off the coloring *) 10 | let use_color = true;; 11 | 12 | let green_str st = if use_color then Printf.sprintf "\027[0;32m%s\027[0m" st else st 13 | let red_str st = if use_color then Printf.sprintf "\027[1;31m%s\027[0m" st else st 14 | let alternative_str st = if use_color then Printf.sprintf "\027[0;36m%s\027[0m" st else st 15 | let get_top_constname = function 16 | | [] -> "" 17 | | (constname, _)::_ -> constname 18 | 19 | let are_top_envs_equal env1 env2 = 20 | ((List.length env1) == (List.length env2)) && ((get_top_constname env1) == (get_top_constname env2)) 21 | 22 | let mutop_to_string optional_val constname is_same_env = 23 | match optional_val with 24 | | Some Closure (_, _, _) -> Printf.sprintf "val %s : \n" constname 25 | | Some value -> Printf.sprintf "%s%s\n" (if is_same_env then "- : val: " else (Printf.sprintf "val %s = " constname))(string_of_value value) 26 | | None -> "" 27 | 28 | let rec line_reader text = 29 | let str_inpt = read_line () in 30 | let new_text = text ^ "\n" ^ str_inpt in 31 | if end_r str_inpt 0 || exit_r str_inpt 0 then 32 | if exit_r str_inpt 0 then 33 | (text, str_inpt) 34 | else 35 | ((String.sub new_text 0 ((String.length new_text))), str_inpt) 36 | else 37 | line_reader new_text 38 | 39 | let rec run_cli statements environment = 40 | let _ = print_string (alternative_str "mutop " ^ green_str "# ") in 41 | let (str_inpt, last_input) = line_reader "" in 42 | if exit_r last_input 0 then 43 | (statements, environment) 44 | else 45 | try 46 | let (_, stms) = parse_mutop(tokenize str_inpt) in 47 | let (final_env, mutop_val) = eval_mutop environment stms in 48 | let _ = print_string (mutop_to_string mutop_val (get_top_constname final_env) (are_top_envs_equal environment final_env)) in 49 | if exit_r last_input 0 then 50 | (statements, environment) 51 | else 52 | run_cli (statements@[str_inpt]) (final_env@environment) 53 | with e -> ( 54 | let msg = Printexc.to_string e in 55 | let t = String.split_on_char '.' msg in 56 | let t = List.nth t (List.length t - 1) in 57 | let p = String.index t '(' + 1 in 58 | let p' = String.index t ')' - p in 59 | let err = String.sub t 0 (p-1) in 60 | let err' = String.sub t p p' in 61 | let text = Printf.sprintf "%s: %s\n" err err' in 62 | print_string (red_str text); 63 | run_cli (statements) (environment));; 64 | run_cli [] [];; 65 | -------------------------------------------------------------------------------- /project4b/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.3) 2 | -------------------------------------------------------------------------------- /project4b/dune-workspace: -------------------------------------------------------------------------------- 1 | (lang dune 1.0) 2 | (context default) 3 | (profile release) 4 | -------------------------------------------------------------------------------- /project4b/microcaml-opsem.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umd-cmsc330/cmsc330spring22/7a9b390852fb63c2607f105e3894865ea66d9c2f/project4b/microcaml-opsem.pdf -------------------------------------------------------------------------------- /project4b/mutop.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | dune exec bin/mutop.exe -------------------------------------------------------------------------------- /project4b/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name p4b) 3 | (modules eval lexer microCamlTypes utils parser tokenTypes) 4 | (libraries str)) 5 | (env 6 | (dev 7 | (flags (:standard -w -27-32-33-34-39)))) 8 | -------------------------------------------------------------------------------- /project4b/src/eval.ml: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | open Utils 3 | 4 | exception TypeError of string 5 | exception DeclareError of string 6 | exception DivByZeroError 7 | 8 | (* Provided functions - DO NOT MODIFY *) 9 | 10 | (* Adds mapping [x:v] to environment [env] *) 11 | let extend env x v = (x, ref v)::env 12 | 13 | (* Returns [v] if [x:v] is a mapping in [env]; uses the 14 | most recent if multiple mappings for [x] are present *) 15 | let rec lookup env x = 16 | match env with 17 | | [] -> raise (DeclareError ("Unbound variable " ^ x)) 18 | | (var, value)::t -> if x = var then !value else lookup t x 19 | 20 | (* Creates a placeholder mapping for [x] in [env]; needed 21 | for handling recursive definitions *) 22 | let extend_tmp env x = (x, ref (Int 0))::env 23 | 24 | (* Updates the (most recent) mapping in [env] for [x] to [v] *) 25 | let rec update env x v = 26 | match env with 27 | | [] -> raise (DeclareError ("Unbound variable " ^ x)) 28 | | (var, value)::t -> if x = var then (value := v) else update t x v 29 | 30 | (* Part 1: Evaluating expressions *) 31 | 32 | (* Evaluates MicroCaml expression [e] in environment [env], 33 | returning a value, or throwing an exception on error *) 34 | let rec eval_expr env e = failwith "unimplemented" 35 | 36 | (* Part 2: Evaluating mutop directive *) 37 | 38 | (* Evaluates MicroCaml mutop directive [m] in environment [env], 39 | returning a possibly updated environment paired with 40 | a value option; throws an exception on error *) 41 | let eval_mutop env m = failwith "unimplemented" -------------------------------------------------------------------------------- /project4b/src/eval.mli: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | 3 | exception TypeError of string 4 | exception DeclareError of string 5 | exception DivByZeroError 6 | 7 | val eval_expr: environment -> expr -> value 8 | val eval_mutop: environment -> mutop -> environment * value option 9 | -------------------------------------------------------------------------------- /project4b/src/lexer.ml: -------------------------------------------------------------------------------- 1 | open TokenTypes 2 | 3 | (* PASTE YOUR LEXER FROM P4A HERE *) 4 | 5 | let tokenize input = failwith "unimplemented" -------------------------------------------------------------------------------- /project4b/src/lexer.mli: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | open TokenTypes 3 | 4 | val tokenize : string -> token list 5 | -------------------------------------------------------------------------------- /project4b/src/microCamlTypes.ml: -------------------------------------------------------------------------------- 1 | (* Provided definitions - DO NOT MODIFY *) 2 | 3 | type op = Add | Sub | Mult | Div | Concat | Greater | Less | GreaterEqual | LessEqual | Equal | NotEqual | Or | And 4 | 5 | type var = string 6 | 7 | type value = 8 | | Int of int 9 | | Bool of bool 10 | | String of string 11 | | Closure of environment * var * expr 12 | 13 | and environment = (var * value ref) list 14 | 15 | and expr = 16 | | Value of value 17 | | ID of var 18 | | Fun of var * expr 19 | | Not of expr 20 | | Binop of op * expr * expr 21 | | If of expr * expr * expr 22 | | FunctionCall of expr * expr 23 | | Let of var * bool * expr * expr 24 | 25 | type mutop = 26 | | Def of var * expr 27 | | Expr of expr 28 | | NoOp -------------------------------------------------------------------------------- /project4b/src/parser.ml: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | open Utils 3 | open TokenTypes 4 | 5 | (* Provided functions - DO NOT MODIFY *) 6 | 7 | (* Matches the next token in the list, throwing an error if it doesn't match the given token *) 8 | let match_token (toks: token list) (tok: token) = 9 | match toks with 10 | | [] -> raise (InvalidInputException(string_of_token tok)) 11 | | h::t when h = tok -> t 12 | | h::_ -> raise (InvalidInputException( 13 | Printf.sprintf "Expected %s from input %s, got %s" 14 | (string_of_token tok) 15 | (string_of_list string_of_token toks) 16 | (string_of_token h))) 17 | 18 | (* Matches a sequence of tokens given as the second list in the order in which they appear, throwing an error if they don't match *) 19 | let match_many (toks: token list) (to_match: token list) = 20 | List.fold_left match_token toks to_match 21 | 22 | (* Return the next token in the token list as an option *) 23 | let lookahead (toks: token list) = 24 | match toks with 25 | | [] -> None 26 | | h::t -> Some h 27 | 28 | (* Return the token at the nth index in the token list as an option*) 29 | let rec lookahead_many (toks: token list) (n: int) = 30 | match toks, n with 31 | | h::_, 0 -> Some h 32 | | _::t, n when n > 0 -> lookahead_many t (n-1) 33 | | _ -> None 34 | 35 | (* PASTE YOUR PARSERS FROM P4A HERE *) 36 | 37 | let rec parse_expr toks = failwith "unimplemented" 38 | 39 | let rec parse_mutop toks = failwith "unimplemented" -------------------------------------------------------------------------------- /project4b/src/parser.mli: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | open TokenTypes 3 | 4 | val parse_expr : token list -> (token list * expr) 5 | val parse_mutop : token list -> (token list * mutop) -------------------------------------------------------------------------------- /project4b/src/tokenTypes.ml: -------------------------------------------------------------------------------- 1 | exception InvalidInputException of string 2 | 3 | type token = 4 | | Tok_RParen 5 | | Tok_LParen 6 | | Tok_Equal 7 | | Tok_NotEqual 8 | | Tok_Greater 9 | | Tok_Less 10 | | Tok_GreaterEqual 11 | | Tok_LessEqual 12 | | Tok_Or 13 | | Tok_And 14 | | Tok_Not 15 | | Tok_If 16 | | Tok_Then 17 | | Tok_Else 18 | | Tok_Add 19 | | Tok_Sub 20 | | Tok_Mult 21 | | Tok_Div 22 | | Tok_Concat 23 | | Tok_Let 24 | | Tok_Rec 25 | | Tok_In 26 | | Tok_Def 27 | | Tok_Fun 28 | | Tok_Arrow 29 | | Tok_Int of int 30 | | Tok_Bool of bool 31 | | Tok_String of string 32 | | Tok_ID of string 33 | | Tok_DoubleSemi -------------------------------------------------------------------------------- /project4b/src/utils.ml: -------------------------------------------------------------------------------- 1 | open MicroCamlTypes 2 | open TokenTypes 3 | 4 | let string_of_token (t : token) : string = match t with 5 | | Tok_Sub -> "Tok_Sub" 6 | | Tok_RParen -> "Tok_RParen" 7 | | Tok_Add -> "Tok_Add" 8 | | Tok_Or -> "Tok_Or" 9 | | Tok_NotEqual -> "Tok_NotEqual" 10 | | Tok_Not -> "Tok_Not" 11 | | Tok_Mult -> "Tok_Mult" 12 | | Tok_LessEqual -> "Tok_LessEqual" 13 | | Tok_Less -> "Tok_Less" 14 | | Tok_LParen -> "Tok_LParen" 15 | | Tok_Int(i) -> "Tok_Int(" ^ (string_of_int i) ^ ")" 16 | | Tok_If -> "Tok_If" 17 | | Tok_ID(id) -> "Tok_ID(\"" ^ id ^ "\")" 18 | | Tok_String(s) -> "Tok_String(\"" ^ s ^ "\")" 19 | | Tok_GreaterEqual -> "Tok_GreaterEqual" 20 | | Tok_Greater -> "Tok_Greater" 21 | | Tok_Equal -> "Tok_Equal" 22 | | Tok_Then -> "Tok_Then" 23 | | Tok_Else -> "Tok_Else" 24 | | Tok_Div -> "Tok_Div" 25 | | Tok_Bool(b) -> "Tok_Bool(" ^ (string_of_bool b) ^ ")" 26 | | Tok_And -> "Tok_And" 27 | | Tok_Concat -> "Tok_Concat" 28 | | Tok_Let -> "Tok_Let" 29 | | Tok_Def -> "Tok_Def" 30 | | Tok_In -> "Tok_In" 31 | | Tok_Rec -> "Tok_Rec" 32 | | Tok_Arrow -> "Tok_Arrow" 33 | | Tok_Fun -> "Tok_Fun" 34 | | Tok_DoubleSemi -> "Tok_DoubleSemi" 35 | 36 | let string_of_list ?newline:(newline=false) (f : 'a -> string) (l : 'a list) : string = 37 | "[" ^ (String.concat ", " @@ List.map f l) ^ "]" ^ (if newline then "\n" else "");; 38 | 39 | let rec string_of_value (v : value) : string = 40 | match v with 41 | | Int(n) -> "Int " ^ string_of_int n 42 | | Bool(b) -> "Bool " ^ string_of_bool b 43 | | String(s) -> "String \"" ^ s ^ "\"" 44 | | Closure(env, s, e) -> 45 | "Closure(" ^ string_of_list (fun (v, v') -> "(" ^ v ^ ", " ^ string_of_value (!v') ^ ")") env ^ ", " ^ s ^ ", " ^ string_of_expr e ^ ")" 46 | 47 | and string_of_expr (e : expr) : string = 48 | let unparse_two (s : string) (e1 : expr) (e2 : expr) = 49 | s ^ "(" ^ string_of_expr e1 ^ ", " ^ string_of_expr e2 ^ ")" 50 | in 51 | match e with 52 | | Value(v) -> string_of_value v 53 | | ID(s) -> "ID \"" ^ s ^ "\"" 54 | | Fun(s, e) -> "Fun(" ^ s ^ ", " ^ string_of_expr e ^ ")" 55 | 56 | | Binop(Add, e1, e2) -> unparse_two "Add" e1 e2 57 | | Binop(Sub, e1, e2) -> unparse_two "Sub" e1 e2 58 | | Binop(Mult, e1, e2) -> unparse_two "Mult" e1 e2 59 | | Binop(Div, e1, e2) -> unparse_two "Div" e1 e2 60 | 61 | | Binop(Concat, e1, e2) -> unparse_two "Concat" e1 e2 62 | 63 | | Binop(Greater, e1, e2) -> unparse_two "Greater" e1 e2 64 | | Binop(Less, e1, e2) -> unparse_two "Less" e1 e2 65 | | Binop(GreaterEqual, e1, e2) -> unparse_two "GreaterEqual" e1 e2 66 | | Binop(LessEqual, e1, e2) -> unparse_two "LessEqual" e1 e2 67 | 68 | | Binop(Equal, e1, e2) -> unparse_two "Equal" e1 e2 69 | | Binop(NotEqual, e1, e2) -> unparse_two "NotEquals" e1 e2 70 | 71 | | Binop(Or, e1, e2) -> unparse_two "Or" e1 e2 72 | | Binop(And, e1, e2) -> unparse_two "Add" e1 e2 73 | | Not(e) -> "Not(" ^ string_of_expr e ^ ")" 74 | | FunctionCall(e1, e2) -> unparse_two "FunctionCall" e1 e2 75 | 76 | | If(e1, e2, e3) -> "If(" ^ string_of_expr e1 ^ ", " ^ string_of_expr e2 ^ ", " ^ string_of_expr e3 ^ ")" 77 | | Let(s, r, e1, e2) -> "Let(" ^ s ^ ", " ^ string_of_bool r ^ "," ^ string_of_expr e1 ^ ", " ^ string_of_expr e2 ^ ")" 78 | 79 | and string_of_mutop (m: mutop) : string = 80 | match m with 81 | | Def(s, e) -> "Def(" ^ s ^ ", " ^ string_of_expr e ^ ")" 82 | | Expr(e) -> "Expr(" ^ string_of_expr e ^ ")" 83 | | NoOp -> "NoOp" -------------------------------------------------------------------------------- /project4b/test/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name testUtils) 3 | (libraries p4b oUnit)) 4 | -------------------------------------------------------------------------------- /project4b/test/pbt/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names pbt) 3 | (libraries p4b oUnit qcheck testUtils)) 4 | -------------------------------------------------------------------------------- /project4b/test/pbt/pbt.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | open QCheck 3 | open P4b.MicroCamlTypes 4 | open P4b.Lexer 5 | open P4b.Parser 6 | 7 | let test_simple_def = 8 | Test.make 9 | ~name:"test_simple_def" 10 | ~count:1000 11 | (small_int) 12 | (fun x -> 13 | let def_string = "def " ^ "validstring" ^ " = " ^ string_of_int x ^ ";;" in 14 | let ast = def_string |> tokenize |> parse_mutop in 15 | ast = ([], Def ("validstring", Value (Int x))) 16 | ) 17 | 18 | let test_simple_let = 19 | Test.make 20 | ~name:"test_simple_let" 21 | ~count:1000 22 | (quad small_int bool small_int small_int) 23 | (fun (w, x, y, z) -> 24 | let let_string_1 = "let validstring1 = " ^ (string_of_int w) ^ " in" in 25 | let let_string_2 = let_string_1 ^ " let validstring2 = if " ^ (string_of_bool x) in 26 | let let_string_3 = let_string_2 ^ " then " ^ (string_of_int y) ^ " else " ^ (string_of_int z) ^ " in validstring2" in 27 | let ast = let_string_3 |> tokenize |> parse_expr in 28 | ast = ([], Let ("validstring1", false, Value (Int w), 29 | Let ("validstring2", false, 30 | If (Value (Bool x), Value (Int y), Value (Int z)), ID "validstring2"))) 31 | ) 32 | 33 | let test_sum_small_ints = 34 | Test.make 35 | ~name:"test_sum_small_ints" 36 | ~count:1000 37 | (pair small_int small_int) 38 | (fun (x, y) -> 39 | let sum = (string_of_int x) ^ " + " ^ (string_of_int y) in 40 | let ast = sum |> tokenize |> parse_expr in 41 | ast = ([], Binop (Add, Value (Int x), Value (Int y))) 42 | ) 43 | 44 | let test_operator_precedence = 45 | Test.make 46 | ~name:"test_operator_precedence" 47 | ~count:1000 48 | (triple small_int small_int small_int) 49 | (fun (x, y, z) -> 50 | let lower_precedence = [(" + ", Add); (" - ", Sub)] in 51 | let higher_precedence = [(" * ", Mult); (" / ", Div)] in 52 | let lower_str, lower_tok = List.nth lower_precedence (x mod 2) in 53 | let higher_str, higher_tok = List.nth higher_precedence (y mod 2) in 54 | let generated = (string_of_int x) ^ higher_str ^ (string_of_int y) ^ lower_str ^ (string_of_int z) in 55 | let ast = generated |> tokenize |> parse_expr in 56 | ast = ([], Binop (lower_tok, Binop(higher_tok, Value (Int x), Value (Int y)), Value (Int z))) 57 | ) 58 | 59 | let suite = 60 | "pbt" >::: [ 61 | QCheck_runner.to_ounit2_test test_simple_def; 62 | QCheck_runner.to_ounit2_test test_simple_let; 63 | QCheck_runner.to_ounit2_test test_sum_small_ints; 64 | QCheck_runner.to_ounit2_test test_operator_precedence; 65 | ] 66 | 67 | let _ = run_test_tt_main suite 68 | -------------------------------------------------------------------------------- /project4b/test/public/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names public) 3 | (libraries p4b oUnit testUtils)) 4 | -------------------------------------------------------------------------------- /project4b/test/testUtils.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | open P4b.Lexer 3 | open P4b.Parser 4 | open P4b.TokenTypes 5 | open P4b.Eval 6 | 7 | (* Assertion wrappers for convenience and readability *) 8 | let assert_true b = assert_equal true b 9 | let assert_false b = assert_equal false b 10 | let assert_succeed () = assert_true true 11 | 12 | let file_to_string file = 13 | let ch = open_in file in 14 | let s = really_input_string ch (in_channel_length ch) in 15 | close_in ch; 16 | s 17 | 18 | let get_file fname = file_to_string ("data/" ^ fname) 19 | 20 | let string_to_tokens s = tokenize s 21 | 22 | let gen_ast_parse_expr file = get_file file |> tokenize |> parse_expr 23 | 24 | let gen_ast_parse_mutop file = get_file file |> tokenize |> parse_mutop 25 | 26 | let get_expr ast = let _, e = ast in e 27 | 28 | let eval_expr_ast env ast = 29 | let e = ast |> get_expr in eval_expr env e 30 | 31 | let eval_mutop_ast env ast = 32 | let e = ast |> get_expr in eval_mutop env e 33 | 34 | let input_handler f = 35 | try 36 | let _ = f () in assert_failure "Expected InvalidInputException, none received" with 37 | | InvalidInputException(_) -> assert_succeed () 38 | | ex -> assert_failure ("Got " ^ (Printexc.to_string ex) ^ ", expected InvalidInputException") 39 | 40 | let div_by_zero_ex_handler f = 41 | try 42 | let _ = f () in assert_failure "Expected DivByZeroError, none received" with 43 | | DivByZeroError -> assert_succeed () 44 | | ex -> assert_failure ("Got " ^ (Printexc.to_string ex) ^ ", expected DivByZeroError") 45 | 46 | let declare_error_ex_handler f = 47 | try 48 | let _ = f () in assert_failure "Expected DeclareError, none received" with 49 | | DeclareError(_) -> assert_succeed () 50 | | ex -> assert_failure ("Got " ^ (Printexc.to_string ex) ^ ", expected DeclareError") 51 | 52 | let type_error_ex_handler f = 53 | try 54 | let _ = f () in assert_failure "Expected TypeError, none received" with 55 | | TypeError(_) -> assert_succeed () 56 | | ex -> assert_failure ("Got " ^ (Printexc.to_string ex) ^ ", expected TypeError") 57 | 58 | let rec lookup env x = 59 | match env with 60 | | [] -> raise (DeclareError ("Unbound variable " ^ x)) 61 | | (var, value)::t -> if x = var then !value else lookup t x -------------------------------------------------------------------------------- /project5/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | -------------------------------------------------------------------------------- /project5/.submit: -------------------------------------------------------------------------------- 1 | [course] 2 | id = 358171 3 | name = "CMSC330" 4 | term = "Spring 2022" 5 | 6 | [assignment] 7 | id = 2023065 8 | name = "Project 5" 9 | files = [ "src/basics.rs", "src/communicator.rs", "src/linkedlist.rs" ] 10 | -------------------------------------------------------------------------------- /project5/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stark_suit_repair" 3 | version = "0.2.0" 4 | authors = [] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /project5/Gc.adoc: -------------------------------------------------------------------------------- 1 | = Garbage Collection and Synchronization 2 | :source-highlighter: highlight.js 3 | 4 | TIP: TLDR: https://doc.rust-lang.org/std/sync/struct.Arc.html[`Arc`] is like `Rc`, https://doc.rust-lang.org/std/sync/struct.RwLock.html[`RwLock]` is like https://doc.rust-lang.org/std/cell/struct.RefCell.html[`RefCell`] (with `RefCell::borrow` and `RefCell::borrow_mut` mapping to https://doc.rust-lang.org/std/sync/struct.RwLock.html#method.read[`RwLock::read`] and https://doc.rust-lang.org/std/sync/struct.RwLock.html#method.write[`RwLock::write`]). 5 | 6 | https://www.cs.umd.edu/class/spring2022/cmsc330/lectures/09-interior-mutability.pdf[From lecture], we saw how we can dynamically manage memory (using `Rc` and `RefCell`) while still following the rules. 7 | 8 | However, these types do not work across multiple threads. Rc and RefCell use counters to keep track of how many handles exist. These counters only work across a single threaded context (they are not atomic , acquiring handles is not synchronized). Thus, you cannot create an Rc and send it to another thread. 9 | 10 | [source, rust] 11 | ---- 12 | let r1 = Rc::new("uwu".to_owned()); 13 | let r2 = Rc::clone(&r1); 14 | drop(r1); // uwu is kept alive by r2 15 | println!("{}", r2); // and heres the proof 16 | 17 | // alas the next line doesn't compile 18 | thread::spawn(move || { println!("{}", r2); }).join(); 19 | ---- 20 | 21 | Arc and RwLock are the multithreaded equivalents, they use synchronized counters so that multiple threads can increment / decrement. Thus we can create / delete handles across many threads. 22 | 23 | [source, rust] 24 | ---- 25 | let r1 = Arc::new("hello".to_owned()); 26 | let r2 = Arc::clone(&r1); 27 | 28 | thread::spawn(move || { println!("{}", r2); }).join().unwrap(); 29 | 30 | println!("{}", r1); // and heres the proof 31 | 32 | ---- 33 | 34 | Here we have created an Arc on one thread, sent it to another thread, used it there, and deleted it on that thread. All while still keeping an Arc for ourselves on the main thread. 35 | 36 | We can mutate the contents of our Arc with RwLock. Like RefCell, RwLock maintain its own seperate counter for the read and write handles it hands out. Like normal rust references, we can have as many readers at a time as we want, XOR 1 writer XOR no handles at all. Because Arc/Rc deref to their contents, we can call write on the arc itself. This is syntactic sugar, the method is coming from RwLock, not Arc. 37 | 38 | [source, rust] 39 | ---- 40 | let r1 = Arc::new(RwLock::new("hello".to_owned())); 41 | let r2 = Arc::clone(&r1); 42 | 43 | thread::spawn(move || { 44 | // edit the string using r2 45 | let mut w_hand = r2.write().unwrap(); 46 | w_hand.push_str(" world!"); 47 | }) 48 | .join() 49 | .unwrap(); 50 | 51 | // it compiles 🙏 52 | let r_hand = r1.read().unwrap(); 53 | // and abserve the change using r1 54 | println!("{}", r_hand); 55 | ---- 56 | -------------------------------------------------------------------------------- /project5/src/basics.rs: -------------------------------------------------------------------------------- 1 | /** 2 | Returns the sum 1 + 2 + ... + n 3 | If n is less than 0, return -1 4 | **/ 5 | pub fn gauss(n: i32) -> i32 { 6 | unimplemented!() 7 | } 8 | 9 | /** 10 | Returns the number of elements in the list that 11 | are in the range [s,e] 12 | **/ 13 | pub fn in_range(ls: &[i32], s: i32, e: i32) -> i32 { 14 | unimplemented!() 15 | } 16 | 17 | /** 18 | Returns true if target is a subset of set, false otherwise 19 | 20 | Ex: [1,3,2] is a subset of [1,2,3,4,5] 21 | **/ 22 | pub fn subset(set: &[T], target: &[T]) -> bool { 23 | unimplemented!() 24 | } 25 | 26 | /** 27 | Returns the mean of elements in ls. If the list is empty, return None 28 | It might be helpful to use the fold method of the Iterator trait 29 | **/ 30 | pub fn mean(ls: &[f64]) -> Option { 31 | unimplemented!() 32 | } 33 | 34 | /** 35 | Converts a binary number to decimal, where each bit is stored in order in the array 36 | 37 | Ex: to_decimal of [1,0,1,0] returns 10 38 | **/ 39 | pub fn to_decimal(ls: &[i32]) -> i32 { 40 | unimplemented!() 41 | } 42 | 43 | /** 44 | Decomposes an integer into its prime factors and returns them in a vector 45 | You can assume factorize will never be passed anything less than 2 46 | 47 | Ex: factorize of 36 should return [2,2,3,3] since 36 = 2 * 2 * 3 * 3 48 | **/ 49 | pub fn factorize(n: u32) -> Vec { 50 | unimplemented!() 51 | } 52 | 53 | /** 54 | Takes all of the elements of the given slice and creates a new vector. 55 | The new vector takes all the elements of the original and rotates them, 56 | so the first becomes the last, the second becomes first, and so on. 57 | 58 | EX: rotate [1,2,3,4] returns [2,3,4,1] 59 | **/ 60 | pub fn rotate(lst: &[i32]) -> Vec { 61 | unimplemented!() 62 | } 63 | 64 | /** 65 | Returns true if target is a subtring of s, false otherwise 66 | You should not use the contains function of the string library in your implementation 67 | 68 | Ex: "ace" is a substring of "rustacean" 69 | **/ 70 | pub fn substr(s: &String, target: &str) -> bool { 71 | unimplemented!() 72 | } 73 | 74 | /** 75 | Takes a string and returns the first longest substring of consecutive equal characters 76 | 77 | EX: longest_sequence of "ababbba" is Some("bbb") 78 | EX: longest_sequence of "aaabbb" is Some("aaa") 79 | EX: longest_sequence of "xyz" is Some("x") 80 | EX: longest_sequence of "" is None 81 | **/ 82 | pub fn longest_sequence(s: &str) -> Option<&str> { 83 | unimplemented!() 84 | } 85 | -------------------------------------------------------------------------------- /project5/src/communicator.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | #[derive(PartialEq)] 3 | pub enum Command 4 | { 5 | Power(bool,i32), // [Increase/Decrease] power by [number]. 6 | Missiles(bool,i32), // [Increase/Decrease] missiles by [number]. 7 | Shield(bool), // Turn [On/Off] the shield. 8 | Try, // Try calling pepper. 9 | Invalid // [anything else] 10 | } 11 | 12 | 13 | /** 14 | Adds functionality to Command enums 15 | Commands can be converted to strings with the as_str method 16 | 17 | Command | String format 18 | --------------------------------------------------------- 19 | Power | /Power (increased|decreased) by [0-9]+%/ 20 | Missiles | /Missiles (increased|decreased) by [0-9]+/ 21 | Shield | /Shield turned (on|off)/ 22 | Try | /Call attempt failed/ 23 | Invalid | /Not a command/ 24 | **/ 25 | impl Command { 26 | pub fn as_str (&self) -> String { 27 | unimplemented!() 28 | } 29 | } 30 | 31 | /** 32 | Complete this method that converts a string to a command 33 | We list the format of the input strings below 34 | 35 | Command | String format 36 | --------------------------------------------- 37 | Power | /power (inc|dec) [0-9]+/ 38 | Missiles | /(fire|add) [0-9]+ missiles/ 39 | Shield | /shield (on|off)/ 40 | Try | /try calling Miss Potts/ 41 | Invalid | Anything else 42 | **/ 43 | pub fn to_command(s: &str) -> Command { 44 | unimplemented!() 45 | } 46 | -------------------------------------------------------------------------------- /project5/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | 3 | pub mod basics; 4 | pub mod communicator; 5 | pub mod linkedlist; 6 | -------------------------------------------------------------------------------- /project5/src/linkedlist.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | borrow::BorrowMut, 3 | ops::{Deref, DerefMut}, 4 | sync::{Arc, RwLock}, 5 | }; 6 | 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 8 | pub enum Component { 9 | Helmet(bool), //is damaged? 10 | LeftThrusters(bool, i32), //is damaged? How much power left? 11 | RightThrusters(bool, i32), //is damaged? How much power left? 12 | LeftRepulsor(bool, i32), //is damaged? How much power left? 13 | RightRepulsor(bool, i32), //is damaged? How much power left? 14 | ChestPiece(bool, i32), //is damaged? How much power left? 15 | Missiles(i32), //how many missiles left? 16 | ArcReactor(i32), // How much power left? 17 | Wifi(bool), // connected to wifi? 18 | } 19 | 20 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 21 | pub struct Armor { 22 | pub component: Component, 23 | pub version: i32, 24 | } 25 | 26 | // Part 2 27 | 28 | // Students should fill in the Link type themselves. The Node and List types are given as is. 29 | type Link = (); 30 | 31 | struct Node { 32 | data: Armor, 33 | rest: Link, 34 | } 35 | 36 | #[derive(Clone)] 37 | pub struct List { 38 | head_link: Link, 39 | size: usize, 40 | } 41 | 42 | impl List { 43 | pub fn new() -> Self { 44 | unimplemented!() 45 | } 46 | 47 | pub fn size(&self) -> usize { 48 | self.size 49 | } 50 | 51 | pub fn peek(&self) -> Option { 52 | unimplemented!() 53 | } 54 | 55 | pub fn push(&mut self, component: Armor) { 56 | unimplemented!() 57 | } 58 | 59 | pub fn pop(&mut self) -> Option { 60 | unimplemented!() 61 | } 62 | } 63 | 64 | // Part 3 65 | 66 | #[derive(Clone)] 67 | pub struct Suit { 68 | pub armor: List, 69 | pub version: i32, 70 | } 71 | 72 | impl Suit { 73 | pub fn is_compatible(&self) -> bool { 74 | unimplemented!() 75 | } 76 | 77 | pub fn repair(&mut self) { 78 | unimplemented!() 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /project5/tests/student/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate stark_suit_repair; 2 | 3 | /* 4 | * Create a new function for each test that you want to run. Please be sure to add 5 | * the #[test] attribute to each of your student tests to ensure they are all run, and 6 | * prefix them all with 'student_' (see example below). 7 | * Then, run `cargo test student` to run all of the student tests. 8 | */ 9 | 10 | #[test] 11 | fn student_example() { 12 | assert_eq!(true, true); 13 | } 14 | -------------------------------------------------------------------------------- /project5/tests/tests.rs: -------------------------------------------------------------------------------- 1 | mod public; 2 | mod student; 3 | --------------------------------------------------------------------------------