├── 02_ts_tutorial.ipynb
├── 03_pure_functions.ipynb
├── 04_first_class_functions.ipynb
├── 05_closures.ipynb
├── 06_sum_types.ipynb
├── 07_recursion.ipynb
├── 08_algebraic_data_types.ipynb
├── 09_problem_solving.ipynb
├── 10_code_org.ipynb
├── 11_react_intro.ipynb
├── 12_regex.ipynb
├── 13_sql
├── 13_sql.ipynb
├── README.md
├── images
│ ├── costco.png
│ ├── items.png
│ ├── lists-with-counts.png
│ ├── lists.png
│ ├── safeway.png
│ └── xkcd_exploits.png
├── pyvenv.cfg
└── requirements.txt
├── 14_concurrency.ipynb
├── 15_go.ipynb
├── 16_streams_generators.ipynb
├── 17_haskell.ipynb
├── 17_ihaskell_install.md
├── 18_lexing_parsing.ipynb
├── 19_interp.ipynb
├── 20_transpilers_compilers.ipynb
├── 21_metaprogramming.ipynb
├── LICENSE
├── README.md
├── lib
├── draw.ts
├── introspect.ts
├── lambdats
│ ├── README.md
│ ├── cloInterp.ts
│ ├── expr.ts
│ ├── lexer.ts
│ ├── main.ts
│ ├── package.json
│ ├── parser.ts
│ ├── substInterp.ts
│ ├── token.ts
│ ├── transpile.ts
│ └── tsconfig.json
├── list.ts
└── tree.ts
├── media
├── cat-no-meme.jpg
├── concur_vs_parallel.png
├── context.png
├── expression-problem-function.svg
├── expression-problem-type.svg
├── expression-problem.svg
├── heap-oop.svg
├── heap-procedural.svg
├── heap-shapes.svg
├── heap-square.svg
├── making_burger.png
├── monad_burrito.png
└── thread_vs_process.png
├── package.json
└── tmp
├── fib_worker.js
├── haskell.txt
├── hello_world.txt
├── notes.txt
└── worker.js
/12_regex.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "id": "952d8d77",
7 | "metadata": {},
8 | "outputs": [
9 | {
10 | "data": {
11 | "text/html": [
12 | "\n",
13 | " \n",
14 | " "
15 | ]
16 | },
17 | "metadata": {},
18 | "output_type": "display_data"
19 | },
20 | {
21 | "data": {
22 | "text/html": [
23 | "\n",
24 | "\n",
30 | " "
31 | ]
32 | },
33 | "metadata": {},
34 | "output_type": "display_data"
35 | }
36 | ],
37 | "source": [
38 | "import { requireCytoscape, requireCarbon } from \"./lib/draw\";\n",
39 | "\n",
40 | "requireCarbon();\n",
41 | "requireCytoscape();"
42 | ]
43 | },
44 | {
45 | "cell_type": "markdown",
46 | "id": "6cc67c69",
47 | "metadata": {},
48 | "source": [
49 | "# Domain Specific Languages (DSL) and Regular Expressions"
50 | ]
51 | },
52 | {
53 | "cell_type": "markdown",
54 | "id": "ca96d89c",
55 | "metadata": {},
56 | "source": [
57 | "## Where Were We?\n",
58 | "\n",
59 | "1. Language primitives (i.e., building blocks of languages)\n",
60 | "2. **Language paradigms** (i.e., combinations of language primitives)\n",
61 | " - Last time: React\n",
62 | " - This time: **domain specific languages** (DSL)\n",
63 | "3. Building a language (i.e., designing your own language)"
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "id": "b840148a",
69 | "metadata": {},
70 | "source": [
71 | "## Goal\n",
72 | "\n",
73 | "1. Introduce programming with **domain specific languages** (DSLs).\n",
74 | "2. Introduce **regular expressions** as an example of a DSL."
75 | ]
76 | },
77 | {
78 | "cell_type": "markdown",
79 | "id": "c710bc65",
80 | "metadata": {},
81 | "source": [
82 | "## Why DSLs?\n",
83 | "\n",
84 | "1. Unlike a general purpose programming language, a DSL is designed to solve a class of problems in a specific domain. Consequently, a DSL is not necessarily Turing complete.\n",
85 | "2. The downside is that there are some programs that you will not be able to write in a DSL.\n",
86 | "3. The upside is that your programs can have special properties that may be useful for your specific setting."
87 | ]
88 | },
89 | {
90 | "cell_type": "markdown",
91 | "id": "5fcc4f24",
92 | "metadata": {},
93 | "source": [
94 | "## Examples of DSLs\n",
95 | "\n",
96 | "1. Mathematica: solving mathematical equations\n",
97 | "2. Matlab: scientific computing\n",
98 | "3. Gradle: build system\n",
99 | "4. YACC: parser generator\n",
100 | "5. SQL: relational database query language"
101 | ]
102 | },
103 | {
104 | "cell_type": "markdown",
105 | "id": "a492680c",
106 | "metadata": {},
107 | "source": [
108 | "### Mathematica\n",
109 | "\n",
110 | "Example code:\n",
111 | "```\n",
112 | "solve x^2 + 4x + 6 = 0\n",
113 | "```\n",
114 | "\n",
115 | "[https://www.wolframalpha.com/](https://www.wolframalpha.com/)"
116 | ]
117 | },
118 | {
119 | "cell_type": "markdown",
120 | "id": "3af1d23d",
121 | "metadata": {},
122 | "source": [
123 | "### Matlab\n",
124 | "\n",
125 | "\n",
126 | "Example code:\n",
127 | "\n",
128 | "```\n",
129 | "Ts = 1/50;\n",
130 | "t = 0:Ts:10-Ts; \n",
131 | "x = sin(2*pi*15*t) + sin(2*pi*20*t);\n",
132 | "plot(t,x)\n",
133 | "xlabel('Time (seconds)')\n",
134 | "ylabel('Amplitude')\n",
135 | "```\n",
136 | "\n",
137 | "[https://www.mathworks.com/products/matlab.html](https://www.mathworks.com/products/matlab.html)"
138 | ]
139 | },
140 | {
141 | "cell_type": "markdown",
142 | "id": "006fad85",
143 | "metadata": {},
144 | "source": [
145 | "### Gradle\n",
146 | "\n",
147 | "Example code:\n",
148 | "```\n",
149 | "dependencies { \n",
150 | " api(\"junit:junit:4.13\")\n",
151 | " implementation(\"junit:junit:4.13\")\n",
152 | " testImplementation(\"junit:junit:4.13\")\n",
153 | "}\n",
154 | "```\n",
155 | "\n",
156 | "[https://docs.gradle.org/current/userguide/userguide.html](https://docs.gradle.org/current/userguide/userguide.html)"
157 | ]
158 | },
159 | {
160 | "cell_type": "markdown",
161 | "id": "4e17a1e7",
162 | "metadata": {},
163 | "source": [
164 | "### SQL (Structured Query Language)\n",
165 | "\n",
166 | "Example code:\n",
167 | "\n",
168 | "```\n",
169 | "SELECT column1, column2 FROM table1, table2 WHERE column2='value';\n",
170 | "```\n",
171 | "\n",
172 | "[https://www.w3schools.com/sql/](https://www.w3schools.com/sql/)"
173 | ]
174 | },
175 | {
176 | "cell_type": "markdown",
177 | "id": "b8f0e31d",
178 | "metadata": {},
179 | "source": [
180 | "### YACC (Yet Another Compiler Compiler)\n",
181 | "\n",
182 | "Example code:\n",
183 | "```\n",
184 | "input :\n",
185 | " | input line\n",
186 | ";\n",
187 | "line : '\\n'\n",
188 | " | exp '\\n' { printf (\"\\t%.10g\\n\", $1); }\n",
189 | ";\n",
190 | "exp : NUM { $$ = $1; }\n",
191 | " | exp exp '+' { $$ = $1 + $2; }\n",
192 | " | exp exp '-' { $$ = $1 - $2; }\n",
193 | " | exp exp '*' { $$ = $1 * $2; }\n",
194 | " | exp exp '/' { $$ = $1 / $2; }\n",
195 | " /* Exponentiation */\n",
196 | " | exp exp '^' { $$ = pow ($1, $2); }\n",
197 | " /* Unary minus */\n",
198 | " | exp 'n' { $$ = -$1; }\n",
199 | "```\n",
200 | "\n",
201 | "[https://www.cs.ccu.edu.tw/~naiwei/cs5605/YaccBison.html](https://www.cs.ccu.edu.tw/~naiwei/cs5605/YaccBison.html)\n",
202 | "\n",
203 | "Question: does YACC code remind you of something that you have seen in class?"
204 | ]
205 | },
206 | {
207 | "cell_type": "markdown",
208 | "id": "61365733",
209 | "metadata": {},
210 | "source": [
211 | "## Common Problem: Pattern Matching on Strings"
212 | ]
213 | },
214 | {
215 | "cell_type": "markdown",
216 | "id": "d86ae0a2",
217 | "metadata": {},
218 | "source": [
219 | "### Let's start with simple patterns"
220 | ]
221 | },
222 | {
223 | "cell_type": "code",
224 | "execution_count": 2,
225 | "id": "71c0082a",
226 | "metadata": {},
227 | "outputs": [],
228 | "source": [
229 | "const files = [\"hw1.ts\", \"hw1.js\", \"hw2.ts\", \"hw2.js\"];"
230 | ]
231 | },
232 | {
233 | "cell_type": "code",
234 | "execution_count": 3,
235 | "id": "939d2a16",
236 | "metadata": {},
237 | "outputs": [
238 | {
239 | "name": "stdout",
240 | "output_type": "stream",
241 | "text": [
242 | "[ \u001b[32m'hw1.ts'\u001b[39m, \u001b[32m'hw2.ts'\u001b[39m ]\n"
243 | ]
244 | }
245 | ],
246 | "source": [
247 | "// Get all strings that end with \".ts\"\n",
248 | "files.filter((x) => x.endsWith(\".ts\"));"
249 | ]
250 | },
251 | {
252 | "cell_type": "code",
253 | "execution_count": 4,
254 | "id": "193c5468",
255 | "metadata": {},
256 | "outputs": [
257 | {
258 | "name": "stdout",
259 | "output_type": "stream",
260 | "text": [
261 | "[ \u001b[32m'hw1.js'\u001b[39m, \u001b[32m'hw2.js'\u001b[39m ]\n"
262 | ]
263 | }
264 | ],
265 | "source": [
266 | "// Get all strings that end with \".js\"\n",
267 | "files.filter((x) => x.endsWith(\".js\"));"
268 | ]
269 | },
270 | {
271 | "cell_type": "code",
272 | "execution_count": 5,
273 | "id": "796251ff",
274 | "metadata": {},
275 | "outputs": [
276 | {
277 | "name": "stdout",
278 | "output_type": "stream",
279 | "text": [
280 | "[ \u001b[32m'hw1.ts'\u001b[39m, \u001b[32m'hw1.js'\u001b[39m ]\n"
281 | ]
282 | }
283 | ],
284 | "source": [
285 | "// Get all strings that begin with \"hw1\"\n",
286 | "files.filter((x) => x.startsWith(\"hw1\"));"
287 | ]
288 | },
289 | {
290 | "cell_type": "code",
291 | "execution_count": 6,
292 | "id": "5c8dfe7f",
293 | "metadata": {},
294 | "outputs": [
295 | {
296 | "name": "stdout",
297 | "output_type": "stream",
298 | "text": [
299 | "[ \u001b[32m'hw1.ts'\u001b[39m ]\n"
300 | ]
301 | }
302 | ],
303 | "source": [
304 | "// Get all strings that begin with \"hw1\" and endsWith \".ts\"\n",
305 | "files.filter((x) => x.startsWith(\"hw1\") && x.endsWith(\".ts\"));"
306 | ]
307 | },
308 | {
309 | "cell_type": "markdown",
310 | "id": "eb665b3d",
311 | "metadata": {},
312 | "source": [
313 | "### More complex patterns\n",
314 | "\n",
315 | "Suppose you want to check if phone numbers are valid."
316 | ]
317 | },
318 | {
319 | "cell_type": "code",
320 | "execution_count": 7,
321 | "id": "cfe20d3e",
322 | "metadata": {},
323 | "outputs": [],
324 | "source": [
325 | "const phoneNumbers = [\"123-456-7890\", \"(123) 456-7890\", \"1234567890\", \"+1 1234567890\"]; // phone numbers"
326 | ]
327 | },
328 | {
329 | "cell_type": "code",
330 | "execution_count": 8,
331 | "id": "9bb7db4c",
332 | "metadata": {},
333 | "outputs": [
334 | {
335 | "name": "stdout",
336 | "output_type": "stream",
337 | "text": [
338 | "1234567890\n"
339 | ]
340 | }
341 | ],
342 | "source": [
343 | "function replaceAll(s: string, find: string, replace: string): string {\n",
344 | " let prev = s;\n",
345 | " let curr = s.replace(find, replace);\n",
346 | " while (prev !== curr) {\n",
347 | " prev = curr;\n",
348 | " curr = curr.replace(find, replace);\n",
349 | " }\n",
350 | " return curr;\n",
351 | "}\n",
352 | "replaceAll(\"123-456-7890\", \"-\", \"\")"
353 | ]
354 | },
355 | {
356 | "cell_type": "code",
357 | "execution_count": 9,
358 | "id": "7a6aceec",
359 | "metadata": {},
360 | "outputs": [
361 | {
362 | "name": "stdout",
363 | "output_type": "stream",
364 | "text": [
365 | "[ \u001b[32m'1234567890'\u001b[39m, \u001b[32m'1234567890'\u001b[39m, \u001b[32m'1234567890'\u001b[39m, \u001b[32m'11234567890'\u001b[39m ]\n"
366 | ]
367 | }
368 | ],
369 | "source": [
370 | "const phoneNumbers2 = phoneNumbers.map((x) => replaceAll(x, \"-\", \"\"))\n",
371 | " .map((x) => replaceAll(x, \" \", \"\"))\n",
372 | " .map((x) => replaceAll(x, \"(\", \"\"))\n",
373 | " .map((x) => replaceAll(x, \")\", \"\"))\n",
374 | " .map((x) => replaceAll(x, \"+\", \"\"))\n",
375 | "phoneNumbers2"
376 | ]
377 | },
378 | {
379 | "cell_type": "code",
380 | "execution_count": 10,
381 | "id": "74e1f53c",
382 | "metadata": {},
383 | "outputs": [],
384 | "source": [
385 | "function isNumber(s: string): boolean {\n",
386 | " for (const c of s) {\n",
387 | " if (! (c === \"0\" || c === \"1\" || c === \"2\" || c === \"3\" || c === \"4\" ||\n",
388 | " c === \"5\" || c === \"6\" || c === \"7\" || c === \"8\" || c === \"9\") ) {\n",
389 | " return false;\n",
390 | " }\n",
391 | " }\n",
392 | " return true\n",
393 | "}"
394 | ]
395 | },
396 | {
397 | "cell_type": "code",
398 | "execution_count": 11,
399 | "id": "0dd17c6d",
400 | "metadata": {},
401 | "outputs": [
402 | {
403 | "name": "stdout",
404 | "output_type": "stream",
405 | "text": [
406 | "[ \u001b[32m'1234567890'\u001b[39m, \u001b[32m'1234567890'\u001b[39m, \u001b[32m'1234567890'\u001b[39m, \u001b[32m'11234567890'\u001b[39m ]\n"
407 | ]
408 | }
409 | ],
410 | "source": [
411 | "phoneNumbers2.filter(isNumber)"
412 | ]
413 | },
414 | {
415 | "cell_type": "markdown",
416 | "id": "a4bd709f",
417 | "metadata": {},
418 | "source": [
419 | "### Problems with Solution\n",
420 | "\n",
421 | "1. Loses information:\n",
422 | " * +1 signifies country code\n",
423 | " * (123) signifies area code\n",
424 | "2. This information may be useful for checking the validity of phone numbers, e.g., not all 10 digit numbers are valid phone numbers. The grouping of the numbers gives geographic information.\n",
425 | "3. Does not enforce that phone numbers have a certain number of digits. For example, is \"12389348762342134\" an area code?"
426 | ]
427 | },
428 | {
429 | "cell_type": "markdown",
430 | "id": "749f56b1",
431 | "metadata": {},
432 | "source": [
433 | "### Other examples of common patterns\n",
434 | "\n",
435 | "1. URLs\n",
436 | " * https://www.google.com, www.google.com\n",
437 | "2. ZIP codes\n",
438 | " * 12345 vs. 12345-678\n",
439 | "3. Valid variable names in a programming language\n",
440 | " * Cannot start variables with a number in TypeScript\n",
441 | "4. Extract emails and links from text"
442 | ]
443 | },
444 | {
445 | "cell_type": "markdown",
446 | "id": "4f5c9a42",
447 | "metadata": {},
448 | "source": [
449 | "## What is the idea of a DSL?\n",
450 | "\n",
451 | "Claim: Using string functions + general-purpose code is a no go for several reasons.\n",
452 | "1. It requires a non-programmer to know how to program in a general-purpose language.\n",
453 | "2. The non-programmer may find a more familar syntax easier to understand.\n",
454 | "3. Therefore, we should design a language that is more familiar and easier to use."
455 | ]
456 | },
457 | {
458 | "cell_type": "markdown",
459 | "id": "30cae047",
460 | "metadata": {},
461 | "source": [
462 | "## Regular Expressions\n",
463 | "\n",
464 | "1. Addresses the string matching problem, thus useful.\n",
465 | "2. Rich connections to formal language theory.\n",
466 | " * Take CSC 520.\n",
467 | " * [https://en.wikipedia.org/wiki/Chomsky_hierarchy](https://en.wikipedia.org/wiki/Chomsky_hierarchy)\n",
468 | " * Example of a DSL designed by computer scientists for computer scientists."
469 | ]
470 | },
471 | {
472 | "cell_type": "markdown",
473 | "id": "81aa6695",
474 | "metadata": {},
475 | "source": [
476 | "### Regular Expressions"
477 | ]
478 | },
479 | {
480 | "cell_type": "markdown",
481 | "id": "c2547fee",
482 | "metadata": {},
483 | "source": [
484 | "### Regular Expressions for File Extensions"
485 | ]
486 | },
487 | {
488 | "cell_type": "code",
489 | "execution_count": 12,
490 | "id": "cdf18f97",
491 | "metadata": {},
492 | "outputs": [],
493 | "source": [
494 | "let regexpFileExt: RegExp = /.*\\.ts$/;"
495 | ]
496 | },
497 | {
498 | "cell_type": "code",
499 | "execution_count": 13,
500 | "id": "68d7bcd7",
501 | "metadata": {},
502 | "outputs": [
503 | {
504 | "name": "stdout",
505 | "output_type": "stream",
506 | "text": [
507 | "[ \u001b[32m'.ts'\u001b[39m, \u001b[32m'hw2.ts'\u001b[39m ]\n"
508 | ]
509 | }
510 | ],
511 | "source": [
512 | "const files = [\".ts\", \"hw1.js\", \"hw2.ts\", \"hw2.js\"];\n",
513 | "files.filter((x) => regexpFileExt.test(x))"
514 | ]
515 | },
516 | {
517 | "cell_type": "markdown",
518 | "id": "49e3b163",
519 | "metadata": {},
520 | "source": [
521 | "Key\n",
522 | "\n",
523 | "1. `/` and `/` signify the start and end of a regular expression similar to \"\" for strings.\n",
524 | "2. `.` is a **wildcard**, i.e., it matches any character.\n",
525 | "3. `\\.` escapes `.` so that it matches a literal period similar to escaping characters in a string.\n",
526 | "4. `t` and `s` stand for literal characters to match.\n",
527 | "5. `$` means end of string."
528 | ]
529 | },
530 | {
531 | "cell_type": "markdown",
532 | "id": "8f71216c",
533 | "metadata": {},
534 | "source": [
535 | "### Regular Expressions for Phone Numbers"
536 | ]
537 | },
538 | {
539 | "cell_type": "markdown",
540 | "id": "59d9e916",
541 | "metadata": {},
542 | "source": [
543 | "#### Phone numbers 1"
544 | ]
545 | },
546 | {
547 | "cell_type": "code",
548 | "execution_count": 14,
549 | "id": "d45dfb32",
550 | "metadata": {},
551 | "outputs": [],
552 | "source": [
553 | "const phoneNumbers = [\"123-456-7890\", \"(123) 456-7890\", \"1234567890\", \"+1 1234567890\"]; // phone numbers"
554 | ]
555 | },
556 | {
557 | "cell_type": "code",
558 | "execution_count": 15,
559 | "id": "84d7ae34",
560 | "metadata": {},
561 | "outputs": [
562 | {
563 | "name": "stdout",
564 | "output_type": "stream",
565 | "text": [
566 | "[ \u001b[32m'1234567890'\u001b[39m ]\n"
567 | ]
568 | }
569 | ],
570 | "source": [
571 | "let regexpPhone: RegExp = /^[0-9]{3}[0-9]{3}[0-9]{4}$/;\n",
572 | "phoneNumbers.filter((x) => regexpPhone.test(x));"
573 | ]
574 | },
575 | {
576 | "cell_type": "markdown",
577 | "id": "9fe9da81",
578 | "metadata": {},
579 | "source": [
580 | "Key\n",
581 | "\n",
582 | "1. `^` means start of string.\n",
583 | "3. `[0-9]` means every character between `0` and `9`.\n",
584 | "4. `{x}` means exactly x matches of the preceeding expression."
585 | ]
586 | },
587 | {
588 | "cell_type": "markdown",
589 | "id": "83142a8a",
590 | "metadata": {},
591 | "source": [
592 | "#### Phone numbers 2"
593 | ]
594 | },
595 | {
596 | "cell_type": "code",
597 | "execution_count": 16,
598 | "id": "4ed78b45",
599 | "metadata": {},
600 | "outputs": [
601 | {
602 | "name": "stdout",
603 | "output_type": "stream",
604 | "text": [
605 | "[ \u001b[32m'123-456-7890'\u001b[39m, \u001b[32m'1234567890'\u001b[39m ]\n"
606 | ]
607 | }
608 | ],
609 | "source": [
610 | "let regexpPhone2: RegExp = /^[0-9]{3}\\s*-?\\s*[0-9]{3}\\s*-?\\s*[0-9]{4}$/;\n",
611 | "phoneNumbers.filter((x) => regexpPhone2.test(x));"
612 | ]
613 | },
614 | {
615 | "cell_type": "markdown",
616 | "id": "b86e8aa8",
617 | "metadata": {},
618 | "source": [
619 | "Key\n",
620 | "\n",
621 | "1. `\\s` means any white space character.\n",
622 | "2. `*` means 0 or more occurrences of previous character.\n",
623 | "3. `?` means 0 or 1 occurrences of previous character."
624 | ]
625 | },
626 | {
627 | "cell_type": "markdown",
628 | "id": "d953d94e",
629 | "metadata": {},
630 | "source": [
631 | "#### Phone numbers 3"
632 | ]
633 | },
634 | {
635 | "cell_type": "code",
636 | "execution_count": 17,
637 | "id": "78a4687f",
638 | "metadata": {},
639 | "outputs": [],
640 | "source": [
641 | "let regexpPhone3: RegExp = /^(\\d{3})|(\\(\\d{3}\\))\\s*-?\\s*\\d{3}\\s*-?\\s*\\d{4}$/;"
642 | ]
643 | },
644 | {
645 | "cell_type": "code",
646 | "execution_count": 18,
647 | "id": "b3bab47e",
648 | "metadata": {},
649 | "outputs": [
650 | {
651 | "name": "stdout",
652 | "output_type": "stream",
653 | "text": [
654 | "[ \u001b[32m'123-456-7890'\u001b[39m, \u001b[32m'(123) 456-7890'\u001b[39m, \u001b[32m'1234567890'\u001b[39m ]\n"
655 | ]
656 | }
657 | ],
658 | "source": [
659 | "phoneNumbers.filter((x) => regexpPhone3.test(x))"
660 | ]
661 | },
662 | {
663 | "cell_type": "markdown",
664 | "id": "a2b02e34",
665 | "metadata": {},
666 | "source": [
667 | "Key\n",
668 | "\n",
669 | "1. `|` means either the left or the right should match.\n",
670 | "2. `\\d` = `[0-9]`\n",
671 | "3. `\\(` means match the literal `(` because it's part of the regular expression language."
672 | ]
673 | },
674 | {
675 | "cell_type": "markdown",
676 | "id": "26ce1828",
677 | "metadata": {},
678 | "source": [
679 | "#### Phone numbers 4"
680 | ]
681 | },
682 | {
683 | "cell_type": "code",
684 | "execution_count": 19,
685 | "id": "97d4ea81",
686 | "metadata": {},
687 | "outputs": [],
688 | "source": [
689 | "let regexpPhone4: RegExp = /^(\\+\\d+)?\\s*(\\d{3})|(\\(\\d{3}\\))\\s*-?\\s*\\d{3}\\s*-?\\s*\\d{4}$/;"
690 | ]
691 | },
692 | {
693 | "cell_type": "code",
694 | "execution_count": 20,
695 | "id": "47105add",
696 | "metadata": {},
697 | "outputs": [
698 | {
699 | "name": "stdout",
700 | "output_type": "stream",
701 | "text": [
702 | "[ \u001b[32m'123-456-7890'\u001b[39m, \u001b[32m'(123) 456-7890'\u001b[39m, \u001b[32m'1234567890'\u001b[39m, \u001b[32m'+1 1234567890'\u001b[39m ]\n"
703 | ]
704 | }
705 | ],
706 | "source": [
707 | "phoneNumbers.filter((x) => regexpPhone4.test(x))"
708 | ]
709 | },
710 | {
711 | "cell_type": "markdown",
712 | "id": "18e79c67",
713 | "metadata": {},
714 | "source": [
715 | "Key\n",
716 | "\n",
717 | "1. `+` means 1 or more occurrences"
718 | ]
719 | },
720 | {
721 | "cell_type": "markdown",
722 | "id": "e055d0e8",
723 | "metadata": {},
724 | "source": [
725 | "### Regular Expressions for Emails"
726 | ]
727 | },
728 | {
729 | "cell_type": "code",
730 | "execution_count": 21,
731 | "id": "140e0d76",
732 | "metadata": {},
733 | "outputs": [
734 | {
735 | "name": "stdout",
736 | "output_type": "stream",
737 | "text": [
738 | "\u001b[33mtrue\u001b[39m\n",
739 | "\u001b[33mfalse\u001b[39m\n",
740 | "\u001b[33mtrue\u001b[39m\n"
741 | ]
742 | }
743 | ],
744 | "source": [
745 | "let regexpEmail: RegExp = /^[\\w.-]+@[\\w.-]+$/;\n",
746 | "console.log(regexpEmail.test(\"bob@sfsu.edu\"));\n",
747 | "console.log(regexpEmail.test(\"bobsfsu.edu\"));\n",
748 | "console.log(regexpEmail.test(\"bob@sfsu\"));"
749 | ]
750 | },
751 | {
752 | "cell_type": "markdown",
753 | "id": "b6952fcc",
754 | "metadata": {},
755 | "source": [
756 | "### Regular Expression Summary\n",
757 | "\n",
758 | "1. `/` and `/` signify the start and end of a regular expression similar to \"\" for strings.\n",
759 | "2. `$` means end of string.\n",
760 | "3. `^` means start of string.\n",
761 | "\n",
762 | "4. `t` and `s` stand for literal characters to match.\n",
763 | "5. `.` is a **wildcard**, i.e., it matches any character.\n",
764 | "6. `\\.` escapes `.` so that it matches a literal period similar to escaping characters in a string.\n",
765 | "\n",
766 | "7. `\\s` means any white space character.\n",
767 | "8. `\\d` = `[0-9]`\n",
768 | "9. `[0-9]` means every character between `0` and `9`.\n",
769 | "\n",
770 | "10. `|` means either the left or the right should match.\n",
771 | "11. `{x}` means exactly x matches of the preceeding expression.\n",
772 | "12. `*` means 0 or more occurrences of previous character.\n",
773 | "13. `?` means 0 or 1 occurrences of previous character.\n",
774 | "14. `+` means 1 or more occurrences"
775 | ]
776 | },
777 | {
778 | "cell_type": "markdown",
779 | "id": "3ba99999",
780 | "metadata": {},
781 | "source": [
782 | "### Implementing Regular Expressions"
783 | ]
784 | },
785 | {
786 | "cell_type": "code",
787 | "execution_count": 22,
788 | "id": "ed99d297",
789 | "metadata": {},
790 | "outputs": [],
791 | "source": [
792 | "type RegExp =\n",
793 | " | { tag: \"VOID\" }\n",
794 | " | { tag: \"EMPTY\" } // \"\"\n",
795 | " | { tag: \"CHAR\", char: string } // match specific character, i.e., character case\n",
796 | " | { tag: \"STAR\", re: RegExp } // match any number, i.e., *\n",
797 | " | { tag: \"CONCAT\", re1: RegExp, re2: RegExp } // match re1 followed by re2, i.e., (re1)(re2)\n",
798 | " | { tag: \"OR\", re1: RegExp, re2: RegExp } // match re1 or match re2, i.e., |"
799 | ]
800 | },
801 | {
802 | "cell_type": "code",
803 | "execution_count": 23,
804 | "id": "416757ae",
805 | "metadata": {},
806 | "outputs": [],
807 | "source": [
808 | "function newVoid(): RegExp { return { tag: \"VOID\" } }\n",
809 | "function newEmpty(): RegExp { return { tag: \"EMPTY\" } }\n",
810 | "function newChar(char: string): RegExp { return { tag: \"CHAR\", char: char } }\n",
811 | "function newStar(re: RegExp): RegExp { return { tag: \"STAR\", re: re } }\n",
812 | "function newConcat(re1: RegExp, re2: RegExp): RegExp { return { tag: \"CONCAT\", re1: re1, re2: re2 } }\n",
813 | "function newOr(re1: RegExp, re2: RegExp): RegExp { return { tag: \"OR\", re1: re1, re2: re2 } }"
814 | ]
815 | },
816 | {
817 | "cell_type": "code",
818 | "execution_count": 24,
819 | "id": "22b487ca",
820 | "metadata": {},
821 | "outputs": [],
822 | "source": [
823 | "// \"asdfasdfasdf\"\n",
824 | "// [a, s, d, f, a, s, d, f, a, s, d, f]\n",
825 | "\n",
826 | "function regexpTest(arr: string[], re: RegExp): boolean {\n",
827 | " switch (re.tag) {\n",
828 | " case \"VOID\": {\n",
829 | " return false;\n",
830 | " }\n",
831 | " case \"EMPTY\": {\n",
832 | " return arr.length === 0;\n",
833 | " }\n",
834 | " case \"CHAR\": {\n",
835 | " return arr.length === 1 ? arr[0] === re.char : false;\n",
836 | " }\n",
837 | " case \"STAR\": {\n",
838 | " if (arr.length === 0) {\n",
839 | " return true;\n",
840 | " }\n",
841 | " for (let i = 1; i <= arr.length; i++) {\n",
842 | " let arr2 = arr.slice(0, i);\n",
843 | " let count = 1;\n",
844 | " while (regexpTest(arr2, re.re) && arr2.length === i) {\n",
845 | " arr2 = arr.slice(count*i, (count+1)*i);\n",
846 | " count += 1;\n",
847 | " }\n",
848 | " if (arr2.length === 0) {\n",
849 | " return true;\n",
850 | " }\n",
851 | " }\n",
852 | " return false;\n",
853 | " }\n",
854 | " case \"CONCAT\": { \n",
855 | " for (let i = 0; i <= arr.length; i++) {\n",
856 | " const left = arr.slice(0, i);\n",
857 | " const right = arr.slice(i);\n",
858 | " if (regexpTest(left, re.re1) && regexpTest(right, re.re2)) {\n",
859 | " return true;\n",
860 | " }\n",
861 | " }\n",
862 | " return false;\n",
863 | " }\n",
864 | " case \"OR\": {\n",
865 | " return regexpTest(arr, re.re1) || regexpTest(arr, re.re2);\n",
866 | " }\n",
867 | " }\n",
868 | "}"
869 | ]
870 | },
871 | {
872 | "cell_type": "code",
873 | "execution_count": 25,
874 | "id": "b57c29f6",
875 | "metadata": {},
876 | "outputs": [
877 | {
878 | "name": "stdout",
879 | "output_type": "stream",
880 | "text": [
881 | "\u001b[33mtrue\u001b[39m\n",
882 | "\u001b[33mfalse\u001b[39m\n",
883 | "\u001b[33mfalse\u001b[39m\n"
884 | ]
885 | }
886 | ],
887 | "source": [
888 | "const re1 = newConcat(newChar('a'), newConcat(newChar('b'), newChar('c')));\n",
889 | "console.log(regexpTest(['a', 'b', 'c'], re1))\n",
890 | "console.log(regexpTest(['a', 'b'], re1))\n",
891 | "console.log(regexpTest(['a', 'a', 'b', 'c'], re1))"
892 | ]
893 | },
894 | {
895 | "cell_type": "code",
896 | "execution_count": 26,
897 | "id": "b1f5df18",
898 | "metadata": {},
899 | "outputs": [
900 | {
901 | "name": "stdout",
902 | "output_type": "stream",
903 | "text": [
904 | "\u001b[33mtrue\u001b[39m\n",
905 | "\u001b[33mtrue\u001b[39m\n",
906 | "\u001b[33mfalse\u001b[39m\n"
907 | ]
908 | }
909 | ],
910 | "source": [
911 | "const re2_ = newConcat(newChar('a'), newChar('b'));\n",
912 | "const re2 = newOr(re1, re2_);\n",
913 | "console.log(regexpTest(['a', 'b', 'c'], re2))\n",
914 | "console.log(regexpTest(['a', 'b'], re2))\n",
915 | "console.log(regexpTest(['a', 'a', 'b', 'c'], re2))"
916 | ]
917 | },
918 | {
919 | "cell_type": "code",
920 | "execution_count": 27,
921 | "id": "82c07435",
922 | "metadata": {},
923 | "outputs": [
924 | {
925 | "name": "stdout",
926 | "output_type": "stream",
927 | "text": [
928 | "\u001b[33mtrue\u001b[39m\n",
929 | "\u001b[33mtrue\u001b[39m\n",
930 | "\u001b[33mtrue\u001b[39m\n",
931 | "\u001b[33mtrue\u001b[39m\n",
932 | "\u001b[33mfalse\u001b[39m\n"
933 | ]
934 | }
935 | ],
936 | "source": [
937 | "const re3 = newStar(re1);\n",
938 | "console.log(regexpTest([], re3))\n",
939 | "console.log(regexpTest(['a', 'b', 'c'], re3))\n",
940 | "console.log(regexpTest(['a', 'b', 'c', 'a', 'b', 'c'], re3))\n",
941 | "console.log(regexpTest(['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'], re3))\n",
942 | "console.log(regexpTest(['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c', 'a'], re3))"
943 | ]
944 | },
945 | {
946 | "cell_type": "markdown",
947 | "id": "7c39fe72",
948 | "metadata": {},
949 | "source": [
950 | "## Summary\n",
951 | "\n",
952 | "1. We introduced the idea of a DSL and saw many examples of DSLs in different domains.\n",
953 | "2. We focused on **regular expressions** as a DSL central to computer science.\n",
954 | "3. Regular expressions can be used for matching patterns on strings."
955 | ]
956 | },
957 | {
958 | "cell_type": "code",
959 | "execution_count": null,
960 | "id": "5f89a8ac",
961 | "metadata": {},
962 | "outputs": [],
963 | "source": []
964 | }
965 | ],
966 | "metadata": {
967 | "kernelspec": {
968 | "display_name": "TypeScript",
969 | "language": "typescript",
970 | "name": "typescript"
971 | },
972 | "language_info": {
973 | "codemirror_mode": {
974 | "mode": "typescript",
975 | "name": "javascript",
976 | "typescript": true
977 | },
978 | "file_extension": ".ts",
979 | "mimetype": "text/typescript",
980 | "name": "typescript",
981 | "version": "3.7.2"
982 | }
983 | },
984 | "nbformat": 4,
985 | "nbformat_minor": 5
986 | }
987 |
--------------------------------------------------------------------------------
/13_sql/README.md:
--------------------------------------------------------------------------------
1 |
2 | This directory has the SQL lesson. It lets you run SQL commands in Jupyter Lab.
3 | These commands hit an in-memory SQLite database that's constructed by the code
4 | in the notebook. If you wreck the database, run all the notebook code again
5 | from the top.
6 |
7 | To set up this directory after cloning the repo:
8 |
9 | % python3 -m venv venv
10 | % source venv/bin/activate
11 | (sql) % rehash
12 | (sql) % pip install -r requirements.txt
13 | (sql) % rehash
14 |
15 | To run the notebook:
16 |
17 | % source venv/bin/activate
18 | (sql) % rehash
19 | (sql) % jupyter lab
20 |
21 | To create this directory from scratch (from the `csc600` directory):
22 |
23 | % mkdir sql
24 | % cd sql
25 | % python3 -m venv venv
26 | % source venv/bin/activate
27 | (sql) % rehash
28 | (sql) % pip install jupyterlab ipython-sql
29 | (sql) % pip freeze > requirements.txt
30 |
31 |
--------------------------------------------------------------------------------
/13_sql/images/costco.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danehuang/PAPL/95893cf97e0195b32b7f5cb2d32c013ec7223008/13_sql/images/costco.png
--------------------------------------------------------------------------------
/13_sql/images/items.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danehuang/PAPL/95893cf97e0195b32b7f5cb2d32c013ec7223008/13_sql/images/items.png
--------------------------------------------------------------------------------
/13_sql/images/lists-with-counts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danehuang/PAPL/95893cf97e0195b32b7f5cb2d32c013ec7223008/13_sql/images/lists-with-counts.png
--------------------------------------------------------------------------------
/13_sql/images/lists.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danehuang/PAPL/95893cf97e0195b32b7f5cb2d32c013ec7223008/13_sql/images/lists.png
--------------------------------------------------------------------------------
/13_sql/images/safeway.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danehuang/PAPL/95893cf97e0195b32b7f5cb2d32c013ec7223008/13_sql/images/safeway.png
--------------------------------------------------------------------------------
/13_sql/images/xkcd_exploits.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danehuang/PAPL/95893cf97e0195b32b7f5cb2d32c013ec7223008/13_sql/images/xkcd_exploits.png
--------------------------------------------------------------------------------
/13_sql/pyvenv.cfg:
--------------------------------------------------------------------------------
1 | home = /usr/local/opt/python@3.9/bin
2 | include-system-site-packages = false
3 | version = 3.9.4
4 |
--------------------------------------------------------------------------------
/13_sql/requirements.txt:
--------------------------------------------------------------------------------
1 | anyio==3.3.4
2 | appnope==0.1.2
3 | argon2-cffi==21.1.0
4 | attrs==21.2.0
5 | Babel==2.9.1
6 | backcall==0.2.0
7 | bleach==4.1.0
8 | certifi==2021.10.8
9 | cffi==1.15.0
10 | charset-normalizer==2.0.7
11 | debugpy==1.5.1
12 | decorator==5.1.0
13 | defusedxml==0.7.1
14 | entrypoints==0.3
15 | greenlet==1.1.2
16 | idna==3.3
17 | ipykernel==6.4.2
18 | ipython==7.28.0
19 | ipython-genutils==0.2.0
20 | ipython-sql==0.4.0
21 | jedi==0.18.0
22 | Jinja2==3.0.2
23 | json5==0.9.6
24 | jsonschema==4.1.2
25 | jupyter-client==7.0.6
26 | jupyter-core==4.8.1
27 | jupyter-server==1.11.1
28 | jupyterlab==3.2.1
29 | jupyterlab-pygments==0.1.2
30 | jupyterlab-server==2.8.2
31 | MarkupSafe==2.0.1
32 | matplotlib-inline==0.1.3
33 | mistune==0.8.4
34 | nbclassic==0.3.3
35 | nbclient==0.5.4
36 | nbconvert==6.2.0
37 | nbformat==5.1.3
38 | nest-asyncio==1.5.1
39 | notebook==6.4.5
40 | packaging==21.0
41 | pandocfilters==1.5.0
42 | parso==0.8.2
43 | pexpect==4.8.0
44 | pickleshare==0.7.5
45 | prettytable==0.7.2
46 | prometheus-client==0.11.0
47 | prompt-toolkit==3.0.21
48 | ptyprocess==0.7.0
49 | pycparser==2.20
50 | Pygments==2.10.0
51 | pyparsing==3.0.0
52 | pyrsistent==0.18.0
53 | python-dateutil==2.8.2
54 | pytz==2021.3
55 | pyzmq==22.3.0
56 | requests==2.26.0
57 | requests-unixsocket==0.2.0
58 | Send2Trash==1.8.0
59 | six==1.16.0
60 | sniffio==1.2.0
61 | SQLAlchemy==1.4.26
62 | sqlparse==0.4.2
63 | terminado==0.12.1
64 | testpath==0.5.0
65 | tornado==6.1
66 | traitlets==5.1.0
67 | urllib3==1.26.7
68 | wcwidth==0.2.5
69 | webencodings==0.5.1
70 | websocket-client==1.2.1
71 |
--------------------------------------------------------------------------------
/17_ihaskell_install.md:
--------------------------------------------------------------------------------
1 | Install instructions
2 | 1. Only works with x86_64 architecture (used homebrew in x86_64 mode)
3 | 2. Works with ghc8.7.10
4 | 3. Change `stack.yaml` to comment out `cairo` dependencies
5 | ```
6 | packages:
7 | - .
8 | - ./ipython-kernel
9 | - ./ghc-parser
10 | - ./ihaskell-display/ihaskell-aeson
11 | - ./ihaskell-display/ihaskell-blaze
12 | # - ./ihaskell-display/ihaskell-charts
13 | # - ./ihaskell-display/ihaskell-diagrams
14 | - ./ihaskell-display/ihaskell-gnuplot
15 | - ./ihaskell-display/ihaskell-graphviz
16 | - ./ihaskell-display/ihaskell-hatex
17 | - ./ihaskell-display/ihaskell-juicypixels
18 | - ./ihaskell-display/ihaskell-magic
19 | # - ./ihaskell-display/ihaskell-plot
20 | # - ./ihaskell-display/ihaskell-static-canvas
21 | - ./ihaskell-display/ihaskell-widgets
22 | ```
23 | 4. Use IHaskell stack instructions
24 |
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2022 Daniel E Huang and Others
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Programming and Programming Languages
2 |
3 | This repository contains lecture notes ([TypeScript](https://www.typescriptlang.org/) Jupyter notebooks) for a 1 semester upper-level course on programming and programming languages. The basis for the lecture notes is a course taught at San Francisco State University (SFSU) in Fall 2021 and Spring 2022.
4 |
5 |
6 | ## Why this Class?
7 |
8 | The hope of this class is to expose you to different ways of programming and how different programming languages support these ways of programming. Such a class is often taught using a non-main-stream langauge (e.g.,Scheme). Instead, we're using TypeScript, a superset of JavaScript. The advantage is that you'll come away learning a popular language that is widely used in web programming.
9 |
10 |
11 | ### Contents
12 |
13 | 1. Language primitives (i.e., building blocks of languages): lecture 02 - lecture 10.
14 | - pure functions and first-class functions
15 | - recursion
16 | - algebraic data-types
17 | - functional vs. object-oriented programming
18 | 2. Language paradigms (i.e., combinations of language primitives): lecture 11 - lecture 17.
19 | - domain-specific languages and declarative programming
20 | - concurrency
21 | - laziness
22 | 3. Building a language (i.e., designing your own language): lecture 18 - lecture 21.
23 | - interpreters and transpilers
24 | - meta-programming
25 |
26 |
27 | #### Special Lectures
28 |
29 | 1. Lecture 13 explores [Sql](https://www.w3schools.com/sql/)
30 | 2. Lecture 15 explores [Go](https://go.dev/)
31 | 3. Lecture 17 explores [Haskell](https://www.haskell.org/)
32 |
33 |
34 | ### Why Jupyter + TypeScript?
35 |
36 | Why not slides? Simply put, slides are a polished product that are not interactive and often do not show the thought proces and trial/error that goes into coming up with a piece of code that works. Jupyter + TypeScript enables you to explore concepts with actual code that you can modify.
37 |
38 |
39 | ## Requirements
40 |
41 | 1. [Jupyter Notebook](https://jupyter.org/)
42 | 2. [Node/JS](https://nodejs.org/en/)
43 |
44 |
45 | ## Usage
46 |
47 | 1. `npm install` (first time)
48 | 2. `jupyter notebook`
49 |
50 |
51 | ## Acknowledgements
52 |
53 | 1. Lawrence Kesteloot, Becker Polverini, and Aaron Bembenek.
54 | 2. The students at SFSU.
55 |
56 |
--------------------------------------------------------------------------------
/lib/draw.ts:
--------------------------------------------------------------------------------
1 | import * as tslab from 'tslab';
2 | import * as util from 'util';
3 | import { v4 as uuidv4 } from 'uuid';
4 |
5 | import * as list from './list';
6 | import * as tree from './tree';
7 | import * as introspect from './introspect';
8 |
9 |
10 |
11 | /* ************************************************************************** */
12 | /* Requiring CSS */
13 | /* ************************************************************************** */
14 |
15 | export function requireCarbon() {
16 | tslab.display.html(`
17 |
18 | `);
19 | }
20 |
21 | export function requireCytoscape() {
22 | tslab.display.html(`
23 |
29 | `);
30 | }
31 |
32 |
33 | /* ************************************************************************** */
34 | /* Draw Data-Structure Functions */
35 | /* ************************************************************************** */
36 |
37 | export const nodeStyle = `
38 | {
39 | selector: 'node',
40 | css: {
41 | 'class': ".bx--tree",
42 | 'label': 'data(label)',
43 | 'height': '10px',
44 | 'width': '10px',
45 | }
46 | },
47 | {
48 | selector: 'edge',
49 | css: {
50 | 'width': 3,
51 | // 'line-color': '#ccc123',
52 | 'curve-style': 'bezier',
53 | 'target-arrow-shape': 'triangle',
54 | 'target-arrow-fill': 'filled',
55 | 'arrow-scale': 1,
56 | }
57 | }
58 | `;
59 |
60 | export function draw(elems, width=800, height=350, layout=listLayout) {
61 | const divId = "mydiv" + uuidv4();
62 | tslab.display.html(`
63 |
71 |
72 | `);
73 | tslab.display.html(`
74 |
86 | `);
87 | }
88 |
89 |
90 | /* -------------------------------------------------------- */
91 | /* Draw List */
92 | /* -------------------------------------------------------- */
93 |
94 | export const listLayout = `{}`;
95 |
96 | export function drawList(ls: list.List, width=800, height=350) {
97 | return draw(list.cytoscapify(ls), width, height, listLayout);
98 | }
99 |
100 |
101 | /* -------------------------------------------------------- */
102 | /* Draw Tree */
103 | /* -------------------------------------------------------- */
104 |
105 | export const treeLayout = `
106 | {
107 | name: 'preset'
108 | }
109 | `;
110 |
111 | export function drawTree(t: tree.Tree, width=800, height=350) {
112 | return draw(tree.cytoscapify(t), width, height, treeLayout);
113 | }
114 |
115 |
116 | /* -------------------------------------------------------- */
117 | /* Draw Memory */
118 | /* -------------------------------------------------------- */
119 |
120 | export const memLayout = `
121 | {
122 | name: 'breadthfirst',
123 | directed: true,
124 | padding: 10
125 | }
126 | `;
127 |
128 | export function drawMemTrace(memTrace: introspect.MemoryTrace, i, width=800, height=350) {
129 | return draw(introspect.cytoscapifyMemTrace(memTrace.memory[i], memTrace.refId), width, height, memLayout);
130 | }
131 |
132 |
133 | /* -------------------------------------------------------- */
134 | /* Draw Call Stack */
135 | /* -------------------------------------------------------- */
136 |
137 | export const callStackLayout = `
138 | {
139 | name: 'breadthfirst',
140 | directed: true,
141 | padding: 10
142 | }
143 | `;
144 |
145 | export function drawCallStack(stk: [string, number, any][], width=800, height=350) {
146 | draw(introspect.cytoscapifyCallStack(stk), width, height, callStackLayout);
147 | }
148 |
149 | export function drawStackTrace(stk: [string, number, any][], drawFn, width=800, height=550) {
150 | let iter = 0;
151 | for (const x of stk) {
152 | if (x[0] == "CALL") {
153 | console.log(`CALL[${iter}]` );
154 | drawFn(x[2][0][1])
155 | } else {
156 | console.log(`RET[${iter}]: ${x[2]}`)
157 | }
158 | iter += 1;
159 | }
160 | }
161 |
162 |
163 | /* ************************************************************************** */
164 | /* Display line plot */
165 | /* ************************************************************************** */
166 |
167 | export function linePlot(xs, ys, chartTitles=[], width=800, height=350): void {
168 | const divId = "mydiv" + uuidv4();
169 |
170 | const colors = [
171 | "rgb(44, 62, 80)",
172 | "rgb(231, 76, 60)",
173 | "rgb(236, 240, 241)",
174 | "rgb(52, 152, 219)"
175 | ];
176 |
177 | let datasets = "";
178 | if (Array.isArray(ys)) {
179 | if (Array.isArray(ys[0])) {
180 | datasets = ys.map((x, i) => {
181 | let title = `${i}`
182 | if (i in chartTitles) {
183 | title = chartTitles[i];
184 | }
185 | return `{
186 | label: "${title}",
187 | backgroundColor: '${colors[i % 4]}',
188 | borderColor: '${colors[i % 4]}',
189 | data: ${util.inspect(x)},
190 | }`}).join(", ");
191 | } else {
192 | datasets = `{
193 | label: "${"data"}",
194 | backgroundColor: '${colors[0]}',
195 | borderColor: '${colors[0]}',
196 | data: ${util.inspect(ys)},
197 | }`
198 | }
199 | }
200 |
201 | tslab.display.html(`
202 |
210 |
211 |
212 |
213 |
214 |
215 |
242 | `);
243 | }
244 |
245 |
246 | /* ************************************************************************** */
247 | /* Display React */
248 | /* ************************************************************************** */
249 |
250 | export function displayReact(components): void {
251 | const divId = "mydiv" + uuidv4();
252 | tslab.display.html(`
253 |
254 |
275 | `)
276 | }
--------------------------------------------------------------------------------
/lib/introspect.ts:
--------------------------------------------------------------------------------
1 | import ts, { idText, isExpressionStatement, SyntaxKind } from "typescript";
2 | import * as util from "util";
3 | import { Map } from 'immutable';
4 |
5 |
6 | /* ************************************************************************** */
7 | /* Call-Stack Tracing 2 */
8 | /* ************************************************************************** */
9 |
10 | export interface CallStackTrace {
11 | func: Function,
12 | stack: [string, number, any][]
13 | };
14 |
15 | const _traceFunction3 = (f: Function,
16 | transformer: (context: ts.TransformationContext) => (rootNode: T) => T,
17 | exports? /* for Jupyter */,
18 | verbose=false) => {
19 | // Function --> string --> ts.SourceFile --> string (instrumented)
20 | const source = f.toString();
21 | const sourceFile: ts.SourceFile = ts.createSourceFile('foobar.ts', source, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS);
22 | const result: ts.TransformationResult = ts.transform(sourceFile, [transformer]);
23 | const transformedSourceFile: ts.SourceFile = result.transformed[0];
24 | const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
25 | const transformedString = printer.printFile(transformedSourceFile);
26 | if (verbose) {
27 | console.log(transformedString);
28 | }
29 |
30 | const instrumentTemplate = `
31 | return (exports) => {
32 | let stack = [];
33 | let count = 0;
34 | let lvl = 0
35 |
36 | const csc600_RecordEnter = (params: [string, any][]): void => {
37 | stack.push(["CALL", lvl, params]);
38 | //for (let param of params) {
39 | // const [name, val] = param;
40 | // stack.push(["CALL", lvl, param]);
41 | //}
42 | lvl += 2;
43 | };
44 |
45 | const csc600_RecordExit = (x) => {
46 | lvl -= 2;
47 | stack.push(["RET", lvl, x]);
48 | return x;
49 | };
50 |
51 | return {func: ${transformedString}), stack: stack};
52 | }
53 | `
54 | return Function(`${ts.transpile(instrumentTemplate)}`)()(exports);
55 | };
56 |
57 |
58 | export function traceCallStack(f: Function, exports? /* for Jupyter */, verbose=false): CallStackTrace {
59 | function traceParam(param: ts.ParameterDeclaration): ts.ArrayLiteralExpression {
60 | const paramStr = ts.factory.createStringLiteral(param.name.getText());
61 | const paramName = ts.factory.createIdentifier(param.name.getText());
62 | return ts.factory.createArrayLiteralExpression([paramStr, paramName]);
63 | }
64 |
65 | // The transformation
66 | const transformer = (context: ts.TransformationContext) => (rootNode: T) => {
67 | function visit(node: ts.Node): ts.Node {
68 | switch (node.kind) {
69 | case ts.SyntaxKind.ArrowFunction: {
70 | const arr = node as ts.ArrowFunction;
71 | const body2 = ts.visitEachChild(arr.body, visit, context);
72 | const params = ts.factory.createArrayLiteralExpression(arr.parameters.map(traceParam));
73 | const functionName = ts.factory.createIdentifier("csc600_RecordEnter");
74 | const instrumented = ts.factory.createCallExpression(functionName, undefined, [params])
75 | const tuple = ts.factory.createArrayLiteralExpression([body2 as ts.Expression].concat(params).concat(instrumented));
76 | const body3 = ts.factory.createElementAccessExpression(tuple, 0);
77 | return ts.factory.createArrowFunction(arr.modifiers, arr.typeParameters, arr.parameters, arr.type, arr.equalsGreaterThanToken, body3);
78 | }
79 | case ts.SyntaxKind.FunctionDeclaration: {
80 | const func = node as ts.FunctionDeclaration;
81 | const body2 = ts.visitEachChild(func.body, visit, context);
82 | const params = ts.factory.createArrayLiteralExpression(func.parameters.map(traceParam));
83 | const functionName = ts.factory.createIdentifier("csc600_RecordEnter");
84 | const prelude: ts.Statement[] = [ts.factory.createExpressionStatement(ts.factory.createCallExpression(functionName, undefined, [params]))];
85 | const body3 = ts.factory.createBlock(prelude.concat(body2.statements), true);
86 | // const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
87 | return ts.factory.createFunctionDeclaration(func.decorators, func.modifiers, func.asteriskToken, func.name, func.typeParameters, func.parameters, func.type, body3);
88 | }
89 | case ts.SyntaxKind.ReturnStatement: {
90 | const ret = node as ts.ReturnStatement;
91 | if (ret.expression) {
92 | const functionName = ts.factory.createIdentifier("csc600_RecordExit");
93 | const instrumented = ts.factory.createCallExpression(functionName, undefined, [ts.visitEachChild(ret.expression, visit, context)]);
94 | return ts.factory.createReturnStatement(instrumented);
95 | } else {
96 | return ts.visitEachChild(node, visit, context);
97 | }
98 | }
99 | default: {
100 | return ts.visitEachChild(node, visit, context);
101 | }
102 | }
103 | };
104 | return ts.visitNode(rootNode, visit);
105 | };
106 |
107 | return _traceFunction3(f, transformer, exports, verbose);
108 | }
109 |
110 | export function cytoscapifyCallStack(stk: [string, number, any][]) {
111 | let count = 0;
112 | function fresh(prefix: string): string {
113 | count += 1;
114 | return prefix + count;
115 | }
116 |
117 | let acc = [];
118 | for (let i = 0; i < stk.length; i++) {
119 | const x = stk[i];
120 | acc.push({ data: {id: i, label: `${x[0]}[${i}](${JSON.stringify(x[2])})`}});
121 | const lvl = x[1];
122 | if (x[0] == "CALL") {
123 | for (let j = i - 1; j >= 0; j--) {
124 | if (stk[j][1] == lvl - 2) {
125 | acc.push({ data: {id: fresh("edge"), source: j, target: i}});
126 | break;
127 | }
128 | }
129 | } else if (x[0] == "RET") {
130 | if (stk[i-1][0] == "CALL" && stk[i-1][1] == lvl) {
131 | acc.push({ data: {id: fresh("edge"), source: i-1, target: i}});
132 | } else {
133 | for (let j = i - 1; j >= 0; j--) {
134 | if (stk[j][0] == "CALL" && stk[j][1] == lvl) {
135 | break;
136 | } else if (stk[j][0] == "RET" && stk[j][1] - 2 == lvl) {
137 | acc.push({ data: {id: fresh("edge"), source: j, target: i}});
138 | }
139 | }
140 | }
141 | }
142 | }
143 |
144 | return util.inspect(acc);
145 | }
146 |
147 |
148 | /* ************************************************************************** */
149 | /* Call-Stack Tracing */
150 | /* ************************************************************************** */
151 |
152 | export interface CallStackTrace {
153 | func: Function,
154 | trace: { [id: string]: any },
155 | refId: WeakMap