├── 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 | 
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 | 
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 | 
15 |
16 | ## Parsing and Grammars, Explanation
17 | ### 1) Terminals, non-terminals and productions
18 | ### 2) Practice designing grammars
19 | 
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 |
--------------------------------------------------------------------------------