1*232a27b1SDavid Spickett{ 2*232a27b1SDavid Spickett "cells": [ 3*232a27b1SDavid Spickett { 4*232a27b1SDavid Spickett "cell_type": "markdown", 5*232a27b1SDavid Spickett "id": "30b1e660", 6*232a27b1SDavid Spickett "metadata": {}, 7*232a27b1SDavid Spickett "source": [ 8*232a27b1SDavid Spickett "# Writing a TableGen Backend in Python" 9*232a27b1SDavid Spickett ] 10*232a27b1SDavid Spickett }, 11*232a27b1SDavid Spickett { 12*232a27b1SDavid Spickett "cell_type": "markdown", 13*232a27b1SDavid Spickett "id": "1f9e9c7f", 14*232a27b1SDavid Spickett "metadata": {}, 15*232a27b1SDavid Spickett "source": [ 16*232a27b1SDavid Spickett "This tutorial is going to walk through creating a TableGen backend using Python.\n", 17*232a27b1SDavid Spickett "\n", 18*232a27b1SDavid Spickett "We are using Python to better fit into a notebook, but backends in LLVM are written in C++. The principles you learn here will still apply and you could port this tutorial to any language that has a JSON parser.\n", 19*232a27b1SDavid Spickett "\n", 20*232a27b1SDavid Spickett "This is the process in LLVM, using a C++ backend:\n", 21*232a27b1SDavid Spickett "```\n", 22*232a27b1SDavid Spickett "TableGen source -> llvm-tblgen -> backend (within llvm-tblgen) -> results\n", 23*232a27b1SDavid Spickett "```\n", 24*232a27b1SDavid Spickett "This is what we will be doing:\n", 25*232a27b1SDavid Spickett "```\n", 26*232a27b1SDavid Spickett "TableGen source -> llvm-tblgen -> JSON -> Python -> results\n", 27*232a27b1SDavid Spickett "```\n", 28*232a27b1SDavid Spickett "\n", 29*232a27b1SDavid Spickett "The backend here is ported from one of several in \"SQLGen\" which was written by Min-Yih Hsu.\n", 30*232a27b1SDavid Spickett "* SQLGen C++ sources - https://github.com/mshockwave/SQLGen\n", 31*232a27b1SDavid Spickett "* LLVM dev presentation - https://www.youtube.com/watch?v=UP-LBRbvI_U\n", 32*232a27b1SDavid Spickett "\n", 33*232a27b1SDavid Spickett "I encourage you to use those resources to supplement this notebook." 34*232a27b1SDavid Spickett ] 35*232a27b1SDavid Spickett }, 36*232a27b1SDavid Spickett { 37*232a27b1SDavid Spickett "cell_type": "markdown", 38*232a27b1SDavid Spickett "id": "3e7fe59c", 39*232a27b1SDavid Spickett "metadata": {}, 40*232a27b1SDavid Spickett "source": [ 41*232a27b1SDavid Spickett "## Compiling TableGen" 42*232a27b1SDavid Spickett ] 43*232a27b1SDavid Spickett }, 44*232a27b1SDavid Spickett { 45*232a27b1SDavid Spickett "cell_type": "markdown", 46*232a27b1SDavid Spickett "id": "691301ba", 47*232a27b1SDavid Spickett "metadata": {}, 48*232a27b1SDavid Spickett "source": [ 49*232a27b1SDavid Spickett "Unlike the other tutorial notebooks we are not using the TableGen kernel. This is an iPython notebook and we're going to run `llvm-tblgen` as a subprocess.\n", 50*232a27b1SDavid Spickett "\n", 51*232a27b1SDavid Spickett "First let's find it, in the same way the TableGen kernel does." 52*232a27b1SDavid Spickett ] 53*232a27b1SDavid Spickett }, 54*232a27b1SDavid Spickett { 55*232a27b1SDavid Spickett "cell_type": "code", 56*232a27b1SDavid Spickett "execution_count": 1, 57*232a27b1SDavid Spickett "id": "d3f6d617", 58*232a27b1SDavid Spickett "metadata": {}, 59*232a27b1SDavid Spickett "outputs": [], 60*232a27b1SDavid Spickett "source": [ 61*232a27b1SDavid Spickett "import os\n", 62*232a27b1SDavid Spickett "import shutil\n", 63*232a27b1SDavid Spickett "\n", 64*232a27b1SDavid Spickett "def find_tblgen():\n", 65*232a27b1SDavid Spickett " path = os.environ.get(\"LLVM_TBLGEN_EXECUTABLE\")\n", 66*232a27b1SDavid Spickett " if path is not None and os.path.isfile(path) and os.access(path, os.X_OK):\n", 67*232a27b1SDavid Spickett " return path\n", 68*232a27b1SDavid Spickett " else:\n", 69*232a27b1SDavid Spickett " path = shutil.which(\"llvm-tblgen\")\n", 70*232a27b1SDavid Spickett " if path is None:\n", 71*232a27b1SDavid Spickett " raise OSError(\"llvm-tblgen not found\")\n", 72*232a27b1SDavid Spickett " return path\n", 73*232a27b1SDavid Spickett " \n", 74*232a27b1SDavid Spickett "_ = find_tblgen()" 75*232a27b1SDavid Spickett ] 76*232a27b1SDavid Spickett }, 77*232a27b1SDavid Spickett { 78*232a27b1SDavid Spickett "cell_type": "markdown", 79*232a27b1SDavid Spickett "id": "6663f383", 80*232a27b1SDavid Spickett "metadata": {}, 81*232a27b1SDavid Spickett "source": [ 82*232a27b1SDavid Spickett "If the above cell raises an exception, either put `llvm-tblgen` on your `PATH` or point to it using the `LLVM_TBLGEN_EXECUTABLE` environment variable. Alternatively, edit the code to use whatever path you want.\n", 83*232a27b1SDavid Spickett "\n", 84*232a27b1SDavid Spickett "Then we need to compile some TableGen by passing it to `llvm-tblgen`'s stdin. We will be using the option `--dump-json` and returning the JSON as a Python dictionary if the compilation succeeds. If it fails, we raise an exception." 85*232a27b1SDavid Spickett ] 86*232a27b1SDavid Spickett }, 87*232a27b1SDavid Spickett { 88*232a27b1SDavid Spickett "cell_type": "code", 89*232a27b1SDavid Spickett "execution_count": 2, 90*232a27b1SDavid Spickett "id": "16ad161b", 91*232a27b1SDavid Spickett "metadata": {}, 92*232a27b1SDavid Spickett "outputs": [ 93*232a27b1SDavid Spickett { 94*232a27b1SDavid Spickett "name": "stdout", 95*232a27b1SDavid Spickett "output_type": "stream", 96*232a27b1SDavid Spickett "text": [ 97*232a27b1SDavid Spickett "{\n", 98*232a27b1SDavid Spickett " \"!instanceof\": {\n", 99*232a27b1SDavid Spickett " \"Foo\": []\n", 100*232a27b1SDavid Spickett " },\n", 101*232a27b1SDavid Spickett " \"!tablegen_json_version\": 1\n", 102*232a27b1SDavid Spickett "}\n" 103*232a27b1SDavid Spickett ] 104*232a27b1SDavid Spickett } 105*232a27b1SDavid Spickett ], 106*232a27b1SDavid Spickett "source": [ 107*232a27b1SDavid Spickett "import subprocess\n", 108*232a27b1SDavid Spickett "import tempfile\n", 109*232a27b1SDavid Spickett "import json\n", 110*232a27b1SDavid Spickett "\n", 111*232a27b1SDavid Spickett "def run_tblgen(src):\n", 112*232a27b1SDavid Spickett " # Passing to stdin requires a file like object.\n", 113*232a27b1SDavid Spickett " with tempfile.TemporaryFile(\"w+\") as f:\n", 114*232a27b1SDavid Spickett " f.write(src)\n", 115*232a27b1SDavid Spickett " f.seek(0)\n", 116*232a27b1SDavid Spickett " got = subprocess.run(\n", 117*232a27b1SDavid Spickett " [find_tblgen(), \"--dump-json\"],\n", 118*232a27b1SDavid Spickett " stdin=f,\n", 119*232a27b1SDavid Spickett " stderr=subprocess.PIPE,\n", 120*232a27b1SDavid Spickett " stdout=subprocess.PIPE,\n", 121*232a27b1SDavid Spickett " universal_newlines=True,\n", 122*232a27b1SDavid Spickett " )\n", 123*232a27b1SDavid Spickett " \n", 124*232a27b1SDavid Spickett " if got.stderr:\n", 125*232a27b1SDavid Spickett " raise RuntimeError(\"llvm-tblgen failed with stderr: \" + got.stderr)\n", 126*232a27b1SDavid Spickett " \n", 127*232a27b1SDavid Spickett " return json.loads(got.stdout)\n", 128*232a27b1SDavid Spickett " \n", 129*232a27b1SDavid Spickett "print(json.dumps(run_tblgen(\"class Foo {}\"), indent=4))" 130*232a27b1SDavid Spickett ] 131*232a27b1SDavid Spickett }, 132*232a27b1SDavid Spickett { 133*232a27b1SDavid Spickett "cell_type": "markdown", 134*232a27b1SDavid Spickett "id": "1cf554d2", 135*232a27b1SDavid Spickett "metadata": {}, 136*232a27b1SDavid Spickett "source": [ 137*232a27b1SDavid Spickett "## Structure of a SQL Query" 138*232a27b1SDavid Spickett ] 139*232a27b1SDavid Spickett }, 140*232a27b1SDavid Spickett { 141*232a27b1SDavid Spickett "cell_type": "markdown", 142*232a27b1SDavid Spickett "id": "eeefca57", 143*232a27b1SDavid Spickett "metadata": {}, 144*232a27b1SDavid Spickett "source": [ 145*232a27b1SDavid Spickett "This backend is going to generate SQL queries. The general form of a SQL query is:\n", 146*232a27b1SDavid Spickett "```\n", 147*232a27b1SDavid Spickett "SELECT <some field names> FROM <table name>\n", 148*232a27b1SDavid Spickett " WHERE <conditions>\n", 149*232a27b1SDavid Spickett " ORDER BY <field tags>;\n", 150*232a27b1SDavid Spickett "```" 151*232a27b1SDavid Spickett ] 152*232a27b1SDavid Spickett }, 153*232a27b1SDavid Spickett { 154*232a27b1SDavid Spickett "cell_type": "markdown", 155*232a27b1SDavid Spickett "id": "71760a38", 156*232a27b1SDavid Spickett "metadata": {}, 157*232a27b1SDavid Spickett "source": [ 158*232a27b1SDavid Spickett "## SQL Query TableGen" 159*232a27b1SDavid Spickett ] 160*232a27b1SDavid Spickett }, 161*232a27b1SDavid Spickett { 162*232a27b1SDavid Spickett "cell_type": "code", 163*232a27b1SDavid Spickett "execution_count": 3, 164*232a27b1SDavid Spickett "id": "2d84b0c8", 165*232a27b1SDavid Spickett "metadata": {}, 166*232a27b1SDavid Spickett "outputs": [], 167*232a27b1SDavid Spickett "source": [ 168*232a27b1SDavid Spickett "query_tblgen = \"\"\"\\\n", 169*232a27b1SDavid Spickett "def all;\n", 170*232a27b1SDavid Spickett "def fields;\n", 171*232a27b1SDavid Spickett "def none;\n", 172*232a27b1SDavid Spickett "\n", 173*232a27b1SDavid Spickett "def eq;\n", 174*232a27b1SDavid Spickett "def ne;\n", 175*232a27b1SDavid Spickett "def gt;\n", 176*232a27b1SDavid Spickett "def ge;\n", 177*232a27b1SDavid Spickett "def and;\n", 178*232a27b1SDavid Spickett "def or;\n", 179*232a27b1SDavid Spickett "\"\"\"" 180*232a27b1SDavid Spickett ] 181*232a27b1SDavid Spickett }, 182*232a27b1SDavid Spickett { 183*232a27b1SDavid Spickett "cell_type": "markdown", 184*232a27b1SDavid Spickett "id": "acfa7e4c", 185*232a27b1SDavid Spickett "metadata": {}, 186*232a27b1SDavid Spickett "source": [ 187*232a27b1SDavid Spickett "Normally you'd write this to a `.td` file but here we have it in a Python string to fit into this notebook. We will add to this string to produce the final source.\n", 188*232a27b1SDavid Spickett "\n", 189*232a27b1SDavid Spickett "This section defines some constants. First are the fields we want to get back from the query:\n", 190*232a27b1SDavid Spickett "* `all` - Return all fields.\n", 191*232a27b1SDavid Spickett "* `fields` - Means that we will provide a list of fields we are interested in.\n", 192*232a27b1SDavid Spickett "\n", 193*232a27b1SDavid Spickett "The second set are the logical operators for what will become the `WHERE` clause (called `condition` in the TableGen). These are string versions of various symbols. For example `ne` means `!=`, which in SQL is `<>`.\n", 194*232a27b1SDavid Spickett "\n", 195*232a27b1SDavid Spickett "Finally `none` is used to mean there is no condition to the query (no `WHERE`)." 196*232a27b1SDavid Spickett ] 197*232a27b1SDavid Spickett }, 198*232a27b1SDavid Spickett { 199*232a27b1SDavid Spickett "cell_type": "code", 200*232a27b1SDavid Spickett "execution_count": 4, 201*232a27b1SDavid Spickett "id": "45c00d5e", 202*232a27b1SDavid Spickett "metadata": {}, 203*232a27b1SDavid Spickett "outputs": [], 204*232a27b1SDavid Spickett "source": [ 205*232a27b1SDavid Spickett "query_tblgen += \"\"\"\\\n", 206*232a27b1SDavid Spickett "class Query <string table, dag query_fields = (all), dag condition = (none)> {\n", 207*232a27b1SDavid Spickett " string TableName = table;\n", 208*232a27b1SDavid Spickett " dag Fields = query_fields;\n", 209*232a27b1SDavid Spickett " dag WhereClause = condition;\n", 210*232a27b1SDavid Spickett " list<string> OrderedBy = [];\n", 211*232a27b1SDavid Spickett "}\n", 212*232a27b1SDavid Spickett "\"\"\"" 213*232a27b1SDavid Spickett ] 214*232a27b1SDavid Spickett }, 215*232a27b1SDavid Spickett { 216*232a27b1SDavid Spickett "cell_type": "markdown", 217*232a27b1SDavid Spickett "id": "3ae4def4", 218*232a27b1SDavid Spickett "metadata": {}, 219*232a27b1SDavid Spickett "source": [ 220*232a27b1SDavid Spickett "Then the Query class. Its arguments are:\n", 221*232a27b1SDavid Spickett "* `table` - The name of the table to query (`FROM <table>`).\n", 222*232a27b1SDavid Spickett "* `query_fields` - The fields you want returned (`SELECT <fields>`).\n", 223*232a27b1SDavid Spickett " * Defaults to `all` meaning return all fields.\n", 224*232a27b1SDavid Spickett "* `condition` - Logic to select entries (`WHERE <conditions>`).\n", 225*232a27b1SDavid Spickett " * Defaults to `none` meaning there is no condition, or in other words select all entries in the table." 226*232a27b1SDavid Spickett ] 227*232a27b1SDavid Spickett }, 228*232a27b1SDavid Spickett { 229*232a27b1SDavid Spickett "cell_type": "markdown", 230*232a27b1SDavid Spickett "id": "1af40e14", 231*232a27b1SDavid Spickett "metadata": {}, 232*232a27b1SDavid Spickett "source": [ 233*232a27b1SDavid Spickett "## Using The Query Class" 234*232a27b1SDavid Spickett ] 235*232a27b1SDavid Spickett }, 236*232a27b1SDavid Spickett { 237*232a27b1SDavid Spickett "cell_type": "code", 238*232a27b1SDavid Spickett "execution_count": 5, 239*232a27b1SDavid Spickett "id": "9b6ecead", 240*232a27b1SDavid Spickett "metadata": {}, 241*232a27b1SDavid Spickett "outputs": [], 242*232a27b1SDavid Spickett "source": [ 243*232a27b1SDavid Spickett "full_tblgen = query_tblgen + \"\"\"\\\n", 244*232a27b1SDavid Spickett "def : Query<\"Customer\">;\n", 245*232a27b1SDavid Spickett "\n", 246*232a27b1SDavid Spickett "def : Query<\"Orders\", (fields \"Person\", \"Amount\")>;\n", 247*232a27b1SDavid Spickett "\n", 248*232a27b1SDavid Spickett "def : Query<\"Customer\", (fields \"Affiliation\"),\n", 249*232a27b1SDavid Spickett " (eq \"Name\", \"Mary Blackburn\":$str)>;\n", 250*232a27b1SDavid Spickett "\n", 251*232a27b1SDavid Spickett "def : Query<\"Orders\", (fields \"ProductName\"),\n", 252*232a27b1SDavid Spickett " (gt \"Amount\", 8)>;\n", 253*232a27b1SDavid Spickett "\n", 254*232a27b1SDavid Spickett "def : Query<\"Orders\", (fields \"ProductName\":$name, \"Person\"),\n", 255*232a27b1SDavid Spickett " (and (gt \"Amount\", 8), (ne \"Person\", 1))> {\n", 256*232a27b1SDavid Spickett " let OrderedBy = [\"$name\"];\n", 257*232a27b1SDavid Spickett "}\n", 258*232a27b1SDavid Spickett "\"\"\"" 259*232a27b1SDavid Spickett ] 260*232a27b1SDavid Spickett }, 261*232a27b1SDavid Spickett { 262*232a27b1SDavid Spickett "cell_type": "markdown", 263*232a27b1SDavid Spickett "id": "13a17bb9", 264*232a27b1SDavid Spickett "metadata": {}, 265*232a27b1SDavid Spickett "source": [ 266*232a27b1SDavid Spickett "Now we can define some queries. Let's go go over the last one in detail.\n", 267*232a27b1SDavid Spickett "\n", 268*232a27b1SDavid Spickett "```\n", 269*232a27b1SDavid Spickett "def : Query<\"Orders\", (fields \"ProductName\":$name, \"Person\"),\n", 270*232a27b1SDavid Spickett " (and (gt \"Amount\", 8), (ne \"Person\", 1))> {\n", 271*232a27b1SDavid Spickett " let OrderedBy = [\"$name\"];\n", 272*232a27b1SDavid Spickett "}\n", 273*232a27b1SDavid Spickett "```\n", 274*232a27b1SDavid Spickett "\n", 275*232a27b1SDavid Spickett "* It will run on a table called `Orders`.\n", 276*232a27b1SDavid Spickett "* We want to see the fields `ProductName` and `Person`.\n", 277*232a27b1SDavid Spickett "* We have tagged `ProductName` with `$name`.\n", 278*232a27b1SDavid Spickett "* The condition is that `Amount` must be greater than `8` and\n", 279*232a27b1SDavid Spickett " `Person` must not be equal to `1`.\n", 280*232a27b1SDavid Spickett "* The results of this query should be ordered by the field\n", 281*232a27b1SDavid Spickett " tagged `$name`, which is `ProductName`.\n", 282*232a27b1SDavid Spickett " \n", 283*232a27b1SDavid Spickett "The condition being of DAG type (Directed Acyclic Graph) allows us to describe nested conditions. You might write this condition in Python as:\n", 284*232a27b1SDavid Spickett "```\n", 285*232a27b1SDavid Spickett "if (Amount > 8) and (Person != 1):\n", 286*232a27b1SDavid Spickett "```\n", 287*232a27b1SDavid Spickett "Putting that into a graph form:\n", 288*232a27b1SDavid Spickett "```\n", 289*232a27b1SDavid Spickett " |------|and|------|\n", 290*232a27b1SDavid Spickett " | |\n", 291*232a27b1SDavid Spickett "| Amount > 8 | | Person != 1 |\n", 292*232a27b1SDavid Spickett "```\n", 293*232a27b1SDavid Spickett "Which is what we're describing with the DAG in TableGen." 294*232a27b1SDavid Spickett ] 295*232a27b1SDavid Spickett }, 296*232a27b1SDavid Spickett { 297*232a27b1SDavid Spickett "cell_type": "markdown", 298*232a27b1SDavid Spickett "id": "9fdb5130", 299*232a27b1SDavid Spickett "metadata": {}, 300*232a27b1SDavid Spickett "source": [ 301*232a27b1SDavid Spickett "## The JSON format" 302*232a27b1SDavid Spickett ] 303*232a27b1SDavid Spickett }, 304*232a27b1SDavid Spickett { 305*232a27b1SDavid Spickett "cell_type": "code", 306*232a27b1SDavid Spickett "execution_count": 6, 307*232a27b1SDavid Spickett "id": "4a57b3f0", 308*232a27b1SDavid Spickett "metadata": {}, 309*232a27b1SDavid Spickett "outputs": [ 310*232a27b1SDavid Spickett { 311*232a27b1SDavid Spickett "name": "stdout", 312*232a27b1SDavid Spickett "output_type": "stream", 313*232a27b1SDavid Spickett "text": [ 314*232a27b1SDavid Spickett "{\n", 315*232a27b1SDavid Spickett " \"!instanceof\": {\n", 316*232a27b1SDavid Spickett " \"Query\": [\n", 317*232a27b1SDavid Spickett " \"anonymous_0\",\n", 318*232a27b1SDavid Spickett " \"anonymous_1\",\n", 319*232a27b1SDavid Spickett " \"anonymous_2\",\n", 320*232a27b1SDavid Spickett " \"anonymous_3\",\n", 321*232a27b1SDavid Spickett " \"anonymous_4\"\n", 322*232a27b1SDavid Spickett " ]\n", 323*232a27b1SDavid Spickett " },\n", 324*232a27b1SDavid Spickett " \"!tablegen_json_version\": 1,\n", 325*232a27b1SDavid Spickett " \"all\": {\n", 326*232a27b1SDavid Spickett " \"!anonymous\": false,\n", 327*232a27b1SDavid Spickett " \"!fields\": [],\n", 328*232a27b1SDavid Spickett " \"!name\": \"all\",\n", 329*232a27b1SDavid Spickett " \"!superclasses\": []\n", 330*232a27b1SDavid Spickett " },\n", 331*232a27b1SDavid Spickett " \"and\": {\n", 332*232a27b1SDavid Spickett " \"!anonymous\": false,\n", 333*232a27b1SDavid Spickett " \"!fields\": [],\n", 334*232a27b1SDavid Spickett " \"!name\": \"and\",\n", 335*232a27b1SDavid Spickett " \"!superclasses\": []\n", 336*232a27b1SDavid Spickett " },\n", 337*232a27b1SDavid Spickett " \"anonymous_0\": {\n", 338*232a27b1SDavid Spickett " \"!anonymous\": true,\n", 339*232a27b1SDavid Spickett " \"!fields\": [],\n", 340*232a27b1SDavid Spickett " \"!name\": \"anonymous_0\",\n", 341*232a27b1SDavid Spickett " \"!superclasses\": [\n", 342*232a27b1SDavid Spickett " \"Query\"\n", 343*232a27b1SDavid Spickett " ],\n", 344*232a27b1SDavid Spickett " \"Fields\": {\n", 345*232a27b1SDavid Spickett " \"args\": [],\n", 346*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 347*232a27b1SDavid Spickett " \"operator\": {\n", 348*232a27b1SDavid Spickett " \"def\": \"all\",\n", 349*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 350*232a27b1SDavid Spickett " \"printable\": \"all\"\n", 351*232a27b1SDavid Spickett " },\n", 352*232a27b1SDavid Spickett " \"printable\": \"(all)\"\n", 353*232a27b1SDavid Spickett " },\n", 354*232a27b1SDavid Spickett " \"OrderedBy\": [],\n", 355*232a27b1SDavid Spickett " \"TableName\": \"Customer\",\n", 356*232a27b1SDavid Spickett " \"WhereClause\": {\n", 357*232a27b1SDavid Spickett " \"args\": [],\n", 358*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 359*232a27b1SDavid Spickett " \"operator\": {\n", 360*232a27b1SDavid Spickett " \"def\": \"none\",\n", 361*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 362*232a27b1SDavid Spickett " \"printable\": \"none\"\n", 363*232a27b1SDavid Spickett " },\n", 364*232a27b1SDavid Spickett " \"printable\": \"(none)\"\n", 365*232a27b1SDavid Spickett " }\n", 366*232a27b1SDavid Spickett " },\n", 367*232a27b1SDavid Spickett " \"anonymous_1\": {\n", 368*232a27b1SDavid Spickett " \"!anonymous\": true,\n", 369*232a27b1SDavid Spickett " \"!fields\": [],\n", 370*232a27b1SDavid Spickett " \"!name\": \"anonymous_1\",\n", 371*232a27b1SDavid Spickett " \"!superclasses\": [\n", 372*232a27b1SDavid Spickett " \"Query\"\n", 373*232a27b1SDavid Spickett " ],\n", 374*232a27b1SDavid Spickett " \"Fields\": {\n", 375*232a27b1SDavid Spickett " \"args\": [\n", 376*232a27b1SDavid Spickett " [\n", 377*232a27b1SDavid Spickett " \"Person\",\n", 378*232a27b1SDavid Spickett " null\n", 379*232a27b1SDavid Spickett " ],\n", 380*232a27b1SDavid Spickett " [\n", 381*232a27b1SDavid Spickett " \"Amount\",\n", 382*232a27b1SDavid Spickett " null\n", 383*232a27b1SDavid Spickett " ]\n", 384*232a27b1SDavid Spickett " ],\n", 385*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 386*232a27b1SDavid Spickett " \"operator\": {\n", 387*232a27b1SDavid Spickett " \"def\": \"fields\",\n", 388*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 389*232a27b1SDavid Spickett " \"printable\": \"fields\"\n", 390*232a27b1SDavid Spickett " },\n", 391*232a27b1SDavid Spickett " \"printable\": \"(fields \\\"Person\\\", \\\"Amount\\\")\"\n", 392*232a27b1SDavid Spickett " },\n", 393*232a27b1SDavid Spickett " \"OrderedBy\": [],\n", 394*232a27b1SDavid Spickett " \"TableName\": \"Orders\",\n", 395*232a27b1SDavid Spickett " \"WhereClause\": {\n", 396*232a27b1SDavid Spickett " \"args\": [],\n", 397*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 398*232a27b1SDavid Spickett " \"operator\": {\n", 399*232a27b1SDavid Spickett " \"def\": \"none\",\n", 400*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 401*232a27b1SDavid Spickett " \"printable\": \"none\"\n", 402*232a27b1SDavid Spickett " },\n", 403*232a27b1SDavid Spickett " \"printable\": \"(none)\"\n", 404*232a27b1SDavid Spickett " }\n", 405*232a27b1SDavid Spickett " },\n", 406*232a27b1SDavid Spickett " \"anonymous_2\": {\n", 407*232a27b1SDavid Spickett " \"!anonymous\": true,\n", 408*232a27b1SDavid Spickett " \"!fields\": [],\n", 409*232a27b1SDavid Spickett " \"!name\": \"anonymous_2\",\n", 410*232a27b1SDavid Spickett " \"!superclasses\": [\n", 411*232a27b1SDavid Spickett " \"Query\"\n", 412*232a27b1SDavid Spickett " ],\n", 413*232a27b1SDavid Spickett " \"Fields\": {\n", 414*232a27b1SDavid Spickett " \"args\": [\n", 415*232a27b1SDavid Spickett " [\n", 416*232a27b1SDavid Spickett " \"Affiliation\",\n", 417*232a27b1SDavid Spickett " null\n", 418*232a27b1SDavid Spickett " ]\n", 419*232a27b1SDavid Spickett " ],\n", 420*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 421*232a27b1SDavid Spickett " \"operator\": {\n", 422*232a27b1SDavid Spickett " \"def\": \"fields\",\n", 423*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 424*232a27b1SDavid Spickett " \"printable\": \"fields\"\n", 425*232a27b1SDavid Spickett " },\n", 426*232a27b1SDavid Spickett " \"printable\": \"(fields \\\"Affiliation\\\")\"\n", 427*232a27b1SDavid Spickett " },\n", 428*232a27b1SDavid Spickett " \"OrderedBy\": [],\n", 429*232a27b1SDavid Spickett " \"TableName\": \"Customer\",\n", 430*232a27b1SDavid Spickett " \"WhereClause\": {\n", 431*232a27b1SDavid Spickett " \"args\": [\n", 432*232a27b1SDavid Spickett " [\n", 433*232a27b1SDavid Spickett " \"Name\",\n", 434*232a27b1SDavid Spickett " null\n", 435*232a27b1SDavid Spickett " ],\n", 436*232a27b1SDavid Spickett " [\n", 437*232a27b1SDavid Spickett " \"Mary Blackburn\",\n", 438*232a27b1SDavid Spickett " \"str\"\n", 439*232a27b1SDavid Spickett " ]\n", 440*232a27b1SDavid Spickett " ],\n", 441*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 442*232a27b1SDavid Spickett " \"operator\": {\n", 443*232a27b1SDavid Spickett " \"def\": \"eq\",\n", 444*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 445*232a27b1SDavid Spickett " \"printable\": \"eq\"\n", 446*232a27b1SDavid Spickett " },\n", 447*232a27b1SDavid Spickett " \"printable\": \"(eq \\\"Name\\\", \\\"Mary Blackburn\\\":$str)\"\n", 448*232a27b1SDavid Spickett " }\n", 449*232a27b1SDavid Spickett " },\n", 450*232a27b1SDavid Spickett " \"anonymous_3\": {\n", 451*232a27b1SDavid Spickett " \"!anonymous\": true,\n", 452*232a27b1SDavid Spickett " \"!fields\": [],\n", 453*232a27b1SDavid Spickett " \"!name\": \"anonymous_3\",\n", 454*232a27b1SDavid Spickett " \"!superclasses\": [\n", 455*232a27b1SDavid Spickett " \"Query\"\n", 456*232a27b1SDavid Spickett " ],\n", 457*232a27b1SDavid Spickett " \"Fields\": {\n", 458*232a27b1SDavid Spickett " \"args\": [\n", 459*232a27b1SDavid Spickett " [\n", 460*232a27b1SDavid Spickett " \"ProductName\",\n", 461*232a27b1SDavid Spickett " null\n", 462*232a27b1SDavid Spickett " ]\n", 463*232a27b1SDavid Spickett " ],\n", 464*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 465*232a27b1SDavid Spickett " \"operator\": {\n", 466*232a27b1SDavid Spickett " \"def\": \"fields\",\n", 467*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 468*232a27b1SDavid Spickett " \"printable\": \"fields\"\n", 469*232a27b1SDavid Spickett " },\n", 470*232a27b1SDavid Spickett " \"printable\": \"(fields \\\"ProductName\\\")\"\n", 471*232a27b1SDavid Spickett " },\n", 472*232a27b1SDavid Spickett " \"OrderedBy\": [],\n", 473*232a27b1SDavid Spickett " \"TableName\": \"Orders\",\n", 474*232a27b1SDavid Spickett " \"WhereClause\": {\n", 475*232a27b1SDavid Spickett " \"args\": [\n", 476*232a27b1SDavid Spickett " [\n", 477*232a27b1SDavid Spickett " \"Amount\",\n", 478*232a27b1SDavid Spickett " null\n", 479*232a27b1SDavid Spickett " ],\n", 480*232a27b1SDavid Spickett " [\n", 481*232a27b1SDavid Spickett " 8,\n", 482*232a27b1SDavid Spickett " null\n", 483*232a27b1SDavid Spickett " ]\n", 484*232a27b1SDavid Spickett " ],\n", 485*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 486*232a27b1SDavid Spickett " \"operator\": {\n", 487*232a27b1SDavid Spickett " \"def\": \"gt\",\n", 488*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 489*232a27b1SDavid Spickett " \"printable\": \"gt\"\n", 490*232a27b1SDavid Spickett " },\n", 491*232a27b1SDavid Spickett " \"printable\": \"(gt \\\"Amount\\\", 8)\"\n", 492*232a27b1SDavid Spickett " }\n", 493*232a27b1SDavid Spickett " },\n", 494*232a27b1SDavid Spickett " \"anonymous_4\": {\n", 495*232a27b1SDavid Spickett " \"!anonymous\": true,\n", 496*232a27b1SDavid Spickett " \"!fields\": [],\n", 497*232a27b1SDavid Spickett " \"!name\": \"anonymous_4\",\n", 498*232a27b1SDavid Spickett " \"!superclasses\": [\n", 499*232a27b1SDavid Spickett " \"Query\"\n", 500*232a27b1SDavid Spickett " ],\n", 501*232a27b1SDavid Spickett " \"Fields\": {\n", 502*232a27b1SDavid Spickett " \"args\": [\n", 503*232a27b1SDavid Spickett " [\n", 504*232a27b1SDavid Spickett " \"ProductName\",\n", 505*232a27b1SDavid Spickett " \"name\"\n", 506*232a27b1SDavid Spickett " ],\n", 507*232a27b1SDavid Spickett " [\n", 508*232a27b1SDavid Spickett " \"Person\",\n", 509*232a27b1SDavid Spickett " null\n", 510*232a27b1SDavid Spickett " ]\n", 511*232a27b1SDavid Spickett " ],\n", 512*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 513*232a27b1SDavid Spickett " \"operator\": {\n", 514*232a27b1SDavid Spickett " \"def\": \"fields\",\n", 515*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 516*232a27b1SDavid Spickett " \"printable\": \"fields\"\n", 517*232a27b1SDavid Spickett " },\n", 518*232a27b1SDavid Spickett " \"printable\": \"(fields \\\"ProductName\\\":$name, \\\"Person\\\")\"\n", 519*232a27b1SDavid Spickett " },\n", 520*232a27b1SDavid Spickett " \"OrderedBy\": [\n", 521*232a27b1SDavid Spickett " \"$name\"\n", 522*232a27b1SDavid Spickett " ],\n", 523*232a27b1SDavid Spickett " \"TableName\": \"Orders\",\n", 524*232a27b1SDavid Spickett " \"WhereClause\": {\n", 525*232a27b1SDavid Spickett " \"args\": [\n", 526*232a27b1SDavid Spickett " [\n", 527*232a27b1SDavid Spickett " {\n", 528*232a27b1SDavid Spickett " \"args\": [\n", 529*232a27b1SDavid Spickett " [\n", 530*232a27b1SDavid Spickett " \"Amount\",\n", 531*232a27b1SDavid Spickett " null\n", 532*232a27b1SDavid Spickett " ],\n", 533*232a27b1SDavid Spickett " [\n", 534*232a27b1SDavid Spickett " 8,\n", 535*232a27b1SDavid Spickett " null\n", 536*232a27b1SDavid Spickett " ]\n", 537*232a27b1SDavid Spickett " ],\n", 538*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 539*232a27b1SDavid Spickett " \"operator\": {\n", 540*232a27b1SDavid Spickett " \"def\": \"gt\",\n", 541*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 542*232a27b1SDavid Spickett " \"printable\": \"gt\"\n", 543*232a27b1SDavid Spickett " },\n", 544*232a27b1SDavid Spickett " \"printable\": \"(gt \\\"Amount\\\", 8)\"\n", 545*232a27b1SDavid Spickett " },\n", 546*232a27b1SDavid Spickett " null\n", 547*232a27b1SDavid Spickett " ],\n", 548*232a27b1SDavid Spickett " [\n", 549*232a27b1SDavid Spickett " {\n", 550*232a27b1SDavid Spickett " \"args\": [\n", 551*232a27b1SDavid Spickett " [\n", 552*232a27b1SDavid Spickett " \"Person\",\n", 553*232a27b1SDavid Spickett " null\n", 554*232a27b1SDavid Spickett " ],\n", 555*232a27b1SDavid Spickett " [\n", 556*232a27b1SDavid Spickett " 1,\n", 557*232a27b1SDavid Spickett " null\n", 558*232a27b1SDavid Spickett " ]\n", 559*232a27b1SDavid Spickett " ],\n", 560*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 561*232a27b1SDavid Spickett " \"operator\": {\n", 562*232a27b1SDavid Spickett " \"def\": \"ne\",\n", 563*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 564*232a27b1SDavid Spickett " \"printable\": \"ne\"\n", 565*232a27b1SDavid Spickett " },\n", 566*232a27b1SDavid Spickett " \"printable\": \"(ne \\\"Person\\\", 1)\"\n", 567*232a27b1SDavid Spickett " },\n", 568*232a27b1SDavid Spickett " null\n", 569*232a27b1SDavid Spickett " ]\n", 570*232a27b1SDavid Spickett " ],\n", 571*232a27b1SDavid Spickett " \"kind\": \"dag\",\n", 572*232a27b1SDavid Spickett " \"operator\": {\n", 573*232a27b1SDavid Spickett " \"def\": \"and\",\n", 574*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 575*232a27b1SDavid Spickett " \"printable\": \"and\"\n", 576*232a27b1SDavid Spickett " },\n", 577*232a27b1SDavid Spickett " \"printable\": \"(and (gt \\\"Amount\\\", 8), (ne \\\"Person\\\", 1))\"\n", 578*232a27b1SDavid Spickett " }\n", 579*232a27b1SDavid Spickett " },\n", 580*232a27b1SDavid Spickett " \"eq\": {\n", 581*232a27b1SDavid Spickett " \"!anonymous\": false,\n", 582*232a27b1SDavid Spickett " \"!fields\": [],\n", 583*232a27b1SDavid Spickett " \"!name\": \"eq\",\n", 584*232a27b1SDavid Spickett " \"!superclasses\": []\n", 585*232a27b1SDavid Spickett " },\n", 586*232a27b1SDavid Spickett " \"fields\": {\n", 587*232a27b1SDavid Spickett " \"!anonymous\": false,\n", 588*232a27b1SDavid Spickett " \"!fields\": [],\n", 589*232a27b1SDavid Spickett " \"!name\": \"fields\",\n", 590*232a27b1SDavid Spickett " \"!superclasses\": []\n", 591*232a27b1SDavid Spickett " },\n", 592*232a27b1SDavid Spickett " \"ge\": {\n", 593*232a27b1SDavid Spickett " \"!anonymous\": false,\n", 594*232a27b1SDavid Spickett " \"!fields\": [],\n", 595*232a27b1SDavid Spickett " \"!name\": \"ge\",\n", 596*232a27b1SDavid Spickett " \"!superclasses\": []\n", 597*232a27b1SDavid Spickett " },\n", 598*232a27b1SDavid Spickett " \"gt\": {\n", 599*232a27b1SDavid Spickett " \"!anonymous\": false,\n", 600*232a27b1SDavid Spickett " \"!fields\": [],\n", 601*232a27b1SDavid Spickett " \"!name\": \"gt\",\n", 602*232a27b1SDavid Spickett " \"!superclasses\": []\n", 603*232a27b1SDavid Spickett " },\n", 604*232a27b1SDavid Spickett " \"ne\": {\n", 605*232a27b1SDavid Spickett " \"!anonymous\": false,\n", 606*232a27b1SDavid Spickett " \"!fields\": [],\n", 607*232a27b1SDavid Spickett " \"!name\": \"ne\",\n", 608*232a27b1SDavid Spickett " \"!superclasses\": []\n", 609*232a27b1SDavid Spickett " },\n", 610*232a27b1SDavid Spickett " \"none\": {\n", 611*232a27b1SDavid Spickett " \"!anonymous\": false,\n", 612*232a27b1SDavid Spickett " \"!fields\": [],\n", 613*232a27b1SDavid Spickett " \"!name\": \"none\",\n", 614*232a27b1SDavid Spickett " \"!superclasses\": []\n", 615*232a27b1SDavid Spickett " },\n", 616*232a27b1SDavid Spickett " \"or\": {\n", 617*232a27b1SDavid Spickett " \"!anonymous\": false,\n", 618*232a27b1SDavid Spickett " \"!fields\": [],\n", 619*232a27b1SDavid Spickett " \"!name\": \"or\",\n", 620*232a27b1SDavid Spickett " \"!superclasses\": []\n", 621*232a27b1SDavid Spickett " }\n", 622*232a27b1SDavid Spickett "}\n" 623*232a27b1SDavid Spickett ] 624*232a27b1SDavid Spickett } 625*232a27b1SDavid Spickett ], 626*232a27b1SDavid Spickett "source": [ 627*232a27b1SDavid Spickett "full_json = run_tblgen(full_tblgen)\n", 628*232a27b1SDavid Spickett "print(json.dumps(full_json, indent=4))" 629*232a27b1SDavid Spickett ] 630*232a27b1SDavid Spickett }, 631*232a27b1SDavid Spickett { 632*232a27b1SDavid Spickett "cell_type": "markdown", 633*232a27b1SDavid Spickett "id": "32b24328", 634*232a27b1SDavid Spickett "metadata": {}, 635*232a27b1SDavid Spickett "source": [ 636*232a27b1SDavid Spickett "The backend is going to walk the JSON we decoded. You can see the full output above in case you want to browse but for now don't read the whole thing. We will highlight the key aspects of it as we go along." 637*232a27b1SDavid Spickett ] 638*232a27b1SDavid Spickett }, 639*232a27b1SDavid Spickett { 640*232a27b1SDavid Spickett "cell_type": "code", 641*232a27b1SDavid Spickett "execution_count": 7, 642*232a27b1SDavid Spickett "id": "f2c0966e", 643*232a27b1SDavid Spickett "metadata": {}, 644*232a27b1SDavid Spickett "outputs": [ 645*232a27b1SDavid Spickett { 646*232a27b1SDavid Spickett "name": "stdout", 647*232a27b1SDavid Spickett "output_type": "stream", 648*232a27b1SDavid Spickett "text": [ 649*232a27b1SDavid Spickett "{'Query': ['anonymous_0', 'anonymous_1', 'anonymous_2', 'anonymous_3', 'anonymous_4']}\n" 650*232a27b1SDavid Spickett ] 651*232a27b1SDavid Spickett } 652*232a27b1SDavid Spickett ], 653*232a27b1SDavid Spickett "source": [ 654*232a27b1SDavid Spickett "print(full_json[\"!instanceof\"])" 655*232a27b1SDavid Spickett ] 656*232a27b1SDavid Spickett }, 657*232a27b1SDavid Spickett { 658*232a27b1SDavid Spickett "cell_type": "markdown", 659*232a27b1SDavid Spickett "id": "ff9c1374", 660*232a27b1SDavid Spickett "metadata": {}, 661*232a27b1SDavid Spickett "source": [ 662*232a27b1SDavid Spickett "Any key beginning with `!` is some sort of metadata about the other keys. Here this is a list of all instances of certain classes. We just have `Query` which lists all the queries we defined." 663*232a27b1SDavid Spickett ] 664*232a27b1SDavid Spickett }, 665*232a27b1SDavid Spickett { 666*232a27b1SDavid Spickett "cell_type": "code", 667*232a27b1SDavid Spickett "execution_count": 8, 668*232a27b1SDavid Spickett "id": "806e9602", 669*232a27b1SDavid Spickett "metadata": {}, 670*232a27b1SDavid Spickett "outputs": [ 671*232a27b1SDavid Spickett { 672*232a27b1SDavid Spickett "name": "stdout", 673*232a27b1SDavid Spickett "output_type": "stream", 674*232a27b1SDavid Spickett "text": [ 675*232a27b1SDavid Spickett "['Query']\n" 676*232a27b1SDavid Spickett ] 677*232a27b1SDavid Spickett } 678*232a27b1SDavid Spickett ], 679*232a27b1SDavid Spickett "source": [ 680*232a27b1SDavid Spickett "print(full_json[\"anonymous_0\"][\"!superclasses\"])" 681*232a27b1SDavid Spickett ] 682*232a27b1SDavid Spickett }, 683*232a27b1SDavid Spickett { 684*232a27b1SDavid Spickett "cell_type": "markdown", 685*232a27b1SDavid Spickett "id": "a7a8e50c", 686*232a27b1SDavid Spickett "metadata": {}, 687*232a27b1SDavid Spickett "source": [ 688*232a27b1SDavid Spickett "On each def there is also a `!superclasses` that gives you the same information. Meaning you could use `!instanceof` to get a list of keys to lookup, or you could walk all keys and check `!superclasses`." 689*232a27b1SDavid Spickett ] 690*232a27b1SDavid Spickett }, 691*232a27b1SDavid Spickett { 692*232a27b1SDavid Spickett "cell_type": "code", 693*232a27b1SDavid Spickett "execution_count": 9, 694*232a27b1SDavid Spickett "id": "9073578b", 695*232a27b1SDavid Spickett "metadata": {}, 696*232a27b1SDavid Spickett "outputs": [ 697*232a27b1SDavid Spickett { 698*232a27b1SDavid Spickett "name": "stdout", 699*232a27b1SDavid Spickett "output_type": "stream", 700*232a27b1SDavid Spickett "text": [ 701*232a27b1SDavid Spickett "{'args': [], 'kind': 'dag', 'operator': {'def': 'all', 'kind': 'def', 'printable': 'all'}, 'printable': '(all)'}\n" 702*232a27b1SDavid Spickett ] 703*232a27b1SDavid Spickett } 704*232a27b1SDavid Spickett ], 705*232a27b1SDavid Spickett "source": [ 706*232a27b1SDavid Spickett "print(full_json[\"anonymous_0\"][\"Fields\"])" 707*232a27b1SDavid Spickett ] 708*232a27b1SDavid Spickett }, 709*232a27b1SDavid Spickett { 710*232a27b1SDavid Spickett "cell_type": "markdown", 711*232a27b1SDavid Spickett "id": "994eb9e0", 712*232a27b1SDavid Spickett "metadata": {}, 713*232a27b1SDavid Spickett "source": [ 714*232a27b1SDavid Spickett "From a def object you can find its attributes. Here we have the fields we want the query to show, which is all of them." 715*232a27b1SDavid Spickett ] 716*232a27b1SDavid Spickett }, 717*232a27b1SDavid Spickett { 718*232a27b1SDavid Spickett "cell_type": "markdown", 719*232a27b1SDavid Spickett "id": "f12f52e3", 720*232a27b1SDavid Spickett "metadata": {}, 721*232a27b1SDavid Spickett "source": [ 722*232a27b1SDavid Spickett "# The Backend" 723*232a27b1SDavid Spickett ] 724*232a27b1SDavid Spickett }, 725*232a27b1SDavid Spickett { 726*232a27b1SDavid Spickett "cell_type": "markdown", 727*232a27b1SDavid Spickett "id": "023227c0", 728*232a27b1SDavid Spickett "metadata": {}, 729*232a27b1SDavid Spickett "source": [ 730*232a27b1SDavid Spickett "The core of a backend is looping over all defs of a certain class and outputting some text based on their properties.\n", 731*232a27b1SDavid Spickett "\n", 732*232a27b1SDavid Spickett "Here we're going to loop over all defs of type `Query` and emit SQL queries for them." 733*232a27b1SDavid Spickett ] 734*232a27b1SDavid Spickett }, 735*232a27b1SDavid Spickett { 736*232a27b1SDavid Spickett "cell_type": "code", 737*232a27b1SDavid Spickett "execution_count": 10, 738*232a27b1SDavid Spickett "id": "f2cfda7e", 739*232a27b1SDavid Spickett "metadata": {}, 740*232a27b1SDavid Spickett "outputs": [ 741*232a27b1SDavid Spickett { 742*232a27b1SDavid Spickett "name": "stdout", 743*232a27b1SDavid Spickett "output_type": "stream", 744*232a27b1SDavid Spickett "text": [ 745*232a27b1SDavid Spickett "['anonymous_0', 'anonymous_1', 'anonymous_2', 'anonymous_3', 'anonymous_4']\n" 746*232a27b1SDavid Spickett ] 747*232a27b1SDavid Spickett } 748*232a27b1SDavid Spickett ], 749*232a27b1SDavid Spickett "source": [ 750*232a27b1SDavid Spickett "def find_all_queries(j):\n", 751*232a27b1SDavid Spickett " queries = []\n", 752*232a27b1SDavid Spickett " for key in j:\n", 753*232a27b1SDavid Spickett " # ! means it is some metadata, not a def.\n", 754*232a27b1SDavid Spickett " if not key.startswith(\"!\"):\n", 755*232a27b1SDavid Spickett " value = full_json[key]\n", 756*232a27b1SDavid Spickett " # If we inherit from Query.\n", 757*232a27b1SDavid Spickett " if \"Query\" in value[\"!superclasses\"]:\n", 758*232a27b1SDavid Spickett " queries.append(value)\n", 759*232a27b1SDavid Spickett " return queries\n", 760*232a27b1SDavid Spickett "\n", 761*232a27b1SDavid Spickett "queries = find_all_queries(full_json)\n", 762*232a27b1SDavid Spickett " \n", 763*232a27b1SDavid Spickett "print([q[\"!name\"] for q in queries])" 764*232a27b1SDavid Spickett ] 765*232a27b1SDavid Spickett }, 766*232a27b1SDavid Spickett { 767*232a27b1SDavid Spickett "cell_type": "markdown", 768*232a27b1SDavid Spickett "id": "e9c82793", 769*232a27b1SDavid Spickett "metadata": {}, 770*232a27b1SDavid Spickett "source": [ 771*232a27b1SDavid Spickett "Why are the names `anonymous_...`? When we defined them we did `def :` and missed out the name. This is allowed and `llvm-tblgen` just came up with a name for us. For this purpose the names are irrelevant.\n", 772*232a27b1SDavid Spickett "\n", 773*232a27b1SDavid Spickett "Now we have the relevant classes we need to \"emit\" them. Meaning produce something from them, in this case a SQL query." 774*232a27b1SDavid Spickett ] 775*232a27b1SDavid Spickett }, 776*232a27b1SDavid Spickett { 777*232a27b1SDavid Spickett "cell_type": "code", 778*232a27b1SDavid Spickett "execution_count": 11, 779*232a27b1SDavid Spickett "id": "f1b795f9", 780*232a27b1SDavid Spickett "metadata": {}, 781*232a27b1SDavid Spickett "outputs": [ 782*232a27b1SDavid Spickett { 783*232a27b1SDavid Spickett "name": "stdout", 784*232a27b1SDavid Spickett "output_type": "stream", 785*232a27b1SDavid Spickett "text": [ 786*232a27b1SDavid Spickett " AND \n" 787*232a27b1SDavid Spickett ] 788*232a27b1SDavid Spickett } 789*232a27b1SDavid Spickett ], 790*232a27b1SDavid Spickett "source": [ 791*232a27b1SDavid Spickett "def emit_operator(operator):\n", 792*232a27b1SDavid Spickett " return {\n", 793*232a27b1SDavid Spickett " 'gt': ' > ',\n", 794*232a27b1SDavid Spickett " 'ge': ' >= ',\n", 795*232a27b1SDavid Spickett " 'lt': ' < ',\n", 796*232a27b1SDavid Spickett " 'le': ' <= ',\n", 797*232a27b1SDavid Spickett " 'ne': ' <> ',\n", 798*232a27b1SDavid Spickett " 'eq': ' = ',\n", 799*232a27b1SDavid Spickett " 'or': ' OR ',\n", 800*232a27b1SDavid Spickett " 'and': ' AND '\n", 801*232a27b1SDavid Spickett " }[operator]\n", 802*232a27b1SDavid Spickett "\n", 803*232a27b1SDavid Spickett "print(emit_operator('and'))" 804*232a27b1SDavid Spickett ] 805*232a27b1SDavid Spickett }, 806*232a27b1SDavid Spickett { 807*232a27b1SDavid Spickett "cell_type": "markdown", 808*232a27b1SDavid Spickett "id": "2fd3a96f", 809*232a27b1SDavid Spickett "metadata": {}, 810*232a27b1SDavid Spickett "source": [ 811*232a27b1SDavid Spickett "The maps our TableGen constants to the equivalent SQL logical operation." 812*232a27b1SDavid Spickett ] 813*232a27b1SDavid Spickett }, 814*232a27b1SDavid Spickett { 815*232a27b1SDavid Spickett "cell_type": "code", 816*232a27b1SDavid Spickett "execution_count": 12, 817*232a27b1SDavid Spickett "id": "a6fa0c43", 818*232a27b1SDavid Spickett "metadata": {}, 819*232a27b1SDavid Spickett "outputs": [ 820*232a27b1SDavid Spickett { 821*232a27b1SDavid Spickett "name": "stdout", 822*232a27b1SDavid Spickett "output_type": "stream", 823*232a27b1SDavid Spickett "text": [ 824*232a27b1SDavid Spickett "Abc, Def\n" 825*232a27b1SDavid Spickett ] 826*232a27b1SDavid Spickett } 827*232a27b1SDavid Spickett ], 828*232a27b1SDavid Spickett "source": [ 829*232a27b1SDavid Spickett "def emit_fields(args):\n", 830*232a27b1SDavid Spickett " # Return a comma separated list of arg names.\n", 831*232a27b1SDavid Spickett " return \", \".join([arg[0] for arg in args])\n", 832*232a27b1SDavid Spickett "\n", 833*232a27b1SDavid Spickett "print(emit_fields([[\"Abc\", None], [\"Def\", None]]))" 834*232a27b1SDavid Spickett ] 835*232a27b1SDavid Spickett }, 836*232a27b1SDavid Spickett { 837*232a27b1SDavid Spickett "cell_type": "markdown", 838*232a27b1SDavid Spickett "id": "43127766", 839*232a27b1SDavid Spickett "metadata": {}, 840*232a27b1SDavid Spickett "source": [ 841*232a27b1SDavid Spickett "This emits the the fields we are selecting. Each field has a name (`arg[0]`) and an optional tag that we will use later." 842*232a27b1SDavid Spickett ] 843*232a27b1SDavid Spickett }, 844*232a27b1SDavid Spickett { 845*232a27b1SDavid Spickett "cell_type": "code", 846*232a27b1SDavid Spickett "execution_count": 13, 847*232a27b1SDavid Spickett "id": "4aa39163", 848*232a27b1SDavid Spickett "metadata": {}, 849*232a27b1SDavid Spickett "outputs": [ 850*232a27b1SDavid Spickett { 851*232a27b1SDavid Spickett "name": "stdout", 852*232a27b1SDavid Spickett "output_type": "stream", 853*232a27b1SDavid Spickett "text": [ 854*232a27b1SDavid Spickett "Name = \"Mary Blackburn\"\n" 855*232a27b1SDavid Spickett ] 856*232a27b1SDavid Spickett } 857*232a27b1SDavid Spickett ], 858*232a27b1SDavid Spickett "source": [ 859*232a27b1SDavid Spickett "from collections.abc import Mapping\n", 860*232a27b1SDavid Spickett "\n", 861*232a27b1SDavid Spickett "def emit_where_clause(where_clause):\n", 862*232a27b1SDavid Spickett " output = \"\"\n", 863*232a27b1SDavid Spickett " num_args = len(where_clause[\"args\"])\n", 864*232a27b1SDavid Spickett " \n", 865*232a27b1SDavid Spickett " for idx, arg in enumerate(where_clause[\"args\"]):\n", 866*232a27b1SDavid Spickett " arg_name, arg_type = arg\n", 867*232a27b1SDavid Spickett "\n", 868*232a27b1SDavid Spickett " if isinstance(arg_name, Mapping):\n", 869*232a27b1SDavid Spickett " # This is a nested where clause.\n", 870*232a27b1SDavid Spickett " output += emit_where_clause(arg_name)\n", 871*232a27b1SDavid Spickett " else:\n", 872*232a27b1SDavid Spickett " # This is some condition.\n", 873*232a27b1SDavid Spickett " if arg_type == \"str\":\n", 874*232a27b1SDavid Spickett " # String types must be emitted with \"\" around them.\n", 875*232a27b1SDavid Spickett " output += '\"' + arg_name + '\"'\n", 876*232a27b1SDavid Spickett " else:\n", 877*232a27b1SDavid Spickett " output += str(arg_name)\n", 878*232a27b1SDavid Spickett "\n", 879*232a27b1SDavid Spickett " # If this is not the last arg, emit the condition.\n", 880*232a27b1SDavid Spickett " if idx != (num_args-1):\n", 881*232a27b1SDavid Spickett " output += emit_operator(where_clause[\"operator\"][\"def\"])\n", 882*232a27b1SDavid Spickett " \n", 883*232a27b1SDavid Spickett " return output\n", 884*232a27b1SDavid Spickett "\n", 885*232a27b1SDavid Spickett "print(emit_where_clause({\n", 886*232a27b1SDavid Spickett "\"args\": [[\"Name\",None], \n", 887*232a27b1SDavid Spickett " [\"Mary Blackburn\", \"str\"]],\n", 888*232a27b1SDavid Spickett "\"kind\": \"dag\",\n", 889*232a27b1SDavid Spickett "\"operator\": {\n", 890*232a27b1SDavid Spickett " \"def\": \"eq\",\n", 891*232a27b1SDavid Spickett " \"kind\": \"def\",\n", 892*232a27b1SDavid Spickett " \"printable\": \"eq\"\n", 893*232a27b1SDavid Spickett "}}))" 894*232a27b1SDavid Spickett ] 895*232a27b1SDavid Spickett }, 896*232a27b1SDavid Spickett { 897*232a27b1SDavid Spickett "cell_type": "markdown", 898*232a27b1SDavid Spickett "id": "f8e6a7fe", 899*232a27b1SDavid Spickett "metadata": {}, 900*232a27b1SDavid Spickett "source": [ 901*232a27b1SDavid Spickett "This emits the condition that goes with the `WHERE`. The condition is a DAG, which means that we will find a possible mix of conditions and other DAGS. We recurse to handle the latter case.\n", 902*232a27b1SDavid Spickett "\n", 903*232a27b1SDavid Spickett "For each part of the condition we print the name of the thing you're checking, then the condition (`=`, `<>`, etc.). The value to check against is last and that goes on the end." 904*232a27b1SDavid Spickett ] 905*232a27b1SDavid Spickett }, 906*232a27b1SDavid Spickett { 907*232a27b1SDavid Spickett "cell_type": "code", 908*232a27b1SDavid Spickett "execution_count": 14, 909*232a27b1SDavid Spickett "id": "92eee280", 910*232a27b1SDavid Spickett "metadata": {}, 911*232a27b1SDavid Spickett "outputs": [ 912*232a27b1SDavid Spickett { 913*232a27b1SDavid Spickett "name": "stdout", 914*232a27b1SDavid Spickett "output_type": "stream", 915*232a27b1SDavid Spickett "text": [ 916*232a27b1SDavid Spickett "\n", 917*232a27b1SDavid Spickett " ORDER BY ABC, DEF\n" 918*232a27b1SDavid Spickett ] 919*232a27b1SDavid Spickett } 920*232a27b1SDavid Spickett ], 921*232a27b1SDavid Spickett "source": [ 922*232a27b1SDavid Spickett "def emit_ordered_by(ordered_by, field_tag_map):\n", 923*232a27b1SDavid Spickett " # No ORDER BY statement to emit.\n", 924*232a27b1SDavid Spickett " if not ordered_by:\n", 925*232a27b1SDavid Spickett " return \"\"\n", 926*232a27b1SDavid Spickett " \n", 927*232a27b1SDavid Spickett " output = \"\\n ORDER BY \"\n", 928*232a27b1SDavid Spickett " num_ordered_by = len(ordered_by)\n", 929*232a27b1SDavid Spickett " \n", 930*232a27b1SDavid Spickett " for idx, field_name in enumerate(ordered_by):\n", 931*232a27b1SDavid Spickett " # If it is a tag\n", 932*232a27b1SDavid Spickett " if field_name.startswith('$'):\n", 933*232a27b1SDavid Spickett " # Find the corresponding field name\n", 934*232a27b1SDavid Spickett " tag_name = field_name[1:]\n", 935*232a27b1SDavid Spickett " field_name = field_tag_map.get(tag_name)\n", 936*232a27b1SDavid Spickett " if field_name is None:\n", 937*232a27b1SDavid Spickett " raise RuntimeError('Unrecognized tag \"{}\"'.format(\n", 938*232a27b1SDavid Spickett " tag_name))\n", 939*232a27b1SDavid Spickett "\n", 940*232a27b1SDavid Spickett " # Separate each tag after the first with \", \".\n", 941*232a27b1SDavid Spickett " if idx != 0:\n", 942*232a27b1SDavid Spickett " output += \", \"\n", 943*232a27b1SDavid Spickett " output += field_name\n", 944*232a27b1SDavid Spickett " \n", 945*232a27b1SDavid Spickett " return output\n", 946*232a27b1SDavid Spickett "\n", 947*232a27b1SDavid Spickett "print(emit_ordered_by([\"$abc\", \"$def\"], {'abc':\"ABC\", 'def':\"DEF\"}))" 948*232a27b1SDavid Spickett ] 949*232a27b1SDavid Spickett }, 950*232a27b1SDavid Spickett { 951*232a27b1SDavid Spickett "cell_type": "markdown", 952*232a27b1SDavid Spickett "id": "8a918b51", 953*232a27b1SDavid Spickett "metadata": {}, 954*232a27b1SDavid Spickett "source": [ 955*232a27b1SDavid Spickett "`emit_ordered_by` produces the `ORDER BY` text. If there is no ordering return nothing, otherwise loop over all the fields we want to order by and emit their names.\n", 956*232a27b1SDavid Spickett "\n", 957*232a27b1SDavid Spickett "If the name is a tag, we look that up in a map to get the real field name. Here's how we build that map:" 958*232a27b1SDavid Spickett ] 959*232a27b1SDavid Spickett }, 960*232a27b1SDavid Spickett { 961*232a27b1SDavid Spickett "cell_type": "code", 962*232a27b1SDavid Spickett "execution_count": 15, 963*232a27b1SDavid Spickett "id": "16faaf30", 964*232a27b1SDavid Spickett "metadata": {}, 965*232a27b1SDavid Spickett "outputs": [ 966*232a27b1SDavid Spickett { 967*232a27b1SDavid Spickett "name": "stdout", 968*232a27b1SDavid Spickett "output_type": "stream", 969*232a27b1SDavid Spickett "text": [ 970*232a27b1SDavid Spickett "{'abc': 'ABC', 'def': 'DEF'}\n" 971*232a27b1SDavid Spickett ] 972*232a27b1SDavid Spickett } 973*232a27b1SDavid Spickett ], 974*232a27b1SDavid Spickett "source": [ 975*232a27b1SDavid Spickett "def build_tag_map(arguments):\n", 976*232a27b1SDavid Spickett " # Args are [Name, Tag]. Reverse this so we have [Tag, Name].\n", 977*232a27b1SDavid Spickett " # Add each one to a dictionary where Tag is the key and Name is the value.\n", 978*232a27b1SDavid Spickett " return dict([reversed(a) for a in arguments])\n", 979*232a27b1SDavid Spickett "\n", 980*232a27b1SDavid Spickett "print(build_tag_map([[\"ABC\", \"abc\"], [\"DEF\", \"def\"]]))" 981*232a27b1SDavid Spickett ] 982*232a27b1SDavid Spickett }, 983*232a27b1SDavid Spickett { 984*232a27b1SDavid Spickett "cell_type": "code", 985*232a27b1SDavid Spickett "execution_count": 16, 986*232a27b1SDavid Spickett "id": "dcf139f2", 987*232a27b1SDavid Spickett "metadata": {}, 988*232a27b1SDavid Spickett "outputs": [], 989*232a27b1SDavid Spickett "source": [ 990*232a27b1SDavid Spickett "def emit_query(q):\n", 991*232a27b1SDavid Spickett " fields_init = q[\"Fields\"]\n", 992*232a27b1SDavid Spickett " field_op_name = fields_init[\"operator\"][\"def\"]\n", 993*232a27b1SDavid Spickett " if not field_op_name in [\"all\", \"fields\"]:\n", 994*232a27b1SDavid Spickett " raise RuntimeError(\"Invalid dag operator \" + field_op_name)\n", 995*232a27b1SDavid Spickett " \n", 996*232a27b1SDavid Spickett " field_tag_map = build_tag_map(fields_init[\"args\"])\n", 997*232a27b1SDavid Spickett " \n", 998*232a27b1SDavid Spickett " where_clause = q[\"WhereClause\"]\n", 999*232a27b1SDavid Spickett " has_where = where_clause[\"operator\"][\"def\"] != \"none\"\n", 1000*232a27b1SDavid Spickett " \n", 1001*232a27b1SDavid Spickett " ret = \"SELECT \"\n", 1002*232a27b1SDavid Spickett " if field_op_name == \"all\":\n", 1003*232a27b1SDavid Spickett " ret += \"*\"\n", 1004*232a27b1SDavid Spickett " ret += emit_fields(fields_init[\"args\"])\n", 1005*232a27b1SDavid Spickett " ret += \" FROM \" + q[\"TableName\"]\n", 1006*232a27b1SDavid Spickett " if has_where:\n", 1007*232a27b1SDavid Spickett " ret += \"\\n WHERE \" + emit_where_clause(where_clause)\n", 1008*232a27b1SDavid Spickett " ret += emit_ordered_by(q[\"OrderedBy\"], field_tag_map)\n", 1009*232a27b1SDavid Spickett " ret += \";\"\n", 1010*232a27b1SDavid Spickett " \n", 1011*232a27b1SDavid Spickett " return ret" 1012*232a27b1SDavid Spickett ] 1013*232a27b1SDavid Spickett }, 1014*232a27b1SDavid Spickett { 1015*232a27b1SDavid Spickett "cell_type": "markdown", 1016*232a27b1SDavid Spickett "id": "5acb7290", 1017*232a27b1SDavid Spickett "metadata": {}, 1018*232a27b1SDavid Spickett "source": [ 1019*232a27b1SDavid Spickett "Finally the main function. It emits the skeleton of the query and calls the helpers we defined earlier to fill in the gaps." 1020*232a27b1SDavid Spickett ] 1021*232a27b1SDavid Spickett }, 1022*232a27b1SDavid Spickett { 1023*232a27b1SDavid Spickett "cell_type": "markdown", 1024*232a27b1SDavid Spickett "id": "661a028b", 1025*232a27b1SDavid Spickett "metadata": {}, 1026*232a27b1SDavid Spickett "source": [ 1027*232a27b1SDavid Spickett "## The Result" 1028*232a27b1SDavid Spickett ] 1029*232a27b1SDavid Spickett }, 1030*232a27b1SDavid Spickett { 1031*232a27b1SDavid Spickett "cell_type": "code", 1032*232a27b1SDavid Spickett "execution_count": 17, 1033*232a27b1SDavid Spickett "id": "0f05368c", 1034*232a27b1SDavid Spickett "metadata": {}, 1035*232a27b1SDavid Spickett "outputs": [ 1036*232a27b1SDavid Spickett { 1037*232a27b1SDavid Spickett "name": "stdout", 1038*232a27b1SDavid Spickett "output_type": "stream", 1039*232a27b1SDavid Spickett "text": [ 1040*232a27b1SDavid Spickett "SELECT * FROM Customer;\n", 1041*232a27b1SDavid Spickett "\n", 1042*232a27b1SDavid Spickett "SELECT Person, Amount FROM Orders;\n", 1043*232a27b1SDavid Spickett "\n", 1044*232a27b1SDavid Spickett "SELECT Affiliation FROM Customer\n", 1045*232a27b1SDavid Spickett " WHERE Name = \"Mary Blackburn\";\n", 1046*232a27b1SDavid Spickett "\n", 1047*232a27b1SDavid Spickett "SELECT ProductName FROM Orders\n", 1048*232a27b1SDavid Spickett " WHERE Amount > 8;\n", 1049*232a27b1SDavid Spickett "\n", 1050*232a27b1SDavid Spickett "SELECT ProductName, Person FROM Orders\n", 1051*232a27b1SDavid Spickett " WHERE Amount > 8 AND Person <> 1\n", 1052*232a27b1SDavid Spickett " ORDER BY ProductName;\n", 1053*232a27b1SDavid Spickett "\n" 1054*232a27b1SDavid Spickett ] 1055*232a27b1SDavid Spickett } 1056*232a27b1SDavid Spickett ], 1057*232a27b1SDavid Spickett "source": [ 1058*232a27b1SDavid Spickett "for q in queries:\n", 1059*232a27b1SDavid Spickett " print(emit_query(q) + \"\\n\")" 1060*232a27b1SDavid Spickett ] 1061*232a27b1SDavid Spickett }, 1062*232a27b1SDavid Spickett { 1063*232a27b1SDavid Spickett "cell_type": "markdown", 1064*232a27b1SDavid Spickett "id": "56a0f062", 1065*232a27b1SDavid Spickett "metadata": {}, 1066*232a27b1SDavid Spickett "source": [ 1067*232a27b1SDavid Spickett "Now we run `emit_query` and print out the results. There you have it, that's a TableGen backend!\n", 1068*232a27b1SDavid Spickett "\n", 1069*232a27b1SDavid Spickett "You've seen the core concepts. Loop over all the defs of a certain class and then emit some other structure based on the fields of each one. In this case it was SQL queries. In LLVM it's most often C++ code but it can be anything you want.\n", 1070*232a27b1SDavid Spickett "\n", 1071*232a27b1SDavid Spickett "If you want to see the same thing done with a C++ backend (one written in C++ that is, not producing it), check out the links at the start of this notebook." 1072*232a27b1SDavid Spickett ] 1073*232a27b1SDavid Spickett } 1074*232a27b1SDavid Spickett ], 1075*232a27b1SDavid Spickett "metadata": { 1076*232a27b1SDavid Spickett "kernelspec": { 1077*232a27b1SDavid Spickett "display_name": "Python 3 (ipykernel)", 1078*232a27b1SDavid Spickett "language": "python", 1079*232a27b1SDavid Spickett "name": "python3" 1080*232a27b1SDavid Spickett }, 1081*232a27b1SDavid Spickett "language_info": { 1082*232a27b1SDavid Spickett "codemirror_mode": { 1083*232a27b1SDavid Spickett "name": "ipython", 1084*232a27b1SDavid Spickett "version": 3 1085*232a27b1SDavid Spickett }, 1086*232a27b1SDavid Spickett "file_extension": ".py", 1087*232a27b1SDavid Spickett "mimetype": "text/x-python", 1088*232a27b1SDavid Spickett "name": "python", 1089*232a27b1SDavid Spickett "nbconvert_exporter": "python", 1090*232a27b1SDavid Spickett "pygments_lexer": "ipython3", 1091*232a27b1SDavid Spickett "version": "3.8.10" 1092*232a27b1SDavid Spickett } 1093*232a27b1SDavid Spickett }, 1094*232a27b1SDavid Spickett "nbformat": 4, 1095*232a27b1SDavid Spickett "nbformat_minor": 5 1096*232a27b1SDavid Spickett} 1097