{ "cells": [ { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "# Efficient Grammar Fuzzing\n", "\n", "In the [chapter on grammars](Grammars.ipynb), we have seen how to use _grammars_ for very effective and efficient testing. In this chapter, we refine the previous _string-based_ algorithm into a _tree-based_ algorithm, which is much faster and allows for much more control over the production of fuzz inputs." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "source": [ "The algorithm in this chapter serves as a foundation for several more techniques; this chapter thus is a \"hub\" in the book." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:56.779422Z", "iopub.status.busy": "2024-01-18T17:15:56.779050Z", "iopub.status.idle": "2024-01-18T17:15:56.833802Z", "shell.execute_reply": "2024-01-18T17:15:56.833493Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from bookutils import YouTubeVideo\n", "YouTubeVideo('Ohl8TLcLl3A')" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "source": [ "**Prerequisites**\n", "\n", "* You should know how grammar-based fuzzing works, e.g. from the [chapter on grammars](Grammars.ipynb)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "## Synopsis\n", "\n", "\n", "To [use the code provided in this chapter](Importing.ipynb), write\n", "\n", "```python\n", ">>> from fuzzingbook.GrammarFuzzer import \n", "```\n", "\n", "and then make use of the following features.\n", "\n", "\n", "### Efficient Grammar Fuzzing\n", "\n", "This chapter introduces `GrammarFuzzer`, an efficient grammar fuzzer that takes a grammar to produce syntactically valid input strings. Here's a typical usage:\n", "\n", "```python\n", ">>> from Grammars import US_PHONE_GRAMMAR\n", ">>> phone_fuzzer = GrammarFuzzer(US_PHONE_GRAMMAR)\n", ">>> phone_fuzzer.fuzz()\n", "'(694)767-9530'\n", "```\n", "The `GrammarFuzzer` constructor takes a number of keyword arguments to control its behavior. `start_symbol`, for instance, allows setting the symbol that expansion starts with (instead of ``):\n", "\n", "```python\n", ">>> area_fuzzer = GrammarFuzzer(US_PHONE_GRAMMAR, start_symbol='')\n", ">>> area_fuzzer.fuzz()\n", "'403'\n", "```\n", "Here's how to parameterize the `GrammarFuzzer` constructor:\n", "\n", "```python\n", "Produce strings from `grammar`, starting with `start_symbol`.\n", "If `min_nonterminals` or `max_nonterminals` is given, use them as limits \n", "for the number of nonterminals produced. \n", "If `disp` is set, display the intermediate derivation trees.\n", "If `log` is set, show intermediate steps as text on standard output.\n", "\n", "```\n", "![](PICS/GrammarFuzzer-synopsis-1.svg)\n", "\n", "### Derivation Trees\n", "\n", "Internally, `GrammarFuzzer` makes use of [derivation trees](#Derivation-Trees), which it expands step by step. After producing a string, the tree produced can be accessed in the `derivation_tree` attribute.\n", "\n", "```python\n", ">>> display_tree(phone_fuzzer.derivation_tree)\n", "```\n", "![](PICS/GrammarFuzzer-synopsis-2.svg)\n", "\n", "In the internal representation of a derivation tree, a _node_ is a pair (`symbol`, `children`). For nonterminals, `symbol` is the symbol that is being expanded, and `children` is a list of further nodes. For terminals, `symbol` is the terminal string, and `children` is empty.\n", "\n", "```python\n", ">>> phone_fuzzer.derivation_tree\n", "('',\n", " [('',\n", " [('(', []),\n", " ('',\n", " [('', [('6', [])]),\n", " ('', [('9', [])]),\n", " ('', [('4', [])])]),\n", " (')', []),\n", " ('',\n", " [('', [('7', [])]),\n", " ('', [('6', [])]),\n", " ('', [('7', [])])]),\n", " ('-', []),\n", " ('',\n", " [('', [('9', [])]),\n", " ('', [('5', [])]),\n", " ('', [('3', [])]),\n", " ('', [('0', [])])])])])\n", "```\n", "The chapter contains various helpers to work with derivation trees, including visualization tools – notably, `display_tree()`, above.\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## An Insufficient Algorithm\n", "\n", "In the [previous chapter](Grammars.ipynb), we have introduced the `simple_grammar_fuzzer()` function which takes a grammar and automatically produces a syntactically valid string from it. However, `simple_grammar_fuzzer()` is just what its name suggests – simple. To illustrate the problem, let us get back to the `expr_grammar` we created from `EXPR_GRAMMAR_BNF` in the [chapter on grammars](Grammars.ipynb):" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:56.855816Z", "iopub.status.busy": "2024-01-18T17:15:56.855640Z", "iopub.status.idle": "2024-01-18T17:15:56.857962Z", "shell.execute_reply": "2024-01-18T17:15:56.857637Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import bookutils.setup" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:56.859789Z", "iopub.status.busy": "2024-01-18T17:15:56.859654Z", "iopub.status.idle": "2024-01-18T17:15:56.861209Z", "shell.execute_reply": "2024-01-18T17:15:56.860935Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from bookutils import quiz" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:56.862830Z", "iopub.status.busy": "2024-01-18T17:15:56.862657Z", "iopub.status.idle": "2024-01-18T17:15:56.864664Z", "shell.execute_reply": "2024-01-18T17:15:56.864408Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from typing import Tuple, List, Optional, Any, Union, Set, Callable, Dict" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:56.866119Z", "iopub.status.busy": "2024-01-18T17:15:56.865979Z", "iopub.status.idle": "2024-01-18T17:15:56.867682Z", "shell.execute_reply": "2024-01-18T17:15:56.867401Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from bookutils import unicode_escape" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:56.869166Z", "iopub.status.busy": "2024-01-18T17:15:56.869060Z", "iopub.status.idle": "2024-01-18T17:15:57.314071Z", "shell.execute_reply": "2024-01-18T17:15:57.313747Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Grammars import EXPR_EBNF_GRAMMAR, convert_ebnf_grammar, Grammar, Expansion\n", "from Grammars import simple_grammar_fuzzer, is_valid_grammar, exp_string" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:57.315881Z", "iopub.status.busy": "2024-01-18T17:15:57.315753Z", "iopub.status.idle": "2024-01-18T17:15:57.318443Z", "shell.execute_reply": "2024-01-18T17:15:57.318180Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "{'': [''],\n", " '': [' + ', ' - ', ''],\n", " '': [' * ', ' / ', ''],\n", " '': ['', '()', ''],\n", " '': ['+', '-'],\n", " '': [''],\n", " '': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],\n", " '': ['.'],\n", " '': ['', ''],\n", " '': ['', ''],\n", " '': ['', '']}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr_grammar = convert_ebnf_grammar(EXPR_EBNF_GRAMMAR)\n", "expr_grammar" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "`expr_grammar` has an interesting property. If we feed it into `simple_grammar_fuzzer()`, the function gets stuck:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:57.320019Z", "iopub.status.busy": "2024-01-18T17:15:57.319911Z", "iopub.status.idle": "2024-01-18T17:15:57.321592Z", "shell.execute_reply": "2024-01-18T17:15:57.321273Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from ExpectError import ExpectTimeout" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:57.323354Z", "iopub.status.busy": "2024-01-18T17:15:57.323228Z", "iopub.status.idle": "2024-01-18T17:15:58.327048Z", "shell.execute_reply": "2024-01-18T17:15:58.326766Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Traceback (most recent call last):\n", " File \"/var/folders/n2/xd9445p97rb3xh7m1dfx8_4h0006ts/T/ipykernel_75835/3259437052.py\", line 2, in \n", " simple_grammar_fuzzer(grammar=expr_grammar, max_nonterminals=3)\n", " File \"/Users/zeller/Projects/fuzzingbook/notebooks/Grammars.ipynb\", line 95, in simple_grammar_fuzzer\n", " new_term = term.replace(symbol_to_expand, expansion, 1)\n", " File \"/Users/zeller/Projects/fuzzingbook/notebooks/Timeout.ipynb\", line 43, in timeout_handler\n", " raise TimeoutError()\n", "TimeoutError (expected)\n" ] } ], "source": [ "with ExpectTimeout(1):\n", " simple_grammar_fuzzer(grammar=expr_grammar, max_nonterminals=3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Why is that so? Have a look at the grammar; remember what you know about `simple_grammar_fuzzer()`; and run `simple_grammar_fuzzer()` with `log=true` argument to see the expansions." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:58.328997Z", "iopub.status.busy": "2024-01-18T17:15:58.328868Z", "iopub.status.idle": "2024-01-18T17:15:58.333731Z", "shell.execute_reply": "2024-01-18T17:15:58.333371Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", "
\n", "

Quiz

\n", "

\n", "

Why does simple_grammar_fuzzer() hang?
\n", "

\n", "

\n", "

\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "

\n", " \n", " \n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "quiz(\"Why does `simple_grammar_fuzzer()` hang?\",\n", " [\n", " \"It produces an infinite number of additions\",\n", " \"It produces an infinite number of digits\",\n", " \"It produces an infinite number of parentheses\",\n", " \"It produces an infinite number of signs\",\n", " ], '(3 * 3 * 3) ** (3 / (3 * 3))')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Indeed! The problem is in this rule:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:58.335435Z", "iopub.status.busy": "2024-01-18T17:15:58.335235Z", "iopub.status.idle": "2024-01-18T17:15:58.337472Z", "shell.execute_reply": "2024-01-18T17:15:58.337168Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "['', '()', '']" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expr_grammar['']" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Here, any choice except for `(expr)` increases the number of symbols, even if only temporary. Since we place a hard limit on the number of symbols to expand, the only choice left for expanding `` is `()`, which leads to an _infinite addition of parentheses._" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "The problem of potentially infinite expansion is only one of the problems with `simple_grammar_fuzzer()`. More problems include:\n", "\n", "1. *It is inefficient*. With each iteration, this fuzzer would go search the string produced so far for symbols to expand. This becomes inefficient as the production string grows.\n", "\n", "2. *It is hard to control.* Even while limiting the number of symbols, it is still possible to obtain very long strings – and even infinitely long ones, as discussed above.\n", "\n", "Let us illustrate both problems by plotting the time required for strings of different lengths." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:15:58.339175Z", "iopub.status.busy": "2024-01-18T17:15:58.339017Z", "iopub.status.idle": "2024-01-18T17:15:58.341021Z", "shell.execute_reply": "2024-01-18T17:15:58.340616Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Grammars import simple_grammar_fuzzer" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:15:58.342931Z", "iopub.status.busy": "2024-01-18T17:15:58.342796Z", "iopub.status.idle": "2024-01-18T17:15:58.344568Z", "shell.execute_reply": "2024-01-18T17:15:58.344301Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Grammars import START_SYMBOL, EXPR_GRAMMAR, URL_GRAMMAR, CGI_GRAMMAR" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:15:58.346040Z", "iopub.status.busy": "2024-01-18T17:15:58.345929Z", "iopub.status.idle": "2024-01-18T17:15:58.347527Z", "shell.execute_reply": "2024-01-18T17:15:58.347284Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Grammars import RE_NONTERMINAL, nonterminals, is_nonterminal" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:15:58.348912Z", "iopub.status.busy": "2024-01-18T17:15:58.348806Z", "iopub.status.idle": "2024-01-18T17:15:58.350512Z", "shell.execute_reply": "2024-01-18T17:15:58.350169Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Timer import Timer" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:15:58.351955Z", "iopub.status.busy": "2024-01-18T17:15:58.351847Z", "iopub.status.idle": "2024-01-18T17:16:06.103639Z", "shell.execute_reply": "2024-01-18T17:16:06.103360Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 \n" ] } ], "source": [ "trials = 50\n", "xs = []\n", "ys = []\n", "for i in range(trials):\n", " with Timer() as t:\n", " s = simple_grammar_fuzzer(EXPR_GRAMMAR, max_nonterminals=15)\n", " xs.append(len(s))\n", " ys.append(t.elapsed_time())\n", " print(i, end=\" \")\n", "print()" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:06.105484Z", "iopub.status.busy": "2024-01-18T17:16:06.105364Z", "iopub.status.idle": "2024-01-18T17:16:06.107395Z", "shell.execute_reply": "2024-01-18T17:16:06.107088Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Average time: 0.15491187840088969\n" ] } ], "source": [ "average_time = sum(ys) / trials\n", "print(\"Average time:\", average_time)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:06.109450Z", "iopub.status.busy": "2024-01-18T17:16:06.109305Z", "iopub.status.idle": "2024-01-18T17:16:06.209628Z", "shell.execute_reply": "2024-01-18T17:16:06.209309Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/png": "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vot98803+uqrr3TbbbepVatW2rlzp2bPnq2rr75aGRkZatSokVv9SZMmKSgoSMnJydq5c6emT5+uiRMn6t133y33OJ6+fs5lwYIFuuuuu9SrVy/dfffdkqT27du71RkyZIjatm2rlJQUff311/rb3/6mX3/9VW+++aZHx7rpppv0008/6Z133tHLL7+skJAQSVLz5s092g+qgQH+z4QJE0xZL4lRo0aZNm3auO5nZmYaSaZ58+bm0KFDrvIpU6YYSaZLly7m5MmTrvJhw4YZPz8/U1hYaIwxpqCgwDRr1syMHTvW7TjZ2dnG4XCUKD/bvHnzjCRz5ZVXmlOnTrnKPdlv165dTUREhFv7P//8cyPJ7bmuWrXKSDKrVq1y22dxH8ybN89VlpycXKIPJRkfHx/zww8/uJXfeeedJiIiwhw4cMCt/LbbbjMOh8McPXrUGGPM9OnTjSTz3nvvueocOXLEdOjQodR2na24/e+//75bedeuXU1oaKg5ePCgq+zbb781Pj4+ZuTIkSWe07Bhw8o9TrFJkyYZm81mNm3a5Co7ePCgCQ4ONpJMZmamMcazv9WoUaOMJPPnP//ZrW63bt1Mjx49XPf/85//GEnmrbfecquXmppaorxNmzZGkklNTS3xHIr7/kwJCQmmXbt2bmWdOnUyffv2LVG3tNdM3759jSTz5ptvusqOHz9uwsPDzc033+wqe/HFF40ks3jxYlfZsWPHzMUXX1yhv3dpbU9LSytx7OL3UHx8vHE6na7yBx54wPj6+rq9L0pT0dfP2Z8dxUp7rzRu3NiMGjWqzLqDBg1yK7/33nuNJPPtt98aY0p/TxaTZJKTk133n3/+ebfXI6yByzc4L7feeqscDofrfmxsrKTT41XOHFsRGxurEydOaO/evZKkZcuW6dChQxo2bJgOHDjguvn6+io2NrbC/yscO3asfH19Xfcrut+srCxt3rxZo0aNcmt/v379FB0dXfkOKUPfvn3d9muM0YcffqjExEQZY9zampCQoLy8PG3cuFGStHTpUkVEROiWW25xbd+oUSPX/yYro/j5jx49WsHBwa7yzp07q1+/flq6dGmJbe65554K7Ts1NVVxcXHq2rWrqyw4OFi33367W73KvAbObkOfPn20Y8cO1/33339fDodD/fr1c9tnjx491KRJkxL7jIqKUkJCQonjnDmuJC8v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gCXU61Bit9uVnJwsu93u7abUefR1zaGvawb9XHPo65rj7b6uFQNdAQBA3Vevz5QAAADrIJQAAABLIJQAAABLIJQAAABLIJQAAABLqNehZNasWWrbtq38/f0VGxurdevWebtJlvbkk0/KZrO53S6++GLX44WFhZowYYIuuOACNWnSRDfffLNycnLc9rF7924NHDhQjRo1UmhoqB566CGdOnXKrc7q1avVvXt32e12dejQQfPnz6+Jp+c1a9asUWJiolq0aCGbzabFixe7PW6M0dSpUxUREaGAgADFx8dr27ZtbnVyc3N1++23KzAwUM2aNdOdd96pw4cPu9X57rvv1KdPH/n7+ysyMlLPPfdciba8//77uvjii+Xv769LL71US5curfLn603n6uvRo0eXeI3379/frQ59fW4pKSm67LLL1LRpU4WGhmrw4MHaunWrW52a/Lyoy5/1Fenrq6++usTr+p577nGrY5m+NvXUokWLjJ+fn3njjTfMDz/8YMaOHWuaNWtmcnJyvN00y0pOTjadOnUyWVlZrtv+/ftdj99zzz0mMjLSrFixwqxfv95cfvnl5oorrnA9furUKRMTE2Pi4+PNpk2bzNKlS01ISIiZMmWKq86OHTtMo0aNTFJSksnIyDAzZswwvr6+JjU1tUafa01aunSpeeyxx8xHH31kJJmPP/7Y7fFnnnnGOBwOs3jxYvPtt9+aQYMGmaioKHPs2DFXnf79+5suXbqYr7/+2vznP/8xHTp0MMOGDXM9npeXZ8LCwsztt99u0tPTzTvvvGMCAgLMq6++6qqzdu1a4+vra5577jmTkZFhHn/8cdOwYUPz/fffV3sf1JRz9fWoUaNM//793V7jubm5bnXo63NLSEgw8+bNM+np6Wbz5s3mhhtuMK1btzaHDx921ampz4u6/llfkb7u27evGTt2rNvrOi8vz/W4lfq63oaSXr16mQkTJrjuFxUVmRYtWpiUlBQvtsrakpOTTZcuXUp97NChQ6Zhw4bm/fffd5X9+OOPRpJJS0szxpz+QvDx8THZ2dmuOrNnzzaBgYHm+PHjxhhjHn74YdOpUye3fQ8dOtQkJCRU8bOxprO/KJ1OpwkPDzfPP/+8q+zQoUPGbrebd955xxhjTEZGhpFkvvnmG1edf//738Zms5m9e/caY4x55ZVXTFBQkKufjTHmkUceMR07dnTdHzJkiBk4cKBbe2JjY824ceOq9DlaRVmh5MYbbyxzG/q6cvbt22ckmS+++MIYU7OfF/Xts/7svjbmdCiZPHlymdtYqa/r5eWbEydOaMOGDYqPj3eV+fj4KD4+XmlpaV5smfVt27ZNLVq0ULt27XT77bdr9+7dkqQNGzbo5MmTbn168cUXq3Xr1q4+TUtL06WXXqqwsDBXnYSEBOXn5+uHH35w1TlzH8V16uvfJTMzU9nZ2W594nA4FBsb69avzZo1U8+ePV114uPj5ePjo//+97+uOldddZX8/PxcdRISErR161b9+uuvrjr0/elT1KGhoerYsaPGjx+vgwcPuh6jrysnLy9PkhQcHCyp5j4v6uNn/dl9Xeytt95SSEiIYmJiNGXKFB09etT1mJX6ulb8SnBVO3DggIqKitz+AJIUFhamLVu2eKlV1hcbG6v58+erY8eOysrK0rRp09SnTx+lp6crOztbfn5+atasmds2YWFhys7OliRlZ2eX2ufFj5VXJz8/X8eOHVNAQEA1PTtrKu6X0vrkzD4LDQ11e7xBgwYKDg52qxMVFVViH8WPBQUFldn3xfuoD/r376+bbrpJUVFR+vnnn/WnP/1JAwYMUFpamnx9fenrSnA6nbr//vvVu3dvxcTESFKNfV78+uuv9eqzvrS+lqThw4erTZs2atGihb777js98sgj2rp1qz766CNJ1urrehlKUDkDBgxw/btz586KjY1VmzZt9N5779W7sIC66bbbbnP9+9JLL1Xnzp3Vvn17rV69Wtddd50XW1Z7TZgwQenp6fryyy+93ZQ6r6y+vvvuu13/vvTSSxUREaHrrrtOP//8s9q3b1/TzSxXvbx8ExISIl9f3xIjvXNychQeHu6lVtU+zZo100UXXaTt27crPDxcJ06c0KFDh9zqnNmn4eHhpfZ58WPl1QkMDKyXwae4X8p7rYaHh2vfvn1uj586dUq5ublV0vf1+T3Rrl07hYSEaPv27ZLoa09NnDhR//rXv7Rq1Sq1atXKVV5Tnxf16bO+rL4uTWxsrCS5va6t0tf1MpT4+fmpR48eWrFihavM6XRqxYoViouL82LLapfDhw/r559/VkREhHr06KGGDRu69enWrVu1e/duV5/GxcXp+++/d/tQX7ZsmQIDAxUdHe2qc+Y+iuvU179LVFSUwsPD3fokPz9f//3vf9369dChQ9qwYYOrzsqVK+V0Ol0fPnFxcVqzZo1OnjzpqrNs2TJ17NhRQUFBrjr0vbv//e9/OnjwoCIiIiTR1xVljNHEiRP18ccfa+XKlSUuZ9XU50V9+Kw/V1+XZvPmzZLk9rq2TF9XeEhsHbNo0SJjt9vN/PnzTUZGhrn77rtNs2bN3EYfw90f//hHs3r1apOZmWnWrl1r4uPjTUhIiNm3b58x5vQUv9atW5uVK1ea9evXm7i4OBMXF+favnja2fXXX282b95sUlNTTfPmzUuddvbQQw+ZH3/80cyaNavOTwkuKCgwmzZtMps2bTKSzEsvvWQ2bdpkdu3aZYw5PSW4WbNm5p///Kf57rvvzI033ljqlOBu3bqZ//73v+bLL780F154ods01UOHDpmwsDBzxx13mPT0dLNo0SLTqFGjEtNUGzRoYF544QXz448/muTk5Do1TdWY8vu6oKDAPPjggyYtLc1kZmaa5cuXm+7du5sLL7zQFBYWuvZBX5/b+PHjjcPhMKtXr3abhnr06FFXnZr6vKjrn/Xn6uvt27ebP//5z2b9+vUmMzPT/POf/zTt2rUzV111lWsfVurrehtKjDFmxowZpnXr1sbPz8/06tXLfP31195ukqUNHTrUREREGD8/P9OyZUszdOhQs337dtfjx44dM/fee68JCgoyjRo1Mr///e9NVlaW2z527txpBgwYYAICAkxISIj54x//aE6ePOlWZ9WqVaZr167Gz8/PtGvXzsybN68mnp7XrFq1ykgqcRs1apQx5vS04CeeeMKEhYUZu91urrvuOrN161a3fRw8eNAMGzbMNGnSxAQGBpoxY8aYgoICtzrffvutufLKK43dbjctW7Y0zzzzTIm2vPfee+aiiy4yfn5+plOnTubTTz+ttuftDeX19dGjR831119vmjdvbho2bGjatGljxo4dW+IDlb4+t9L6WJLbe7kmPy/q8mf9ufp69+7d5qqrrjLBwcHGbrebDh06mIceeshtnRJjrNPXtv97UgAAAF5VL8eUAAAA6yGUAAAASyCUAAAASyCUAAAASyCUAAAASyCUAAAASyCUAAAASyCUAAAASyCUAAAASyCUAAAASyCUAAAAS/j/aOuyf/z7I3gAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "plt.scatter(xs, ys)\n", "plt.title('Time required for generating an output');" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "We see that (1) the time needed to generate an output increases quadratically with the length of that output, and that (2) a large portion of the produced outputs are tens of thousands of characters long." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To address these problems, we need a _smarter algorithm_ – one that is more efficient, that gets us better control over expansions, and that is able to foresee in `expr_grammar` that the `(expr)` alternative yields a potentially infinite expansion, in contrast to the other two." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Derivation Trees\n", "\n", "To both obtain a more efficient algorithm _and_ exercise better control over expansions, we will use a special representation for the strings that our grammar produces. The general idea is to use a *tree* structure that will be subsequently expanded – a so-called *derivation tree*. This representation allows us to always keep track of our expansion status – answering questions such as which elements have been expanded into which others, and which symbols still need to be expanded. Furthermore, adding new elements to a tree is far more efficient than replacing strings again and again." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "Like other trees used in programming, a derivation tree (also known as *parse tree* or *concrete syntax tree*) consists of *nodes* which have other nodes (called *child nodes*) as their *children*. The tree starts with one node that has no parent; this is called the *root node*; a node without children is called a *leaf*." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "The grammar expansion process with derivation trees is illustrated in the following steps, using the arithmetic grammar [from\n", "the chapter on grammars](Grammars.ipynb). We start with a single node as root of the tree, representing the *start symbol* – in our case ``." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:06.211669Z", "iopub.status.busy": "2024-01-18T17:16:06.211537Z", "iopub.status.idle": "2024-01-18T17:16:06.219291Z", "shell.execute_reply": "2024-01-18T17:16:06.219033Z" }, "ipub": { "ignore": true }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "# ignore\n", "from graphviz import Digraph" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:06.221030Z", "iopub.status.busy": "2024-01-18T17:16:06.220943Z", "iopub.status.idle": "2024-01-18T17:16:06.222962Z", "shell.execute_reply": "2024-01-18T17:16:06.222658Z" }, "ipub": { "ignore": true }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "# ignore\n", "tree = Digraph(\"root\")\n", "tree.attr('node', shape='plain')\n", "tree.node(r\"\\\")" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:06.224573Z", "iopub.status.busy": "2024-01-18T17:16:06.224475Z", "iopub.status.idle": "2024-01-18T17:16:06.607798Z", "shell.execute_reply": "2024-01-18T17:16:06.607437Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "root\n", "\n", "\n", "\n", "\\<start\\>\n", "<start>\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ignore\n", "tree" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "To expand the tree, we traverse it, searching for a nonterminal symbol $S$ without children. $S$ thus is a symbol that still has to be expanded. We then chose an expansion for $S$ from the grammar. Then, we add the expansion as a new child of $S$. For our start symbol ``, the only expansion is ``, so we add it as a child." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:06.609694Z", "iopub.status.busy": "2024-01-18T17:16:06.609551Z", "iopub.status.idle": "2024-01-18T17:16:06.611457Z", "shell.execute_reply": "2024-01-18T17:16:06.611217Z" }, "ipub": { "ignore": true }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "# ignore\n", "tree.edge(r\"\\\", r\"\\\")" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:06.612988Z", "iopub.status.busy": "2024-01-18T17:16:06.612875Z", "iopub.status.idle": "2024-01-18T17:16:06.979735Z", "shell.execute_reply": "2024-01-18T17:16:06.979337Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "root\n", "\n", "\n", "\n", "\\<start\\>\n", "<start>\n", "\n", "\n", "\n", "\\<expr\\>\n", "<expr>\n", "\n", "\n", "\n", "\\<start\\>->\\<expr\\>\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ignore\n", "tree" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "To construct the produced string from a derivation tree, we traverse the tree in order and collect the symbols at the leaves of the tree. In the case above, we obtain the string `\"\"`.\n", "\n", "To further expand the tree, we choose another symbol to expand, and add its expansion as new children. This would get us the `` symbol, which gets expanded into ` + `, adding three children." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:06.981601Z", "iopub.status.busy": "2024-01-18T17:16:06.981469Z", "iopub.status.idle": "2024-01-18T17:16:06.983584Z", "shell.execute_reply": "2024-01-18T17:16:06.983289Z" }, "ipub": { "ignore": true }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "# ignore\n", "tree.edge(r\"\\\", r\"\\ \")\n", "tree.edge(r\"\\\", r\"+\")\n", "tree.edge(r\"\\\", r\"\\\")" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:06.985271Z", "iopub.status.busy": "2024-01-18T17:16:06.985158Z", "iopub.status.idle": "2024-01-18T17:16:07.347384Z", "shell.execute_reply": "2024-01-18T17:16:07.346950Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "root\n", "\n", "\n", "\n", "\\<start\\>\n", "<start>\n", "\n", "\n", "\n", "\\<expr\\>\n", "<expr>\n", "\n", "\n", "\n", "\\<start\\>->\\<expr\\>\n", "\n", "\n", "\n", "\n", "\n", "\\<expr\\> \n", "<expr> \n", "\n", "\n", "\n", "\\<expr\\>->\\<expr\\> \n", "\n", "\n", "\n", "\n", "\n", "+\n", "+\n", "\n", "\n", "\n", "\\<expr\\>->+\n", "\n", "\n", "\n", "\n", "\n", "\\<term\\>\n", "<term>\n", "\n", "\n", "\n", "\\<expr\\>->\\<term\\>\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ignore\n", "tree" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "We repeat the expansion until there are no symbols left to expand:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.349179Z", "iopub.status.busy": "2024-01-18T17:16:07.349046Z", "iopub.status.idle": "2024-01-18T17:16:07.351781Z", "shell.execute_reply": "2024-01-18T17:16:07.351502Z" }, "ipub": { "ignore": true }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "# ignore\n", "tree.edge(r\"\\ \", r\"\\ \")\n", "tree.edge(r\"\\ \", r\"\\ \")\n", "tree.edge(r\"\\ \", r\"\\ \")\n", "tree.edge(r\"\\ \", r\"\\ \")\n", "tree.edge(r\"\\ \", r\"2 \")\n", "\n", "tree.edge(r\"\\\", r\"\\\")\n", "tree.edge(r\"\\\", r\"\\\")\n", "tree.edge(r\"\\\", r\"\\\")\n", "tree.edge(r\"\\\", r\"2\")" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.353270Z", "iopub.status.busy": "2024-01-18T17:16:07.353158Z", "iopub.status.idle": "2024-01-18T17:16:07.714812Z", "shell.execute_reply": "2024-01-18T17:16:07.714436Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "root\n", "\n", "\n", "\n", "\\<start\\>\n", "<start>\n", "\n", "\n", "\n", "\\<expr\\>\n", "<expr>\n", "\n", "\n", "\n", "\\<start\\>->\\<expr\\>\n", "\n", "\n", "\n", "\n", "\n", "\\<expr\\> \n", "<expr> \n", "\n", "\n", "\n", "\\<expr\\>->\\<expr\\> \n", "\n", "\n", "\n", "\n", "\n", "+\n", "+\n", "\n", "\n", "\n", "\\<expr\\>->+\n", "\n", "\n", "\n", "\n", "\n", "\\<term\\>\n", "<term>\n", "\n", "\n", "\n", "\\<expr\\>->\\<term\\>\n", "\n", "\n", "\n", "\n", "\n", "\\<term\\> \n", "<term> \n", "\n", "\n", "\n", "\\<expr\\> ->\\<term\\> \n", "\n", "\n", "\n", "\n", "\n", "\\<factor\\>\n", "<factor>\n", "\n", "\n", "\n", "\\<term\\>->\\<factor\\>\n", "\n", "\n", "\n", "\n", "\n", "\\<factor\\> \n", "<factor> \n", "\n", "\n", "\n", "\\<term\\> ->\\<factor\\> \n", "\n", "\n", "\n", "\n", "\n", "\\<integer\\> \n", "<integer> \n", "\n", "\n", "\n", "\\<factor\\> ->\\<integer\\> \n", "\n", "\n", "\n", "\n", "\n", "\\<digit\\> \n", "<digit> \n", "\n", "\n", "\n", "\\<integer\\> ->\\<digit\\> \n", "\n", "\n", "\n", "\n", "\n", "2 \n", "2 \n", "\n", "\n", "\n", "\\<digit\\> ->2 \n", "\n", "\n", "\n", "\n", "\n", "\\<integer\\>\n", "<integer>\n", "\n", "\n", "\n", "\\<factor\\>->\\<integer\\>\n", "\n", "\n", "\n", "\n", "\n", "\\<digit\\>\n", "<digit>\n", "\n", "\n", "\n", "\\<integer\\>->\\<digit\\>\n", "\n", "\n", "\n", "\n", "\n", "2\n", "2\n", "\n", "\n", "\n", "\\<digit\\>->2\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ignore\n", "tree" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "We now have a representation for the string `2 + 2`. In contrast to the string alone, though, the derivation tree records _the entire structure_ (and production history, or _derivation_ history) of the produced string. It also allows for simple comparison and manipulation – say, replacing one subtree (substructure) against another." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Representing Derivation Trees\n", "\n", "To represent a derivation tree in Python, we use the following format. A node is a pair\n", "\n", "```python\n", "(SYMBOL_NAME, CHILDREN)\n", "```\n", "\n", "where `SYMBOL_NAME` is a string representing the node (i.e. `\"\"` or `\"+\"`) and `CHILDREN` is a list of children nodes.\n", "\n", "`CHILDREN` can take some special values:\n", "\n", "1. `None` as a placeholder for future expansion. This means that the node is a *nonterminal symbol* that should be expanded further.\n", "2. `[]` (i.e., the empty list) to indicate _no_ children. This means that the node is a *terminal symbol* that can no longer be expanded." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The type `DerivationTree` captures this very structure. (`Any` should actually read `DerivationTree`, but the Python static type checker cannot handle recursive types well.)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:07.716799Z", "iopub.status.busy": "2024-01-18T17:16:07.716682Z", "iopub.status.idle": "2024-01-18T17:16:07.718747Z", "shell.execute_reply": "2024-01-18T17:16:07.718473Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "DerivationTree = Tuple[str, Optional[List[Any]]]" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "Let us take a very simple derivation tree, representing the intermediate step ` + `, above." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.720159Z", "iopub.status.busy": "2024-01-18T17:16:07.720057Z", "iopub.status.idle": "2024-01-18T17:16:07.721762Z", "shell.execute_reply": "2024-01-18T17:16:07.721513Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "derivation_tree: DerivationTree = (\"\",\n", " [(\"\",\n", " [(\"\", None),\n", " (\" + \", []),\n", " (\"\", None)]\n", " )])" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "To better understand the structure of this tree, let us introduce a function `display_tree()` that visualizes this tree." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Excursion: Implementing `display_tree()`" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "We use the `dot` drawing program from the `graphviz` package algorithmically, traversing the above structure. (Unless you're deeply interested in tree visualization, you can directly skip to the example below.)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.723340Z", "iopub.status.busy": "2024-01-18T17:16:07.723216Z", "iopub.status.idle": "2024-01-18T17:16:07.724817Z", "shell.execute_reply": "2024-01-18T17:16:07.724570Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from graphviz import Digraph" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.726201Z", "iopub.status.busy": "2024-01-18T17:16:07.726115Z", "iopub.status.idle": "2024-01-18T17:16:07.727833Z", "shell.execute_reply": "2024-01-18T17:16:07.727586Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from IPython.display import display" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.729359Z", "iopub.status.busy": "2024-01-18T17:16:07.729273Z", "iopub.status.idle": "2024-01-18T17:16:07.730835Z", "shell.execute_reply": "2024-01-18T17:16:07.730605Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import re\n", "import string" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.732408Z", "iopub.status.busy": "2024-01-18T17:16:07.732321Z", "iopub.status.idle": "2024-01-18T17:16:07.735213Z", "shell.execute_reply": "2024-01-18T17:16:07.734938Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "def dot_escape(s: str, show_ascii=None) -> str:\n", " \"\"\"Return s in a form suitable for dot.\n", " If `show_ascii` is True or length of `s` is 1, also append ascii value.\"\"\"\n", " escaped_s = ''\n", " if show_ascii is None:\n", " show_ascii = (len(s) == 1) # Default: Single chars only\n", "\n", " if show_ascii and s == '\\n':\n", " return '\\\\\\\\n (10)'\n", "\n", " s = s.replace('\\n', '\\\\n')\n", " for c in s:\n", " if re.match('[,<>\\\\\\\\\"]', c):\n", " escaped_s += '\\\\' + c\n", " elif c in string.printable and 31 < ord(c) < 127:\n", " escaped_s += c\n", " else:\n", " escaped_s += '\\\\\\\\x' + format(ord(c), '02x')\n", "\n", " if show_ascii:\n", " escaped_s += f' ({ord(c)})'\n", "\n", " return escaped_s" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.736710Z", "iopub.status.busy": "2024-01-18T17:16:07.736615Z", "iopub.status.idle": "2024-01-18T17:16:07.738511Z", "shell.execute_reply": "2024-01-18T17:16:07.738270Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "assert dot_escape(\"hello\") == \"hello\"" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.739944Z", "iopub.status.busy": "2024-01-18T17:16:07.739861Z", "iopub.status.idle": "2024-01-18T17:16:07.741544Z", "shell.execute_reply": "2024-01-18T17:16:07.741273Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "assert dot_escape(\", world\") == \"\\\\\\\\, world\"" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.742874Z", "iopub.status.busy": "2024-01-18T17:16:07.742788Z", "iopub.status.idle": "2024-01-18T17:16:07.744393Z", "shell.execute_reply": "2024-01-18T17:16:07.744159Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "assert dot_escape(\"\\\\n\") == \"\\\\\\\\n\"" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.745768Z", "iopub.status.busy": "2024-01-18T17:16:07.745686Z", "iopub.status.idle": "2024-01-18T17:16:07.747506Z", "shell.execute_reply": "2024-01-18T17:16:07.747211Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "assert dot_escape(\"\\n\", show_ascii=False) == \"\\\\\\\\n\"" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.748908Z", "iopub.status.busy": "2024-01-18T17:16:07.748825Z", "iopub.status.idle": "2024-01-18T17:16:07.750627Z", "shell.execute_reply": "2024-01-18T17:16:07.750383Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "assert dot_escape(\"\\n\", show_ascii=True) == \"\\\\\\\\n (10)\"" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.751943Z", "iopub.status.busy": "2024-01-18T17:16:07.751863Z", "iopub.status.idle": "2024-01-18T17:16:07.753401Z", "shell.execute_reply": "2024-01-18T17:16:07.753176Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "assert dot_escape(\"\\n\", show_ascii=True) == \"\\\\\\\\n (10)\"" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:07.754780Z", "iopub.status.busy": "2024-01-18T17:16:07.754677Z", "iopub.status.idle": "2024-01-18T17:16:07.756325Z", "shell.execute_reply": "2024-01-18T17:16:07.756086Z" }, "slideshow": { "slide_type": "fragment" }, "tags": [] }, "outputs": [], "source": [ "assert dot_escape('\\x01', show_ascii=False) == \"\\\\\\\\x01\"" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:07.757757Z", "iopub.status.busy": "2024-01-18T17:16:07.757674Z", "iopub.status.idle": "2024-01-18T17:16:07.759385Z", "shell.execute_reply": "2024-01-18T17:16:07.759142Z" }, "slideshow": { "slide_type": "fragment" }, "tags": [] }, "outputs": [], "source": [ "assert dot_escape('\\x01') == \"\\\\\\\\x01 (1)\"" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "While we are interested at present in visualizing a `derivation_tree`, it is in our interest to generalize the visualization procedure. In particular, it would be helpful if our method `display_tree()` can display *any* tree like data structure. To enable this, we define a helper method `extract_node()` that extract the current symbol and children from a given data structure. The default implementation simply extracts the symbol, children, and annotation from any `derivation_tree` node." ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:07.760914Z", "iopub.status.busy": "2024-01-18T17:16:07.760780Z", "iopub.status.idle": "2024-01-18T17:16:07.762940Z", "shell.execute_reply": "2024-01-18T17:16:07.762623Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def extract_node(node, id):\n", " symbol, children, *annotation = node\n", " return symbol, children, ''.join(str(a) for a in annotation)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "While visualizing a tree, it is often useful to display certain nodes differently. For example, it is sometimes useful to distinguish between non-processed nodes and processed nodes. We define a helper procedure `default_node_attr()` that provides the basic display, which can be customized by the user." ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:07.764598Z", "iopub.status.busy": "2024-01-18T17:16:07.764479Z", "iopub.status.idle": "2024-01-18T17:16:07.766182Z", "shell.execute_reply": "2024-01-18T17:16:07.765925Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def default_node_attr(dot, nid, symbol, ann):\n", " dot.node(repr(nid), dot_escape(symbol))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Similar to nodes, the edges may also require modifications. We define `default_edge_attr()` as a helper procedure that can be customized by the user." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:07.767602Z", "iopub.status.busy": "2024-01-18T17:16:07.767504Z", "iopub.status.idle": "2024-01-18T17:16:07.769197Z", "shell.execute_reply": "2024-01-18T17:16:07.768953Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def default_edge_attr(dot, start_node, stop_node):\n", " dot.edge(repr(start_node), repr(stop_node))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "While visualizing a tree, one may sometimes wish to change the appearance of the tree. For example, it is sometimes easier to view the tree if it was laid out left to right rather than top to bottom. We define another helper procedure `default_graph_attr()` for that." ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:07.770659Z", "iopub.status.busy": "2024-01-18T17:16:07.770553Z", "iopub.status.idle": "2024-01-18T17:16:07.772233Z", "shell.execute_reply": "2024-01-18T17:16:07.771982Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def default_graph_attr(dot):\n", " dot.attr('node', shape='plain')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Finally, we define a method `display_tree()` that accepts these four functions `extract_node()`, `default_edge_attr()`, `default_node_attr()` and `default_graph_attr()` and uses them to display the tree." ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:07.773701Z", "iopub.status.busy": "2024-01-18T17:16:07.773605Z", "iopub.status.idle": "2024-01-18T17:16:07.776406Z", "shell.execute_reply": "2024-01-18T17:16:07.776157Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "def display_tree(derivation_tree: DerivationTree,\n", " log: bool = False,\n", " extract_node: Callable = extract_node,\n", " node_attr: Callable = default_node_attr,\n", " edge_attr: Callable = default_edge_attr,\n", " graph_attr: Callable = default_graph_attr) -> Any:\n", "\n", " # If we import display_tree, we also have to import its functions\n", " from graphviz import Digraph\n", "\n", " counter = 0\n", "\n", " def traverse_tree(dot, tree, id=0):\n", " (symbol, children, annotation) = extract_node(tree, id)\n", " node_attr(dot, id, symbol, annotation)\n", "\n", " if children:\n", " for child in children:\n", " nonlocal counter\n", " counter += 1\n", " child_id = counter\n", " edge_attr(dot, id, child_id)\n", " traverse_tree(dot, child, child_id)\n", "\n", " dot = Digraph(comment=\"Derivation Tree\")\n", " graph_attr(dot)\n", " traverse_tree(dot, derivation_tree)\n", " if log:\n", " print(dot)\n", " return dot" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### End of Excursion" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "This is what our tree visualizes into:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:07.777877Z", "iopub.status.busy": "2024-01-18T17:16:07.777769Z", "iopub.status.idle": "2024-01-18T17:16:08.145074Z", "shell.execute_reply": "2024-01-18T17:16:08.144638Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "3\n", " + \n", "\n", "\n", "\n", "1->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", "<term>\n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_tree(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.146990Z", "iopub.status.busy": "2024-01-18T17:16:08.146866Z", "iopub.status.idle": "2024-01-18T17:16:08.151294Z", "shell.execute_reply": "2024-01-18T17:16:08.151044Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", "
\n", "

Quiz

\n", "

\n", "

And which of these is the internal representation of derivation_tree?
\n", "

\n", "

\n", "

\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "

\n", " \n", " \n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "quiz(\"And which of these is the internal representation of `derivation_tree`?\",\n", " [\n", " \"`('', [('', ([' + ']))])`\",\n", " \"`('', [('', (['', ' + ', ']))])`\",\n", " \"`\" + repr(derivation_tree) + \"`\",\n", " \"`(\" + repr(derivation_tree) + \", None)`\"\n", " ], len(\"eleven\") - len(\"one\"))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "You can check it out yourself:" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.152801Z", "iopub.status.busy": "2024-01-18T17:16:08.152710Z", "iopub.status.idle": "2024-01-18T17:16:08.155068Z", "shell.execute_reply": "2024-01-18T17:16:08.154801Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "('', [('', [('', None), (' + ', []), ('', None)])])" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "derivation_tree" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Within this book, we also occasionally use a function `display_annotated_tree()` which allows adding annotations to individual nodes." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Excursion: Source code and example for `display_annotated_tree()`" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "`display_annotated_tree()` displays an annotated tree structure, and lays out the graph left to right." ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.156945Z", "iopub.status.busy": "2024-01-18T17:16:08.156810Z", "iopub.status.idle": "2024-01-18T17:16:08.160525Z", "shell.execute_reply": "2024-01-18T17:16:08.160244Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "def display_annotated_tree(tree: DerivationTree,\n", " a_nodes: Dict[int, str],\n", " a_edges: Dict[Tuple[int, int], str],\n", " log: bool = False):\n", " def graph_attr(dot):\n", " dot.attr('node', shape='plain')\n", " dot.graph_attr['rankdir'] = 'LR'\n", "\n", " def annotate_node(dot, nid, symbol, ann):\n", " if nid in a_nodes:\n", " dot.node(repr(nid), \n", " \"%s (%s)\" % (dot_escape(unicode_escape(symbol)),\n", " a_nodes[nid]))\n", " else:\n", " dot.node(repr(nid), dot_escape(unicode_escape(symbol)))\n", "\n", " def annotate_edge(dot, start_node, stop_node):\n", " if (start_node, stop_node) in a_edges:\n", " dot.edge(repr(start_node), repr(stop_node),\n", " a_edges[(start_node, stop_node)])\n", " else:\n", " dot.edge(repr(start_node), repr(stop_node))\n", "\n", " return display_tree(tree, log=log,\n", " node_attr=annotate_node,\n", " edge_attr=annotate_edge,\n", " graph_attr=graph_attr)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.161955Z", "iopub.status.busy": "2024-01-18T17:16:08.161866Z", "iopub.status.idle": "2024-01-18T17:16:08.536496Z", "shell.execute_reply": "2024-01-18T17:16:08.536157Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "3\n", " +  (plus)\n", "\n", "\n", "\n", "1->3\n", "\n", "\n", "op\n", "\n", "\n", "\n", "4\n", "<term>\n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_annotated_tree(derivation_tree, {3: 'plus'}, {(1, 3): 'op'}, log=False)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### End of Excursion" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "If we want to see all the leaf nodes in a tree as a string, the following `all_terminals()` function comes in handy:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.538340Z", "iopub.status.busy": "2024-01-18T17:16:08.538219Z", "iopub.status.idle": "2024-01-18T17:16:08.540711Z", "shell.execute_reply": "2024-01-18T17:16:08.540457Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def all_terminals(tree: DerivationTree) -> str:\n", " (symbol, children) = tree\n", " if children is None:\n", " # This is a nonterminal symbol not expanded yet\n", " return symbol\n", "\n", " if len(children) == 0:\n", " # This is a terminal symbol\n", " return symbol\n", "\n", " # This is an expanded symbol:\n", " # Concatenate all terminal symbols from all children\n", " return ''.join([all_terminals(c) for c in children])" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.542128Z", "iopub.status.busy": "2024-01-18T17:16:08.542019Z", "iopub.status.idle": "2024-01-18T17:16:08.544441Z", "shell.execute_reply": "2024-01-18T17:16:08.544175Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "' + '" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "all_terminals(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The alternative `tree_to_string()` function also converts the tree to a string; however, it replaces nonterminal symbols by empty strings." ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.546037Z", "iopub.status.busy": "2024-01-18T17:16:08.545931Z", "iopub.status.idle": "2024-01-18T17:16:08.548040Z", "shell.execute_reply": "2024-01-18T17:16:08.547737Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def tree_to_string(tree: DerivationTree) -> str:\n", " symbol, children, *_ = tree\n", " if children:\n", " return ''.join(tree_to_string(c) for c in children)\n", " else:\n", " return '' if is_nonterminal(symbol) else symbol" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.549503Z", "iopub.status.busy": "2024-01-18T17:16:08.549377Z", "iopub.status.idle": "2024-01-18T17:16:08.551563Z", "shell.execute_reply": "2024-01-18T17:16:08.551274Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "' + '" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_to_string(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Expanding a Node" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "Let us now develop an algorithm that takes a tree with non-expanded symbols (say, `derivation_tree`, above), and expands all these symbols one after the other. As with earlier fuzzers, we create a special subclass of `Fuzzer` – in this case, `GrammarFuzzer`. A `GrammarFuzzer` gets a grammar and a start symbol; the other parameters will be used later to further control creation and to support debugging." ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.553068Z", "iopub.status.busy": "2024-01-18T17:16:08.552957Z", "iopub.status.idle": "2024-01-18T17:16:08.554498Z", "shell.execute_reply": "2024-01-18T17:16:08.554265Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Fuzzer import Fuzzer" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.555797Z", "iopub.status.busy": "2024-01-18T17:16:08.555710Z", "iopub.status.idle": "2024-01-18T17:16:08.558316Z", "shell.execute_reply": "2024-01-18T17:16:08.558079Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class GrammarFuzzer(Fuzzer):\n", " \"\"\"Produce strings from grammars efficiently, using derivation trees.\"\"\"\n", "\n", " def __init__(self,\n", " grammar: Grammar,\n", " start_symbol: str = START_SYMBOL,\n", " min_nonterminals: int = 0,\n", " max_nonterminals: int = 10,\n", " disp: bool = False,\n", " log: Union[bool, int] = False) -> None:\n", " \"\"\"Produce strings from `grammar`, starting with `start_symbol`.\n", " If `min_nonterminals` or `max_nonterminals` is given, use them as limits \n", " for the number of nonterminals produced. \n", " If `disp` is set, display the intermediate derivation trees.\n", " If `log` is set, show intermediate steps as text on standard output.\"\"\"\n", "\n", " self.grammar = grammar\n", " self.start_symbol = start_symbol\n", " self.min_nonterminals = min_nonterminals\n", " self.max_nonterminals = max_nonterminals\n", " self.disp = disp\n", " self.log = log\n", " self.check_grammar() # Invokes is_valid_grammar()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To add further methods to `GrammarFuzzer`, we use the hack already introduced for [the `MutationFuzzer` class](MutationFuzzer.ipynb). The construct\n", "\n", "```python\n", "class GrammarFuzzer(GrammarFuzzer):\n", " def new_method(self, args):\n", " pass\n", "```\n", "\n", "allows us to add a new method `new_method()` to the `GrammarFuzzer` class. (Actually, we get a new `GrammarFuzzer` class that extends the old one, but for all our purposes, this does not matter.)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Excursion: `check_grammar()` implementation" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We can use the above hack to define the helper method `check_grammar()`, which checks the given grammar for consistency:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.559837Z", "iopub.status.busy": "2024-01-18T17:16:08.559745Z", "iopub.status.idle": "2024-01-18T17:16:08.562003Z", "shell.execute_reply": "2024-01-18T17:16:08.561759Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def check_grammar(self) -> None:\n", " \"\"\"Check the grammar passed\"\"\"\n", " assert self.start_symbol in self.grammar\n", " assert is_valid_grammar(\n", " self.grammar,\n", " start_symbol=self.start_symbol,\n", " supported_opts=self.supported_opts())\n", "\n", " def supported_opts(self) -> Set[str]:\n", " \"\"\"Set of supported options. To be overloaded in subclasses.\"\"\"\n", " return set() # We don't support specific options" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### End of Excursion" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "Let us now define a helper method `init_tree()` that constructs a tree with just the start symbol:" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.563574Z", "iopub.status.busy": "2024-01-18T17:16:08.563481Z", "iopub.status.idle": "2024-01-18T17:16:08.565332Z", "shell.execute_reply": "2024-01-18T17:16:08.565095Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def init_tree(self) -> DerivationTree:\n", " return (self.start_symbol, None)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.566778Z", "iopub.status.busy": "2024-01-18T17:16:08.566683Z", "iopub.status.idle": "2024-01-18T17:16:08.955693Z", "shell.execute_reply": "2024-01-18T17:16:08.955321Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(EXPR_GRAMMAR)\n", "display_tree(f.init_tree())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "This is the tree we want to expand." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Picking a Children Alternative to be Expanded" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "One of the central methods in `GrammarFuzzer` is `choose_node_expansion()`. This method gets a node (say, the `` node) and a list of possible lists of children to be expanded (one for every possible expansion from the grammar), chooses one of them, and returns its index in the possible children list." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "By overloading this method (notably in later chapters), we can implement different strategies – for now, it simply randomly picks one of the given lists of children (which in turn are lists of derivation trees)." ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.957718Z", "iopub.status.busy": "2024-01-18T17:16:08.957588Z", "iopub.status.idle": "2024-01-18T17:16:08.959951Z", "shell.execute_reply": "2024-01-18T17:16:08.959677Z" }, "slideshow": { "slide_type": "subslide" }, "tags": [] }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def choose_node_expansion(self, node: DerivationTree,\n", " children_alternatives: List[List[DerivationTree]]) -> int:\n", " \"\"\"Return index of expansion in `children_alternatives` to be selected.\n", " 'children_alternatives`: a list of possible children for `node`.\n", " Defaults to random. To be overloaded in subclasses.\"\"\"\n", " return random.randrange(0, len(children_alternatives))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Getting a List of Possible Expansions" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "To actually obtain the list of possible children, we will need a helper function `expansion_to_children()` that takes an expansion string and decomposes it into a list of derivation trees – one for each symbol (terminal or nonterminal) in the string." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Excursion: Implementing `expansion_to_children()`" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "The function `expansion_to_children()` uses the `re.split()` method to split an expansion string into a list of children nodes:" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.961806Z", "iopub.status.busy": "2024-01-18T17:16:08.961678Z", "iopub.status.idle": "2024-01-18T17:16:08.963985Z", "shell.execute_reply": "2024-01-18T17:16:08.963712Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "def expansion_to_children(expansion: Expansion) -> List[DerivationTree]:\n", " # print(\"Converting \" + repr(expansion))\n", " # strings contains all substrings -- both terminals and nonterminals such\n", " # that ''.join(strings) == expansion\n", "\n", " expansion = exp_string(expansion)\n", " assert isinstance(expansion, str)\n", "\n", " if expansion == \"\": # Special case: epsilon expansion\n", " return [(\"\", [])]\n", "\n", " strings = re.split(RE_NONTERMINAL, expansion)\n", " return [(s, None) if is_nonterminal(s) else (s, [])\n", " for s in strings if len(s) > 0]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### End of Excursion" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.965550Z", "iopub.status.busy": "2024-01-18T17:16:08.965435Z", "iopub.status.idle": "2024-01-18T17:16:08.967680Z", "shell.execute_reply": "2024-01-18T17:16:08.967412Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "[('', None), (' + ', []), ('', None)]" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expansion_to_children(\" + \")" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "The case of an *epsilon expansion*, i.e. expanding into an empty string as in ` ::=` needs special treatment:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.969087Z", "iopub.status.busy": "2024-01-18T17:16:08.968973Z", "iopub.status.idle": "2024-01-18T17:16:08.971015Z", "shell.execute_reply": "2024-01-18T17:16:08.970755Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "[('', [])]" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expansion_to_children(\"\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Just like `nonterminals()` in the [chapter on Grammars](Grammars.ipynb), we provide for future extensions, allowing the expansion to be a tuple with extra data (which will be ignored)." ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.972572Z", "iopub.status.busy": "2024-01-18T17:16:08.972464Z", "iopub.status.idle": "2024-01-18T17:16:08.974716Z", "shell.execute_reply": "2024-01-18T17:16:08.974479Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "[('+', []), ('', None)]" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "expansion_to_children((\"+\", {\"extra_data\": 1234}))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We realize this helper as a method in `GrammarFuzzer` such that it can be overloaded by subclasses:" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.976196Z", "iopub.status.busy": "2024-01-18T17:16:08.976075Z", "iopub.status.idle": "2024-01-18T17:16:08.977933Z", "shell.execute_reply": "2024-01-18T17:16:08.977665Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def expansion_to_children(self, expansion: Expansion) -> List[DerivationTree]:\n", " return expansion_to_children(expansion)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Putting Things Together" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "With this, we can now take\n", "\n", "1. some non-expanded node in the tree, \n", "2. choose a random expansion, and\n", "3. return the new tree." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "This is what the method `expand_node_randomly()` does." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Excursion: `expand_node_randomly()` implementation" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "The function `expand_node_randomly()` uses a helper function `choose_node_expansion()` to randomly pick an index from an array of possible children. (`choose_node_expansion()` can be overloaded in subclasses.)" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.979486Z", "iopub.status.busy": "2024-01-18T17:16:08.979377Z", "iopub.status.idle": "2024-01-18T17:16:08.980978Z", "shell.execute_reply": "2024-01-18T17:16:08.980696Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import random" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.982528Z", "iopub.status.busy": "2024-01-18T17:16:08.982419Z", "iopub.status.idle": "2024-01-18T17:16:08.984981Z", "shell.execute_reply": "2024-01-18T17:16:08.984729Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def expand_node_randomly(self, node: DerivationTree) -> DerivationTree:\n", " \"\"\"Choose a random expansion for `node` and return it\"\"\"\n", " (symbol, children) = node\n", " assert children is None\n", "\n", " if self.log:\n", " print(\"Expanding\", all_terminals(node), \"randomly\")\n", "\n", " # Fetch the possible expansions from grammar...\n", " expansions = self.grammar[symbol]\n", " children_alternatives: List[List[DerivationTree]] = [\n", " self.expansion_to_children(expansion) for expansion in expansions\n", " ]\n", "\n", " # ... and select a random expansion\n", " index = self.choose_node_expansion(node, children_alternatives)\n", " chosen_children = children_alternatives[index]\n", "\n", " # Process children (for subclasses)\n", " chosen_children = self.process_chosen_children(chosen_children,\n", " expansions[index])\n", "\n", " # Return with new children\n", " return (symbol, chosen_children)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The generic `expand_node()` method can later be used to select different expansion strategies; as of now, it only uses `expand_node_randomly()`." ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.986550Z", "iopub.status.busy": "2024-01-18T17:16:08.986415Z", "iopub.status.idle": "2024-01-18T17:16:08.988272Z", "shell.execute_reply": "2024-01-18T17:16:08.988013Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def expand_node(self, node: DerivationTree) -> DerivationTree:\n", " return self.expand_node_randomly(node)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The helper function `process_chosen_children()` does nothing; it can be overloaded by subclasses to process the children once chosen." ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:08.989945Z", "iopub.status.busy": "2024-01-18T17:16:08.989837Z", "iopub.status.idle": "2024-01-18T17:16:08.991591Z", "shell.execute_reply": "2024-01-18T17:16:08.991353Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def process_chosen_children(self,\n", " chosen_children: List[DerivationTree],\n", " expansion: Expansion) -> List[DerivationTree]:\n", " \"\"\"Process children after selection. By default, does nothing.\"\"\"\n", " return chosen_children" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### End of Excursion" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "This is how `expand_node_randomly()` works:" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:08.993244Z", "iopub.status.busy": "2024-01-18T17:16:08.993133Z", "iopub.status.idle": "2024-01-18T17:16:09.399018Z", "shell.execute_reply": "2024-01-18T17:16:09.398563Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Before expand_node_randomly():\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<integer>\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(EXPR_GRAMMAR, log=True)\n", "\n", "print(\"Before expand_node_randomly():\")\n", "expr_tree = (\"\", None)\n", "display_tree(expr_tree)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:09.401479Z", "iopub.status.busy": "2024-01-18T17:16:09.401292Z", "iopub.status.idle": "2024-01-18T17:16:09.793892Z", "shell.execute_reply": "2024-01-18T17:16:09.793299Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After expand_node_randomly():\n", "Expanding randomly\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<integer>\n", "\n", "\n", "\n", "1\n", "<digit>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<integer>\n", "\n", "\n", "\n", "0->2\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(\"After expand_node_randomly():\")\n", "expr_tree = f.expand_node_randomly(expr_tree)\n", "display_tree(expr_tree)" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:09.795951Z", "iopub.status.busy": "2024-01-18T17:16:09.795819Z", "iopub.status.idle": "2024-01-18T17:16:09.797831Z", "shell.execute_reply": "2024-01-18T17:16:09.797549Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# docassert\n", "assert expr_tree[1][0][0] == ''" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:09.799463Z", "iopub.status.busy": "2024-01-18T17:16:09.799330Z", "iopub.status.idle": "2024-01-18T17:16:09.803390Z", "shell.execute_reply": "2024-01-18T17:16:09.803082Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", "
\n", "

Quiz

\n", "

\n", "

What tree do we get if we expand the <digit> subtree?
\n", "

\n", "

\n", "

\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "

\n", " \n", " \n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "quiz(\"What tree do we get if we expand the `` subtree?\",\n", " [\n", " \"We get another `` as new child of ``\",\n", " \"We get some digit as child of ``\",\n", " \"We get another `` as second child of ``\",\n", " \"The entire tree becomes a single node with a digit\"\n", " ], 'len(\"2\") + len(\"2\")')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We can surely put this to the test, right? Here we go:" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:09.804796Z", "iopub.status.busy": "2024-01-18T17:16:09.804712Z", "iopub.status.idle": "2024-01-18T17:16:10.215377Z", "shell.execute_reply": "2024-01-18T17:16:10.214977Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<digit>\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "digit_subtree = expr_tree[1][0] # type: ignore\n", "display_tree(digit_subtree)" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:10.217198Z", "iopub.status.busy": "2024-01-18T17:16:10.217057Z", "iopub.status.idle": "2024-01-18T17:16:10.618206Z", "shell.execute_reply": "2024-01-18T17:16:10.617805Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After expanding the subtree:\n", "Expanding randomly\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<digit>\n", "\n", "\n", "\n", "1\n", "6 (54)\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(\"After expanding the subtree:\")\n", "digit_subtree = f.expand_node_randomly(digit_subtree)\n", "display_tree(digit_subtree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We see that `` gets expanded again according to the grammar rules – namely, into a single digit." ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:10.619975Z", "iopub.status.busy": "2024-01-18T17:16:10.619860Z", "iopub.status.idle": "2024-01-18T17:16:10.623517Z", "shell.execute_reply": "2024-01-18T17:16:10.623156Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", "
\n", "

Quiz

\n", "

\n", "

Is the original expr_tree affected by this change?
\n", "

\n", "

\n", "

\n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "

\n", " \n", " \n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "quiz(\"Is the original `expr_tree` affected by this change?\",\n", " [\n", " \"No, it is unchanged\",\n", " \"Yes, it has also gained a new child\"\n", " ], \"1 ** (1 - 1)\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Although we have changed one of the subtrees, the original `expr_tree` is unaffected:" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:10.625254Z", "iopub.status.busy": "2024-01-18T17:16:10.625152Z", "iopub.status.idle": "2024-01-18T17:16:11.003618Z", "shell.execute_reply": "2024-01-18T17:16:11.003170Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<integer>\n", "\n", "\n", "\n", "1\n", "<digit>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<integer>\n", "\n", "\n", "\n", "0->2\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_tree(expr_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "That is because `expand_node_randomly()` returns a new (expanded) tree and does not change the tree passed as argument." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Expanding a Tree\n", "\n", "Let us now apply our functions for expanding a single node to some node in the tree. To this end, we first need to _search the tree for non-expanded nodes_. `possible_expansions()` counts how many unexpanded symbols there are in a tree:" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:11.024413Z", "iopub.status.busy": "2024-01-18T17:16:11.024181Z", "iopub.status.idle": "2024-01-18T17:16:11.026785Z", "shell.execute_reply": "2024-01-18T17:16:11.026514Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def possible_expansions(self, node: DerivationTree) -> int:\n", " (symbol, children) = node\n", " if children is None:\n", " return 1\n", "\n", " return sum(self.possible_expansions(c) for c in children)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:11.028567Z", "iopub.status.busy": "2024-01-18T17:16:11.028437Z", "iopub.status.idle": "2024-01-18T17:16:11.031060Z", "shell.execute_reply": "2024-01-18T17:16:11.030774Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] } ], "source": [ "f = GrammarFuzzer(EXPR_GRAMMAR)\n", "print(f.possible_expansions(derivation_tree))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "The method `any_possible_expansions()` returns True if the tree has any non-expanded nodes." ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:11.032810Z", "iopub.status.busy": "2024-01-18T17:16:11.032695Z", "iopub.status.idle": "2024-01-18T17:16:11.034776Z", "shell.execute_reply": "2024-01-18T17:16:11.034532Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def any_possible_expansions(self, node: DerivationTree) -> bool:\n", " (symbol, children) = node\n", " if children is None:\n", " return True\n", "\n", " return any(self.any_possible_expansions(c) for c in children)" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:11.036272Z", "iopub.status.busy": "2024-01-18T17:16:11.036162Z", "iopub.status.idle": "2024-01-18T17:16:11.038458Z", "shell.execute_reply": "2024-01-18T17:16:11.038175Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(EXPR_GRAMMAR)\n", "f.any_possible_expansions(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "Here comes `expand_tree_once()`, the core method of our tree expansion algorithm. It first checks whether it is currently being applied on a nonterminal symbol without expansion; if so, it invokes `expand_node()` on it, as discussed above. " ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "If the node is already expanded (i.e. has children), it checks the subset of children which still have non-expanded symbols, randomly selects one of them, and applies itself recursively on that child." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Excursion: `expand_tree_once()` implementation" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "The `expand_tree_once()` method replaces the child _in place_, meaning that it actually mutates the tree being passed as an argument rather than returning a new tree. This in-place mutation is what makes this function particularly efficient. Again, we use a helper method (`choose_tree_expansion()`) to return the chosen index from a list of children that can be expanded." ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:11.040260Z", "iopub.status.busy": "2024-01-18T17:16:11.040118Z", "iopub.status.idle": "2024-01-18T17:16:11.043399Z", "shell.execute_reply": "2024-01-18T17:16:11.043057Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def choose_tree_expansion(self,\n", " tree: DerivationTree,\n", " children: List[DerivationTree]) -> int:\n", " \"\"\"Return index of subtree in `children` to be selected for expansion.\n", " Defaults to random.\"\"\"\n", " return random.randrange(0, len(children))\n", "\n", " def expand_tree_once(self, tree: DerivationTree) -> DerivationTree:\n", " \"\"\"Choose an unexpanded symbol in tree; expand it.\n", " Can be overloaded in subclasses.\"\"\"\n", " (symbol, children) = tree\n", " if children is None:\n", " # Expand this node\n", " return self.expand_node(tree)\n", "\n", " # Find all children with possible expansions\n", " expandable_children = [\n", " c for c in children if self.any_possible_expansions(c)]\n", "\n", " # `index_map` translates an index in `expandable_children`\n", " # back into the original index in `children`\n", " index_map = [i for (i, c) in enumerate(children)\n", " if c in expandable_children]\n", "\n", " # Select a random child\n", " child_to_be_expanded = \\\n", " self.choose_tree_expansion(tree, expandable_children)\n", "\n", " # Expand in place\n", " children[index_map[child_to_be_expanded]] = \\\n", " self.expand_tree_once(expandable_children[child_to_be_expanded])\n", "\n", " return tree" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### End of Excursion" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "Let us illustrate how `expand_tree_once()` works. We start with our derivation tree from above..." ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:11.045205Z", "iopub.status.busy": "2024-01-18T17:16:11.045105Z", "iopub.status.idle": "2024-01-18T17:16:11.455649Z", "shell.execute_reply": "2024-01-18T17:16:11.455278Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "3\n", " + \n", "\n", "\n", "\n", "1->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", "<term>\n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "derivation_tree = (\"\",\n", " [(\"\",\n", " [(\"\", None),\n", " (\" + \", []),\n", " (\"\", None)]\n", " )])\n", "display_tree(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "... and now expand it twice:" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:11.457724Z", "iopub.status.busy": "2024-01-18T17:16:11.457560Z", "iopub.status.idle": "2024-01-18T17:16:11.841703Z", "shell.execute_reply": "2024-01-18T17:16:11.841237Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Expanding randomly\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "3\n", " + \n", "\n", "\n", "\n", "1->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", "<term>\n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<factor>\n", "\n", "\n", "\n", "4->5\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(EXPR_GRAMMAR, log=True)\n", "derivation_tree = f.expand_tree_once(derivation_tree)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:11.843840Z", "iopub.status.busy": "2024-01-18T17:16:11.843704Z", "iopub.status.idle": "2024-01-18T17:16:12.251414Z", "shell.execute_reply": "2024-01-18T17:16:12.250784Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Expanding randomly\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "4\n", " + \n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<term>\n", "\n", "\n", "\n", "1->5\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<factor>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "derivation_tree = f.expand_tree_once(derivation_tree)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "We see that with each step, one more symbol is expanded. Now all it takes is to apply this again and again, expanding the tree further and further." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Closing the Expansion\n", "\n", "With `expand_tree_once()`, we can keep on expanding the tree – but how do we actually stop? The key idea here, introduced by Luke in \\cite{Luke2000}, is that after inflating the derivation tree to some maximum size, we _only want to apply expansions that increase the size of the tree by a minimum_. For ``, for instance, we would prefer an expansion into ``, as this will not introduce further recursion (and potential size inflation); for ``, likewise, an expansion into `` is preferred, as it will less increase tree size than ``." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "To identify the _cost_ of expanding a symbol, we introduce two functions that mutually rely on each other:\n", "\n", "* `symbol_cost()` returns the minimum cost of all expansions of a symbol, using `expansion_cost()` to compute the cost for each expansion.\n", "* `expansion_cost()` returns the sum of all expansions in `expansions`. If a nonterminal is encountered again during traversal, the cost of the expansion is $\\infty$, indicating (potentially infinite) recursion." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Excursion: Implementing Cost Functions" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:12.253525Z", "iopub.status.busy": "2024-01-18T17:16:12.253378Z", "iopub.status.idle": "2024-01-18T17:16:12.256707Z", "shell.execute_reply": "2024-01-18T17:16:12.256379Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def symbol_cost(self, symbol: str, seen: Set[str] = set()) \\\n", " -> Union[int, float]:\n", " expansions = self.grammar[symbol]\n", " return min(self.expansion_cost(e, seen | {symbol}) for e in expansions)\n", "\n", " def expansion_cost(self, expansion: Expansion,\n", " seen: Set[str] = set()) -> Union[int, float]:\n", " symbols = nonterminals(expansion)\n", " if len(symbols) == 0:\n", " return 1 # no symbol\n", "\n", " if any(s in seen for s in symbols):\n", " return float('inf')\n", "\n", " # the value of a expansion is the sum of all expandable variables\n", " # inside + 1\n", " return sum(self.symbol_cost(s, seen) for s in symbols) + 1" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### End of Excursion" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "Here are two examples: The minimum cost of expanding a digit is 1, since we have to choose from one of its expansions." ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:12.258431Z", "iopub.status.busy": "2024-01-18T17:16:12.258318Z", "iopub.status.idle": "2024-01-18T17:16:12.260452Z", "shell.execute_reply": "2024-01-18T17:16:12.260195Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "f = GrammarFuzzer(EXPR_GRAMMAR)\n", "assert f.symbol_cost(\"\") == 1" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "The minimum cost of expanding ``, though, is five, as this is the minimum number of expansions required. (`` $\\rightarrow$ `` $\\rightarrow$ `` $\\rightarrow$ `` $\\rightarrow$ `` $\\rightarrow$ 1)" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:12.261965Z", "iopub.status.busy": "2024-01-18T17:16:12.261869Z", "iopub.status.idle": "2024-01-18T17:16:12.263739Z", "shell.execute_reply": "2024-01-18T17:16:12.263475Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "assert f.symbol_cost(\"\") == 5" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "We define `expand_node_by_cost(self, node, choose)`, a variant of `expand_node()` that takes the above cost into account. It determines the minimum cost `cost` across all children and then chooses a child from the list using the `choose` function, which by default is the minimum cost. If multiple children all have the same minimum cost, it chooses randomly between these." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Excursion: `expand_node_by_cost()` implementation" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:12.265281Z", "iopub.status.busy": "2024-01-18T17:16:12.265186Z", "iopub.status.idle": "2024-01-18T17:16:12.268544Z", "shell.execute_reply": "2024-01-18T17:16:12.268278Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def expand_node_by_cost(self, node: DerivationTree, \n", " choose: Callable = min) -> DerivationTree:\n", " (symbol, children) = node\n", " assert children is None\n", "\n", " # Fetch the possible expansions from grammar...\n", " expansions = self.grammar[symbol]\n", "\n", " children_alternatives_with_cost = [(self.expansion_to_children(expansion),\n", " self.expansion_cost(expansion, {symbol}),\n", " expansion)\n", " for expansion in expansions]\n", "\n", " costs = [cost for (child, cost, expansion)\n", " in children_alternatives_with_cost]\n", " chosen_cost = choose(costs)\n", " children_with_chosen_cost = [child for (child, child_cost, _) \n", " in children_alternatives_with_cost\n", " if child_cost == chosen_cost]\n", " expansion_with_chosen_cost = [expansion for (_, child_cost, expansion)\n", " in children_alternatives_with_cost\n", " if child_cost == chosen_cost]\n", "\n", " index = self.choose_node_expansion(node, children_with_chosen_cost)\n", "\n", " chosen_children = children_with_chosen_cost[index]\n", " chosen_expansion = expansion_with_chosen_cost[index]\n", " chosen_children = self.process_chosen_children(\n", " chosen_children, chosen_expansion)\n", "\n", " # Return with a new list\n", " return (symbol, chosen_children)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### End of Excursion" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The shortcut `expand_node_min_cost()` passes `min()` as the `choose` function, which makes it expand nodes at minimum cost." ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:12.270113Z", "iopub.status.busy": "2024-01-18T17:16:12.270016Z", "iopub.status.idle": "2024-01-18T17:16:12.272036Z", "shell.execute_reply": "2024-01-18T17:16:12.271803Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def expand_node_min_cost(self, node: DerivationTree) -> DerivationTree:\n", " if self.log:\n", " print(\"Expanding\", all_terminals(node), \"at minimum cost\")\n", "\n", " return self.expand_node_by_cost(node, min)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "We can now apply this function to close the expansion of our derivation tree, using `expand_tree_once()` with the above `expand_node_min_cost()` as expansion function." ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:12.273611Z", "iopub.status.busy": "2024-01-18T17:16:12.273518Z", "iopub.status.idle": "2024-01-18T17:16:12.275495Z", "shell.execute_reply": "2024-01-18T17:16:12.275183Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def expand_node(self, node: DerivationTree) -> DerivationTree:\n", " return self.expand_node_min_cost(node)" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:12.276930Z", "iopub.status.busy": "2024-01-18T17:16:12.276829Z", "iopub.status.idle": "2024-01-18T17:16:12.672130Z", "shell.execute_reply": "2024-01-18T17:16:12.671772Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "4\n", " + \n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<term>\n", "\n", "\n", "\n", "1->5\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<factor>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(EXPR_GRAMMAR, log=True)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:12.674027Z", "iopub.status.busy": "2024-01-18T17:16:12.673890Z", "iopub.status.idle": "2024-01-18T17:16:12.676244Z", "shell.execute_reply": "2024-01-18T17:16:12.675925Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# docassert\n", "assert f.any_possible_expansions(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:12.677736Z", "iopub.status.busy": "2024-01-18T17:16:12.677600Z", "iopub.status.idle": "2024-01-18T17:16:13.075967Z", "shell.execute_reply": "2024-01-18T17:16:13.075541Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Expanding at minimum cost\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "4\n", " + \n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<term>\n", "\n", "\n", "\n", "1->5\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<factor>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n", "7\n", "<integer>\n", "\n", "\n", "\n", "6->7\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "if f.any_possible_expansions(derivation_tree):\n", " derivation_tree = f.expand_tree_once(derivation_tree)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:13.077928Z", "iopub.status.busy": "2024-01-18T17:16:13.077791Z", "iopub.status.idle": "2024-01-18T17:16:13.079569Z", "shell.execute_reply": "2024-01-18T17:16:13.079337Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# docassert\n", "assert f.any_possible_expansions(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:13.081212Z", "iopub.status.busy": "2024-01-18T17:16:13.081087Z", "iopub.status.idle": "2024-01-18T17:16:13.464329Z", "shell.execute_reply": "2024-01-18T17:16:13.463950Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Expanding at minimum cost\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "4\n", " + \n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<term>\n", "\n", "\n", "\n", "1->5\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<factor>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n", "7\n", "<integer>\n", "\n", "\n", "\n", "6->7\n", "\n", "\n", "\n", "\n", "\n", "8\n", "<digit>\n", "\n", "\n", "\n", "7->8\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "if f.any_possible_expansions(derivation_tree):\n", " derivation_tree = f.expand_tree_once(derivation_tree)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:13.466021Z", "iopub.status.busy": "2024-01-18T17:16:13.465900Z", "iopub.status.idle": "2024-01-18T17:16:13.467626Z", "shell.execute_reply": "2024-01-18T17:16:13.467383Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# docassert\n", "assert f.any_possible_expansions(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:13.469161Z", "iopub.status.busy": "2024-01-18T17:16:13.469042Z", "iopub.status.idle": "2024-01-18T17:16:13.848343Z", "shell.execute_reply": "2024-01-18T17:16:13.847900Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Expanding at minimum cost\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "4\n", " + \n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<term>\n", "\n", "\n", "\n", "1->5\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<factor>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n", "7\n", "<integer>\n", "\n", "\n", "\n", "6->7\n", "\n", "\n", "\n", "\n", "\n", "8\n", "<digit>\n", "\n", "\n", "\n", "7->8\n", "\n", "\n", "\n", "\n", "\n", "9\n", "7 (55)\n", "\n", "\n", "\n", "8->9\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "if f.any_possible_expansions(derivation_tree):\n", " derivation_tree = f.expand_tree_once(derivation_tree)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We keep on expanding until all nonterminals are expanded." ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:13.850132Z", "iopub.status.busy": "2024-01-18T17:16:13.850005Z", "iopub.status.idle": "2024-01-18T17:16:13.852321Z", "shell.execute_reply": "2024-01-18T17:16:13.852053Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Expanding at minimum cost\n", "Expanding at minimum cost\n", "Expanding at minimum cost\n", "Expanding at minimum cost\n" ] } ], "source": [ "while f.any_possible_expansions(derivation_tree):\n", " derivation_tree = f.expand_tree_once(derivation_tree) " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Here is the final tree:" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:13.854219Z", "iopub.status.busy": "2024-01-18T17:16:13.854035Z", "iopub.status.idle": "2024-01-18T17:16:14.233074Z", "shell.execute_reply": "2024-01-18T17:16:14.232714Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "8\n", " + \n", "\n", "\n", "\n", "1->8\n", "\n", "\n", "\n", "\n", "\n", "9\n", "<term>\n", "\n", "\n", "\n", "1->9\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", "<factor>\n", "\n", "\n", "\n", "3->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<integer>\n", "\n", "\n", "\n", "4->5\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<digit>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n", "7\n", "8 (56)\n", "\n", "\n", "\n", "6->7\n", "\n", "\n", "\n", "\n", "\n", "10\n", "<factor>\n", "\n", "\n", "\n", "9->10\n", "\n", "\n", "\n", "\n", "\n", "11\n", "<integer>\n", "\n", "\n", "\n", "10->11\n", "\n", "\n", "\n", "\n", "\n", "12\n", "<digit>\n", "\n", "\n", "\n", "11->12\n", "\n", "\n", "\n", "\n", "\n", "13\n", "7 (55)\n", "\n", "\n", "\n", "12->13\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_tree(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "We see that in each step, `expand_node_min_cost()` chooses an expansion that does not increase the number of symbols, eventually closing all open expansions." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Node Inflation\n", "\n", "Especially at the beginning of an expansion, we may be interested in getting _as many nodes as possible_ – that is, we'd like to prefer expansions that give us _more_ nonterminals to expand. This is actually the exact opposite of what `expand_node_min_cost()` gives us, and we can implement a method `expand_node_max_cost()` that will always choose among the nodes with the _highest_ cost:" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:14.235029Z", "iopub.status.busy": "2024-01-18T17:16:14.234910Z", "iopub.status.idle": "2024-01-18T17:16:14.237231Z", "shell.execute_reply": "2024-01-18T17:16:14.236939Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def expand_node_max_cost(self, node: DerivationTree) -> DerivationTree:\n", " if self.log:\n", " print(\"Expanding\", all_terminals(node), \"at maximum cost\")\n", "\n", " return self.expand_node_by_cost(node, max)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To illustrate `expand_node_max_cost()`, we can again redefine `expand_node()` to use it, and then use `expand_tree_once()` to show a few expansion steps:" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:14.238899Z", "iopub.status.busy": "2024-01-18T17:16:14.238783Z", "iopub.status.idle": "2024-01-18T17:16:14.240796Z", "shell.execute_reply": "2024-01-18T17:16:14.240442Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def expand_node(self, node: DerivationTree) -> DerivationTree:\n", " return self.expand_node_max_cost(node)" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:14.242338Z", "iopub.status.busy": "2024-01-18T17:16:14.242233Z", "iopub.status.idle": "2024-01-18T17:16:14.244016Z", "shell.execute_reply": "2024-01-18T17:16:14.243770Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "derivation_tree = (\"\",\n", " [(\"\",\n", " [(\"\", None),\n", " (\" + \", []),\n", " (\"\", None)]\n", " )])" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:14.245425Z", "iopub.status.busy": "2024-01-18T17:16:14.245309Z", "iopub.status.idle": "2024-01-18T17:16:14.611459Z", "shell.execute_reply": "2024-01-18T17:16:14.611086Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "3\n", " + \n", "\n", "\n", "\n", "1->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", "<term>\n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(EXPR_GRAMMAR, log=True)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:14.613109Z", "iopub.status.busy": "2024-01-18T17:16:14.612997Z", "iopub.status.idle": "2024-01-18T17:16:14.614769Z", "shell.execute_reply": "2024-01-18T17:16:14.614511Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "# docassert\n", "assert f.any_possible_expansions(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:14.616310Z", "iopub.status.busy": "2024-01-18T17:16:14.616204Z", "iopub.status.idle": "2024-01-18T17:16:14.971017Z", "shell.execute_reply": "2024-01-18T17:16:14.970689Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Expanding at maximum cost\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "6\n", " + \n", "\n", "\n", "\n", "1->6\n", "\n", "\n", "\n", "\n", "\n", "7\n", "<term>\n", "\n", "\n", "\n", "1->7\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", " + \n", "\n", "\n", "\n", "2->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<expr>\n", "\n", "\n", "\n", "2->5\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "if f.any_possible_expansions(derivation_tree):\n", " derivation_tree = f.expand_tree_once(derivation_tree)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:14.972783Z", "iopub.status.busy": "2024-01-18T17:16:14.972648Z", "iopub.status.idle": "2024-01-18T17:16:14.974523Z", "shell.execute_reply": "2024-01-18T17:16:14.974269Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# docassert\n", "assert f.any_possible_expansions(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:14.975881Z", "iopub.status.busy": "2024-01-18T17:16:14.975781Z", "iopub.status.idle": "2024-01-18T17:16:15.361957Z", "shell.execute_reply": "2024-01-18T17:16:15.361576Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Expanding at maximum cost\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "6\n", " + \n", "\n", "\n", "\n", "1->6\n", "\n", "\n", "\n", "\n", "\n", "7\n", "<term>\n", "\n", "\n", "\n", "1->7\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", " + \n", "\n", "\n", "\n", "2->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<expr>\n", "\n", "\n", "\n", "2->5\n", "\n", "\n", "\n", "\n", "\n", "8\n", "<factor>\n", "\n", "\n", "\n", "7->8\n", "\n", "\n", "\n", "\n", "\n", "9\n", " / \n", "\n", "\n", "\n", "7->9\n", "\n", "\n", "\n", "\n", "\n", "10\n", "<term>\n", "\n", "\n", "\n", "7->10\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "if f.any_possible_expansions(derivation_tree):\n", " derivation_tree = f.expand_tree_once(derivation_tree)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:15.363632Z", "iopub.status.busy": "2024-01-18T17:16:15.363506Z", "iopub.status.idle": "2024-01-18T17:16:15.365335Z", "shell.execute_reply": "2024-01-18T17:16:15.365057Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# docassert\n", "assert f.any_possible_expansions(derivation_tree)" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:15.366939Z", "iopub.status.busy": "2024-01-18T17:16:15.366821Z", "iopub.status.idle": "2024-01-18T17:16:15.728302Z", "shell.execute_reply": "2024-01-18T17:16:15.727963Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Expanding at maximum cost\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "9\n", " + \n", "\n", "\n", "\n", "1->9\n", "\n", "\n", "\n", "\n", "\n", "10\n", "<term>\n", "\n", "\n", "\n", "1->10\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "7\n", " + \n", "\n", "\n", "\n", "2->7\n", "\n", "\n", "\n", "\n", "\n", "8\n", "<expr>\n", "\n", "\n", "\n", "2->8\n", "\n", "\n", "\n", "\n", "\n", "4\n", "<factor>\n", "\n", "\n", "\n", "3->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", " * \n", "\n", "\n", "\n", "3->5\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<term>\n", "\n", "\n", "\n", "3->6\n", "\n", "\n", "\n", "\n", "\n", "11\n", "<factor>\n", "\n", "\n", "\n", "10->11\n", "\n", "\n", "\n", "\n", "\n", "12\n", " / \n", "\n", "\n", "\n", "10->12\n", "\n", "\n", "\n", "\n", "\n", "13\n", "<term>\n", "\n", "\n", "\n", "10->13\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "if f.any_possible_expansions(derivation_tree):\n", " derivation_tree = f.expand_tree_once(derivation_tree)\n", "display_tree(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We see that with each step, the number of nonterminals increases. Obviously, we have to put a limit on this number." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Three Expansion Phases\n", "\n", "We can now put all three phases together in a single function `expand_tree()` which will work as follows:\n", "\n", "1. **Max cost expansion.** Expand the tree using expansions with maximum cost until we have at least `min_nonterminals` nonterminals. This phase can be easily skipped by setting `min_nonterminals` to zero.\n", "2. **Random expansion.** Keep on expanding the tree randomly until we reach `max_nonterminals` nonterminals.\n", "3. **Min cost expansion.** Close the expansion with minimum cost.\n", "\n", "We implement these three phases by having `expand_node` reference the expansion method to apply. This is controlled by setting `expand_node` (the method reference) to first `expand_node_max_cost` (i.e., calling `expand_node()` invokes `expand_node_max_cost()`), then `expand_node_randomly`, and finally `expand_node_min_cost`. In the first two phases, we also set a maximum limit of `min_nonterminals` and `max_nonterminals`, respectively." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### Excursion: Implementation of three-phase `expand_tree()`" ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:15.730162Z", "iopub.status.busy": "2024-01-18T17:16:15.730038Z", "iopub.status.idle": "2024-01-18T17:16:15.734346Z", "shell.execute_reply": "2024-01-18T17:16:15.734054Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def log_tree(self, tree: DerivationTree) -> None:\n", " \"\"\"Output a tree if self.log is set; if self.display is also set, show the tree structure\"\"\"\n", " if self.log:\n", " print(\"Tree:\", all_terminals(tree))\n", " if self.disp:\n", " display(display_tree(tree))\n", " # print(self.possible_expansions(tree), \"possible expansion(s) left\")\n", "\n", " def expand_tree_with_strategy(self, tree: DerivationTree,\n", " expand_node_method: Callable,\n", " limit: Optional[int] = None):\n", " \"\"\"Expand tree using `expand_node_method` as node expansion function\n", " until the number of possible expansions reaches `limit`.\"\"\"\n", " self.expand_node = expand_node_method # type: ignore\n", " while ((limit is None\n", " or self.possible_expansions(tree) < limit)\n", " and self.any_possible_expansions(tree)):\n", " tree = self.expand_tree_once(tree)\n", " self.log_tree(tree)\n", " return tree\n", "\n", " def expand_tree(self, tree: DerivationTree) -> DerivationTree:\n", " \"\"\"Expand `tree` in a three-phase strategy until all expansions are complete.\"\"\"\n", " self.log_tree(tree)\n", " tree = self.expand_tree_with_strategy(\n", " tree, self.expand_node_max_cost, self.min_nonterminals)\n", " tree = self.expand_tree_with_strategy(\n", " tree, self.expand_node_randomly, self.max_nonterminals)\n", " tree = self.expand_tree_with_strategy(\n", " tree, self.expand_node_min_cost)\n", "\n", " assert self.possible_expansions(tree) == 0\n", "\n", " return tree" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### End of Excursion" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let us try this out on our example. We start with a half-expanded derivation tree:" ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:15.735843Z", "iopub.status.busy": "2024-01-18T17:16:15.735743Z", "iopub.status.idle": "2024-01-18T17:16:15.737642Z", "shell.execute_reply": "2024-01-18T17:16:15.737419Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "initial_derivation_tree: DerivationTree = (\"\",\n", " [(\"\",\n", " [(\"\", None),\n", " (\" + \", []),\n", " (\"\", None)]\n", " )])" ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:15.738902Z", "iopub.status.busy": "2024-01-18T17:16:15.738826Z", "iopub.status.idle": "2024-01-18T17:16:16.106680Z", "shell.execute_reply": "2024-01-18T17:16:16.106299Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "3\n", " + \n", "\n", "\n", "\n", "1->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", "<term>\n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_tree(initial_derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We now apply our expansion strategy on this tree. We see that initially, nodes are expanded at maximum cost, then randomly, and then closing the expansion at minimum cost." ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:16.108410Z", "iopub.status.busy": "2024-01-18T17:16:16.108295Z", "iopub.status.idle": "2024-01-18T17:16:16.112378Z", "shell.execute_reply": "2024-01-18T17:16:16.112099Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Tree: + \n", "Expanding at maximum cost\n", "Tree: + / \n", "Expanding randomly\n", "Tree: + . / \n", "Expanding randomly\n", "Tree: + + . / \n", "Expanding at minimum cost\n", "Tree: + + . / \n", "Expanding at minimum cost\n", "Tree: + + . / \n", "Expanding at minimum cost\n", "Tree: + + . / \n", "Expanding at minimum cost\n", "Tree: + + . / \n", "Expanding at minimum cost\n", "Tree: + + . / \n", "Expanding at minimum cost\n", "Tree: + + . / \n", "Expanding at minimum cost\n", "Tree: + + . / 9\n", "Expanding at minimum cost\n", "Tree: + + . / 9\n", "Expanding at minimum cost\n", "Tree: + + . / 9\n", "Expanding at minimum cost\n", "Tree: + + . / 9\n", "Expanding at minimum cost\n", "Tree: + + . / 9\n", "Expanding at minimum cost\n", "Tree: + + . / 9\n", "Expanding at minimum cost\n", "Tree: + + .3 / 9\n", "Expanding at minimum cost\n", "Tree: + + 7.3 / 9\n", "Expanding at minimum cost\n", "Tree: 4 + + 7.3 / 9\n", "Expanding at minimum cost\n", "Tree: 4 + + 7.3 / 9\n", "Expanding at minimum cost\n", "Tree: 4 + 7 + 7.3 / 9\n" ] } ], "source": [ "f = GrammarFuzzer(\n", " EXPR_GRAMMAR,\n", " min_nonterminals=3,\n", " max_nonterminals=5,\n", " log=True)\n", "derivation_tree = f.expand_tree(initial_derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "This is the final derivation tree:" ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:16.113938Z", "iopub.status.busy": "2024-01-18T17:16:16.113846Z", "iopub.status.idle": "2024-01-18T17:16:16.472735Z", "shell.execute_reply": "2024-01-18T17:16:16.472338Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<expr>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "15\n", " + \n", "\n", "\n", "\n", "1->15\n", "\n", "\n", "\n", "\n", "\n", "16\n", "<term>\n", "\n", "\n", "\n", "1->16\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<term>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "8\n", " + \n", "\n", "\n", "\n", "2->8\n", "\n", "\n", "\n", "\n", "\n", "9\n", "<expr>\n", "\n", "\n", "\n", "2->9\n", "\n", "\n", "\n", "\n", "\n", "4\n", "<factor>\n", "\n", "\n", "\n", "3->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<integer>\n", "\n", "\n", "\n", "4->5\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<digit>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n", "7\n", "4 (52)\n", "\n", "\n", "\n", "6->7\n", "\n", "\n", "\n", "\n", "\n", "10\n", "<term>\n", "\n", "\n", "\n", "9->10\n", "\n", "\n", "\n", "\n", "\n", "11\n", "<factor>\n", "\n", "\n", "\n", "10->11\n", "\n", "\n", "\n", "\n", "\n", "12\n", "<integer>\n", "\n", "\n", "\n", "11->12\n", "\n", "\n", "\n", "\n", "\n", "13\n", "<digit>\n", "\n", "\n", "\n", "12->13\n", "\n", "\n", "\n", "\n", "\n", "14\n", "7 (55)\n", "\n", "\n", "\n", "13->14\n", "\n", "\n", "\n", "\n", "\n", "17\n", "<factor>\n", "\n", "\n", "\n", "16->17\n", "\n", "\n", "\n", "\n", "\n", "25\n", " / \n", "\n", "\n", "\n", "16->25\n", "\n", "\n", "\n", "\n", "\n", "26\n", "<term>\n", "\n", "\n", "\n", "16->26\n", "\n", "\n", "\n", "\n", "\n", "18\n", "<integer>\n", "\n", "\n", "\n", "17->18\n", "\n", "\n", "\n", "\n", "\n", "21\n", ". (46)\n", "\n", "\n", "\n", "17->21\n", "\n", "\n", "\n", "\n", "\n", "22\n", "<integer>\n", "\n", "\n", "\n", "17->22\n", "\n", "\n", "\n", "\n", "\n", "19\n", "<digit>\n", "\n", "\n", "\n", "18->19\n", "\n", "\n", "\n", "\n", "\n", "20\n", "7 (55)\n", "\n", "\n", "\n", "19->20\n", "\n", "\n", "\n", "\n", "\n", "23\n", "<digit>\n", "\n", "\n", "\n", "22->23\n", "\n", "\n", "\n", "\n", "\n", "24\n", "3 (51)\n", "\n", "\n", "\n", "23->24\n", "\n", "\n", "\n", "\n", "\n", "27\n", "<factor>\n", "\n", "\n", "\n", "26->27\n", "\n", "\n", "\n", "\n", "\n", "28\n", "<integer>\n", "\n", "\n", "\n", "27->28\n", "\n", "\n", "\n", "\n", "\n", "29\n", "<digit>\n", "\n", "\n", "\n", "28->29\n", "\n", "\n", "\n", "\n", "\n", "30\n", "9 (57)\n", "\n", "\n", "\n", "29->30\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 116, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_tree(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "And this is the resulting string:" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:16.474485Z", "iopub.status.busy": "2024-01-18T17:16:16.474366Z", "iopub.status.idle": "2024-01-18T17:16:16.476649Z", "shell.execute_reply": "2024-01-18T17:16:16.476369Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'4 + 7 + 7.3 / 9'" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "all_terminals(derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Putting it all Together" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "Based on this, we can now define a function `fuzz()` that – like `simple_grammar_fuzzer()` – simply takes a grammar and produces a string from it. It thus no longer exposes the complexity of derivation trees." ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:16.478196Z", "iopub.status.busy": "2024-01-18T17:16:16.478083Z", "iopub.status.idle": "2024-01-18T17:16:16.480492Z", "shell.execute_reply": "2024-01-18T17:16:16.480262Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class GrammarFuzzer(GrammarFuzzer):\n", " def fuzz_tree(self) -> DerivationTree:\n", " \"\"\"Produce a derivation tree from the grammar.\"\"\"\n", " tree = self.init_tree()\n", " # print(tree)\n", "\n", " # Expand all nonterminals\n", " tree = self.expand_tree(tree)\n", " if self.log:\n", " print(repr(all_terminals(tree)))\n", " if self.disp:\n", " display(display_tree(tree))\n", " return tree\n", "\n", " def fuzz(self) -> str:\n", " \"\"\"Produce a string from the grammar.\"\"\"\n", " self.derivation_tree = self.fuzz_tree()\n", " return all_terminals(self.derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "We can now apply this on all our defined grammars (and visualize the derivation tree along)" ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:16.481944Z", "iopub.status.busy": "2024-01-18T17:16:16.481844Z", "iopub.status.idle": "2024-01-18T17:16:16.497275Z", "shell.execute_reply": "2024-01-18T17:16:16.497000Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'+31.14 * -9 * -+(++(-(0.98 - 0 - 7) - +-+1.7 - -6 + 3 * 4)) * 5.0 + 70'" ] }, "execution_count": 119, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(EXPR_GRAMMAR)\n", "f.fuzz()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "After calling `fuzz()`, the produced derivation tree is accessible in the `derivation_tree` attribute:" ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:16.498704Z", "iopub.status.busy": "2024-01-18T17:16:16.498620Z", "iopub.status.idle": "2024-01-18T17:16:16.867520Z", "shell.execute_reply": "2024-01-18T17:16:16.867173Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<expr>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<term>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "128\n", " + \n", "\n", "\n", "\n", "1->128\n", "\n", "\n", "\n", "\n", "\n", "129\n", "<expr>\n", "\n", "\n", "\n", "1->129\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<factor>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "19\n", " * \n", "\n", "\n", "\n", "2->19\n", "\n", "\n", "\n", "\n", "\n", "20\n", "<term>\n", "\n", "\n", "\n", "2->20\n", "\n", "\n", "\n", "\n", "\n", "4\n", "+ (43)\n", "\n", "\n", "\n", "3->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<factor>\n", "\n", "\n", "\n", "3->5\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<integer>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n", "12\n", ". (46)\n", "\n", "\n", "\n", "5->12\n", "\n", "\n", "\n", "\n", "\n", "13\n", "<integer>\n", "\n", "\n", "\n", "5->13\n", "\n", "\n", "\n", "\n", "\n", "7\n", "<digit>\n", "\n", "\n", "\n", "6->7\n", "\n", "\n", "\n", "\n", "\n", "9\n", "<integer>\n", "\n", "\n", "\n", "6->9\n", "\n", "\n", "\n", "\n", "\n", "8\n", "3 (51)\n", "\n", "\n", "\n", "7->8\n", "\n", "\n", "\n", "\n", "\n", "10\n", "<digit>\n", "\n", "\n", "\n", "9->10\n", "\n", "\n", "\n", "\n", "\n", "11\n", "1 (49)\n", "\n", "\n", "\n", "10->11\n", "\n", "\n", "\n", "\n", "\n", "14\n", "<digit>\n", "\n", "\n", "\n", "13->14\n", "\n", "\n", "\n", "\n", "\n", "16\n", "<integer>\n", "\n", "\n", "\n", "13->16\n", "\n", "\n", "\n", "\n", "\n", "15\n", "1 (49)\n", "\n", "\n", "\n", "14->15\n", "\n", "\n", "\n", "\n", "\n", "17\n", "<digit>\n", "\n", "\n", "\n", "16->17\n", "\n", "\n", "\n", "\n", "\n", "18\n", "4 (52)\n", "\n", "\n", "\n", "17->18\n", "\n", "\n", "\n", "\n", "\n", "21\n", "<factor>\n", "\n", "\n", "\n", "20->21\n", "\n", "\n", "\n", "\n", "\n", "27\n", " * \n", "\n", "\n", "\n", "20->27\n", "\n", "\n", "\n", "\n", "\n", "28\n", "<term>\n", "\n", "\n", "\n", "20->28\n", "\n", "\n", "\n", "\n", "\n", "22\n", "- (45)\n", "\n", "\n", "\n", "21->22\n", "\n", "\n", "\n", "\n", "\n", "23\n", "<factor>\n", "\n", "\n", "\n", "21->23\n", "\n", "\n", "\n", "\n", "\n", "24\n", "<integer>\n", "\n", "\n", "\n", "23->24\n", "\n", "\n", "\n", "\n", "\n", "25\n", "<digit>\n", "\n", "\n", "\n", "24->25\n", "\n", "\n", "\n", "\n", "\n", "26\n", "9 (57)\n", "\n", "\n", "\n", "25->26\n", "\n", "\n", "\n", "\n", "\n", "29\n", "<factor>\n", "\n", "\n", "\n", "28->29\n", "\n", "\n", "\n", "\n", "\n", "118\n", " * \n", "\n", "\n", "\n", "28->118\n", "\n", "\n", "\n", "\n", "\n", "119\n", "<term>\n", "\n", "\n", "\n", "28->119\n", "\n", "\n", "\n", "\n", "\n", "30\n", "- (45)\n", "\n", "\n", "\n", "29->30\n", "\n", "\n", "\n", "\n", "\n", "31\n", "<factor>\n", "\n", "\n", "\n", "29->31\n", "\n", "\n", "\n", "\n", "\n", "32\n", "+ (43)\n", "\n", "\n", "\n", "31->32\n", "\n", "\n", "\n", "\n", "\n", "33\n", "<factor>\n", "\n", "\n", "\n", "31->33\n", "\n", "\n", "\n", "\n", "\n", "34\n", "( (40)\n", "\n", "\n", "\n", "33->34\n", "\n", "\n", "\n", "\n", "\n", "35\n", "<expr>\n", "\n", "\n", "\n", "33->35\n", "\n", "\n", "\n", "\n", "\n", "117\n", ") (41)\n", "\n", "\n", "\n", "33->117\n", "\n", "\n", "\n", "\n", "\n", "36\n", "<term>\n", "\n", "\n", "\n", "35->36\n", "\n", "\n", "\n", "\n", "\n", "37\n", "<factor>\n", "\n", "\n", "\n", "36->37\n", "\n", "\n", "\n", "\n", "\n", "38\n", "+ (43)\n", "\n", "\n", "\n", "37->38\n", "\n", "\n", "\n", "\n", "\n", "39\n", "<factor>\n", "\n", "\n", "\n", "37->39\n", "\n", "\n", "\n", "\n", "\n", "40\n", "+ (43)\n", "\n", "\n", "\n", "39->40\n", "\n", "\n", "\n", "\n", "\n", "41\n", "<factor>\n", "\n", "\n", "\n", "39->41\n", "\n", "\n", "\n", "\n", "\n", "42\n", "( (40)\n", "\n", "\n", "\n", "41->42\n", "\n", "\n", "\n", "\n", "\n", "43\n", "<expr>\n", "\n", "\n", "\n", "41->43\n", "\n", "\n", "\n", "\n", "\n", "116\n", ") (41)\n", "\n", "\n", "\n", "41->116\n", "\n", "\n", "\n", "\n", "\n", "44\n", "<term>\n", "\n", "\n", "\n", "43->44\n", "\n", "\n", "\n", "\n", "\n", "77\n", " - \n", "\n", "\n", "\n", "43->77\n", "\n", "\n", "\n", "\n", "\n", "78\n", "<expr>\n", "\n", "\n", "\n", "43->78\n", "\n", "\n", "\n", "\n", "\n", "45\n", "<factor>\n", "\n", "\n", "\n", "44->45\n", "\n", "\n", "\n", "\n", "\n", "46\n", "- (45)\n", "\n", "\n", "\n", "45->46\n", "\n", "\n", "\n", "\n", "\n", "47\n", "<factor>\n", "\n", "\n", "\n", "45->47\n", "\n", "\n", "\n", "\n", "\n", "48\n", "( (40)\n", "\n", "\n", "\n", "47->48\n", "\n", "\n", "\n", "\n", "\n", "49\n", "<expr>\n", "\n", "\n", "\n", "47->49\n", "\n", "\n", "\n", "\n", "\n", "76\n", ") (41)\n", "\n", "\n", "\n", "47->76\n", "\n", "\n", "\n", "\n", "\n", "50\n", "<term>\n", "\n", "\n", "\n", "49->50\n", "\n", "\n", "\n", "\n", "\n", "62\n", " - \n", "\n", "\n", "\n", "49->62\n", "\n", "\n", "\n", "\n", "\n", "63\n", "<expr>\n", "\n", "\n", "\n", "49->63\n", "\n", "\n", "\n", "\n", "\n", "51\n", "<factor>\n", "\n", "\n", "\n", "50->51\n", "\n", "\n", "\n", "\n", "\n", "52\n", "<integer>\n", "\n", "\n", "\n", "51->52\n", "\n", "\n", "\n", "\n", "\n", "55\n", ". (46)\n", "\n", "\n", "\n", "51->55\n", "\n", "\n", "\n", "\n", "\n", "56\n", "<integer>\n", "\n", "\n", "\n", "51->56\n", "\n", "\n", "\n", "\n", "\n", "53\n", "<digit>\n", "\n", "\n", "\n", "52->53\n", "\n", "\n", "\n", "\n", "\n", "54\n", "0 (48)\n", "\n", "\n", "\n", "53->54\n", "\n", "\n", "\n", "\n", "\n", "57\n", "<digit>\n", "\n", "\n", "\n", "56->57\n", "\n", "\n", "\n", "\n", "\n", "59\n", "<integer>\n", "\n", "\n", "\n", "56->59\n", "\n", "\n", "\n", "\n", "\n", "58\n", "9 (57)\n", "\n", "\n", "\n", "57->58\n", "\n", "\n", "\n", "\n", "\n", "60\n", "<digit>\n", "\n", "\n", "\n", "59->60\n", "\n", "\n", "\n", "\n", "\n", "61\n", "8 (56)\n", "\n", "\n", "\n", "60->61\n", "\n", "\n", "\n", "\n", "\n", "64\n", "<term>\n", "\n", "\n", "\n", "63->64\n", "\n", "\n", "\n", "\n", "\n", "69\n", " - \n", "\n", "\n", "\n", "63->69\n", "\n", "\n", "\n", "\n", "\n", "70\n", "<expr>\n", "\n", "\n", "\n", "63->70\n", "\n", "\n", "\n", "\n", "\n", "65\n", "<factor>\n", "\n", "\n", "\n", "64->65\n", "\n", "\n", "\n", "\n", "\n", "66\n", "<integer>\n", "\n", "\n", "\n", "65->66\n", "\n", "\n", "\n", "\n", "\n", "67\n", "<digit>\n", "\n", "\n", "\n", "66->67\n", "\n", "\n", "\n", "\n", "\n", "68\n", "0 (48)\n", "\n", "\n", "\n", "67->68\n", "\n", "\n", "\n", "\n", "\n", "71\n", "<term>\n", "\n", "\n", "\n", "70->71\n", "\n", "\n", "\n", "\n", "\n", "72\n", "<factor>\n", "\n", "\n", "\n", "71->72\n", "\n", "\n", "\n", "\n", "\n", "73\n", "<integer>\n", "\n", "\n", "\n", "72->73\n", "\n", "\n", "\n", "\n", "\n", "74\n", "<digit>\n", "\n", "\n", "\n", "73->74\n", "\n", "\n", "\n", "\n", "\n", "75\n", "7 (55)\n", "\n", "\n", "\n", "74->75\n", "\n", "\n", "\n", "\n", "\n", "79\n", "<term>\n", "\n", "\n", "\n", "78->79\n", "\n", "\n", "\n", "\n", "\n", "94\n", " - \n", "\n", "\n", "\n", "78->94\n", "\n", "\n", "\n", "\n", "\n", "95\n", "<expr>\n", "\n", "\n", "\n", "78->95\n", "\n", "\n", "\n", "\n", "\n", "80\n", "<factor>\n", "\n", "\n", "\n", "79->80\n", "\n", "\n", "\n", "\n", "\n", "81\n", "+ (43)\n", "\n", "\n", "\n", "80->81\n", "\n", "\n", "\n", "\n", "\n", "82\n", "<factor>\n", "\n", "\n", "\n", "80->82\n", "\n", "\n", "\n", "\n", "\n", "83\n", "- (45)\n", "\n", "\n", "\n", "82->83\n", "\n", "\n", "\n", "\n", "\n", "84\n", "<factor>\n", "\n", "\n", "\n", "82->84\n", "\n", "\n", "\n", "\n", "\n", "85\n", "+ (43)\n", "\n", "\n", "\n", "84->85\n", "\n", "\n", "\n", "\n", "\n", "86\n", "<factor>\n", "\n", "\n", "\n", "84->86\n", "\n", "\n", "\n", "\n", "\n", "87\n", "<integer>\n", "\n", "\n", "\n", "86->87\n", "\n", "\n", "\n", "\n", "\n", "90\n", ". (46)\n", "\n", "\n", "\n", "86->90\n", "\n", "\n", "\n", "\n", "\n", "91\n", "<integer>\n", "\n", "\n", "\n", "86->91\n", "\n", "\n", "\n", "\n", "\n", "88\n", "<digit>\n", "\n", "\n", "\n", "87->88\n", "\n", "\n", "\n", "\n", "\n", "89\n", "1 (49)\n", "\n", "\n", "\n", "88->89\n", "\n", "\n", "\n", "\n", "\n", "92\n", "<digit>\n", "\n", "\n", "\n", "91->92\n", "\n", "\n", "\n", "\n", "\n", "93\n", "7 (55)\n", "\n", "\n", "\n", "92->93\n", "\n", "\n", "\n", "\n", "\n", "96\n", "<term>\n", "\n", "\n", "\n", "95->96\n", "\n", "\n", "\n", "\n", "\n", "103\n", " + \n", "\n", "\n", "\n", "95->103\n", "\n", "\n", "\n", "\n", "\n", "104\n", "<expr>\n", "\n", "\n", "\n", "95->104\n", "\n", "\n", "\n", "\n", "\n", "97\n", "<factor>\n", "\n", "\n", "\n", "96->97\n", "\n", "\n", "\n", "\n", "\n", "98\n", "- (45)\n", "\n", "\n", "\n", "97->98\n", "\n", "\n", "\n", "\n", "\n", "99\n", "<factor>\n", "\n", "\n", "\n", "97->99\n", "\n", "\n", "\n", "\n", "\n", "100\n", "<integer>\n", "\n", "\n", "\n", "99->100\n", "\n", "\n", "\n", "\n", "\n", "101\n", "<digit>\n", "\n", "\n", "\n", "100->101\n", "\n", "\n", "\n", "\n", "\n", "102\n", "6 (54)\n", "\n", "\n", "\n", "101->102\n", "\n", "\n", "\n", "\n", "\n", "105\n", "<term>\n", "\n", "\n", "\n", "104->105\n", "\n", "\n", "\n", "\n", "\n", "106\n", "<factor>\n", "\n", "\n", "\n", "105->106\n", "\n", "\n", "\n", "\n", "\n", "110\n", " * \n", "\n", "\n", "\n", "105->110\n", "\n", "\n", "\n", "\n", "\n", "111\n", "<term>\n", "\n", "\n", "\n", "105->111\n", "\n", "\n", "\n", "\n", "\n", "107\n", "<integer>\n", "\n", "\n", "\n", "106->107\n", "\n", "\n", "\n", "\n", "\n", "108\n", "<digit>\n", "\n", "\n", "\n", "107->108\n", "\n", "\n", "\n", "\n", "\n", "109\n", "3 (51)\n", "\n", "\n", "\n", "108->109\n", "\n", "\n", "\n", "\n", "\n", "112\n", "<factor>\n", "\n", "\n", "\n", "111->112\n", "\n", "\n", "\n", "\n", "\n", "113\n", "<integer>\n", "\n", "\n", "\n", "112->113\n", "\n", "\n", "\n", "\n", "\n", "114\n", "<digit>\n", "\n", "\n", "\n", "113->114\n", "\n", "\n", "\n", "\n", "\n", "115\n", "4 (52)\n", "\n", "\n", "\n", "114->115\n", "\n", "\n", "\n", "\n", "\n", "120\n", "<factor>\n", "\n", "\n", "\n", "119->120\n", "\n", "\n", "\n", "\n", "\n", "121\n", "<integer>\n", "\n", "\n", "\n", "120->121\n", "\n", "\n", "\n", "\n", "\n", "124\n", ". (46)\n", "\n", "\n", "\n", "120->124\n", "\n", "\n", "\n", "\n", "\n", "125\n", "<integer>\n", "\n", "\n", "\n", "120->125\n", "\n", "\n", "\n", "\n", "\n", "122\n", "<digit>\n", "\n", "\n", "\n", "121->122\n", "\n", "\n", "\n", "\n", "\n", "123\n", "5 (53)\n", "\n", "\n", "\n", "122->123\n", "\n", "\n", "\n", "\n", "\n", "126\n", "<digit>\n", "\n", "\n", "\n", "125->126\n", "\n", "\n", "\n", "\n", "\n", "127\n", "0 (48)\n", "\n", "\n", "\n", "126->127\n", "\n", "\n", "\n", "\n", "\n", "130\n", "<term>\n", "\n", "\n", "\n", "129->130\n", "\n", "\n", "\n", "\n", "\n", "131\n", "<factor>\n", "\n", "\n", "\n", "130->131\n", "\n", "\n", "\n", "\n", "\n", "132\n", "<integer>\n", "\n", "\n", "\n", "131->132\n", "\n", "\n", "\n", "\n", "\n", "133\n", "<digit>\n", "\n", "\n", "\n", "132->133\n", "\n", "\n", "\n", "\n", "\n", "135\n", "<integer>\n", "\n", "\n", "\n", "132->135\n", "\n", "\n", "\n", "\n", "\n", "134\n", "7 (55)\n", "\n", "\n", "\n", "133->134\n", "\n", "\n", "\n", "\n", "\n", "136\n", "<digit>\n", "\n", "\n", "\n", "135->136\n", "\n", "\n", "\n", "\n", "\n", "137\n", "0 (48)\n", "\n", "\n", "\n", "136->137\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 120, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_tree(f.derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let us try out the grammar fuzzer (and its trees) on other grammar formats." ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:16.869186Z", "iopub.status.busy": "2024-01-18T17:16:16.869093Z", "iopub.status.idle": "2024-01-18T17:16:16.873150Z", "shell.execute_reply": "2024-01-18T17:16:16.872894Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'https://www.google.com:63/x01?x71=81&x04=x51&abc=2'" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(URL_GRAMMAR)\n", "f.fuzz()" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:16.874574Z", "iopub.status.busy": "2024-01-18T17:16:16.874485Z", "iopub.status.idle": "2024-01-18T17:16:17.237619Z", "shell.execute_reply": "2024-01-18T17:16:17.237246Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<url>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<scheme>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "4\n", "://\n", "\n", "\n", "\n", "1->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<authority>\n", "\n", "\n", "\n", "1->5\n", "\n", "\n", "\n", "\n", "\n", "15\n", "<path>\n", "\n", "\n", "\n", "1->15\n", "\n", "\n", "\n", "\n", "\n", "23\n", "<query>\n", "\n", "\n", "\n", "1->23\n", "\n", "\n", "\n", "\n", "\n", "3\n", "https\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<host>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n", "8\n", ": (58)\n", "\n", "\n", "\n", "5->8\n", "\n", "\n", "\n", "\n", "\n", "9\n", "<port>\n", "\n", "\n", "\n", "5->9\n", "\n", "\n", "\n", "\n", "\n", "7\n", "www.google.com\n", "\n", "\n", "\n", "6->7\n", "\n", "\n", "\n", "\n", "\n", "10\n", "<nat>\n", "\n", "\n", "\n", "9->10\n", "\n", "\n", "\n", "\n", "\n", "11\n", "<digit>\n", "\n", "\n", "\n", "10->11\n", "\n", "\n", "\n", "\n", "\n", "13\n", "<digit>\n", "\n", "\n", "\n", "10->13\n", "\n", "\n", "\n", "\n", "\n", "12\n", "6 (54)\n", "\n", "\n", "\n", "11->12\n", "\n", "\n", "\n", "\n", "\n", "14\n", "3 (51)\n", "\n", "\n", "\n", "13->14\n", "\n", "\n", "\n", "\n", "\n", "16\n", "/ (47)\n", "\n", "\n", "\n", "15->16\n", "\n", "\n", "\n", "\n", "\n", "17\n", "<id>\n", "\n", "\n", "\n", "15->17\n", "\n", "\n", "\n", "\n", "\n", "18\n", "x (120)\n", "\n", "\n", "\n", "17->18\n", "\n", "\n", "\n", "\n", "\n", "19\n", "<digit>\n", "\n", "\n", "\n", "17->19\n", "\n", "\n", "\n", "\n", "\n", "21\n", "<digit>\n", "\n", "\n", "\n", "17->21\n", "\n", "\n", "\n", "\n", "\n", "20\n", "0 (48)\n", "\n", "\n", "\n", "19->20\n", "\n", "\n", "\n", "\n", "\n", "22\n", "1 (49)\n", "\n", "\n", "\n", "21->22\n", "\n", "\n", "\n", "\n", "\n", "24\n", "? (63)\n", "\n", "\n", "\n", "23->24\n", "\n", "\n", "\n", "\n", "\n", "25\n", "<params>\n", "\n", "\n", "\n", "23->25\n", "\n", "\n", "\n", "\n", "\n", "26\n", "<param>\n", "\n", "\n", "\n", "25->26\n", "\n", "\n", "\n", "\n", "\n", "39\n", "& (38)\n", "\n", "\n", "\n", "25->39\n", "\n", "\n", "\n", "\n", "\n", "40\n", "<params>\n", "\n", "\n", "\n", "25->40\n", "\n", "\n", "\n", "\n", "\n", "27\n", "<id>\n", "\n", "\n", "\n", "26->27\n", "\n", "\n", "\n", "\n", "\n", "33\n", "= (61)\n", "\n", "\n", "\n", "26->33\n", "\n", "\n", "\n", "\n", "\n", "34\n", "<nat>\n", "\n", "\n", "\n", "26->34\n", "\n", "\n", "\n", "\n", "\n", "28\n", "x (120)\n", "\n", "\n", "\n", "27->28\n", "\n", "\n", "\n", "\n", "\n", "29\n", "<digit>\n", "\n", "\n", "\n", "27->29\n", "\n", "\n", "\n", "\n", "\n", "31\n", "<digit>\n", "\n", "\n", "\n", "27->31\n", "\n", "\n", "\n", "\n", "\n", "30\n", "7 (55)\n", "\n", "\n", "\n", "29->30\n", "\n", "\n", "\n", "\n", "\n", "32\n", "1 (49)\n", "\n", "\n", "\n", "31->32\n", "\n", "\n", "\n", "\n", "\n", "35\n", "<digit>\n", "\n", "\n", "\n", "34->35\n", "\n", "\n", "\n", "\n", "\n", "37\n", "<digit>\n", "\n", "\n", "\n", "34->37\n", "\n", "\n", "\n", "\n", "\n", "36\n", "8 (56)\n", "\n", "\n", "\n", "35->36\n", "\n", "\n", "\n", "\n", "\n", "38\n", "1 (49)\n", "\n", "\n", "\n", "37->38\n", "\n", "\n", "\n", "\n", "\n", "41\n", "<param>\n", "\n", "\n", "\n", "40->41\n", "\n", "\n", "\n", "\n", "\n", "55\n", "& (38)\n", "\n", "\n", "\n", "40->55\n", "\n", "\n", "\n", "\n", "\n", "56\n", "<params>\n", "\n", "\n", "\n", "40->56\n", "\n", "\n", "\n", "\n", "\n", "42\n", "<id>\n", "\n", "\n", "\n", "41->42\n", "\n", "\n", "\n", "\n", "\n", "48\n", "= (61)\n", "\n", "\n", "\n", "41->48\n", "\n", "\n", "\n", "\n", "\n", "49\n", "<id>\n", "\n", "\n", "\n", "41->49\n", "\n", "\n", "\n", "\n", "\n", "43\n", "x (120)\n", "\n", "\n", "\n", "42->43\n", "\n", "\n", "\n", "\n", "\n", "44\n", "<digit>\n", "\n", "\n", "\n", "42->44\n", "\n", "\n", "\n", "\n", "\n", "46\n", "<digit>\n", "\n", "\n", "\n", "42->46\n", "\n", "\n", "\n", "\n", "\n", "45\n", "0 (48)\n", "\n", "\n", "\n", "44->45\n", "\n", "\n", "\n", "\n", "\n", "47\n", "4 (52)\n", "\n", "\n", "\n", "46->47\n", "\n", "\n", "\n", "\n", "\n", "50\n", "x (120)\n", "\n", "\n", "\n", "49->50\n", "\n", "\n", "\n", "\n", "\n", "51\n", "<digit>\n", "\n", "\n", "\n", "49->51\n", "\n", "\n", "\n", "\n", "\n", "53\n", "<digit>\n", "\n", "\n", "\n", "49->53\n", "\n", "\n", "\n", "\n", "\n", "52\n", "5 (53)\n", "\n", "\n", "\n", "51->52\n", "\n", "\n", "\n", "\n", "\n", "54\n", "1 (49)\n", "\n", "\n", "\n", "53->54\n", "\n", "\n", "\n", "\n", "\n", "57\n", "<param>\n", "\n", "\n", "\n", "56->57\n", "\n", "\n", "\n", "\n", "\n", "58\n", "<id>\n", "\n", "\n", "\n", "57->58\n", "\n", "\n", "\n", "\n", "\n", "60\n", "= (61)\n", "\n", "\n", "\n", "57->60\n", "\n", "\n", "\n", "\n", "\n", "61\n", "<nat>\n", "\n", "\n", "\n", "57->61\n", "\n", "\n", "\n", "\n", "\n", "59\n", "abc\n", "\n", "\n", "\n", "58->59\n", "\n", "\n", "\n", "\n", "\n", "62\n", "<digit>\n", "\n", "\n", "\n", "61->62\n", "\n", "\n", "\n", "\n", "\n", "63\n", "2 (50)\n", "\n", "\n", "\n", "62->63\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 122, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_tree(f.derivation_tree)" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:17.239350Z", "iopub.status.busy": "2024-01-18T17:16:17.239220Z", "iopub.status.idle": "2024-01-18T17:16:17.242406Z", "shell.execute_reply": "2024-01-18T17:16:17.242078Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'4%ca5%c3'" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(CGI_GRAMMAR, min_nonterminals=3, max_nonterminals=5)\n", "f.fuzz()" ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:17.244018Z", "iopub.status.busy": "2024-01-18T17:16:17.243923Z", "iopub.status.idle": "2024-01-18T17:16:17.605111Z", "shell.execute_reply": "2024-01-18T17:16:17.604742Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<string>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "<letter>\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "5\n", "<string>\n", "\n", "\n", "\n", "1->5\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<other>\n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", "4 (52)\n", "\n", "\n", "\n", "3->4\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<letter>\n", "\n", "\n", "\n", "5->6\n", "\n", "\n", "\n", "\n", "\n", "13\n", "<string>\n", "\n", "\n", "\n", "5->13\n", "\n", "\n", "\n", "\n", "\n", "7\n", "<percent>\n", "\n", "\n", "\n", "6->7\n", "\n", "\n", "\n", "\n", "\n", "8\n", "% (37)\n", "\n", "\n", "\n", "7->8\n", "\n", "\n", "\n", "\n", "\n", "9\n", "<hexdigit>\n", "\n", "\n", "\n", "7->9\n", "\n", "\n", "\n", "\n", "\n", "11\n", "<hexdigit>\n", "\n", "\n", "\n", "7->11\n", "\n", "\n", "\n", "\n", "\n", "10\n", "c (99)\n", "\n", "\n", "\n", "9->10\n", "\n", "\n", "\n", "\n", "\n", "12\n", "a (97)\n", "\n", "\n", "\n", "11->12\n", "\n", "\n", "\n", "\n", "\n", "14\n", "<letter>\n", "\n", "\n", "\n", "13->14\n", "\n", "\n", "\n", "\n", "\n", "17\n", "<string>\n", "\n", "\n", "\n", "13->17\n", "\n", "\n", "\n", "\n", "\n", "15\n", "<other>\n", "\n", "\n", "\n", "14->15\n", "\n", "\n", "\n", "\n", "\n", "16\n", "5 (53)\n", "\n", "\n", "\n", "15->16\n", "\n", "\n", "\n", "\n", "\n", "18\n", "<letter>\n", "\n", "\n", "\n", "17->18\n", "\n", "\n", "\n", "\n", "\n", "19\n", "<percent>\n", "\n", "\n", "\n", "18->19\n", "\n", "\n", "\n", "\n", "\n", "20\n", "% (37)\n", "\n", "\n", "\n", "19->20\n", "\n", "\n", "\n", "\n", "\n", "21\n", "<hexdigit>\n", "\n", "\n", "\n", "19->21\n", "\n", "\n", "\n", "\n", "\n", "23\n", "<hexdigit>\n", "\n", "\n", "\n", "19->23\n", "\n", "\n", "\n", "\n", "\n", "22\n", "c (99)\n", "\n", "\n", "\n", "21->22\n", "\n", "\n", "\n", "\n", "\n", "24\n", "3 (51)\n", "\n", "\n", "\n", "23->24\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 124, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_tree(f.derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "How do we stack up against `simple_grammar_fuzzer()`?" ] }, { "cell_type": "code", "execution_count": 125, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:17.606830Z", "iopub.status.busy": "2024-01-18T17:16:17.606713Z", "iopub.status.idle": "2024-01-18T17:16:18.563689Z", "shell.execute_reply": "2024-01-18T17:16:18.563399Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 \n" ] } ], "source": [ "trials = 50\n", "xs = []\n", "ys = []\n", "f = GrammarFuzzer(EXPR_GRAMMAR, max_nonterminals=20)\n", "for i in range(trials):\n", " with Timer() as t:\n", " s = f.fuzz()\n", " xs.append(len(s))\n", " ys.append(t.elapsed_time())\n", " print(i, end=\" \")\n", "print()" ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:18.565330Z", "iopub.status.busy": "2024-01-18T17:16:18.565216Z", "iopub.status.idle": "2024-01-18T17:16:18.567007Z", "shell.execute_reply": "2024-01-18T17:16:18.566731Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Average time: 0.01903207001829287\n" ] } ], "source": [ "average_time = sum(ys) / trials\n", "print(\"Average time:\", average_time)" ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:16:18.568528Z", "iopub.status.busy": "2024-01-18T17:16:18.568421Z", "iopub.status.idle": "2024-01-18T17:16:18.686883Z", "shell.execute_reply": "2024-01-18T17:16:18.685541Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiwAAAGzCAYAAAAMr0ziAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABFcElEQVR4nO3deXxU1eH///ckkIUlwxKyAIFEoGIIEAUSoyhWo8FSELUaqJZAKW6AYKwV/CjRT/02WMWmAoXi51OsoEKplQqlsRAWP2oQJVAb44IWgUImYZEEAwHMnN8f/GZkyGSZkGRuktfz8ZiHzp0zZ86dO5N5c89ybcYYIwAAAAsL8HcDAAAA6kJgAQAAlkdgAQAAlkdgAQAAlkdgAQAAlkdgAQAAlkdgAQAAlkdgAQAAlkdgAQAAlkdgQaOZPHmyYmNj/d0My3vyySdls9ma5bW2bt0qm82mrVu31ln2gw8+0FVXXaWOHTvKZrNp9+7dTd6+tsyXYwOAwII62Gy2et34o9uynT17VnfccYeOHTum3/zmN1qxYoX69u3r72a1Cr/73e/00ksv+bsZrcavfvUrrV27tlle67333tOTTz6p48ePN8vroXY2riWE2qxcudLj/ssvv6yNGzdqxYoVHttvvPFGdevWTU6nU8HBwc3ZxBbn22+/1bfffquQkJAmf62tW7fq+9//vrZs2aLrrruuxnKffvqpLrvsMr344ov62c9+1uTtaksSEhIUHh5eLdQ7nU6dOXNGQUFBCgjg34711alTJ/3oRz9qlhD43HPP6ZFHHtHevXs5e2wB7fzdAFjb3Xff7XF/+/bt2rhxY7XtLUVFRYU6duzo1za0a9dO7drV/tVz/Zg1R6iRpNLSUklSly5dGq1OK7zXjc0Yo8rKSoWGhl50XQEBAc12fIHWgFiPRnPhGJavvvpKNptNzz33nBYvXqxLLrlEHTp00E033aQDBw7IGKNf/vKX6t27t0JDQ3XLLbfo2LFj1er9+9//rmuuuUYdO3ZU586dNWbMGH388cd1tuell16SzWbTtm3b9MADDygiIkK9e/f2ud61a9cqISFBISEhSkhI0BtvvFFtX2saj+B6D87/16C3MSw2m00zZszQK6+8okGDBik4OFi5ubmSpIMHD+qnP/2pIiMjFRwcrEGDBukPf/hDtXb+5z//0fjx49WxY0dFRETooYce0unTp+t8nyZPnqxRo0ZJku644w7ZbDaPszGbN292v09dunTRLbfcok8++cSjDtc+FRUV6cc//rG6du2qkSNH1vq6H330kUaNGqXQ0FD17t1bTz/9tJYvXy6bzaavvvrKo2x9jtXkyZPVqVMnHTx4UOPHj1enTp3Uo0cP/fznP1dVVZVHWafTqZycHA0aNEghISGKjIzUvffeq6+//tqjXGxsrH74wx/qrbfe0vDhwxUaGqrf//73kqTly5fr+uuvV0REhIKDgxUfH68lS5ZUe/7HH3+sbdu2ubtPXe+tt8/Mddddp4SEBBUVFen73/++OnTooF69eunXv/51tfdv3759GjdunMfxfuutt+rVRbtv3z498MADuvTSSxUaGqru3bvrjjvuqPa+u75D7777rjIzM9WjRw917NhRt956qw4fPlzra7jU5/NT0/i3C78rNptNFRUV+uMf/+h+PydPnuxR9tNPP9Wdd96psLAwde/eXbNmzVJlZaW7Dm/fyfPrf/LJJ931PfLII5KkuLg49+td+B6h+XCGBU3ulVde0ZkzZzRz5kwdO3ZMv/71r3XnnXfq+uuv19atW/Xoo4/qiy++0MKFC/Xzn//c48d4xYoVysjIUFpamp555hmdPHlSS5Ys0ciRI7Vr1656naZ94IEH1KNHD82bN08VFRU+1fuPf/xDt99+u+Lj45Wdna2jR49qypQpHsGnsWzevFl/+tOfNGPGDIWHhys2NlYlJSW68sor3YGmR48e+vvf/66pU6eqvLxcs2fPliSdOnVKN9xwg/bv368HH3xQPXv21IoVK7R58+Y6X/fee+9Vr1699Ktf/UoPPvigRowYocjISEnSpk2bdPPNN+uSSy7Rk08+qVOnTmnhwoW6+uqrVVBQUO39v+OOOzRgwAD96le/Um29zQcPHtT3v/992Ww2zZ07Vx07dtT//M//eO1O9OUzUFVVpbS0NCUnJ+u5557Tpk2btGDBAvXr10/333+/xz6/9NJLmjJlih588EHt3btXixYt0q5du/Tuu++qffv27rKfffaZJk6cqHvvvVfTpk3TpZdeKklasmSJBg0apHHjxqldu3Zat26dHnjgATmdTk2fPl2SlJOTo5kzZ6pTp076r//6L0lyv7c1+frrrzV69GjddtttuvPOO/XnP/9Zjz76qAYPHqybb75Z0rmzV9dff72Ki4s1a9YsRUVF6dVXX9WWLVtqrdvlgw8+0HvvvacJEyaod+/e+uqrr7RkyRJdd911KioqUocOHTzKz5w5U127dlVWVpa++uor5eTkaMaMGVq9enWtr+Pr56cuK1as0M9+9jMlJSXpnnvukST169fPo8ydd96p2NhYZWdna/v27XrhhRf09ddf6+WXX/bptW677TZ9/vnneu211/Sb3/xG4eHhkqQePXr4VA8akQF8MH36dFPTxyYjI8P07dvXfX/v3r1GkunRo4c5fvy4e/vcuXONJDN06FBz9uxZ9/aJEyeaoKAgU1lZaYwx5sSJE6ZLly5m2rRpHq/jcDiM3W6vtv1Cy5cvN5LMyJEjzbfffuve7ku9iYmJJjo62qP9//jHP4wkj33dsmWLkWS2bNniUafrPVi+fLl7W1ZWVrX3UJIJCAgwH3/8scf2qVOnmujoaHPkyBGP7RMmTDB2u92cPHnSGGNMTk6OkWT+9Kc/uctUVFSY/v37e23XhVztX7Nmjcf2xMREExERYY4ePere9s9//tMEBASYSZMmVduniRMn1vo6LjNnzjQ2m83s2rXLve3o0aOmW7duRpLZu3evMca3Y5WRkWEkmf/+7//2KHv55ZebYcOGue//3//9n5FkXnnlFY9yubm51bb37dvXSDK5ubnV9sH13p8vLS3NXHLJJR7bBg0aZEaNGlWtrLfPzKhRo4wk8/LLL7u3nT592kRFRZnbb7/dvW3BggVGklm7dq1726lTp8zAgQPrdby9tT0/P7/aa7u+Q6mpqcbpdLq3P/TQQyYwMNDje+FNfT8/F/7tcPH2XenYsaPJyMiosey4ceM8tj/wwANGkvnnP/9pjPH+nXSRZLKystz3n332WY/PI/yLLiE0uTvuuEN2u919Pzk5WdK58THnj+VITk7WmTNndPDgQUnSxo0bdfz4cU2cOFFHjhxx3wIDA5WcnFzvf01OmzZNgYGB7vv1rbe4uFi7d+9WRkaGR/tvvPFGxcfHN/wNqcGoUaM86jXG6PXXX9fYsWNljPFoa1pamsrKylRQUCBJ2rBhg6Kjo/WjH/3I/fwOHTq4/xXaEK79nzx5srp16+bePmTIEN14443asGFDtefcd9999ao7NzdXKSkpSkxMdG/r1q2b7rrrLo9yDfkMXNiGa665Rv/+97/d99esWSO73a4bb7zRo85hw4apU6dO1eqMi4tTWlpatdc5fxxLWVmZjhw5olGjRunf//63ysrK6vU+eNOpUyePMWJBQUFKSkry2Ifc3Fz16tVL48aNc28LCQnRtGnT6vUa57f97NmzOnr0qPr3768uXbq4P1Pnu+eeezy6Zq655hpVVVVp3759Nb5GQz4/jcF1dstl5syZktRkr4fmQ5cQmlyfPn087rt+/GNiYrxud40j2LNnjyTp+uuv91pvWFhYvV4/Li7O435963X9MR4wYEC1MpdeeqnXP+wX48J2Hj58WMePH9eyZcu0bNkyr89xDZbdt2+f+vfvX21sjKv7oiFc+++tjssuu0xvvfVWtYG1F+5DbXWnpKRU296/f3+P+75+BkJCQqqdsu/atavH2JQ9e/aorKxMERERXut0vacuNe3Tu+++q6ysLOXn5+vkyZMej5WVlXmEXF/07t272nHs2rWrPvroI/f9ffv2qV+/ftXKXfj+1eTUqVPKzs7W8uXLdfDgQY/uO29h68LvcNeuXSWp2pif8zXk89MYLvy+9uvXTwEBAYw9aQUILGhy55/dqM921x9Pp9Mp6Vy/dVRUVLVydc20cblwRkdj1Xu+mhaCu3CwZ21qaufdd9+tjIwMr88ZMmRIvetvDo0xe+Z8vh6rmj5TF9YZERGhV155xevjFwYeb/v05Zdf6oYbbtDAgQP1/PPPKyYmRkFBQdqwYYN+85vfuNvdEHV9LxrDzJkztXz5cs2ePVspKSmy2+2y2WyaMGGC17Y3dZsa4/tT37qb8rXQtAgssCzXYLqIiAilpqY2e72uhdNc/8o/32effeZx3/UvzgsXmKrtlHldevTooc6dO6uqqqrO/e/bt68KCwtljPH4g3xhO33h2n9vdXz66acKDw9v8L+O+/btqy+++KLa9gu3NcVnoF+/ftq0aZOuvvrqBgesdevW6fTp03rzzTc9zj5466JqilWN+/btq6KiomrH29t76s2f//xnZWRkaMGCBe5tlZWVjbpAmi+fn65du3p9bW/fn7rezz179nicFfviiy/kdDrdA3x9+a4214rUqB/GsMCy0tLSFBYWpl/96lc6e/ZstcfrO62yofVGR0crMTFRf/zjHz1Ok2/cuFFFRUUez+nbt68CAwP19ttve2z/3e9+16A2Suf+VXv77bfr9ddfV2FhYY3tlKQf/OAHOnTokP785z+7t508ebLGrqT6OH//z//jXlhYqH/84x/6wQ9+0OC609LSlJ+f77H8/7Fjx6qd9WiKz8Cdd96pqqoq/fKXv6z22LfffluvH23XGYcLu1KWL19erWzHjh0bfaXUtLQ0HTx4UG+++aZ7W2VlpV588cV6PT8wMLDa2ZGFCxc26lkGXz4//fr1U1lZmUe3V3Fxsd54441q9db1fi5evNjj/sKFCyXJPcMqLCxM4eHh9fquugIVK91aA2dYYFlhYWFasmSJfvKTn+iKK67QhAkT1KNHD+3fv19/+9vfdPXVV2vRokVNWm92drbGjBmjkSNH6qc//amOHTumhQsXatCgQfrmm2/cddrtdt1xxx1auHChbDab+vXrp/Xr11cbD+Gr+fPna8uWLUpOTta0adMUHx+vY8eOqaCgQJs2bXKvWzNt2jQtWrRIkyZN0s6dOxUdHa0VK1ZUm57qq2effVY333yzUlJSNHXqVPe0VLvd7l6voiF+8YtfaOXKlbrxxhs1c+ZM97TmPn366NixY+5/2TbFZ2DUqFG69957lZ2drd27d+umm25S+/bttWfPHq1Zs0a//e1vPQYve3PTTTcpKChIY8eO1b333qtvvvlGL774oiIiIlRcXOxRdtiwYVqyZImefvpp9e/fXxERETWOyamve++9V4sWLdLEiRM1a9YsRUdH65VXXnEvRFfXmYEf/vCHWrFihex2u+Lj45Wfn69Nmzape/fuF9WuC9X38zNhwgQ9+uijuvXWW/Xggw+6p65/73vfqzZWbNiwYdq0aZOef/559ezZU3Fxce6B/JK0d+9ejRs3TqNHj1Z+fr5WrlypH//4xxo6dKi7zM9+9jPNnz9fP/vZzzR8+HC9/fbb+vzzz6u1f9iwYZKk//qv/9KECRPUvn17jR07ttUtiNhi+Gt6ElqmhkxrfvbZZz3K1TSF1jWF8oMPPqhWPi0tzdjtdhMSEmL69etnJk+ebD788MNa21pTfb7W+/rrr5vLLrvMBAcHm/j4ePOXv/zF6zTMw4cPm9tvv9106NDBdO3a1dx7772msLCw3tOap0+f7rWdJSUlZvr06SYmJsa0b9/eREVFmRtuuMEsW7bMo9y+ffvMuHHjTIcOHUx4eLiZNWuWe6puQ6c1G2PMpk2bzNVXX21CQ0NNWFiYGTt2rCkqKvIo49qnw4cP1/o659u1a5e55pprTHBwsOndu7fJzs42L7zwgpFkHA5HtfbVdawyMjJMx44dq72Ot/fbGGOWLVtmhg0bZkJDQ03nzp3N4MGDzS9+8Qtz6NAhd5m+ffuaMWPGeG3/m2++aYYMGWJCQkJMbGyseeaZZ8wf/vCHatNgHQ6HGTNmjOncubOR5J7iXNO05kGDBlV7LW+ft3//+99mzJgxJjQ01PTo0cM8/PDD5vXXXzeSzPbt27222eXrr782U6ZMMeHh4aZTp04mLS3NfPrpp6Zv374eU4Zr+07W53NlTP0+P8acWy4gISHBBAUFmUsvvdSsXLnS67H79NNPzbXXXmtCQ0ONJHd7XWWLiorMj370I9O5c2fTtWtXM2PGDHPq1CmPOk6ePGmmTp1q7Ha76dy5s7nzzjtNaWlptWnNxhjzy1/+0vTq1csEBAQwxdnPuJYQ0ACTJ0/W1q1bmXnQyGbPnq3f//73+uabb+o1gBaecnJy9NBDD+k///mPevXq5e/mNKsnn3xSTz31lA4fPuxe5A2tC2NYAPjFqVOnPO4fPXpUK1as0MiRIwkr9XDh+1dZWanf//73GjBgQJsLK2gbGMMCwC9SUlJ03XXX6bLLLlNJSYn+93//V+Xl5XriiSf83bQW4bbbblOfPn2UmJiosrIyrVy5Up9++mmN07WBlo7AAsAvfvCDH+jPf/6zli1bJpvNpiuuuEL/+7//q2uvvdbfTWsR0tLS9D//8z965ZVXVFVVpfj4eK1atUrp6en+bhrQJBjDAgAALI8xLAAAwPIILAAAwPJaxRgWp9OpQ4cOqXPnziylDABAC2GM0YkTJ9SzZ08FBNR+DqVVBJZDhw5Vu/IvAABoGQ4cOKDevXvXWqZVBJbOnTtLOrfDF15uHgAAWFN5ebliYmLcv+O1aRWB5fzrjhBYAABoWeoznINBtwAAwPIILAAAwPIILAAAwPIILAAAwPIILAAAwPIILAAAwPIILAAAwPIILAAAwPJaxcJxAACgaVQ5jXbsPabSE5WK6ByipLhuCgxo/uv2EVgAAIBXuYXFempdkYrLKt3bou0hyhobr9EJ0c3aFrqEAABANbmFxbp/ZYFHWJEkR1ml7l9ZoNzC4mZtD4EFAAB4qHIaPbWuSMbLY65tT60rUpXTW4mmQWABAAAeduw9Vu3MyvmMpOKySu3Ye6zZ2kRgAQAAHkpP1BxWGlKuMRBYAACAh4jOIY1arjEQWAAAgIekuG6KtoeopsnLNp2bLZQU163Z2kRgAQAAHgIDbMoaGy9J1UKL637W2PhmXY+FwAIAAKoZnRCtJXdfoSi7Z7dPlD1ES+6+otnXYWHhOAAA4NXohGjdGB/FSrcAAMDaAgNsSunX3d/NoEsIAABYH4EFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYXoMCy+LFixUbG6uQkBAlJydrx44dNZb9+OOPdfvttys2NlY2m005OTm11j1//nzZbDbNnj27IU0DAACtkM+BZfXq1crMzFRWVpYKCgo0dOhQpaWlqbS01Gv5kydP6pJLLtH8+fMVFRVVa90ffPCBfv/732vIkCG+NgsAALRiPgeW559/XtOmTdOUKVMUHx+vpUuXqkOHDvrDH/7gtfyIESP07LPPasKECQoODq6x3m+++UZ33XWXXnzxRXXt2tXXZgEAgFbMp8By5swZ7dy5U6mpqd9VEBCg1NRU5efnX1RDpk+frjFjxnjUXZPTp0+rvLzc4wYAAFovnwLLkSNHVFVVpcjISI/tkZGRcjgcDW7EqlWrVFBQoOzs7HqVz87Olt1ud99iYmIa/NoAAMD6/D5L6MCBA5o1a5ZeeeUVhYSE1Os5c+fOVVlZmft24MCBJm4lAADwp3a+FA4PD1dgYKBKSko8tpeUlNQ5oLYmO3fuVGlpqa644gr3tqqqKr399ttatGiRTp8+rcDAQI/nBAcH1zoeBgAAtC4+nWEJCgrSsGHDlJeX597mdDqVl5enlJSUBjXghhtu0L/+9S/t3r3bfRs+fLjuuusu7d69u1pYAQAAbY9PZ1gkKTMzUxkZGRo+fLiSkpKUk5OjiooKTZkyRZI0adIk9erVyz0e5cyZMyoqKnL//8GDB7V792516tRJ/fv3V+fOnZWQkODxGh07dlT37t2rbQcAAG2Tz4ElPT1dhw8f1rx58+RwOJSYmKjc3Fz3QNz9+/crIOC7EzeHDh3S5Zdf7r7/3HPP6bnnntOoUaO0devWi98DAADQ6tmMMcbfjbhY5eXlstvtKisrU1hYmL+bAwAA6sGX32+/zxICAACoC4EFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYns/XEgIAAK1HldNox95jKj1RqYjOIUqK66bAAJu/m1UNgQUAgDYqt7BYT60rUnFZpXtbtD1EWWPjNToh2o8tq44uIQAA2qDcwmLdv7LAI6xIkqOsUvevLFBuYbGfWuYdgQUAgDamymn01LoiGS+PubY9ta5IVU5vJfyDwAIAQBuzY++xamdWzmckFZdVasfeY83XqDoQWAAAaGNKT9QcVhpSrjkQWAAAaGMiOoc0arnmQGABAKCNSYrrpmh7iGqavGzTudlCSXHdmrNZtSKwAADQxgQG2JQ1Nl6SqoUW1/2ssfGWWo+FwAIAQBs0OiFaS+6+QlF2z26fKHuIltx9heXWYWHhOAAA2qjRCdG6MT6KlW4BAIC1BQbYlNKvu7+bUSe6hAAAgOURWAAAgOURWAAAgOURWAAAgOURWAAAgOURWAAAgOURWAAAgOURWAAAgOURWAAAgOURWAAAgOURWAAAgOURWAAAgOU1KLAsXrxYsbGxCgkJUXJysnbs2FFj2Y8//li33367YmNjZbPZlJOTU61Mdna2RowYoc6dOysiIkLjx4/XZ5991pCmAQCAVsjnwLJ69WplZmYqKytLBQUFGjp0qNLS0lRaWuq1/MmTJ3XJJZdo/vz5ioqK8lpm27Ztmj59urZv366NGzfq7Nmzuummm1RRUeFr8wAAQCtkM8YYX56QnJysESNGaNGiRZIkp9OpmJgYzZw5U3PmzKn1ubGxsZo9e7Zmz55da7nDhw8rIiJC27Zt07XXXltnm8rLy2W321VWVqawsLB67wsAAPAfX36/fTrDcubMGe3cuVOpqanfVRAQoNTUVOXn5zestV6UlZVJkrp16+b18dOnT6u8vNzjBgAAWi+fAsuRI0dUVVWlyMhIj+2RkZFyOByN0iCn06nZs2fr6quvVkJCgtcy2dnZstvt7ltMTEyjvDYAALAmy80Smj59ugoLC7Vq1aoay8ydO1dlZWXu24EDB5qxhQAAoLm186VweHi4AgMDVVJS4rG9pKSkxgG1vpgxY4bWr1+vt99+W717966xXHBwsIKDgy/69QAAQMvg0xmWoKAgDRs2THl5ee5tTqdTeXl5SklJaXAjjDGaMWOG3njjDW3evFlxcXENrgsAALQ+Pp1hkaTMzExlZGRo+PDhSkpKUk5OjioqKjRlyhRJ0qRJk9SrVy9lZ2dLOjdQt6ioyP3/Bw8e1O7du9WpUyf1799f0rluoFdffVV//etf1blzZ/d4GLvdrtDQ0EbZUQAA0HL5PK1ZkhYtWqRnn31WDodDiYmJeuGFF5ScnCxJuu666xQbG6uXXnpJkvTVV195PWMyatQobd269VwjbDavr7N8+XJNnjy5zvYwrRkAgJbHl9/vBgUWqyGwAADQ8jTZOiwAAAD+QGABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACWR2ABAACW16DAsnjxYsXGxiokJETJycnasWNHjWU//vhj3X777YqNjZXNZlNOTs5F1wkAANoWnwPL6tWrlZmZqaysLBUUFGjo0KFKS0tTaWmp1/InT57UJZdcovnz5ysqKqpR6gQAAG2LzRhjfHlCcnKyRowYoUWLFkmSnE6nYmJiNHPmTM2ZM6fW58bGxmr27NmaPXt2o9UpSeXl5bLb7SorK1NYWJgvuwMAAPzEl99vn86wnDlzRjt37lRqaup3FQQEKDU1Vfn5+Q1qbEPqPH36tMrLyz1uAACg9fIpsBw5ckRVVVWKjIz02B4ZGSmHw9GgBjSkzuzsbNntdvctJiamQa8NAABahhY5S2ju3LkqKytz3w4cOODvJgEAgCbUzpfC4eHhCgwMVElJicf2kpKSGgfUNkWdwcHBCg4ObtDrAQCAlsenMyxBQUEaNmyY8vLy3NucTqfy8vKUkpLSoAY0RZ0AAKB18ekMiyRlZmYqIyNDw4cPV1JSknJyclRRUaEpU6ZIkiZNmqRevXopOztb0rlBtUVFRe7/P3jwoHbv3q1OnTqpf//+9aoTAAC0bT4HlvT0dB0+fFjz5s2Tw+FQYmKicnNz3YNm9+/fr4CA707cHDp0SJdffrn7/nPPPafnnntOo0aN0tatW+tVJwAAaNt8XofFiliHBQCAlqfJ1mEBAADwBwILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwPAILAACwvAYFlsWLFys2NlYhISFKTk7Wjh07ai2/Zs0aDRw4UCEhIRo8eLA2bNjg8fg333yjGTNmqHfv3goNDVV8fLyWLl3akKYBAIBWyOfAsnr1amVmZiorK0sFBQUaOnSo0tLSVFpa6rX8e++9p4kTJ2rq1KnatWuXxo8fr/Hjx6uwsNBdJjMzU7m5uVq5cqU++eQTzZ49WzNmzNCbb77Z8D0DAACths0YY3x5QnJyskaMGKFFixZJkpxOp2JiYjRz5kzNmTOnWvn09HRVVFRo/fr17m1XXnmlEhMT3WdREhISlJ6erieeeMJdZtiwYbr55pv19NNP19mm8vJy2e12lZWVKSwszJfdAQAAfuLL77dPZ1jOnDmjnTt3KjU19bsKAgKUmpqq/Px8r8/Jz8/3KC9JaWlpHuWvuuoqvfnmmzp48KCMMdqyZYs+//xz3XTTTV7rPH36tMrLyz1uAACg9fIpsBw5ckRVVVWKjIz02B4ZGSmHw+H1OQ6Ho87yCxcuVHx8vHr37q2goCCNHj1aixcv1rXXXuu1zuzsbNntdvctJibGl90AAAAtjCVmCS1cuFDbt2/Xm2++qZ07d2rBggWaPn26Nm3a5LX83LlzVVZW5r4dOHCgmVsMAACaUztfCoeHhyswMFAlJSUe20tKShQVFeX1OVFRUbWWP3XqlB577DG98cYbGjNmjCRpyJAh2r17t5577rlq3UmSFBwcrODgYF+aDgAAWjCfzrAEBQVp2LBhysvLc29zOp3Ky8tTSkqK1+ekpKR4lJekjRs3usufPXtWZ8+eVUCAZ1MCAwPldDp9aR4AAGilfDrDIp2bgpyRkaHhw4crKSlJOTk5qqio0JQpUyRJkyZNUq9evZSdnS1JmjVrlkaNGqUFCxZozJgxWrVqlT788EMtW7ZMkhQWFqZRo0bpkUceUWhoqPr27att27bp5Zdf1vPPP9+IuwoAAFoqnwNLenq6Dh8+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Qo888oi+/fbb5tyVVoXA4sXq1auVmZmprKwsFRQUaOjQoUpLS1Npaam/m9aqDRo0SMXFxe7bO++8437soYce0rp167RmzRpt27ZNhw4d0m233ebH1rYeFRUVGjp0qBYvXuz18V//+td64YUXtHTpUr3//vvq2LGj0tLSVFlZ6S5z11136eOPP9bGjRu1fv16vf3227rnnnuaaxdajbqOhSSNHj3a43vy2muveTzOsbh427Zt0/Tp07V9+3Zt3LhRZ8+e1U033aSKigp3mbr+JlVVVWnMmDE6c+aM3nvvPf3xj3/USy+9pHnz5vljl1oHg2qSkpLM9OnT3ferqqpMz549TXZ2th9b1bplZWWZoUOHen3s+PHjpn379mbNmjXubZ988omRZPLz85uphW2DJPPGG2+47zudThMVFWWeffZZ97bjx4+b4OBg89prrxljjCkqKjKSzAcffOAu8/e//93YbDZz8ODBZmt7a3PhsTDGmIyMDHPLLbfU+ByORdMoLS01ksy2bduMMfX7m7RhwwYTEBBgHA6Hu8ySJUtMWFiYOX36dPPuQCvBGZYLnDlzRjt37lRqaqp7W0BAgFJTU5Wfn+/HlrV+e/bsUc+ePXXJJZforrvu0v79+yVJO3fu1NmzZz2OycCBA9WnTx+OSRPbu3evHA6Hx3tvt9uVnJzsfu/z8/PVpUsXDR8+3F0mNTVVAQEBev/995u9za3d1q1bFRERoUsvvVT333+/jh496n6MY9E0ysrKJEndunWTVL+/Sfn5+Ro8eLAiIyPdZdLS0lReXq6PP/64GVvfehBYLnDkyBFVVVV5fMgkKTIyUg6Hw0+tav2Sk5P10ksvKTc3V0uWLNHevXt1zTXX6MSJE3I4HAoKClKXLl08nsMxaXqu97e274PD4VBERITH4+3atVO3bt04Po1s9OjRevnll5WXl6dnnnlG27Zt080336yqqipJHIum4HQ6NXv2bF199dVKSEiQpHr9TXI4HF6/N67H4Lt2/m4AIEk333yz+/+HDBmi5ORk9e3bV3/6058UGhrqx5YB1jFhwgT3/w8ePFhDhgxRv379tHXrVt1www1+bFnrNX36dBUWFnqMqYN/cIblAuHh4QoMDKw22rukpERRUVF+alXb06VLF33ve9/TF198oaioKJ05c0bHjx/3KMMxaXqu97e270NUVFS1Aenffvutjh07xvFpYpdcconCw8P1xRdfSOJYNLYZM2Zo/fr12rJli3r37u3eXp+/SVFRUV6/N67H4DsCywWCgoI0bNgw5eXlubc5nU7l5eUpJSXFjy1rW7755ht9+eWXio6O1rBhw9S+fXuPY/LZZ59p//79HJMmFhcXp6ioKI/3vry8XO+//777vU9JSdHx48e1c+dOd5nNmzfL6XQqOTm52dvclvznP//R0aNHFR0dLYlj0ViMMZoxY4beeOMNbd68WXFxcR6P1+dvUkpKiv71r395BMiNGzcqLCxM8fHxzbMjrY2/R/1a0apVq0xwcLB56aWXTFFRkbnnnntMly5dPEZ7o3E9/PDDZuvWrWbv3r3m3XffNampqSY8PNyUlpYaY4y57777TJ8+fczmzZvNhx9+aFJSUkxKSoqfW906nDhxwuzatcvs2rXLSDLPP/+82bVrl9m3b58xxpj58+ebLl26mL/+9a/mo48+MrfccouJi4szp06dctcxevRoc/nll5v333/fvPPOO2bAgAFm4sSJ/tqlFqu2Y3HixAnz85//3OTn55u9e/eaTZs2mSuuuMIMGDDAVFZWuuvgWFy8+++/39jtdrN161ZTXFzsvp08edJdpq6/Sd9++61JSEgwN910k9m9e7fJzc01PXr0MHPnzvXHLrUKBJYaLFy40PTp08cEBQWZpKQks337dn83qVVLT0830dHRJigoyPTq1cukp6ebL774wv34qVOnzAMPPGC6du1qOnToYG699VZTXFzsxxa3Hlu2bDGSqt0yMjKMMeemNj/xxBMmMjLSBAcHmxtuuMF89tlnHnUcPXrUTJw40XTq1MmEhYWZKVOmmBMnTvhhb1q22o7FyZMnzU033WR69Ohh2rdvb/r27WumTZtW7R9SHIuL5+0YSDLLly93l6nP36SvvvrK3HzzzSY0NNSEh4ebhx9+2Jw9e7aZ96b1sBljTHOf1QEAAPAFY1gAAIDlEVgAAIDlEVgAAIDlEVgAAIDlEVgAAIDlEVgAAIDlEVgAAIDlEVgAAIDlEVgAAIDlEVgAAIDlEVgAAIDl/X/gFt3/MAU7hAAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "plt.scatter(xs, ys)\n", "plt.title('Time required for generating an output');" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "Our test generation is much faster, but also our inputs are much smaller. We see that with derivation trees, we can get much better control over grammar production." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Finally, how does `GrammarFuzzer` work with `expr_grammar`, where `simple_grammar_fuzzer()` failed? It works without any issue:" ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:18.695173Z", "iopub.status.busy": "2024-01-18T17:16:18.694795Z", "iopub.status.idle": "2024-01-18T17:16:18.717877Z", "shell.execute_reply": "2024-01-18T17:16:18.714265Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'5.5 * (6 / 4 + (2 - 5) * 6 / 1 * 0 + 1 + 8)'" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = GrammarFuzzer(expr_grammar, max_nonterminals=10)\n", "f.fuzz()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "With `GrammarFuzzer`, we now have a solid foundation on which to build further fuzzers and illustrate more exciting concepts from the world of generating software tests. Many of these do not even require writing a grammar – instead, they _infer_ a grammar from the domain at hand, and thus allow using grammar-based fuzzing even without writing a grammar. Stay tuned!" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "## Synopsis" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Efficient Grammar Fuzzing" ] }, { "cell_type": "markdown", "metadata": { "jp-MarkdownHeadingCollapsed": true, "slideshow": { "slide_type": "fragment" }, "tags": [] }, "source": [ "This chapter introduces `GrammarFuzzer`, an efficient grammar fuzzer that takes a grammar to produce syntactically valid input strings. Here's a typical usage:" ] }, { "cell_type": "code", "execution_count": 129, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:18.727987Z", "iopub.status.busy": "2024-01-18T17:16:18.727626Z", "iopub.status.idle": "2024-01-18T17:16:18.751786Z", "shell.execute_reply": "2024-01-18T17:16:18.750662Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Grammars import US_PHONE_GRAMMAR" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:18.764663Z", "iopub.status.busy": "2024-01-18T17:16:18.764404Z", "iopub.status.idle": "2024-01-18T17:16:18.777140Z", "shell.execute_reply": "2024-01-18T17:16:18.773108Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'(694)767-9530'" ] }, "execution_count": 130, "metadata": {}, "output_type": "execute_result" } ], "source": [ "phone_fuzzer = GrammarFuzzer(US_PHONE_GRAMMAR)\n", "phone_fuzzer.fuzz()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The `GrammarFuzzer` constructor takes a number of keyword arguments to control its behavior. `start_symbol`, for instance, allows setting the symbol that expansion starts with (instead of ``):" ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:18.784512Z", "iopub.status.busy": "2024-01-18T17:16:18.784149Z", "iopub.status.idle": "2024-01-18T17:16:18.797247Z", "shell.execute_reply": "2024-01-18T17:16:18.796421Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'403'" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "area_fuzzer = GrammarFuzzer(US_PHONE_GRAMMAR, start_symbol='')\n", "area_fuzzer.fuzz()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Here's how to parameterize the `GrammarFuzzer` constructor:" ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:18.816375Z", "iopub.status.busy": "2024-01-18T17:16:18.803212Z", "iopub.status.idle": "2024-01-18T17:16:18.838356Z", "shell.execute_reply": "2024-01-18T17:16:18.833871Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "# ignore\n", "import inspect" ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:18.840524Z", "iopub.status.busy": "2024-01-18T17:16:18.840416Z", "iopub.status.idle": "2024-01-18T17:16:18.843194Z", "shell.execute_reply": "2024-01-18T17:16:18.842553Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Produce strings from `grammar`, starting with `start_symbol`.\n", "If `min_nonterminals` or `max_nonterminals` is given, use them as limits \n", "for the number of nonterminals produced. \n", "If `disp` is set, display the intermediate derivation trees.\n", "If `log` is set, show intermediate steps as text on standard output.\n" ] } ], "source": [ "# ignore\n", "print(inspect.getdoc(GrammarFuzzer.__init__))" ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:18.846114Z", "iopub.status.busy": "2024-01-18T17:16:18.845177Z", "iopub.status.idle": "2024-01-18T17:16:18.848585Z", "shell.execute_reply": "2024-01-18T17:16:18.848053Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# ignore\n", "from ClassDiagram import display_class_hierarchy" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:18.850650Z", "iopub.status.busy": "2024-01-18T17:16:18.850495Z", "iopub.status.idle": "2024-01-18T17:16:19.379588Z", "shell.execute_reply": "2024-01-18T17:16:19.379165Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "GrammarFuzzer\n", "\n", "\n", "GrammarFuzzer\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "fuzz()\n", "\n", "\n", "\n", "fuzz_tree()\n", "\n", "\n", "\n", "any_possible_expansions()\n", "\n", "\n", "\n", "check_grammar()\n", "\n", "\n", "\n", "choose_node_expansion()\n", "\n", "\n", "\n", "choose_tree_expansion()\n", "\n", "\n", "\n", "expand_node()\n", "\n", "\n", "\n", "expand_node_by_cost()\n", "\n", "\n", "\n", "expand_node_max_cost()\n", "\n", "\n", "\n", "expand_node_min_cost()\n", "\n", "\n", "\n", "expand_node_randomly()\n", "\n", "\n", "\n", "expand_tree()\n", "\n", "\n", "\n", "expand_tree_once()\n", "\n", "\n", "\n", "expand_tree_with_strategy()\n", "\n", "\n", "\n", "expansion_cost()\n", "\n", "\n", "\n", "expansion_to_children()\n", "\n", "\n", "\n", "init_tree()\n", "\n", "\n", "\n", "log_tree()\n", "\n", "\n", "\n", "possible_expansions()\n", "\n", "\n", "\n", "process_chosen_children()\n", "\n", "\n", "\n", "supported_opts()\n", "\n", "\n", "\n", "symbol_cost()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Fuzzer\n", "\n", "\n", "Fuzzer\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "fuzz()\n", "\n", "\n", "\n", "run()\n", "\n", "\n", "\n", "runs()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "GrammarFuzzer->Fuzzer\n", "\n", "\n", "\n", "\n", "\n", "Legend\n", "Legend\n", "• \n", "public_method()\n", "• \n", "private_method()\n", "• \n", "overloaded_method()\n", "Hover over names to see doc\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ignore\n", "display_class_hierarchy([GrammarFuzzer],\n", " public_methods=[\n", " Fuzzer.__init__,\n", " Fuzzer.fuzz,\n", " Fuzzer.run,\n", " Fuzzer.runs,\n", " GrammarFuzzer.__init__,\n", " GrammarFuzzer.fuzz,\n", " GrammarFuzzer.fuzz_tree,\n", " ],\n", " types={\n", " 'DerivationTree': DerivationTree,\n", " 'Expansion': Expansion,\n", " 'Grammar': Grammar\n", " },\n", " project='fuzzingbook')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Derivation Trees" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Internally, `GrammarFuzzer` makes use of [derivation trees](#Derivation-Trees), which it expands step by step. After producing a string, the tree produced can be accessed in the `derivation_tree` attribute." ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.381467Z", "iopub.status.busy": "2024-01-18T17:16:19.381321Z", "iopub.status.idle": "2024-01-18T17:16:19.766058Z", "shell.execute_reply": "2024-01-18T17:16:19.765664Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "<start>\n", "\n", "\n", "\n", "1\n", "<phone-number>\n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "( (40)\n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "3\n", "<area>\n", "\n", "\n", "\n", "1->3\n", "\n", "\n", "\n", "\n", "\n", "10\n", ") (41)\n", "\n", "\n", "\n", "1->10\n", "\n", "\n", "\n", "\n", "\n", "11\n", "<exchange>\n", "\n", "\n", "\n", "1->11\n", "\n", "\n", "\n", "\n", "\n", "18\n", "- (45)\n", "\n", "\n", "\n", "1->18\n", "\n", "\n", "\n", "\n", "\n", "19\n", "<line>\n", "\n", "\n", "\n", "1->19\n", "\n", "\n", "\n", "\n", "\n", "4\n", "<lead-digit>\n", "\n", "\n", "\n", "3->4\n", "\n", "\n", "\n", "\n", "\n", "6\n", "<digit>\n", "\n", "\n", "\n", "3->6\n", "\n", "\n", "\n", "\n", "\n", "8\n", "<digit>\n", "\n", "\n", "\n", "3->8\n", "\n", "\n", "\n", "\n", "\n", "5\n", "6 (54)\n", "\n", "\n", "\n", "4->5\n", "\n", "\n", "\n", "\n", "\n", "7\n", "9 (57)\n", "\n", "\n", "\n", "6->7\n", "\n", "\n", "\n", "\n", "\n", "9\n", "4 (52)\n", "\n", "\n", "\n", "8->9\n", "\n", "\n", "\n", "\n", "\n", "12\n", "<lead-digit>\n", "\n", "\n", "\n", "11->12\n", "\n", "\n", "\n", "\n", "\n", "14\n", "<digit>\n", "\n", "\n", "\n", "11->14\n", "\n", "\n", "\n", "\n", "\n", "16\n", "<digit>\n", "\n", "\n", "\n", "11->16\n", "\n", "\n", "\n", "\n", "\n", "13\n", "7 (55)\n", "\n", "\n", "\n", "12->13\n", "\n", "\n", "\n", "\n", "\n", "15\n", "6 (54)\n", "\n", "\n", "\n", "14->15\n", "\n", "\n", "\n", "\n", "\n", "17\n", "7 (55)\n", "\n", "\n", "\n", "16->17\n", "\n", "\n", "\n", "\n", "\n", "20\n", "<digit>\n", "\n", "\n", "\n", "19->20\n", "\n", "\n", "\n", "\n", "\n", "22\n", "<digit>\n", "\n", "\n", "\n", "19->22\n", "\n", "\n", "\n", "\n", "\n", "24\n", "<digit>\n", "\n", "\n", "\n", "19->24\n", "\n", "\n", "\n", "\n", "\n", "26\n", "<digit>\n", "\n", "\n", "\n", "19->26\n", "\n", "\n", "\n", "\n", "\n", "21\n", "9 (57)\n", "\n", "\n", "\n", "20->21\n", "\n", "\n", "\n", "\n", "\n", "23\n", "5 (53)\n", "\n", "\n", "\n", "22->23\n", "\n", "\n", "\n", "\n", "\n", "25\n", "3 (51)\n", "\n", "\n", "\n", "24->25\n", "\n", "\n", "\n", "\n", "\n", "27\n", "0 (48)\n", "\n", "\n", "\n", "26->27\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 136, "metadata": {}, "output_type": "execute_result" } ], "source": [ "display_tree(phone_fuzzer.derivation_tree)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In the internal representation of a derivation tree, a _node_ is a pair (`symbol`, `children`). For nonterminals, `symbol` is the symbol that is being expanded, and `children` is a list of further nodes. For terminals, `symbol` is the terminal string, and `children` is empty." ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.768024Z", "iopub.status.busy": "2024-01-18T17:16:19.767898Z", "iopub.status.idle": "2024-01-18T17:16:19.771001Z", "shell.execute_reply": "2024-01-18T17:16:19.770718Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "('',\n", " [('',\n", " [('(', []),\n", " ('',\n", " [('', [('6', [])]),\n", " ('', [('9', [])]),\n", " ('', [('4', [])])]),\n", " (')', []),\n", " ('',\n", " [('', [('7', [])]),\n", " ('', [('6', [])]),\n", " ('', [('7', [])])]),\n", " ('-', []),\n", " ('',\n", " [('', [('9', [])]),\n", " ('', [('5', [])]),\n", " ('', [('3', [])]),\n", " ('', [('0', [])])])])])" ] }, "execution_count": 137, "metadata": {}, "output_type": "execute_result" } ], "source": [ "phone_fuzzer.derivation_tree" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The chapter contains various helpers to work with derivation trees, including visualization tools – notably, `display_tree()`, above." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Lessons Learned\n", "\n", "* _Derivation trees_ are important for expressing input structure\n", "* _Grammar fuzzing based on derivation trees_ \n", " 1. is much more efficient than string-based grammar fuzzing,\n", " 2. gives much better control over input generation, and\n", " 3. effectively avoids running into infinite expansions." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "source": [ "## Next Steps\n", "\n", "Congratulations! You have reached one of the central \"hubs\" of the book. From here, there is a wide range of techniques that build on grammar fuzzing." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "source": [ "### Extending Grammars\n", "\n", "First, we have a number of techniques that all _extend_ grammars in some form:\n", "\n", "* [Parsing and recombining inputs](Parser.ipynb) allows making use of existing inputs, again using derivation trees\n", "* [Covering grammar expansions](GrammarCoverageFuzzer.ipynb) allows for _combinatorial_ coverage\n", "* [Assigning _probabilities_ to individual expansions](ProbabilisticGrammarFuzzer.ipynb) gives additional control over expansions\n", "* [Assigning _constraints_ to individual expansions](GeneratorGrammarFuzzer.ipynb) allows expressing _semantic constraints_ on individual rules." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "source": [ "### Applying Grammars\n", "\n", "Second, we can _apply_ grammars in a variety of contexts that all involve some form of learning it automatically:\n", "\n", "* [Fuzzing APIs](APIFuzzer.ipynb), learning a grammar from APIs\n", "* [Fuzzing graphical user interfaces](WebFuzzer.ipynb), learning a grammar from user interfaces for subsequent fuzzing\n", "* [Mining grammars](GrammarMiner.ipynb), learning a grammar for arbitrary input formats\n", "\n", "Keep on expanding!" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Background\n", "\n", "Derivation trees (then frequently called _parse trees_) are a standard data structure into which *parsers* decompose inputs. The *Dragon Book* (also known as *Compilers: Principles, Techniques, and Tools*) \\cite{Aho2006} discusses parsing into derivation trees as part of compiling programs. We also use derivation trees [when parsing and recombining inputs](Parser.ipynb).\n", "\n", "The key idea in this chapter, namely expanding until a limit of symbols is reached, and then always choosing the shortest path, stems from Luke \\cite{Luke2000}." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Exercises" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "### Exercise 1: Caching Method Results\n", "\n", "Tracking `GrammarFuzzer` reveals that some methods are called again and again, always with the same values. \n", "\n", "Set up a class `FasterGrammarFuzzer` with a _cache_ that checks whether the method has been called before, and if so, return the previously computed \"memoized\" value. Do this for `expansion_to_children()`. Compare the number of invocations before and after the optimization." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" }, "solution": "hidden", "solution2": "hidden", "solution2_first": true, "solution_first": true }, "source": [ "**Important**: For `expansion_to_children()`, make sure that each list returned is an individual copy. If you return the same (cached) list, this will interfere with the in-place modification of `GrammarFuzzer`. Use the Python `copy.deepcopy()` function for this purpose." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "**Solution.** Let us demonstrate this for `expansion_to_children()`:" ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.772878Z", "iopub.status.busy": "2024-01-18T17:16:19.772728Z", "iopub.status.idle": "2024-01-18T17:16:19.774887Z", "shell.execute_reply": "2024-01-18T17:16:19.774478Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "import copy" ] }, { "cell_type": "code", "execution_count": 139, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.776408Z", "iopub.status.busy": "2024-01-18T17:16:19.776312Z", "iopub.status.idle": "2024-01-18T17:16:19.779342Z", "shell.execute_reply": "2024-01-18T17:16:19.779077Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "class FasterGrammarFuzzer(GrammarFuzzer):\n", " \"\"\"Variant of `GrammarFuzzer` with memoized values\"\"\"\n", "\n", " def __init__(self, *args, **kwargs) -> None:\n", " super().__init__(*args, **kwargs)\n", " self._expansion_cache: Dict[Expansion, List[DerivationTree]] = {}\n", " self._expansion_invocations = 0\n", " self._expansion_invocations_cached = 0\n", "\n", " def expansion_to_children(self, expansion: Expansion) \\\n", " -> List[DerivationTree]:\n", " self._expansion_invocations += 1\n", " if expansion in self._expansion_cache:\n", " self._expansion_invocations_cached += 1\n", " cached_result = copy.deepcopy(self._expansion_cache[expansion])\n", " return cached_result\n", "\n", " result = super().expansion_to_children(expansion)\n", " self._expansion_cache[expansion] = copy.deepcopy(result)\n", " return result" ] }, { "cell_type": "code", "execution_count": 140, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.780723Z", "iopub.status.busy": "2024-01-18T17:16:19.780632Z", "iopub.status.idle": "2024-01-18T17:16:19.784641Z", "shell.execute_reply": "2024-01-18T17:16:19.784378Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "data": { "text/plain": [ "'1 * 7 - -7 * 7 - 2'" ] }, "execution_count": 140, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = FasterGrammarFuzzer(EXPR_GRAMMAR, min_nonterminals=3, max_nonterminals=5)\n", "f.fuzz()" ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.786079Z", "iopub.status.busy": "2024-01-18T17:16:19.785966Z", "iopub.status.idle": "2024-01-18T17:16:19.788004Z", "shell.execute_reply": "2024-01-18T17:16:19.787748Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "data": { "text/plain": [ "115" ] }, "execution_count": 141, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f._expansion_invocations" ] }, { "cell_type": "code", "execution_count": 142, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.789407Z", "iopub.status.busy": "2024-01-18T17:16:19.789309Z", "iopub.status.idle": "2024-01-18T17:16:19.791674Z", "shell.execute_reply": "2024-01-18T17:16:19.791148Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "data": { "text/plain": [ "91" ] }, "execution_count": 142, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f._expansion_invocations_cached" ] }, { "cell_type": "code", "execution_count": 143, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.793355Z", "iopub.status.busy": "2024-01-18T17:16:19.793204Z", "iopub.status.idle": "2024-01-18T17:16:19.795130Z", "shell.execute_reply": "2024-01-18T17:16:19.794896Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "79.13% of invocations can be cached\n" ] } ], "source": [ "print(\"%.2f%% of invocations can be cached\" %\n", " (f._expansion_invocations_cached * 100 / f._expansion_invocations))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" }, "solution2": "hidden", "solution2_first": true }, "source": [ "### Exercise 2: Grammar Pre-Compilation\n", "\n", "Some methods such as `symbol_cost()` or `expansion_cost()` return a value that is dependent on the grammar only. Set up a class `EvenFasterGrammarFuzzer()` that pre-computes these values once upon initialization, such that later invocations of `symbol_cost()` or `expansion_cost()` need only look up these values." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "**Solution.** Here's a possible solution, using a hack to substitute the `symbol_cost()` and `expansion_cost()` functions once the pre-computed values are set up." ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.796603Z", "iopub.status.busy": "2024-01-18T17:16:19.796492Z", "iopub.status.idle": "2024-01-18T17:16:19.801357Z", "shell.execute_reply": "2024-01-18T17:16:19.801095Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "class EvenFasterGrammarFuzzer(GrammarFuzzer):\n", " \"\"\"Variant of `GrammarFuzzer` with precomputed costs\"\"\"\n", "\n", " def __init__(self, *args, **kwargs) -> None:\n", " super().__init__(*args, **kwargs)\n", " self._symbol_costs: Dict[str, Union[int, float]] = {}\n", " self._expansion_costs: Dict[Expansion, Union[int, float]] = {}\n", " self.precompute_costs()\n", "\n", " def new_symbol_cost(self, symbol: str,\n", " seen: Set[str] = set()) -> Union[int, float]:\n", " return self._symbol_costs[symbol]\n", "\n", " def new_expansion_cost(self, expansion: Expansion,\n", " seen: Set[str] = set()) -> Union[int, float]:\n", " return self._expansion_costs[expansion]\n", "\n", " def precompute_costs(self) -> None:\n", " for symbol in self.grammar:\n", " self._symbol_costs[symbol] = super().symbol_cost(symbol)\n", " for expansion in self.grammar[symbol]:\n", " self._expansion_costs[expansion] = \\\n", " super().expansion_cost(expansion)\n", "\n", " # Make sure we now call the caching methods\n", " self.symbol_cost = self.new_symbol_cost # type: ignore\n", " self.expansion_cost = self.new_expansion_cost # type: ignore" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.802740Z", "iopub.status.busy": "2024-01-18T17:16:19.802654Z", "iopub.status.idle": "2024-01-18T17:16:19.805306Z", "shell.execute_reply": "2024-01-18T17:16:19.805068Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "f = EvenFasterGrammarFuzzer(EXPR_GRAMMAR)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "Here are the individual costs:" ] }, { "cell_type": "code", "execution_count": 146, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.806805Z", "iopub.status.busy": "2024-01-18T17:16:19.806701Z", "iopub.status.idle": "2024-01-18T17:16:19.809180Z", "shell.execute_reply": "2024-01-18T17:16:19.808867Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "data": { "text/plain": [ "{'': 6,\n", " '': 5,\n", " '': 4,\n", " '': 3,\n", " '': 2,\n", " '': 1}" ] }, "execution_count": 146, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f._symbol_costs" ] }, { "cell_type": "code", "execution_count": 147, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.810834Z", "iopub.status.busy": "2024-01-18T17:16:19.810704Z", "iopub.status.idle": "2024-01-18T17:16:19.813185Z", "shell.execute_reply": "2024-01-18T17:16:19.812916Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "data": { "text/plain": [ "{'': 6,\n", " ' + ': 10,\n", " ' - ': 10,\n", " '': 5,\n", " ' * ': 8,\n", " ' / ': 8,\n", " '': 4,\n", " '+': 4,\n", " '-': 4,\n", " '()': 6,\n", " '.': 5,\n", " '': 3,\n", " '': 4,\n", " '': 2,\n", " '0': 1,\n", " '1': 1,\n", " '2': 1,\n", " '3': 1,\n", " '4': 1,\n", " '5': 1,\n", " '6': 1,\n", " '7': 1,\n", " '8': 1,\n", " '9': 1}" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f._expansion_costs" ] }, { "cell_type": "code", "execution_count": 148, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.815050Z", "iopub.status.busy": "2024-01-18T17:16:19.814906Z", "iopub.status.idle": "2024-01-18T17:16:19.821944Z", "shell.execute_reply": "2024-01-18T17:16:19.821654Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "data": { "text/plain": [ "'----59.7 + +(1 + 9 + 3) * 2.3 * 4 - (5 - 0) + 6 / 4'" ] }, "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = EvenFasterGrammarFuzzer(EXPR_GRAMMAR)\n", "f.fuzz()" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" }, "solution": "hidden", "solution2": "hidden", "solution2_first": true, "solution_first": true }, "source": [ "### Exercise 3: Maintaining Trees to be Expanded\n", "\n", "In `expand_tree_once()`, the algorithm traverses the tree again and again to find nonterminals that still can be extended. Speed up the process by keeping a list of nonterminal symbols in the tree that still can be expanded." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" }, "solution": "hidden", "solution2": "hidden" }, "source": [ "**Solution.** Left as exercise for the reader." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Exercise 4: Alternate Random Expansions" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We could define `expand_node_randomly()` such that it simply invokes `expand_node_by_cost(node, random.choice)`:" ] }, { "cell_type": "code", "execution_count": 149, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:16:19.823755Z", "iopub.status.busy": "2024-01-18T17:16:19.823631Z", "iopub.status.idle": "2024-01-18T17:16:19.825903Z", "shell.execute_reply": "2024-01-18T17:16:19.825551Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class ExerciseGrammarFuzzer(GrammarFuzzer):\n", " def expand_node_randomly(self, node: DerivationTree) -> DerivationTree:\n", " if self.log:\n", " print(\"Expanding\", all_terminals(node), \"randomly by cost\")\n", "\n", " return self.expand_node_by_cost(node, random.choice)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" }, "solution2": "hidden", "solution2_first": true }, "source": [ "What is the difference between the original implementation and this alternative?" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" }, "solution": "hidden", "solution2": "hidden" }, "source": [ "**Solution.** The alternative in `ExerciseGrammarFuzzer` has another probability distribution. In the original `GrammarFuzzer`, all expansions have the same likelihood of being expanded. In `ExerciseGrammarFuzzer`, first, a cost is chosen (randomly); then, one of the expansions with this cost is chosen (again randomly). This means that expansions whose cost is unique have a higher chance of being selected." ] } ], "metadata": { "ipub": { "bibliography": "fuzzingbook.bib", "toc": true }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.2" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true }, "toc-autonumbering": false, "toc-showcode": false, "toc-showmarkdowntxt": false, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false }, "vscode": { "interpreter": { "hash": "4185989cf89c47c310c2629adcadd634093b57a2c49dffb5ae8d0d14fa302f2b" } } }, "nbformat": 4, "nbformat_minor": 4 }