diff --git a/numba_examples/pho_a.wav b/numba_examples/pho_a.wav new file mode 100755 index 0000000..14e2c91 Binary files /dev/null and b/numba_examples/pho_a.wav differ diff --git a/numba_examples/rpde.ipynb b/numba_examples/rpde.ipynb new file mode 100644 index 0000000..1b87642 --- /dev/null +++ b/numba_examples/rpde.ipynb @@ -0,0 +1,464 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + " \n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import librosa\n", + "import numpy as np\n", + "import plotly.express as px\n", + "import plotly.graph_objs as go\n", + "from plotly.offline import init_notebook_mode, iplot\n", + "init_notebook_mode(connected=True)\n", + "from numpy.linalg import norm\n", + "\n", + "from tqdm import tqdm\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### First things first\n", + "\n", + "_Loading the audio data, and embedding the audio time series_\n", + " Our algorithm operates on time series\n", + "(here, it's audio, what a coincidence!).\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# loads the sound data into a float32\n", + "data, rate = librosa.core.load(\"pho_a.wav\")\n", + "\n", + "px.line(data, x=range(len(data)), y=data)\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def embed_time_series(data: np.ndarray, dim: int, tau: int):\n", + " \"\"\"This creates an special embedding for the input data. The\n", + " output shape of this function is (N, D) with\n", + " N = len(data) - (dim - 1) * tau\n", + " D = dim\"\"\"\n", + " embed_points_nb = data.shape[0] - (dim - 1) * tau\n", + " embed_mask = np.arange(embed_points_nb)[:, np.newaxis] + np.arange(dim)\n", + " tau_offsets = np.arange(dim) * (tau - 1)\n", + " embed_mask = embed_mask + tau_offsets\n", + " return data[embed_mask]" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "# this creates an embedding of a slice of the time series\n", + "ts = embed_time_series(data[:2000], dim=4, tau=22)\n", + "print(ts.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "trace = go.Scatter3d(\n", + " x=ts[:,0],\n", + " y=ts[:,1],\n", + " z=ts[:,2],\n", + " name=\"Pho_a\",\n", + " mode='markers',\n", + " marker=dict(\n", + " color=\"blue\",\n", + " size=2,\n", + " symbol='circle',\n", + " line=dict(\n", + " color='rgb(204, 204, 204)',\n", + " width=1\n", + " ),\n", + " opacity=0.7\n", + " )\n", + " )\n", + "layout = go.Layout(margin=dict(l=0,r=0,b=0,t=0),\n", + " scene = dict(\n", + " xaxis={\"title\":\"f1\"},\n", + " yaxis={\"title\":\"f2\"},\n", + " zaxis={\"title\":\"f3\"})\n", + " )\n", + "fig = go.Figure(data=[trace], layout=layout)\n", + "iplot(fig, filename=\"/tmp/pho_a_trajectory.png\")" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### This is where the fun begins\n", + "\n", + "Now, let's implement the RPDE algorithm.\n", + "I've provided 4 different implementations:\n", + "\n", + "1. The first one is a plain python implem of the recurrence diagram\n", + "2. The second one is numba implem without any parallelization\n", + "2. The third one is a naïve parallelized numba implem. It's \"almost\" working,\n", + "as it outputs a value close to the actual value, but some race conditions make it\n", + "undeterministic.\n", + "3. The third one is a fixed parallelized version of the previous numba implementation,\n", + "fully adapted to work in parallel" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "def plain_python_recurrence_histogram(ts: np.ndarray, epsilon: float, t_max: int):\n", + " distances_histogram = np.zeros(len(ts))\n", + " epsilon = 0.25\n", + " for i in tqdm(np.arange(len(ts))):\n", + " # finding the first \"out of ball\" index\n", + " first_out = len(ts) # security\n", + " for j in np.arange(i + 1, len(ts)):\n", + " d = norm(ts[i] - ts[j])\n", + " if d > epsilon:\n", + " first_out = j\n", + " break\n", + "\n", + " # finding the first \"back to the ball\" index\n", + " for j in np.arange(first_out + 1, len(ts)):\n", + " d = norm(ts[i] - ts[j])\n", + " if d < epsilon:\n", + " distances_histogram[j - i] += 1\n", + " break\n", + " return distances_histogram" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "from numba import jit, float32, prange, int32" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [], + "source": [ + "@jit(int32[:](float32[:,:], float32, int32), nopython=True)\n", + "def recurrence_histogram(ts: np.ndarray, epsilon: float, t_max: int):\n", + " distances_histogram = np.zeros(len(ts), dtype=np.int32)\n", + " for i in np.arange(len(ts)):\n", + " # finding the first \"out of ball\" index\n", + " first_out = len(ts) # security\n", + " for j in np.arange(i + 1, len(ts)):\n", + " if t_max > 0 and j - i > t_max:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d > epsilon:\n", + " first_out = j\n", + " break\n", + "\n", + " # finding the first \"back to the ball\" index\n", + " for j in np.arange(first_out + 1, len(ts)):\n", + " if 0 < t_max < j - i:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d < epsilon:\n", + " distances_histogram[j - i] += 1\n", + " break\n", + " return distances_histogram" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# Here, we naively just switched out all \"np.arange\" occurences to numba.prange.\n", + "@jit(int32[:](float32[:,:], float32, int32), nopython=True)\n", + "def wrong_parallel_recurrence_histogram(ts: np.ndarray, epsilon: float, t_max: int):\n", + " distances_histogram = np.zeros(len(ts), dtype=np.int32)\n", + " for i in prange(len(ts)):\n", + " # finding the first \"out of ball\" index\n", + " first_out = len(ts) # security\n", + " for j in prange(i + 1, len(ts)):\n", + " if t_max > 0 and j - i > t_max:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d > epsilon:\n", + " first_out = j\n", + " break\n", + "\n", + " # finding the first \"back to the ball\" index\n", + " for j in prange(first_out + 1, len(ts)):\n", + " if 0 < t_max < j - i:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d < epsilon:\n", + " distances_histogram[j - i] += 1\n", + " break\n", + " return distances_histogram" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [], + "source": [ + "@jit(int32[:](float32[:,:], float32, int32), parallel=True, nopython=True)\n", + "def parallel_recurrence_histogram(ts: np.ndarray, epsilon: float, t_max: int):\n", + " return_distances = np.zeros(len(ts), dtype=np.int32)\n", + " for i in prange(len(ts)):\n", + " # finding the first \"out of ball\" index\n", + " first_out = len(ts) # security\n", + " for j in np.arange(i + 1, len(ts)):\n", + " if t_max > 0 and j - i > t_max:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d > epsilon:\n", + " first_out = j\n", + " break\n", + "\n", + " # finding the first \"back to the ball\" index\n", + " for j in np.arange(first_out + 1, len(ts)):\n", + " if t_max > 0 and j - i > t_max:\n", + " break\n", + " d = norm(ts[i] - ts[j])\n", + " if d < epsilon:\n", + " return_distances[i] = j - i\n", + " break\n", + " \n", + " # building histogram, can't be parallel\n", + " distance_histogram = np.zeros(len(ts), dtype=np.int32)\n", + " for i in np.arange(len(ts)):\n", + " if return_distances[i] != 0:\n", + " distance_histogram[return_distances[i]] += 1\n", + " return distance_histogram" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Running stuff\n", + "\n", + "Let's run the good ol' python implementation, just to take a look at what does\n", + "the algorithm's output look like.\n", + "\n", + "Also, let's check that the various numba implementations's outputs match the plain\n", + "python \"ground truth\"." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 1min 30s, sys: 67.9 ms, total: 1min 30s\n", + "Wall time: 55.6 s\n" + ] + }, + { + "data": { + "text/plain": [ + "array([0, 0, 0, ..., 0, 0, 0], dtype=int32)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "distances_histogram = plain_python_recurrence_histogram(ts, 0.12, 10000)\n", + "px.line(distances_histogram, x=range(len(distances_histogram)), y=distances_histogram)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### The ~~hour~~ seconds of truth" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "%time plain_python_recurrence_histogram(ts, 0.12, 10000)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "%time numba_recurrence_histogram(ts, 0.12, 10000)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "%time parallel_recurrence_histogram(ts, 0.12, 10000)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 16min 10s, sys: 3.01 s, total: 16min 13s\n", + "Wall time: 6min 59s\n" + ] + }, + { + "data": { + "text/plain": [ + "array([0, 0, 0, ..., 0, 0, 0], dtype=int32)" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [] + } + ], + "metadata": { + "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.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3d0cba1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +sly +numba +scipy +numpy +tqdm +librosa +jupyter +dataclasses >= 0.7; python_version <'3.7' \ No newline at end of file diff --git a/sly_examples/json_parser.py b/sly_examples/json_parser.py new file mode 100644 index 0000000..9ae07b6 --- /dev/null +++ b/sly_examples/json_parser.py @@ -0,0 +1,107 @@ +from sly import Lexer, Parser +import pprint + +""" +Implementation with SLY of a very simple JSON parser. +Little reminder: here's the grammar + + json := object | array + object := '{' members '}' + members := pair | pair ',' members + array := '[' elements ']' + elements := value | value "," elements + pair := STRING ':' value + value := STRING | INTEGER | FLOAT | object | array + +""" + + +class JSONLexer(Lexer): + tokens = {"FLOAT", "INTEGER", "STRING"} + + literals = {'{', '}', '[', ']', ',', ':'} + ignore = " \t\n" + + @_(r"\".*?\"") + def STRING(self, t): + t.value = t.value.strip("\"") + return t + + @_(r"\d+\.\d*") + def FLOAT(self, t): + t.value = float(t.value) + return t + + @_(r"\d+") + def INTEGER(self, t): + t.value = int(t.value) + return t + + +class JSONParser(Parser): + tokens = JSONLexer.tokens + start = "json" + + @_('object', + 'array') + def json(self, p): + return p[0] + + @_('"{" members "}"') + def object(self, p): + return {key: value for key, value in p.members} + + @_('pair') + def members(self, p): + return [p.pair] + + @_('pair "," members') + def members(self, p): + return [p.pair] + p.members + + @_('STRING ":" value') + def pair(self, p): + return p.STRING, p.value + + @_('"[" elements "]"') + def array(self, p): + return p.elements + + @_('value') + def elements(self, p): + return [p.value] + + @_('value "," elements') + def elements(self, p): + return [p.value] + p.elements + + @_('STRING', + 'INTEGER', + 'FLOAT', + 'object', + 'array') + def value(self, p): + return p[0] + + def error(self, p): + raise ValueError("Parsing error at token %s" % str(p)) + + +if __name__ == "__main__": + lexer = JSONLexer() + parser = JSONParser() + json_text = """{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ], + "delay" : 1.5 + } + }} + """ + result = parser.parse(lexer.tokenize(json_text)) + pprint.pprint(result) diff --git a/sly_examples/star.stl b/sly_examples/star.stl new file mode 100644 index 0000000..0292428 --- /dev/null +++ b/sly_examples/star.stl @@ -0,0 +1,478 @@ +solid Star + facet normal 0 -1 0 + outer loop + vertex 0.83404 0 0.694596 + vertex 0.36904 0 1.5 + vertex 1.78814e-006 0 0.75 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 0.36904 0 1.5 + vertex 0.83404 0 2.3054 + vertex 1.78814e-006 0 2.25 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 1.29904 0 0 + vertex 1.76404 0 0.694596 + vertex 0.83404 0 0.694596 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 0.83404 0 2.3054 + vertex 1.76404 0 2.3054 + vertex 1.29904 0 3 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 1.76404 0 0.694596 + vertex 2.59808 0 0.75 + vertex 2.22904 0 1.5 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 2.22904 0 1.5 + vertex 2.59808 0 2.25 + vertex 1.76404 0 2.3054 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 0.83404 0 0.694596 + vertex 0.83404 0 2.3054 + vertex 0.36904 0 1.5 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 0.83404 0 0.694596 + vertex 1.76404 0 0.694596 + vertex 0.83404 0 2.3054 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 1.76404 0 0.694596 + vertex 2.22904 0 1.5 + vertex 0.83404 0 2.3054 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 2.22904 0 1.5 + vertex 1.76404 0 2.3054 + vertex 0.83404 0 2.3054 + endloop + endfacet + facet normal -0.897262 0 -0.441498 + outer loop + vertex 0.36904 0 1.5 + vertex 1.78814e-006 0 2.25 + vertex 0.36904 0.25 1.5 + endloop + endfacet + facet normal -0.897262 0 -0.441498 + outer loop + vertex 1.78814e-006 0 2.25 + vertex 1.78814e-006 0.25 2.25 + vertex 0.36904 0.25 1.5 + endloop + endfacet + facet normal -0.0662822 0 0.997801 + outer loop + vertex 1.78814e-006 0 2.25 + vertex 0.83404 0 2.3054 + vertex 1.78814e-006 0.25 2.25 + endloop + endfacet + facet normal -0.0662822 0 0.997801 + outer loop + vertex 0.83404 0 2.3054 + vertex 0.83404 0.25 2.3054 + vertex 1.78814e-006 0.25 2.25 + endloop + endfacet + facet normal -0.83098 0 0.556302 + outer loop + vertex 0.83404 0 2.3054 + vertex 1.29904 0 3 + vertex 0.83404 0.25 2.3054 + endloop + endfacet + facet normal -0.83098 0 0.556302 + outer loop + vertex 1.29904 0 3 + vertex 1.29904 0.25 3 + vertex 0.83404 0.25 2.3054 + endloop + endfacet + facet normal 0.83098 0 0.556303 + outer loop + vertex 1.76404 0 2.3054 + vertex 1.76404 0.25 2.3054 + vertex 1.29904 0 3 + endloop + endfacet + facet normal 0.83098 0 0.556303 + outer loop + vertex 1.29904 0 3 + vertex 1.76404 0.25 2.3054 + vertex 1.29904 0.25 3 + endloop + endfacet + facet normal 0.0662822 0 0.997801 + outer loop + vertex 2.59808 0 2.25 + vertex 2.59808 0.25 2.25 + vertex 1.76404 0 2.3054 + endloop + endfacet + facet normal 0.0662822 0 0.997801 + outer loop + vertex 1.76404 0 2.3054 + vertex 2.59808 0.25 2.25 + vertex 1.76404 0.25 2.3054 + endloop + endfacet + facet normal 0.897262 0 -0.441498 + outer loop + vertex 2.22904 0 1.5 + vertex 2.22904 0.25 1.5 + vertex 2.59808 0 2.25 + endloop + endfacet + facet normal 0.897262 0 -0.441498 + outer loop + vertex 2.59808 0 2.25 + vertex 2.22904 0.25 1.5 + vertex 2.59808 0.25 2.25 + endloop + endfacet + facet normal 0.897262 0 0.441498 + outer loop + vertex 2.59808 0 0.75 + vertex 2.59808 0.25 0.75 + vertex 2.22904 0 1.5 + endloop + endfacet + facet normal 0.897262 0 0.441498 + outer loop + vertex 2.22904 0 1.5 + vertex 2.59808 0.25 0.75 + vertex 2.22904 0.25 1.5 + endloop + endfacet + facet normal 0.0662821 0 -0.997801 + outer loop + vertex 1.76404 0 0.694596 + vertex 1.76404 0.25 0.694596 + vertex 2.59808 0 0.75 + endloop + endfacet + facet normal 0.0662821 0 -0.997801 + outer loop + vertex 2.59808 0 0.75 + vertex 1.76404 0.25 0.694596 + vertex 2.59808 0.25 0.75 + endloop + endfacet + facet normal 0.83098 0 -0.556302 + outer loop + vertex 1.29904 0 0 + vertex 1.29904 0.25 0 + vertex 1.76404 0 0.694596 + endloop + endfacet + facet normal 0.83098 0 -0.556302 + outer loop + vertex 1.76404 0 0.694596 + vertex 1.29904 0.25 0 + vertex 1.76404 0.25 0.694596 + endloop + endfacet + facet normal -0.83098 0 -0.556302 + outer loop + vertex 1.29904 0 0 + vertex 0.83404 0 0.694596 + vertex 1.29904 0.25 0 + endloop + endfacet + facet normal -0.83098 0 -0.556302 + outer loop + vertex 0.83404 0 0.694596 + vertex 0.83404 0.25 0.694596 + vertex 1.29904 0.25 0 + endloop + endfacet + facet normal -0.066282 0 -0.997801 + outer loop + vertex 0.83404 0 0.694596 + vertex 1.78814e-006 0 0.75 + vertex 0.83404 0.25 0.694596 + endloop + endfacet + facet normal -0.066282 0 -0.997801 + outer loop + vertex 1.78814e-006 0 0.75 + vertex 1.78814e-006 0.25 0.75 + vertex 0.83404 0.25 0.694596 + endloop + endfacet + facet normal -0.897262 0 0.441498 + outer loop + vertex 1.78814e-006 0 0.75 + vertex 0.36904 0 1.5 + vertex 1.78814e-006 0.25 0.75 + endloop + endfacet + facet normal -0.897262 0 0.441498 + outer loop + vertex 0.36904 0 1.5 + vertex 0.36904 0.25 1.5 + vertex 1.78814e-006 0.25 0.75 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.48049 0.35 1.5 + vertex 0.155677 0.35 2.16012 + vertex 0.889765 0.35 2.20888 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 0.155677 0.35 0.839879 + vertex 0.48049 0.35 1.5 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 2.20888 + vertex 1.29904 0.35 2.82024 + vertex 1.70831 0.35 2.20888 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 1.29904 0.35 0.179758 + vertex 0.889765 0.35 0.791115 + vertex 1.70831 0.35 0.791115 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 2.11759 0.35 1.5 + vertex 1.70831 0.35 2.20888 + vertex 2.4424 0.35 2.16012 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 1.70831 0.35 0.791115 + vertex 2.11759 0.35 1.5 + vertex 2.4424 0.35 0.839879 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 0.48049 0.35 1.5 + vertex 0.889765 0.35 2.20888 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 0.889765 0.35 2.20888 + vertex 1.70831 0.35 2.20888 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 1.70831 0.35 2.20888 + vertex 2.11759 0.35 1.5 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 0.889765 0.35 0.791115 + vertex 2.11759 0.35 1.5 + vertex 1.70831 0.35 0.791115 + endloop + endfacet + facet normal -0.63446 0.707107 0.312186 + outer loop + vertex 1.78814e-006 0.25 0.75 + vertex 0.36904 0.25 1.5 + vertex 0.155677 0.35 0.839879 + endloop + endfacet + facet normal -0.63446 0.707107 0.312186 + outer loop + vertex 0.36904 0.25 1.5 + vertex 0.48049 0.35 1.5 + vertex 0.155677 0.35 0.839879 + endloop + endfacet + facet normal -0.63446 0.707107 -0.312186 + outer loop + vertex 0.36904 0.25 1.5 + vertex 0.155677 0.35 2.16012 + vertex 0.48049 0.35 1.5 + endloop + endfacet + facet normal -0.634459 0.707107 -0.312186 + outer loop + vertex 0.36904 0.25 1.5 + vertex 1.78814e-006 0.25 2.25 + vertex 0.155677 0.35 2.16012 + endloop + endfacet + facet normal -0.0468686 0.707106 0.705552 + outer loop + vertex 1.78814e-006 0.25 2.25 + vertex 0.83404 0.25 2.3054 + vertex 0.155677 0.35 2.16012 + endloop + endfacet + facet normal -0.0468684 0.707107 0.705552 + outer loop + vertex 0.83404 0.25 2.3054 + vertex 0.889765 0.35 2.20888 + vertex 0.155677 0.35 2.16012 + endloop + endfacet + facet normal -0.587592 0.707107 0.393365 + outer loop + vertex 0.83404 0.25 2.3054 + vertex 1.29904 0.25 3 + vertex 1.29904 0.35 2.82024 + endloop + endfacet + facet normal -0.587591 0.707107 0.393365 + outer loop + vertex 0.83404 0.25 2.3054 + vertex 1.29904 0.35 2.82024 + vertex 0.889765 0.35 2.20888 + endloop + endfacet + facet normal 0.587591 0.707107 0.393365 + outer loop + vertex 1.76404 0.25 2.3054 + vertex 1.29904 0.35 2.82024 + vertex 1.29904 0.25 3 + endloop + endfacet + facet normal 0.587591 0.707107 0.393365 + outer loop + vertex 1.76404 0.25 2.3054 + vertex 1.70831 0.35 2.20888 + vertex 1.29904 0.35 2.82024 + endloop + endfacet + facet normal 0.0468686 0.707106 0.705552 + outer loop + vertex 2.59808 0.25 2.25 + vertex 2.4424 0.35 2.16012 + vertex 1.76404 0.25 2.3054 + endloop + endfacet + facet normal 0.0468684 0.707107 0.705552 + outer loop + vertex 1.76404 0.25 2.3054 + vertex 2.4424 0.35 2.16012 + vertex 1.70831 0.35 2.20888 + endloop + endfacet + facet normal 0.63446 0.707107 -0.312187 + outer loop + vertex 2.22904 0.25 1.5 + vertex 2.4424 0.35 2.16012 + vertex 2.59808 0.25 2.25 + endloop + endfacet + facet normal 0.63446 0.707107 -0.312187 + outer loop + vertex 2.22904 0.25 1.5 + vertex 2.11759 0.35 1.5 + vertex 2.4424 0.35 2.16012 + endloop + endfacet + facet normal 0.63446 0.707106 0.312187 + outer loop + vertex 2.59808 0.25 0.75 + vertex 2.4424 0.35 0.839879 + vertex 2.22904 0.25 1.5 + endloop + endfacet + facet normal 0.63446 0.707107 0.312187 + outer loop + vertex 2.22904 0.25 1.5 + vertex 2.4424 0.35 0.839879 + vertex 2.11759 0.35 1.5 + endloop + endfacet + facet normal 0.0468685 0.707107 -0.705552 + outer loop + vertex 1.76404 0.25 0.694596 + vertex 2.4424 0.35 0.839879 + vertex 2.59808 0.25 0.75 + endloop + endfacet + facet normal 0.0468684 0.707107 -0.705552 + outer loop + vertex 1.76404 0.25 0.694596 + vertex 1.70831 0.35 0.791115 + vertex 2.4424 0.35 0.839879 + endloop + endfacet + facet normal 0.587591 0.707107 -0.393365 + outer loop + vertex 1.76404 0.25 0.694596 + vertex 1.29904 0.35 0.179758 + vertex 1.70831 0.35 0.791115 + endloop + endfacet + facet normal 0.587592 0.707107 -0.393365 + outer loop + vertex 1.29904 0.25 0 + vertex 1.29904 0.35 0.179758 + vertex 1.76404 0.25 0.694596 + endloop + endfacet + facet normal -0.587592 0.707107 -0.393365 + outer loop + vertex 0.83404 0.25 0.694596 + vertex 0.889765 0.35 0.791115 + vertex 1.29904 0.35 0.179758 + endloop + endfacet + facet normal -0.587592 0.707107 -0.393365 + outer loop + vertex 1.29904 0.25 0 + vertex 0.83404 0.25 0.694596 + vertex 1.29904 0.35 0.179758 + endloop + endfacet + facet normal -0.0468684 0.707107 -0.705552 + outer loop + vertex 0.83404 0.25 0.694596 + vertex 0.155677 0.35 0.839879 + vertex 0.889765 0.35 0.791115 + endloop + endfacet + facet normal -0.0468685 0.707107 -0.705552 + outer loop + vertex 0.83404 0.25 0.694596 + vertex 1.78814e-006 0.25 0.75 + vertex 0.155677 0.35 0.839879 + endloop + endfacet +endsolid Star \ No newline at end of file diff --git a/sly_examples/stl_parser.py b/sly_examples/stl_parser.py new file mode 100644 index 0000000..e84c6e3 --- /dev/null +++ b/sly_examples/stl_parser.py @@ -0,0 +1,115 @@ +import pprint +from dataclasses import dataclass +from typing import List, Tuple + +from sly import Lexer, Parser + + +@dataclass +class Vertex: + coords: Tuple[float, float, float] + + def __repr__(self): + return "VERTEX(%f,%f,%f)" % self.coords + + +@dataclass +class Facet: + vertices: Tuple[Vertex, Vertex, Vertex] + normal: Vertex + + def __repr__(self): + return "\t FACET({normal})\n\t\t{vertices}".format( + normal=repr(self.normal), + vertices="\n\t\t".join(repr(vertex) for vertex in self.vertices) + ) + + +@dataclass +class Solid: + name: str + facets: List[Facet] + + def __repr__(self): + return "SOLID({name})\n{facets}\n".format( + name=self.name, + facets="\n".join(repr(facet) for facet in self.facets) + ) + + +class STLLexer(Lexer): + tokens = {"SOLID_START", "SOLID_END", + "FACET_START", "FACET_END", "NAME_LITERAL", + "LOOP_START", "LOOP_END", + "NAME_LITERAL", "VERTEX_TAG", "FLOAT"} + + @_(r"[ \t\n]+") + def ignore_whitespace(self, t): + self.lineno += t.value.count("\n") + + SOLID_START = r"solid" + SOLID_END = r"endsolid" + FACET_START = r"facet normal" + FACET_END = r"endfacet" + LOOP_START = r"outer loop" + LOOP_END = r"endloop" + VERTEX_TAG = r"vertex" + + @_("[+-]?\d+(\.\d*(e[+-]?\d+)?)?") + def FLOAT(self, t): + t.value = float(t.value) + return t + + NAME_LITERAL = r"\w+" + + +class STLParser(Parser): + tokens = STLLexer.tokens + start = "stl" + + @_("solid") + def stl(self, p): + return p.solid + + @_("SOLID_START NAME_LITERAL facets_list SOLID_END") + @_("SOLID_START NAME_LITERAL facets_list SOLID_END NAME_LITERAL") + def solid(self, p): + return Solid(name=p[1], facets=p.facets_list) + + @_("facet") + def facets_list(self, p): + return [p.facet] + + @_("facet facets_list") + def facets_list(self, p): + return [p.facet] + p.facets_list + + @_("FACET_START triplet loop FACET_END") + def facet(self, p): + return Facet(vertices=p.loop, normal=Vertex(p.triplet)) + + @_("LOOP_START vertex vertex vertex LOOP_END") + def loop(self, p): + return p[1], p[2], p[3] + + @_("VERTEX_TAG triplet") + def vertex(self, p): + return Vertex(p.triplet) + + @_("FLOAT FLOAT FLOAT") + def triplet(self, p): + return p[0], p[1], p[2] + + def error(self, token): + pass + + +if __name__ == "__main__": + lexer = STLLexer() + parser = STLParser() + + with open("star.stl") as stl_file: + # pprint.pprint(list(lexer.tokenize(stl_file.read()))) + solid = parser.parse(lexer.tokenize(stl_file.read())) + + print(solid) \ No newline at end of file