From 30a635dc181baad78f9c9a0860f03a0e29e26233 Mon Sep 17 00:00:00 2001 From: Automated Build Date: Wed, 10 Jan 2024 03:51:30 +0000 Subject: [PATCH] New build Wed Jan 10 03:51:30 UTC 2024 --- docs/2020/01/24/php-generator.html | 250 + docs/2020/02/12/crafting-interpreters.html | 64 + docs/2020/02/18/sudoku-solver.html | 207 + docs/2020/02/21/grotsky-part1.html | 244 + docs/2020/03/15/grotsky-part2.html | 219 + docs/2020/04/01/grotsky-part3.html | 321 ++ docs/2020/04/11/custom-malloc.html | 250 + docs/2020/04/19/mysql-python.html | 284 ++ docs/2020/12/17/grotsky-getmyip.html | 233 + docs/2021/04/01/mlisp-wasm.html | 187 + docs/2021/10/04/new-blog-engine.html | 118 + docs/2021/12/31/playing-with-js.html | 355 ++ docs/2022/05/05/rust-blake-mysql.html | 58 + docs/2022/07/20/cloud-outdated-release.html | 137 + docs/2022/09/20/branchable-mysql.html | 214 + docs/2022/09/22/heroku-to-fly.html | 116 + docs/2022/11/26/grotsky-quine.html | 103 + docs/2023/04/08/redis-clone.html | 601 +++ docs/2023/06/02/rewrite-grotsky-rust.html | 75 + .../2023/07/15/the-end-of-a-side-project.html | 97 + docs/2023/09/23/grotsky-rust-part2.html | 75 + docs/2023/11/23/grotsky-rust-part3.html | 187 + .../12/25/favourite-advent-of-code-2023.html | 412 ++ docs/2024/01/04/new-markdown-generator.html | 97 + docs/assets/css/style.css | 114 + .../images/branchable-mysql/diagram1.png | Bin 0 -> 27143 bytes .../images/branchable-mysql/diagram2.png | Bin 0 -> 35630 bytes docs/assets/images/favicon.ico | Bin 0 -> 15406 bytes docs/assets/images/grotsky-part2/AST.png | Bin 0 -> 6603 bytes docs/assets/images/kandinsky.jpeg | Bin 0 -> 58912 bytes docs/assets/images/nyc.jpg | Bin 0 -> 268172 bytes docs/assets/images/socials/github.png | Bin 0 -> 1041 bytes docs/assets/images/socials/linkedin.png | Bin 0 -> 445 bytes docs/assets/images/socials/twitter.png | Bin 0 -> 596 bytes docs/assets/images/socials/x.ico | Bin 0 -> 549 bytes docs/assets/mlisp/mlisp.js | 1 + docs/assets/mlisp/mlisp.wasm | Bin 0 -> 83808 bytes docs/feed.xml | 4460 +++++++++++++++++ docs/googlee7507b09f10f12d1.html | 1 + docs/index.html | 45 + 40 files changed, 9525 insertions(+) create mode 100644 docs/2020/01/24/php-generator.html create mode 100644 docs/2020/02/12/crafting-interpreters.html create mode 100644 docs/2020/02/18/sudoku-solver.html create mode 100644 docs/2020/02/21/grotsky-part1.html create mode 100644 docs/2020/03/15/grotsky-part2.html create mode 100644 docs/2020/04/01/grotsky-part3.html create mode 100644 docs/2020/04/11/custom-malloc.html create mode 100644 docs/2020/04/19/mysql-python.html create mode 100644 docs/2020/12/17/grotsky-getmyip.html create mode 100644 docs/2021/04/01/mlisp-wasm.html create mode 100644 docs/2021/10/04/new-blog-engine.html create mode 100644 docs/2021/12/31/playing-with-js.html create mode 100644 docs/2022/05/05/rust-blake-mysql.html create mode 100644 docs/2022/07/20/cloud-outdated-release.html create mode 100644 docs/2022/09/20/branchable-mysql.html create mode 100644 docs/2022/09/22/heroku-to-fly.html create mode 100644 docs/2022/11/26/grotsky-quine.html create mode 100644 docs/2023/04/08/redis-clone.html create mode 100644 docs/2023/06/02/rewrite-grotsky-rust.html create mode 100644 docs/2023/07/15/the-end-of-a-side-project.html create mode 100644 docs/2023/09/23/grotsky-rust-part2.html create mode 100644 docs/2023/11/23/grotsky-rust-part3.html create mode 100644 docs/2023/12/25/favourite-advent-of-code-2023.html create mode 100644 docs/2024/01/04/new-markdown-generator.html create mode 100644 docs/assets/css/style.css create mode 100644 docs/assets/images/branchable-mysql/diagram1.png create mode 100644 docs/assets/images/branchable-mysql/diagram2.png create mode 100644 docs/assets/images/favicon.ico create mode 100644 docs/assets/images/grotsky-part2/AST.png create mode 100644 docs/assets/images/kandinsky.jpeg create mode 100644 docs/assets/images/nyc.jpg create mode 100644 docs/assets/images/socials/github.png create mode 100644 docs/assets/images/socials/linkedin.png create mode 100644 docs/assets/images/socials/twitter.png create mode 100644 docs/assets/images/socials/x.ico create mode 100644 docs/assets/mlisp/mlisp.js create mode 100644 docs/assets/mlisp/mlisp.wasm create mode 100644 docs/feed.xml create mode 100644 docs/googlee7507b09f10f12d1.html create mode 100644 docs/index.html diff --git a/docs/2020/01/24/php-generator.html b/docs/2020/01/24/php-generator.html new file mode 100644 index 0000000..f082947 --- /dev/null +++ b/docs/2020/01/24/php-generator.html @@ -0,0 +1,250 @@ +Reinventing the Wheel: PHP Generators +

Blog

Reading time: +

+ +

Reinventing the Wheel: PHP Generators +

+ +

First thing first. How a generator works? +

+ +

Starting back at C +

+Let's create a function that each time we call it we get the next number of the fibonacci sequence. + +
+int fibonacci()
+{
+    static int a = 0;
+    static int b = 1;
+    int aux = b;
+    b = a + b;
+    a = aux;
+    return a;
+}
+
+ +If we call fibonacci(), the first time we'll get 1, the second time 1, the third 2, the fourth 3, and so on... + +This happens because we declared variables a, b to be static. This means that they mantain the value after the function returns. Normally, what happens (if we don't declare a variable as static) is that the variables inside the function don't mantain the values of the last execution. + + +

First generator for PHP +

+The equivalent function in PHP is pretty similar to C's approach. + +
+
+
+function fibonacci()
+{
+    static $a = 0;
+    static $b = 1;
+    $aux = $b;
+    $b = $a + $b;
+    $a = $aux;
+    return $a;
+}
+
+$out = [];
+
+for ($i = 1; $i <= 10; $i++) {
+    $out[] = fibonacci();
+}
+
+echo implode(', ', $out) . "\n";
+
+/*
+Output: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
+*/
+
+ +Let's compare this to the real PHP version using yield. + +
+
+
+function fibonacci($N)
+{
+    $a = 0;
+    $b = 1;
+    for ($i = 0; $i < $N; $i++) {
+        $aux = $b;
+        $b = $a + $b;
+        $a = $aux;
+        yield $a;
+    }
+}
+
+$out = [];
+
+foreach (fibonacci(10) as $fib) {
+    $out[] = $fib;
+}
+
+echo implode(', ', $out) . "\n";
+
+/*
+Output: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
+*/
+
+ + +

Creating a custom version of PHP yield +

+This is my own version using the library parallel and channels (probably uses yield internally). + +
+
+
+class MyGenerator implements Iterator
+{
+    private $chan;
+    private $current;
+    private $iteratorFn;
+    private $runtime;
+    private $key = -1;
+    private $valid = true;
+
+    public function __construct($iteratorFn)
+    {
+        $this->iteratorFn = $iteratorFn;
+        $this->runtime = new \parallel\Runtime();
+        $channel = new \parallel\Channel();
+
+        $this->runtime->run(function() use ($iteratorFn, $channel) {
+            $iteratorFn(function ($val) use ($channel) {
+                $channel->send($val);
+            });
+            $channel->close();
+        });
+
+        $this->chan = $channel;
+        $this->next();
+    }
+
+    public function current()
+    {
+        return $this->current;
+    }
+
+    public function next()
+    {
+        try {
+            ++$this->key;
+            $val = $this->chan->recv();
+            $this->current = $val;
+        } catch (\parallel\Channel\Error\Closed $e) {
+            $this->valid = false;
+        }
+        return $this->current;
+    }
+
+    public function key() {return $this->key;}
+    public function valid() {return $this->valid;}
+    public function rewind() {}
+}
+
+
+function fibonacci($N)
+{
+    return new MyGenerator(function ($yield) use ($N) {
+        $a = 0;
+        $b = 1;
+        for ($i = 0; $i < $N; $i++) {
+            $aux = $b;
+            $b = $a + $b;
+            $a = $aux;
+            $yield($a);
+        }
+    });
+}
+
+$out = [];
+
+foreach (fibonacci(10) as $fib) {
+    $out[] = $fib;
+}
+
+echo implode(', ', $out) . "\n";
+
+ + +

Performance comparison: PHP vs Custom +

+ +
Tested code +
+
+for ($i = 0; $i < 1000; ++$i) {
+    foreach (fibonacci(100) as $fib) {
+        $out[] = $fib;
+    }
+}
+
+ + +
yield version +
+
+real    0m0,083s
+user    0m0,059s
+sys     0m0,023s
+
+ + +
MyGenerator version +
+
+real    0m2,138s
+user    0m1,426s
+sys     0m1,363s
+
+ +So, it's aproximately 26 times slower :-) + +
\ No newline at end of file diff --git a/docs/2020/02/12/crafting-interpreters.html b/docs/2020/02/12/crafting-interpreters.html new file mode 100644 index 0000000..7c9a068 --- /dev/null +++ b/docs/2020/02/12/crafting-interpreters.html @@ -0,0 +1,64 @@ +Crafting interpreters +

Blog

Reading time: +

+ +

Crafting interpreters +

+I've just finished the section 2 of the book _Crafting Interpreters_, and I wanted to upload it to github right away. + +Take a look at the source code. + +Beside the lox specification I've added: + +
  • The keyword until that is a variation of while loops (as in ruby). also +
  • print is a function instead of a statement. +
  • eval function that let's you eval source code in runtime. +
  • Class methods. + +I'll implement another language interpreter, this time using golang and with a syntax similar to ruby. + +
  • \ No newline at end of file diff --git a/docs/2020/02/18/sudoku-solver.html b/docs/2020/02/18/sudoku-solver.html new file mode 100644 index 0000000..cf4944f --- /dev/null +++ b/docs/2020/02/18/sudoku-solver.html @@ -0,0 +1,207 @@ +Sudoku Solver +

    Blog

    Reading time: +

    + +

    Sudoku Solver +

    +I wanted to make my own sudoku solver to challenge myself. + +Im not a sudoku player so my approach is a brute force scan of possible combinations sort-of. + +I just know the basic rules: + +
  • Numbers 1-9 are allowed. +
  • Numbers in the same row cannot be repeated. +
  • Numbers in the same column cannot be repeated. +
  • Numbers in the 3x3 square cannot be repeated. + +The first thing i did was to build a some classes that calculates the possible values a cell can have if it's empty, based on the constraints. + +I came up with 3 classes: + +
  • Board that stores the entire board. +
  • BoardSlice that stores a slice of a board. An object of this type is returned when a Board is sliced (method __getitem__). +
  • Cell that stores the value of a single cell and calculates all possible values a cell can take. + +The class Cell receives a board, the coordinates on the board, and the value that holds. Also has the method options that uses python set data structure to calculate the posibilites. + +If you look at the following snippet you can see that the method options +generates the sets: options that contains all possible options (1-9), row that contains all the numbers that are in the same row, column that contains all the numbers that are in the same column and square that contains all the numbers that are in the same 3x3 square. The return value is options without all the used values. + +
    +class Cell:
    +    def __init__(self, b, i, j, value):
    +        self.b = b
    +        self.value = value
    +        self.i = i
    +        self.j = j
    +
    +    def options(self):
    +        if self.value != 0:
    +            return {self.value}
    +        options = set(range(1, 10))
    +        row = set(map(lambda x: x.value, self.b[self.i]))
    +        column = set(map(lambda x: x.value, self.b[:][self.j]))
    +        def to_square(k): return slice((k // 3) * 3, (k // 3) * 3 + 3)
    +        square = set(
    +            map(lambda x: x.value,
    +                self.b[to_square(self.i)][to_square(self.j)]))
    +        return options - row - column - square - {0}
    +
    + +To make easier the implementation of the square I used the class BoardSlice that contains a slice of a board and implements the magic method __getitem__. + +
    +class BoardSlice:
    +    def __init__(self, board_slice):
    +        self.board_slice = board_slice
    +
    +    def __getitem__(self, items):
    +        if type(items) == slice:
    +            return (el for row in self.board_slice for el in row[items])
    +        if type(items) == int:
    +            return (row[items] for row in self.board_slice)
    +        raise KeyError
    +
    + +The base class: Board contains the board and a copy method that copies all the values and creates a new Board object. This is necessary to avoid messing with object references and have a clean object when needed. + +
    +class Board:
    +    def __init__(self, board):
    +        self.board = [[Cell(self, i, j, value)
    +                       for (j, value) in enumerate(row)] for (i, row) in enumerate(board)]
    +
    +    def copy(self):
    +        return Board(((cell.value for cell in row) for row in self.board))
    +
    +    def __getitem__(self, items):
    +        if type(items) == int:
    +            return self.board[items]
    +        if type(items) == slice:
    +            return BoardSlice(self.board[items])
    +        raise KeyError
    +
    +    def __repr__(self):
    +        return repr(self.board)
    +
    + +With these tools the next step is to solve the problem! + +My idea was to generate a mixed iterative-recursive algorithm. + +The first pass will be iterative, and if needed, the second pass will be recursive. + + +
  • Iterative pass +
    +Iterates over the whole board and calculates the options that each cell can have. If a cell has only one option set that option on the cell and set a flag to repeat the iterative pass, if has 0 options return None meaning that the board has no solutions, and if has more than one option store the options for the recursive pass. + +If the loop ends and we found that no cell has more than one option then we solved the board! + +The idea of this first step is to solve an _easy_ board quickly. + + +
    Recursive pass +
    +If the iterative pass ends and we found that a cell has more than one option then we try all that options and call solve again! + +If solve returns a board that means we've found the solution! + +If solve returns None (back at the iterative passs) we have to try with another options. + + +
    BoardSolver +
    +The class is pretty straightforward. + +
    +class SudokuSolver:
    +    @staticmethod
    +    def solve(board):
    +        b = board.copy()
    +        # First pass: Iterative
    +        board_map = {}
    +        exhaust = False
    +        while not exhaust:
    +            exhaust = True
    +            for i in range(9):
    +                for j in range(9):
    +                    cell = b[i][j]
    +                    if cell.value == 0:
    +                        options = cell.options()
    +                        if len(options) == 1:
    +                            cell.value = options.pop()
    +                            exhaust = False
    +                        elif len(options) == 0:
    +                            return None
    +                        elif len(board_map) == 0:
    +                            board_map[(i, j)] = options
    +
    +        # Second pass: Recursive
    +        for ((i, j), options) in board_map.items():
    +            for op in options:
    +                b[i][j].value = op
    +                solved = SudokuSolver.solve(b)
    +                if solved:
    +                    return solved
    +            return None
    +
    +        return b
    +
    + + +
    Conclusions +
    +Actually my implementation is not a brute force algorithm, is a search algorithm, that searches the path to solving a board. Because it doesn't try all values on all cells nonsensically, it rather tries _some_ options for a given cell and advances to the next option as _soon_ as it detects that it's not the correct path. + + +

    Source +

    +Take a look at the source code. + +
    \ No newline at end of file diff --git a/docs/2020/02/21/grotsky-part1.html b/docs/2020/02/21/grotsky-part1.html new file mode 100644 index 0000000..c556c8d --- /dev/null +++ b/docs/2020/02/21/grotsky-part1.html @@ -0,0 +1,244 @@ +Grotsky Part 1: Syntax +

    Blog

    Reading time: +

    + +

    Grotsky Part 1: Syntax +

    + +

    Syntax Restrictions +

    +
  • No use of semicolon ; +
  • Block statements delimited by begin and end +
  • Function definition using fn keyword +
  • Logic operators in plain english or, and, not +
  • Conditional statements use the following keywords: if, elif, else +
  • There is no switch statement +
  • Class definition with class keyword +
  • Arithmetic operations: *, /, -, +, ^ +
  • Grouping with parentheses () +
  • Native support for python-like lists and dictionaries: [], {} +
  • Support for enhanced for loop: for i, el in array +
  • Keywords and identifiers can only use alphabethic characters + + +
  • Primitives +

    +
  • nil +
  • Integers +
  • Floats +
  • Booleans +
  • Strings +
  • Lists +
  • Dictionaries + + +
  • Example of functions and operations +

    +
    +## Arithmethic
    +print(2^10 - 2323*3)
    +# Output: -5945
    +print(2^(12*3+400/-4+10*5/2))
    +# Output: 1.8189894035458565e-12
    +
    +## Logic
    +print(true or false)
    +# Output: true (short circuit)
    +print(false and true)
    +# Output: false (short circuit)
    +
    +## Conditionals
    +if 3 > 2 or (1 < 3 and 2 == 2) begin
    +    print('Condition is true')
    +end
    +elif 3 == 4 begin
    +    print('Condition 2 is true')
    +end
    +else begin
    +    print('Conditions are false')
    +end
    +
    +## Lists
    +for i in [1, 2, 3, 4] begin
    +    print(i)
    +end
    +
    +let lst = [1, 2, 3, 4]
    +lst[0] = -1
    +print(lst) # Output: [-1, 2, 3, 4]
    +print(lst[1:3]) # Output: [2, 3]
    +
    +## Dictionaries
    +# (dictionaries and lists not allowed as keys)
    +let dct = {
    +    "Key1": "Val1",
    +    2: "Val2",
    +    true: false
    +}
    +for key, val in dct begin
    +    print(key, val)
    +end
    +
    +## Functions
    +fn square(x)
    +begin
    +    return x^2
    +end
    +
    +fn operate(x, operation)
    +begin
    +    return operation(x)
    +end
    +
    +## Clojure
    +fn makeCounter()
    +begin
    +    let n = 0
    +    return fn() begin
    +        n = n+1
    +        return n
    +    end
    +end
    +
    +## Classes
    +class Counter
    +begin
    +    init(start) begin
    +        self.start = start
    +    end
    +    count() begin
    +        self.start = self.start+1
    +        return self.start
    +    end
    +end
    +
    +class CounterTwo
    +begin
    +    count() begin
    +        return super.count()*2
    +    end
    +end
    +
    + + +

    Syntax definition +

    +Let's build a syntax definition in backus naur format that will be easy to parse with a recursive descent parser. + + +
    Expresions +
    +
    +expression       assignment;
    +list             "[" arguments? "]";
    +dictionary       "{" dict_elements? "}";
    +dict_elements    keyval ("," keyval)*;
    +keyval           expression ":" expression;
    +assignment       (call ".")? IDENTIFIER "=" assignment | access;
    +access           logic_or ("[" slice "]")*;
    +logic_or         logic_and ("or" logic_and)*;
    +logic_and        equality ("and" equality)*;
    +equality         comparison (("!=" | "==") comparison)*;
    +comparison       addition ((">" | ">=" | "<" | "<=") addition)*;
    +addition         multiplication (("-" | "+") multiplication)*;
    +multiplication   power (("/" | "*") power)*;
    +power            unary ("^" unary)*;
    +unary            ("not" | "-") unary | call;
    +call             primary ("(" arguments? ")" | "." IDENTIFIER)*;
    +arguments        expression ("," expression)*;
    +slice            (":" expression)
    +                | (":" expression ":" expression)
    +                | (":" ":" expression)
    +                | expression
    +                | (expression ":")
    +                | (expression ":" expression)
    +                | (expression ":" ":" expression)
    +                | (expression ":" expression ":" expression);
    +primary          NUMBER
    +                | STRING
    +                | "false"
    +                | "true"
    +                | "nil"
    +                | IDENTIFIER
    +                | "(" expression ")"
    +                | fnAnon
    +                | list
    +                | dictionary;
    +fnAnon           "fn" "(" parameters? ")" block;
    +
    + + +
    Statements +
    +
    +program         declaration* EOF;
    +declaration     classDecl | funDecl | varDecl | statement;
    +classDecl       "class" IDENTIFIER ( "<" IDENTIFIER )? "begin" methodDecl* "end" NEWLINE;
    +methodDecl      "class"? function;
    +funDecl         "fn" function ;
    +function        IDENTIFIER "(" parameters? ")" block ;
    +parameters      IDENTIFIER ( "," IDENTIFIER )* ;
    +varDecl         "let" IDENTIFIER ("=" expression)? NEWLINE;
    +statement       forStmt
    +                | ifStmt
    +                | returnStmt
    +                | whileStmt
    +                | exprStmt
    +                | block;
    +exprStmt        expression NEWLINE;
    +forStmt         "for"  (classicFor | newFor) statement;
    +classicFor      (varDecl | exprStmt | ",") expression? "," expression?;
    +newFor          IDENTIFIER ("," IDENTIFIER)? "in" expression;
    +ifStmt          "if" expression statement ("elif" expression statement)* ("else" statement)?;
    +returnStmt      "return" expression? NEWLINE;
    +whileStmt       "while" expression statement;
    +block           "begin" NEWLINE declaration* "end" NEWLINE;
    +
    + +That's it! The next step is to build a lexer and a parser. + +
    \ No newline at end of file diff --git a/docs/2020/03/15/grotsky-part2.html b/docs/2020/03/15/grotsky-part2.html new file mode 100644 index 0000000..931ee15 --- /dev/null +++ b/docs/2020/03/15/grotsky-part2.html @@ -0,0 +1,219 @@ +Grotsky Part 2: Parsing expressions +

    Blog

    Reading time: +

    + +

    Grotsky Part 2: Parsing expressions +

    + +

    Expressions +

    +Parsing an expression like 1+2*3 requires a complex representation on memory. Just looking at it we think that it's pretty simple, but there is some hidden hierarchy that we have to pay attention to, like the fact that first we have to compute 2*3 and then add 1 to it. + +To represent that in a data structure the best thing we can come up to is a tree, as seen in the next figure: + +![image](/assets/images/grotsky-part2/AST.png) + +As you can see the leaves of the tree are literals and the root and intermediate nodes are operations that have to be applied from the bottom up. That means that we traverse the tree until we reach the bottom and start computing the results by going up. + + +

    Defining node types +

    +> Not all operations are created equal. + +We have to define how each node fits into the tree. + +I'll use the following syntax: Binary -> left expr, operator token, right expr. Which means that a binary operation (as we have seen in the image before) links to 2 expressions (left and right) and stores 1 value (operator). + + +
    Let's define all posible operations on literals +
    +
    +Literal -> value object
    +# 1, "asd", 5.2, true, false
    +
    +Binary -> left expr, operator token, right expr
    +# 1+2, 3*3, 4^2+1
    +
    +Grouping -> expression expr
    +# (1+2)
    +
    +Logical -> left expr, operator token, right expr
    +# true or false, false and true
    +
    +Unary: operator token, right expr
    +# not true, -5
    +
    +List -> elements []expr
    +# [1, 2, 3, [4], "asd"]
    +
    +Dictionary -> elements []expr
    +# {"a": 1, "b": 2, 3: 4}
    +
    +Access -> object expr, slice expr
    +# [1, 2, 3][0], {"a":1}["a"]
    +
    +Slice -> first expr, second expr, third expr
    +# [1, 2, 3, 4, 5, 6][1:4:2]
    +
    + + +

    Traversing the abstract syntax tree +

    +To traverse the syntax tree we need a pattern that's uniform and easily scalable when we have to add other types of expressions and statements. + +For that we'll use the Visitor Pattern. + + +

    Visitor Pattern +

    +First we need an interface for the expression that allows a visitor to visit it. + +
    +type expr interface {
    +    accept(exprVisitor) interface{}
    +}
    +
    + +An expression visitor should have a method for each kind of expression it has to visit. + +
    +type exprVisitor interface {
    +    visitLiteralExpr(expr expr) interface{}
    +    visitBinaryExpr(expr expr) interface{}
    +    visitGroupingExpr(expr expr) interface{}
    +    visitLogicalExpr(expr expr) interface{}
    +    visitUnaryExpr(expr expr) interface{}
    +    visitListExpr(expr expr) interface{}
    +    visitDictionaryExpr(expr expr) interface{}
    +    visitAccessExpr(expr expr) interface{}
    +    visitSliceExpr(expr expr) interface{}
    +}
    +
    + +Then we have to define a type for each kind of expression that implements expr interface. For example, this is the implementation for a binary expression: + +
    +type binaryExpr struct {
    +    left expr
    +    operator *token
    +    right expr
    +}
    +
    +func (s *binaryExpr) accept(visitor exprVisitor) R {
    +    return visitor.visitBinaryExpr(s)
    +}
    +
    + +For all other expressions the definition is practically the same. + + +

    String Visitor +

    +To finish this chapter, let's define a visitor that allows you to print the syntax tree in a lisp-like syntax, ex: (+ 1 2). + +Here is the implementation of the string visitor for a binary expression: + +
    +type stringVisitor struct{}
    +
    +func (v stringVisitor) visitBinaryExpr(expr expr) R {
    +    binary := expr.(*binaryExpr)
    +    return fmt.Sprintf("(%s %v %v)", binary.operator.lexeme, binary.left.accept(v), binary.right.accept(v))
    +}
    +
    + + +

    Grotsky expression +

    +You can check out the state of the Grotsky project right here: https://github.com/mliezun/grotsky. + +Grotsky it's able to parse and print all types of expressions defined in this article right now. + + +

    Expressions +

    +Examples of operations supported: + +
    +# Math operations
    +1+2*3^2-(4+123)/2.6
    +=> (- (+ 1 (* 2 (^ 3 2))) (/ (+ 4 123) 2.6))
    +
    +# Logical operations
    +true or false
    +=> (or true false)
    +
    +# Comparisons
    +1 == 1 and (1 > 3 or 11/5.5 <= 3+2^2 and 1 != 2)
    +=> (and (== 1 1) (or (> 1 3) (and (<= (/ 11 5.5) (+ 3 (^ 2 2))) (!= 1 2))))
    +
    +# Lists
    +[1, 2, [3], "asd"]
    +=> (list 1 2 (list 3) "asd")
    +
    +# List slicing
    +[1,2,3,4][1:3][::2][0]
    +=> (#0 (#::2 (#1:3 (list 1 2 3 4))))
    +
    +# Dictionary
    +{
    +    1: 2,
    +    3: 4,
    +    "asd": 3.14
    +}
    +=> (dict 1=>2 3=>4 "asd"=>3.14)
    +
    +# Dictionary key lookup
    +{"key":0.6}["key"]
    +=> (#"key" (dict "key"=>0.6))
    +
    + +That's it for now. In the next chapter we'll traverse the tree but instead of printing we'll execute the operations listed before. + +If you have questions or suggestions please get in touch. + +
    \ No newline at end of file diff --git a/docs/2020/04/01/grotsky-part3.html b/docs/2020/04/01/grotsky-part3.html new file mode 100644 index 0000000..fa7d182 --- /dev/null +++ b/docs/2020/04/01/grotsky-part3.html @@ -0,0 +1,321 @@ +Grotsky Part 3: Interpreting +

    Blog

    Reading time: +

    + +

    Grotsky Part 3: Interpreting +

    + +

    It's slow! +

    +My interpreter it's really, really, wait for it... _Really slow_. + +An example of a bad performing grotsky code: + +
    +# fib: calculates the n-th fibonacci number recursively
    +fn fib(n) begin
    +    if n < 2 return n
    +    return fib(n-2) + fib(n-1)
    +end
    +println(fib(30))
    +
    + + +
    Running the code +
    +
    +$ time ./grotsky examples/fib.g
    +
    + +Gives a wooping result of: + +
    +832040
    +
    +real    0m11,154s
    +user    0m11,806s
    +sys     0m0,272s
    +
    + +Almost twelve seconds!!! + +Comparing with a similar python code + +
    +def fib(n):
    +    if n < 2: return n
    +    return fib(n-2) + fib(n-1)
    +print(fib(30))
    +
    + +Gives a result of: + +
    +832040
    +
    +real    0m0,423s
    +user    0m0,387s
    +sys     0m0,021s
    +
    + +That means, my interpreter is at least 20 times slower than Cpython. + + +
    Why is it so slow? +
    +Here is an explanation. + +As the person from the first comment states, go garbage collector is not well suited for this kind of scenario with heavy allocation of objects. + +> Go's GC is not generational, so allocation requires (comparatively speaking) much more work. It's also tuned for low latency (smallest pause when GC has to stop the program) at the expense of throughput (i.e. total speed). This is the right trade-off for most programs but doesn't perform optimally on micro-benchmarks that measure throughtput. + +Setting the gc percent at 800 (100 by default) more than halves the time that the function takes to compute: + +
    +$ time GOGC=800 ./grotsky examples/fib.g
    +832040
    +
    +real    0m5,110s
    +user    0m5,182s
    +sys     0m0,061s
    +
    + + +

    Interpreting functions +

    +Callable interface + +
    +type callable interface {
    +	arity() int
    +	call(exec *exec, arguments []interface{}) interface{}
    +}
    +
    + +_All grotsky functions must be an object that implements the callable interface._ + +For that I defined two kind of structs: + +
    +type function struct {
    +	declaration   *fnStmt
    +	closure       *env
    +	isInitializer bool
    +}
    +
    +type nativeFn struct {
    +	arityValue int
    +	callFn  func(exec *exec, arguments []interface{}) interface{}
    +}
    +
    + + +
    nativeFn +
    +Let's you define standard functions available on all grotsky interpreters. Line println. + +
    +func (n *nativeFn) arity() int {
    +	return n.arityValue
    +}
    +
    +func (n *nativeFn) call(exec *exec, arguments []interface{}) interface{} {
    +	return n.callFn(exec, arguments)
    +}
    +
    + +From that, println would be pretty straight forward: + +
    +...
    +
    +var println nativeFn
    +println.arityValue = 1
    +println.callFn = func(exec *exec, arguments []interface{}) interface{} {
    +    fmt.Println(arguments[0])
    +    return nil
    +}
    +...
    +
    + + +
    Ordinary grotsky functions +
    +For ordinary grotsky functions the things are a little bit messier. + +First I got to introduce the environment that is an object that holds map[string]interface{} as a dictionary for variables in the local scope and a pointer to another environment that contains variables for the outer scope. + +
    +type env struct {
    +	state *state
    +
    +	enclosing *env
    +	values    map[string]interface{}
    +}
    +
    +func newEnv(state *state, enclosing *env) *env {
    +	return &env{
    +		state:     state,
    +		enclosing: enclosing,
    +		values:    make(map[string]interface{}),
    +	}
    +}
    +
    +func (e *env) get(name *token) interface{} {
    +	if value, ok := e.values[name.lexeme]; ok {
    +		return value
    +	}
    +	if e.enclosing != nil {
    +		return e.enclosing.get(name)
    +	}
    +	e.state.runtimeErr(errUndefinedVar, name)
    +	return nil
    +}
    +
    +func (e *env) define(name string, value interface{}) {
    +	e.values[name] = value
    +}
    +
    + +As you can see, the define method creates a variable on the local scope, and the get methods tries to retrieve a variable first from the local scope and then from the outer scope. + +Let's see how functions are implemented. + +
    +func (f *function) arity() int {
    +	return len(f.declaration.params)
    +}
    +
    +func (f *function) call(exec *exec, arguments []interface{}) (result interface{}) {
    +	env := newEnv(exec.state, f.closure)
    +	for i := range f.declaration.params {
    +		env.define(f.declaration.params[i].lexeme, arguments[i])
    +	}
    +
    +	defer func() {
    +		if r := recover(); r != nil {
    +			if returnVal, isReturn := r.(returnValue); isReturn {
    +				result = returnVal
    +			} else {
    +				panic(r)
    +			}
    +		}
    +	}()
    +
    +	exec.executeBlock(f.declaration.body, env)
    +
    +	return nil
    +}
    +
    + +Function arity is pretty simple. + +The function call takes an exec object, that is no more than an instance of the interpreter, and the arguments to the function as an array of objects. Then creates a new environment the is surrounded by the environment local to the function definition and defines all the function parameters. Then comes the tricky part, first there is a deferred call to an anonymous function, let's ignore that for a moment, in the end, the function executeBlock gets called. Let's see what that function does: + +
    +func (e *exec) executeBlock(stmts []stmt, env *env) {
    +	previous := e.env
    +	defer func() {
    +		e.env = previous
    +	}()
    +	e.env = env
    +	for _, s := range stmts {
    +		e.execute(s)
    +	}
    +}
    +
    + +What's happening here is that the interpreter steps into the new environment, saving the previous environment in a variable, and execute all given statements, after that it restores the environment to the previous one. Exactly as a function does. + + +
    What happens when you hit a return +
    +
    +type returnValue interface{}
    +
    +...
    +
    +func (e *exec) visitReturnStmt(stmt *returnStmt) R {
    +	if stmt.value != nil {
    +		panic(returnValue(stmt.value.accept(e)))
    +	}
    +	return nil
    +}
    +
    + +When you get to a return node in the ast, the nodes panics with a return value. This has to do with the fact that you need to go up the call stack and finish the execution of the function, otherwise the function will keep it's execution. + +That's the reason of the deferred function we forgot a couple seconds ago: + +
    +func (f *function) call(exec *exec, arguments []interface{}) (result interface{}) {
    +    ...
    +
    +    defer func() {
    +		if r := recover(); r != nil {
    +			if returnVal, isReturn := r.(returnValue); isReturn {
    +				result = returnVal
    +			} else {
    +				panic(r)
    +			}
    +		}
    +    }()
    +
    +    ...
    +}
    +
    + +This function recovers from a panic. If the value recovered is of type returnValue it recovers successfully and sets the result value of the function call to the return value, else it panics again. + + +

    Hasta la vista, baby +

    +That's it for now. There are a lot of nifty stuff to keep talking about. But I think it's enough for now. + +Remember to check out the source code. And stay tuned for more. + +
    \ No newline at end of file diff --git a/docs/2020/04/11/custom-malloc.html b/docs/2020/04/11/custom-malloc.html new file mode 100644 index 0000000..2ace0ea --- /dev/null +++ b/docs/2020/04/11/custom-malloc.html @@ -0,0 +1,250 @@ +Writing your own C malloc and free +

    Blog

    Reading time: +

    + +

    Writing your own C malloc and free +

    + +

    Challenge +

    +This challenge comes from the book Crafting Interpreters by Bob Nystrom. And can be found in Chapter 14 - Challenge 3. + +The challenge goes: + +> You are allowed to call malloc() once, at the beginning of the interpreters execution, to allocate a single big block of memory which your reallocate() function has access to. It parcels out blobs of memory from that single region, your own personal heap. Its your job to define how it does that. + + +

    Check out this +comparison of malloc() to S3.

    Solution +

    +As stated in the challenge I'll be using a big chunk of _contiguous_ memory. The main idea of my solution is to store the blocks of memory in the array prepending a header with metadata. + +
    + _______________________________________________
    +|head_0|block_0 ... |head_1|block_1    ...      |
    + 
    +
    + +The structure of the header is pretty similar to that of a linked list. + +
    +struct block_meta
    +{
    +    size_t size;
    +    struct block_meta *next;
    +    int free;
    +};
    +
    +#define META_SIZE sizeof(struct block_meta)
    +
    + +It stores the size of the block, a pointer to the next block and a flag to mark wether it's free or not. + +Then, a function to traverse the list of blocks and find if there is any freed block is needed: + +
    +void *first_block = NULL;
    +
    +struct block_meta *find_free_block(struct block_meta **last, size_t size)
    +{
    +    struct block_meta *current = first_block;
    +    while (current && !(current->free && current->size >= size))
    +    {
    +        *last = current;
    +        current = current->next;
    +    }
    +    return current;
    +}
    +
    + +This function receives a double pointer to a block_meta struct called last that at the end of the execution should be pointing to the last node of the list and a size_t variable that indicates the minimum size that the block needs to be. + + +
    Memory initialization +
    +Two functions are needed to handle the big chunk of memory, one to initialize and the other to free it. + +
    +void initMemory();
    +void freeMemory();
    +
    + +To implement initMemory I've decided that I would ask for the maximum amount of memory that I could get from the OS. + +
    +#define MINREQ 0x20000
    +
    +// Big block of memory
    +void *memory = NULL;
    +// Position where the last block ends
    +size_t endpos = 0;
    +
    +void initMemory()
    +{
    +    size_t required = PTRDIFF_MAX;
    +    while (memory == NULL)
    +    {
    +        memory = malloc(required);
    +        if (required < MINREQ)
    +        {
    +            if (memory)
    +            {
    +                free(memory);
    +            }
    +            printf("Cannot allocate enough memory\n");
    +            exit(ENOMEM);
    +        }
    +        required >>= 1;
    +    }
    +}
    +
    +void freeMemory()
    +{
    +    free(memory);
    +}
    +
    + +As you can see, initMemory starts trying to allocate the maximum amount a memory allowed, and starts to divide that amount by 2 every time the allocation fails. If there isn't at least 128KB of memory available the program crashes with ENOMEM. + +Now that we have our chunk of memory ready to go, we can start to start giving blocks away. + +
    +struct block_meta *request_block(size_t size)
    +{
    +    struct block_meta *last = NULL;
    +    struct block_meta *block = find_free_block(&last, size);
    +    if (block)
    +    {
    +        block->free = 0;
    +        return block;
    +    }
    +    // Append new block to list
    +    block = memory + endpos;
    +    endpos += META_SIZE + size;
    +    if (last)
    +    {
    +        last->next = block;
    +    }
    +    else
    +    {
    +        first_block = block;
    +    }
    +    block->free = 0;
    +    block->next = NULL;
    +    block->size = size;
    +    return block;
    +}
    +
    + +How request_block works: + +1. Tries to find a free block with enough space. If there is one, it is set as occupied and returns that block. +2. If there isn't a free block available. It adds a new block with enough space at the end of memory (the big chunk). +3. If this is the first call, points the head of the list to the recently created block, else point the last node to the block. +4. Set the new block as occupied, set the size and next to null. Then return the new block. + +With this function, implementing malloc and free is pretty easy: + +
    +void *my_malloc(size_t size)
    +{
    +    struct block_meta *block = request_block(size);
    +    return block + 1;
    +}
    +
    +void my_free(void *ptr)
    +{
    +    struct block_meta *block = ptr - META_SIZE;
    +    block->free = 1;
    +}
    +
    + +To finish the challenge, I have to implement realloc, that is a little bit more tricky. + +
    +void *my_realloc(void *ptr, size_t size)
    +{
    +    if (!ptr)
    +    {
    +        return my_malloc(size);
    +    }
    +    struct block_meta *block = ptr - META_SIZE;
    +    if (block->size >= size)
    +    {
    +        return block + 1;
    +    }
    +    uint8_t *newptr = my_malloc(size);
    +    size_t i;
    +    for (i = 0; i < (block->size < size ? block->size : size); i++)
    +    {
    +        newptr[i] = ((uint8_t *)ptr)[i];
    +    }
    +    block->free = 1;
    +    return newptr;
    +}
    +
    + +How realloc works: + +
  • If the pointer to reallocate is null, works just like malloc. +
  • If the given size is bigger than the prior size, it allocates a bigger block and copies all data from the original block to the new block. +
  • If the given size is smaller than the prior size, it allocates a smaller block and copies just the data that fits into the smaller block. + + +
  • New challenge +

    +In my implementation I used a linked list where each node holds a pointer to the next, but given that I have control over the _entire_ memory this actualy isn't necessary. + +My challenge to you is that you remove the pointer to next from the block_meta struct. + + +

    Resources +

    +
  • https://danluu.com/malloc-tutorial/ +
  • http://www.craftinginterpreters.com/chunks-of-bytecode.html + +
  • \ No newline at end of file diff --git a/docs/2020/04/19/mysql-python.html b/docs/2020/04/19/mysql-python.html new file mode 100644 index 0000000..f3d6526 --- /dev/null +++ b/docs/2020/04/19/mysql-python.html @@ -0,0 +1,284 @@ +Executing python code from MySQL Server +

    Blog

    Reading time: +

    + +

    Executing python code from MySQL Server +

    + +

    Trying py_eval +

    + +
    Generate a list of integers from 0 to 10 +
    +
    +> select py_eval('[i for i in range(10)]') list;
    ++--------------------------------+
    +| list                           |
    ++--------------------------------+
    +| [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
    ++--------------------------------+
    +
    + + +
    Generate a dictionary (json object) from a list of dicts +
    +
    +> select replace(py_eval('{ str(user["id"]) : user for user in [{"id": 33, "name": "John"}, {"id": 44, "name": "George"}] }'), "'", '"') dict;
    ++------------------------------------------------------------------------+
    +| dict                                                                   |
    ++------------------------------------------------------------------------+
    +| {"33": {"id": 33, "name": "John"}, "44": {"id": 44, "name": "George"}} |
    ++------------------------------------------------------------------------+
    +
    + +Replace is needed here, because python uses single quotes for dictionaries. + + +
    Make a function that receives a json array and a key and sorts the array by key +
    +
    +DROP function IF EXISTS sort_by_key;
    +DELIMITER $$
    +CREATE FUNCTION sort_by_key (arr json, k text)
    +RETURNS json
    +BEGIN
    +    RETURN replace(py_eval(CONCAT("sorted(", arr, ", key=lambda e: e['", k, "'])")), "'", '"');
    +END$$
    +DELIMITER ;
    +
    + +Test + +
    +> select sort_by_key('[{"a":2}, {"a":1}, {"a": 722}, {"a": 0}]', 'a') sorted;
    ++--------------------------------------------+
    +| sorted                                     |
    ++--------------------------------------------+
    +| [{"a": 0}, {"a": 1}, {"a": 2}, {"a": 722}] |
    ++--------------------------------------------+
    +
    + + +

    How to write a MySQL UDF +

    +There is a pretty good guide at the MySQL 8.0 Reference Manual. I'll give you a brief explanation so you can start quickly, but reading the full guide is highly recomended. + +MySQL's UDFs are written in C++ and need to follow certain conventions so they can be recognized as such. + +In our case, we want our MySQL function to be called py_eval, so we have to define the following C++ functions: + +
  • py_eval_init or py_eval_deinit +
  • py_eval + +**py_eval_init**: (Optional) Initializes memory and data structures for the function execution. + +**py_eval**: Executes the actual function, in our case evaluates a python expression. + +**py_eval_deinit**: (Optional) If any memory was allocated in the init function, this is the place where we free it. + +For py_eval we only need **py_eval_init** and **py_eval**. + + +
  • Functions signatures +

    +
    +bool py_eval_init(UDF_INIT *initid, UDF_ARGS *args,
    +                             char *message);
    +
    +char *py_eval(UDF_INIT *, UDF_ARGS *args, char *result,
    +                         unsigned long *res_length, unsigned char *null_value,
    +                         unsigned char *);
    +
    + +These are the standard definitions for MySQL functions that return string, as is the case of py_eval. To be able to declare this functions, you need to have the definition of UDF_INIT and UDF_ARGS, you can find that at the source code of mysql server -> right here. + + +

    Evaluating python expression +

    +For evaluating python expression, we'll be using pybind11. That gives us the ability to directly access the python interpreter and execute code. + + +
    Example +
    +Make sure you have g++ installed. Try executing: g++ --help. And some version of python running of your system, for this tutorial I'll be using version _3.8_. + +
    +$ mkdir py_eval && cd py_eval
    +$ git clone https://github.com/pybind/pybind11
    +
    + +Create a new file called main.cpp with the following content: + +
    +#include "pybind11/include/pybind11/embed.h"
    +#include "pybind11/include/pybind11/eval.h"
    +#include 
    +
    +namespace py = pybind11;
    +
    +py::scoped_interpreter guard{}; // We need this to keep the interpreter alive
    +
    +int main(void) {
    +    auto obj = py::eval("[i for i in range(10)]");
    +    std::cout << std::string(py::str(obj)) << std::endl;
    +}
    +
    + +To run the example we have to compile the file. + +First, we need the compilation flags. + +
    +$ pkg-config python-3.8 --libs --cflags
    +-I/usr/include/python3.8
    +
    + +Then, we can compile and run our code with the following. + +
    +$ g++ main.cpp -I/usr/include/python3.8 -lpython3.8
    +$ ./a.out
    +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    +
    + + +

    Putting all together +

    +Download udf types to the project folder. + +
    +$ wget https://raw.githubusercontent.com/mysql/mysql-server/8.0/include/mysql/udf_registration_types.h
    +
    + +Create a new file called py_eval.cpp, with the following content: + +
    c++
    +#include "pybind11/include/pybind11/embed.h"
    +#include "pybind11/include/pybind11/eval.h"
    +#include "udf_registration_types.h"
    +#include 
    +
    +namespace py = pybind11;
    +
    +py::scoped_interpreter guard{}; // We need this to keep the interpreter alive
    +
    +extern "C" bool py_eval_init(UDF_INIT *initid, UDF_ARGS *args,
    +                             char *message)
    +{
    +    // Here we can check if we received one argument
    +    if (args->arg_count != 1)
    +    {
    +        // The function returns true if there is an error,
    +        // the error message is copied to the message arg.
    +        strcpy(message, "py_eval must have one argument");
    +        return true;
    +    }
    +    // Cast the passed argument to string
    +    args->arg_type[0] = STRING_RESULT;
    +    initid->maybe_null = true; /* The result may be null */
    +    return false;
    +}
    +
    +extern "C" char *py_eval(UDF_INIT *, UDF_ARGS *args, char *result,
    +                         unsigned long *res_length, unsigned char *null_value,
    +                         unsigned char *)
    +{
    +    // Evaluate the argument as a python expression
    +    auto obj = py::eval(args->args[0]);
    +    // Cast the result to std::string
    +    std::string res_str = std::string(py::str(obj));
    +
    +    // Copy the output string from py::eval to the result argument
    +    strcpy(result, res_str.c_str());
    +
    +    // Set the length of the result string
    +    *res_length = res_str.length();
    +
    +    return result;
    +}
    +
    + +Then, we have to compile the project as a shared library, and move it to the plugin folder of mysql (in your case, it could be located in some other directory). + +
    +$ g++ -I/usr/include/python3.8 -lpython3.8 -shared -fPIC -o py_eval.so py_eval.cpp
    +$ sudo cp py_eval.so /usr/lib/mysql/plugin/
    +
    + +Now, it's time to try it from mysql. + +First, connect to your server as root. + +
    +$ sudo mysql -uroot
    +
    + +Create and test the function. + +
    +> create function py_eval returns string soname 'py_eval.so';
    +Query OK, 0 rows affected (0.029 sec)
    +
    +> select py_eval('[i for i in range(10)]') list;
    ++--------------------------------+
    +| list                           |
    ++--------------------------------+
    +| [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
    ++--------------------------------+
    +1 row in set (0.001 sec)
    +
    + + +

    Future +

    +There is a lot to do, for example there is no error control on the function execution. The python expression that we are trying to evaluate could fail causing a server reboot. Also, there is some extra work to do to be able to use import. And there are many concerns regarding concurrency issues. + +If you want to contribute to improve execution of python code on mysql server, please go to my github project and make a PR. + +I hope you enjoyed this tutorial and come back soon for more. + +
    \ No newline at end of file diff --git a/docs/2020/12/17/grotsky-getmyip.html b/docs/2020/12/17/grotsky-getmyip.html new file mode 100644 index 0000000..9bc0eb0 --- /dev/null +++ b/docs/2020/12/17/grotsky-getmyip.html @@ -0,0 +1,233 @@ +Grotsky Part 4: Writing a service to get your public IP +

    Blog

    Reading time: +

    + +

    Writing a service to get your public IP +

    +Grotsky (my toy programming language) finally can be used to make something useful. + +In this post I want to show you how I made a service that let's your retrieve your public IP as a response to a HTTP Request. + + +

    Show me the code +

    +Let's start by building the http request handler. + +The service will be deployed to heroku. Heroku passes the port that the http server has to listen as an environment variable named PORT. + + +
    Let's get the server up and running +
    +
    +let listen = ":8092"
    +let port = env.get("PORT")
    +if port != "" {
    +    listen = ":" + port
    +}
    +
    +io.println("Listen " + listen)
    +http.listen(listen)
    +
    + +We listen by default at the port 8092 and if the environment variable is given we change it. + +Then we print what is the port and start the server with http.listen. That blocks the execution and starts the server. + +Grotsky interpreter is written in Go, and uses Go's standard http server. Each requests is handled by a goroutine, but because Grotsky is single threaded only one goroutine executes at any given point in time. + +When a request is received the goroutine has to hold the GIL (Global Interrupt Lock) to be able to give control to the interpreter. + + +
    Now lets add some code to handle requests +
    +
    +fn getIP(rq, rs) {
    +    io.println("Request from --> " + rq.address)
    +    rs.write(200, rq.address)
    +}
    +
    +http.handler("/", getIP)
    +
    +let listen = ":8092"
    +let port = env.get("PORT")
    +if port != "" {
    +    listen = ":" + port
    +}
    +
    +io.println("Listen " + listen)
    +http.listen(listen)
    +
    + +Now we have something interesting to try out! + +What we've done is to log and write back as response the address of the device that is doing the request. + +To try it out you need to download grotsky. + +
    +$ go get github.com/mliezun/grotsky/cmd/grotsky
    +
    + +Save the Grotsky code under a filed called getip.g and the execute it using the grotsky interpreter: + +
    +$ go run $(go env GOPATH)/src/github.com/mliezun/grotsky/cmd/grotsky getip.g
    +
    + +Output: +
    +Listen :8092
    +
    + +Now you can make a request to see if it is working + +
    +$ curl localhost:8092
    +
    + +Output: +
    +[::1]:43464
    +
    + +We see that the address contains the port we want to split it and show just the IP. + + + +
    Let's write a couple functions to do that +
    +
    +fn findReversed(string, char) {
    +    for let i = string.length-1; i > -1; i = i - 1 {
    +        if string[i] == char {
    +            return i
    +        }
    +    }
    +    return -1
    +}
    +
    +fn parseIP(address) {
    +    let ix = findReversed(address, ":")
    +    return address[:ix]
    +}
    +
    + +The function findReversed finds the first index where char appears in string starting from the end. + +The function parseIP uses findReversed to obtain the index where ":" splits the IP and the PORT and uses that index to return just the IP address. + + +
    Now we can send just the IP address +
    +
    +fn getIP(rq, rs) {
    +    let address = parseIP(rq.address)
    +    io.println("Request from --> " + address)
    +    rs.write(200, address)
    +}
    +
    + +Add the two functions at the beginning of the file and modify the getIP function. + +Restart the server and now if you make a request you should get just the IP. + +
    +$ curl localhost:8092
    +[::1]
    +
    + +Voila! + + + +
    We have just one last issue: Proxies! +
    +Our service will probably sit behind a proxy, so we need to read the address from a special header X-Forwarded-For. + +Let's implement that! + +
    +fn getIP(rq, rs) {
    +    let address = parseIP(rq.address)
    +    let forwarded = rq.headers["X-Forwarded-For"]
    +    if forwarded != nil {
    +        address = forwarded[0]
    +    }
    +    io.println("Request from --> " + address)
    +    rs.write(200, address)
    +}
    +
    + +We read the header from the request and if X-Forwarded-For is present we sent that as a response to the user. + + +
    Our work is complete. Let's try it! +
    +
    +$ curl localhost:8092 -H 'X-Forwarded-For: 8.8.8.8'
    +8.8.8.8
    +
    +$ curl localhost:8092
    +[::1]
    +
    + +Well done. Now you can deploy it to Heroku (that's up to you) or any other cloud platform. + +I have my own version running under: https://peaceful-lowlands-45821.herokuapp.com/ + + + +

    Deployed to Fly.io after Heroku killed the free plan: +http://morning-breeze-4255.fly.dev +

    Try it from your command line +
    +
    +$ curl http://morning-breeze-4255.fly.dev
    +
    + + +
    \ No newline at end of file diff --git a/docs/2021/04/01/mlisp-wasm.html b/docs/2021/04/01/mlisp-wasm.html new file mode 100644 index 0000000..325ba0d --- /dev/null +++ b/docs/2021/04/01/mlisp-wasm.html @@ -0,0 +1,187 @@ +Mlisp: My own lisp implementation compiled to WASM +

    Blog

    Reading time: +

    + +

    Mlisp, My own lisp implementation +

    +Mlisp a tiny lispy language based on the book Build Your Own Lisp. + +The interpreter is written in C and compiled directly to WASM. You can try it in this page by openning the developer console of your browser and typing Mlisp.interpret("+ 2 2") or using the repl shown below. + + +

    Interface +

    +To be able to access C functions from your browser you have to export them. Let's see how we can define a function that is exported. + +
    +#if __EMSCRIPTEN__
    +EMSCRIPTEN_KEEPALIVE
    +#endif
    +int mlisp_init();
    +
    + +When compilen with emcc the emscripten compiler to wasm, you have to add EMSCRIPTEN_KEEPALIVE macro before your function so it doesn't get optimized away. + +The exported functions in this project are: + +
    +int mlisp_init();
    +char *mlisp_interpret(char *input);
    +void mlisp_cleanup();
    +
    + +The project is then compiled with: + +
    +emcc -std=c99  -Wall -O3 -s WASM=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]'
    +
    + +That means that you would be able to access the exported functions using a cwrap that let's you wrap a C function call from a Javascript function call. + +This compilation generates two files mlisp.js and mlisp.wasm. + +The javascript file defines a Module that provides useful tool to access exported functions. + + +

    Let's start using it +

    +
    +const Mlisp = {
    +    init: Module.cwrap('mlisp_init', 'number', []),
    +    interpret: Module.cwrap('mlisp_interpret', 'string', ['string']),
    +    cleanup: Module.cwrap('mlisp_cleanup', 'void', []),
    +};
    +
    +// Init interpreter
    +Mlisp.init();
    +
    +// Run some commands
    +console.log(Mlisp.interpret("+ 2 2"));
    +
    +// Cleanup interpreter
    +Mlisp.cleanup();
    +
    + + +

    Automated Build & Release from github +

    +I made a github workflow for this project to automatically build and release so you can retrieve them from Github. + + + +

    REPL +

    + + + + +
    +
    + + Input some commands"> +
    +
    + + + + + + +

    Interesting commands to try out +

    +
  • foldl: Fold left (same as reduce left) + - (foldl + 0 {1 2 3 4 5}): Sum of elements +
  • filter + - (filter (\ {e} {> e 3}) {1 2 3 4 5 6}): Elements bigger than 3 +
  • map + - (foldl * 1 (map (\ {e} {* e 2}) {1 1 1 1 1})): Multiply elements by 2 and then multiply all elements + + +
  • \ No newline at end of file diff --git a/docs/2021/10/04/new-blog-engine.html b/docs/2021/10/04/new-blog-engine.html new file mode 100644 index 0000000..4d982c2 --- /dev/null +++ b/docs/2021/10/04/new-blog-engine.html @@ -0,0 +1,118 @@ +I created a programming language and this blog is powered by it +

    Blog

    Reading time: +

    I created a programming language and this blog is powered by it

    Why did I do it?

    Mostly for fun.

    If you follow my blog or take a look at some of the posts that I made, you will see that I was +building a programming language called +Grotksy. Just a toy programming language that I made based on the book +Crafting Interpreters, which I totally recommend buying and reading if you haven't yet. +

    I wanted to build something interesting but simple enough that could be made with Grotsky. +I tought that replacing Jekyll with my own engine was a task worth a try. +There is nothing groundbreaking or innovative being made here, just a little experimentation. +

    I have to give credit to the project +lith, because the 'templating' engine for the blog is inspired by it. +

    How did I do it?

    That's a good question.

    Originally, this blog was powered by Jekyll, that translated markdown to html and hosted + by Github Pages. I decided that I was going to build a templating engine and generate html + to keep things simple. +

    But also, as a challenge I made a simple HTTP server to use as a dev server when trying the blog locally. +

    HTTP Server

    For the purpose of having a custom HTTP Server I had to add support for TCP sockets to the language. +I wrapped the go standard library in some functions and exposed that to the Grotsky language. +In grotsky looks something like this +

    +let socket = net.listenTcp(host + ":" + port)
    +let conn
    +while true {
    +	try {
    +		conn = socket.accept()
    +	} catch err {
    +		io.println("Error accepting new connection", err)
    +		continue
    +	}
    +	try {
    +		# Call function that handles connection
    +		handleConn(conn)
    +	} catch err {
    +		io.println("Error handling connection", err)
    +	}
    +	try {
    +		conn.close()
    +	} catch err {}
    +}
    +				
    +

    This means that the server listens on a socket, accepts connections, writes some text/bytes to the connection + and then closes the connection. +

    Template Engine

    The templating engine is built using the native support Grotsky provide for lists. +A regular page for the blog looks like this: +

    +let base = import("../base.gr")
    +# Create new Post Object
    +let post = base.Post(
    +	"Title",
    +	"Brief description of blog post.",
    +	"Author Name",
    +	"descriptive,tags",
    +	[
    +		[
    +			"h2",
    +			[],
    +			[
    +				"Title"
    +			]
    +		],
    +		[
    +			"div",
    +			["class", "content"],
    +			[
    +				"Content line 1",
    +				"Content line 2",
    +			]
    +		]
    +	]
    +)
    +			

    It's pretty straightforward: the first element of the list is the html tag, the second is +an array of properties for the tag and the last one is a list that contains what will be +the *content* of enclosed by the tags. +

    Resources

    If you want to take a peek, the source code for both projects is available on github: +

    \ No newline at end of file diff --git a/docs/2021/12/31/playing-with-js.html b/docs/2021/12/31/playing-with-js.html new file mode 100644 index 0000000..ffb2ac4 --- /dev/null +++ b/docs/2021/12/31/playing-with-js.html @@ -0,0 +1,355 @@ +Playing with Javascript Proxies (getters/setters) +

    Blog

    Reading time: +

    Playing with Javascript Proxies (getters/setters)

    Overview

    Happy New Year!

    This is my final post for the 2021. This year I didn't post that much, but a lot of work was put into + the blog to rewrite it using +Grotksy. +I hope everyone has a great 2022 and that next year is much better than the last one. +

    The inspiration for this blog post comes from the idea of building a tiny db that feels more natural to Javscript. +All the databases that I've seen make a heavy use of methods like: +db.get(), +db.put(), +db.scan(), +db.query(). + And many others that Im sure you have seen. +I think it would be great to see something like: +

    const db = getDb("...")
    +// Create new user
    +const u = {username: "jdoe", email: "jdoe@example.com", id: 100}
    +// Store new user in the database
    +db.objects.users[u.username] = u
    +// Commit the changes to the database
    +db.actions.save()
    +

    In this blog post we will be building a much simpler version that stores everything in memory. Each change + made to the objects will be stored in a log (called layers) and the final object will be composed of all + the small layers present in the log. +

    Defining a proxy

    We need to implement some generic getters/setters. +

    const objects = new Proxy({}, {
    +    get: function(obj, prop) {
    +        validate(prop, null)
    +        // Implementation
    +    },
    +    set: function(obj, prop, value) {
    +        validate(prop, value)
    +        // Implementation
    +    }
    +})
    +

    Let's define the validation function. In this case we want the objects to be able to be serialized to JSON. +

    +const validate = (prop, value) => {
    +    // Make sure that the property and value are serializable
    +    // JSON.stringify throws an error if not serializable
    +    const l = {}
    +    l[prop] = value
    +    JSON.stringify(l)
    +    return l
    +}
    +
    +

    This empty proxy will validate that the values and prop are serializable and do nothing else. Now we can start +building on top of it. +

    Building a tree to hold everything together

    We need a root object where we will store all the changes that are applied to an object. +We will have a sort of tree structure to hold everything together. +It will look something like this: +

    +              rootObject({})  -> layers([{users: {jdoe: ...}}, {tokens: {tk1: ...}}])
    +                    |
    +        --------------------------
    +        |                        |
    + child(.users{})          child(.tokens{})
    +        |                        |
    +       ...                      ...
    +
    +

    The root object contains the layers with all the changes made from the beginning of the existence of the object. +Each time a property of the root object is accessed a child is returned that internally holds a reference to the root. +This way we can go through the entire chain of access and be able to reach the stored layers. +By chain of access I mean the following: objects.users.jdoe.metadata.login.ip. +As you can see, we need to traverse through many objects to be able to reach the ip field. But the layer that contains + the information is only stored in the root, so each child needs to mantain a reference to the parent to be able to reach + the root node. +

    Let's define a simple function to be able to create a new rootObject. +

    +const wrapObject = (parent, key, current) => {
    +    const rootObj = {
    +        parent: Object.freeze(parent),
    +        layers: [Object.freeze({'value': current, 'previous': null})],
    +        pushLayer (l) {}, // Push new layer
    +        getLayer (ks) {}, // Get layer where information is stored based on given keys
    +        getValue (k) {} // Get value that matches given key
    +    }
    +
    +    const rootProxy = {
    +        get: function(obj, prop) {
    +            validate(prop, null)
    +            const val = rootObj.getValue(prop)
    +            if (typeof val == 'object') {
    +                // If the value is an object we need to have a child instance
    +                // with a reference to the parent
    +                return wrapObject(rootObj, prop, val).objects
    +            }
    +            // If the value is other kind like a number or string we can safely return that
    +            return val
    +        },
    +        set: function(obj, prop, value) {
    +            const l = validate(prop, value)
    +            // Add new layer to the rootObj
    +            rootObj.pushLayer({'value': l})
    +        }
    +    }
    +
    +    return {
    +        actions: {
    +            revert () {
    +                // Deleting the last layer will revert the changes
    +                const pop = rootObj.layers[rootObj.layers.length-1]
    +                rootObj.layers.splice(rootObj.layers.length-1, rootObj.layers.length)
    +                return pop
    +            }
    +        },
    +        objects: new Proxy({}, rootProxy)
    +    }
    +}
    +
    +

    Handling layers

    The layer format: +

    +const layer = {
    +    value: {status: 'active'},
    +    previous: null // Reference to a previous layer that has the key 'status' in it
    +}
    +
    +
    The layers are stored in an array, each layer holds the value and a reference to the previous layer + that set a value for the same key (in this case the key was 'status'). Also the layers form a simple + linked list through the 'previous' reference. That way we have the entire history of a given key. +

    We would need a function to be able to tell if an object has a list of nested keys. Trust me for now, you'll see. +

    +const nestedExists = (obj, ks) => {
    +    for (let j = 0; j < ks.length; j++) {
    +        let k = ks[j];
    +        if (!(k in obj)) {
    +            return false
    +        }
    +        obj = obj[k]
    +    }
    +    return true
    +}
    +
    +
    In this function we receive an object and a list of keys, we start accessing the first internal object with the first key + and we keep doing the same till we make sure that all the keys are present. +

    Now we're almost done. Let's define the functions for handling the store and retrieval of layers. +

    +const rootObj = {
    +    parent: Object.freeze(parent),
    +    layers: [Object.freeze({'value': current, 'previous': null})],
    +    pushLayer (l) {
    +        // If this is a child object we need to build the entire chain of access
    +        // from the bottom up
    +        if (parent) {
    +            const ll = {}
    +            ll[key] = l['value']
    +            // Search for a previous layer modifying the same key
    +            const previous = parent.getLayer([key])
    +            // Tell the parent object to push the new layer
    +            parent.pushLayer(Object.freeze({'value': ll, previous}))
    +        } else {
    +            // We are in the root object, add the layer to the array
    +            this.layers.push(Object.freeze(l))
    +        }
    +    },
    +    getLayer (ks) {
    +        // Search through the entire list of layers to see if one contains all the keys
    +        // that we are looking for. Start from the end of the array (top of the stack)
    +        for (let i = this.layers.length - 1; i >= 0; i--) {
    +            let v = nestedExists(this.layers[i]['value'], ks)
    +            if (v) {
    +                return this.layers[i]
    +            }
    +        }
    +        if (parent) {
    +            // If we are in a child object, look through all the previous layers
    +            // and see if the key we're looking for is contained in one of them.
    +            let ll = parent.getLayer([key].concat(ks))
    +            while (ll) {
    +                let a = nestedExists(ll['value'][key], ks)
    +                if (a) {
    +                    return Object.freeze({'value': ll['value'][key]})
    +                }
    +                ll = ll.previous
    +            }
    +        }
    +    },
    +    getValue (k) {
    +        // Straightforward, get layer and return value
    +        const l = this.getLayer([k])
    +        if (l) {
    +            return Object.freeze(l['value'][k])
    +        }
    +    }
    +}
    +
    +
    That's all we need. We can create a new object and start adding and modifying properties. Each change will be added + to the end of the log and worked out when a property is accessed. +

    Wrapping Up

    Let's try the final result. The source code is loaded in this page, so you can open a dev console in the browser and + try for yourself. +

    +const store = wrapObject(null, null, {})
    +
    +// Create new user
    +const user = {username: 'jdoe', email: 'jdoe@example.com', name: 'John Doe', id: 100}
    +
    +// Add new user
    +store.objects.users = {}
    +store.objects.users[user.username] = user
    +
    +// Print user email
    +console.log(store.objects.users.jdoe.email)
    +
    +// Change user email and print
    +store.objects.users.jdoe.email = 'jdoe2@example.com'
    +console.log(store.objects.users.jdoe.email)
    +
    +// Revert last change and print email again
    +store.actions.revert()
    +console.log(store.objects.users.jdoe.email)
    +
    +

    That's it for now. We defined a Javascript object that contains the entire history of changes that were made to itself. +And at any point we can revert the changes and go back to a previous state. +Everything is stored in an array and is easily serializable. +If we wanted to take this to the next level, each change could be written to a persistence storage (s3, sqlite, mysql, ...) +

    The full source code is available in a +public gist. +

    \ No newline at end of file diff --git a/docs/2022/05/05/rust-blake-mysql.html b/docs/2022/05/05/rust-blake-mysql.html new file mode 100644 index 0000000..b2e5a57 --- /dev/null +++ b/docs/2022/05/05/rust-blake-mysql.html @@ -0,0 +1,58 @@ +Blake3 hash plugin for MySQL written in Rust +

    Blog

    Reading time: +

    Implementing a blake3 hash plugin for MySQL in Rust

    It's been long since I've written something, I wanted to bring you some new stuff, so here you have a short blog post. +I encourage you to try this new plugin that uses this +blake3 hash implementation. +Blake3 is secure, unlike MD5 and SHA-1. And secure against length extension, unlike SHA-2. +Start using it and create an issue in the github repo if you would like a feature implemented! +

    Checkout +blake-udf source code.

    How to use

    Download and install MySQL plugin

    $ wget 'https://github.com/mliezun/blake-udf/releases/download/v0.1.0/libblake_udf.so'
    +$ mv libblake_udf.so /usr/lib/mysql/plugin/
    +

    Load UDF in MySQL

    $ mysql -uroot -p -e 'create function blake3_hash returns string soname "libblake_udf.so";'
    +

    Execute function

    $ mysql --binary-as-hex=0 -uroot -p -e 'select blake3_hash("a");'
    +
    Output: 17762fddd969a453925d65717ac3eea21320b66b54342fde15128d6caf21215f +
    \ No newline at end of file diff --git a/docs/2022/07/20/cloud-outdated-release.html b/docs/2022/07/20/cloud-outdated-release.html new file mode 100644 index 0000000..ef06dce --- /dev/null +++ b/docs/2022/07/20/cloud-outdated-release.html @@ -0,0 +1,137 @@ +Webscraping as a side project +

    Blog

    Reading time: +

    Webscraping as a side project

    A friend and I were looking for a side project to work together. We realized we both faced a similar problem. +

    Let's use AWS Lambda Python runtime as an example. +AWS will send out emails when a version is at the end of life making it difficult to stay +on the latest if desired. +Plus, reacting to them usually means you are many many versions behind already. +

    Our journey started. +We made a list of providers for the MVP: AWS, GCP and Azure. +Then a list of the services that have versions (for example S3 doesn't have versions). +After that we realized that we could get some versions using APIs. +Other services exclusively require webscraping. +

    We support 46 services and counting. +Take a look at +Cloud Outdated and subscribe to get notified. +If you are looking for a service that's not there +contact us. +

    Picking a language, framework and platform

    We're both Python programmers. The choice was obvious. +"Let's use Python and Django framework for the job" we said. +We didn't want to spend our innovation tokens on new language/framework. +So we chose Boring Technology. +

    For the db we spent our first innovation token. +We decided to go with the flashy new serverless postgres-compatible +CockroachDB. +

    On the hosting side we're using AWS Lambda. Taking advantage of the free compute time. +Helps mantaining the costs down. +

    Make webscraping reliable

    A webpage that's being scraped can change at any time. First thing we did was account for those edge cases. +We created a custom exception that is triggered when something changed. So that we can react to that downstream. +

    +class ScrapingError(Exception):
    +    pass
    +
    +
    We wanted to keep the implementation simple. Each service is scraped by a single function. +The signature of the function is something like +aws_lambda_python() -> List[Version]. +All the implementations follow a similar pattern: +
    +def aws_lambda_python():
    +    # Read versions from aws docs website:
    +    # https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html
    +
    +    if not found_versions:
    +        raise ScrappingError
    +
    +    # Process and return versions
    +
    +
    That's ^ what we call a poll function. +

    We pass poll functions through a polling class that handles all the errors and results. +When we detect an scraping error we have a special case. We send an email with the details +of what happened. Because the problem is something that requires manual action. We receive +that email in our personal inboxes and fix the problem ASAP. +

    The poll class that handles all the magic behind cloud outdated is actually very simple: +

    +class PollService:
    +    def __init__(self, service: Service, poll_fn: Callable):
    +        self.poll_fn = poll_fn
    +        # Some other attributes...
    +
    +    def poll(self):
    +        try:
    +            results = self.poll_fn()
    +            self.process_results(results)
    +        except ScrapingError as e:
    +            notify_operator(
    +                f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e.__str__()}"
    +            )
    +
    +    def process_results(self, results):
    +        # if results contains new versions:
    +        #     save new versions to db
    +        # if results contains deprecated versions:
    +        #     set versions in db as depreacted
    +
    +

    That's the hearth of Cloud Outdated. +After that we have to send notifications to subscribed users. +That part is trivial. +We send an email that contains the difference between what was last sent to a user and what we +have stored in the db at the moment. +

    Last toughts

    Having a side project is usually a good idea. +For us has been a journey were we got to know some new stuff (CockroachDB). +We also learned about how to build a product and keep a MVP mentality. +The most difficult challenge we face is to bring more users to the platform. +

    We'd love to see more people subscribed. +If this blogpost sparked your interest go to +Cloud Outdated and subscribe to start getting emails. +

    See you next time! +

    \ No newline at end of file diff --git a/docs/2022/09/20/branchable-mysql.html b/docs/2022/09/20/branchable-mysql.html new file mode 100644 index 0000000..38c47c1 --- /dev/null +++ b/docs/2022/09/20/branchable-mysql.html @@ -0,0 +1,214 @@ +Branchable MySQL: Managing multiple dev environments +

    Blog

    Reading time: +

    Branchable MySQL: Managing multiple dev environments

    When teams start to grow, having a single dev environment becomes an issue. People start stepping on each others toes. +A common problem is that two people want to apply incompatible migrations on the database. That problem is impossible +to fix if folks are working on parallel branches. +If we can have a database for each branch of a project, that will remove much of the pain of having multiple devs applying +changes to the db.

    There are already projects that solve this problem: +PlanetScale and +Neon. +

    A common case where this problem arises is when two devs want to add a column to the same table. +

    Two devs applying changes to the same table in the database.

    We have a +people table in the database. One of the devs wants to add the +last_name column and the other one wants to add the +address. +

    Dev1's code thinks the table will have 3 columns after he applies his operation: +id, name, last_name. +

    Dev2's code also thinks the table will have 3 columns: +id, name, address. +

    In reality the table will have 4 columns. +So neither of them will be able to run their code unless they talk to each other and figure out how to make this work. +

    This is far from ideal. +

    What we want instead, is that each one of them can develop their features independently. +

    Two devs applying changes to the same table in different database branches.

    They both apply to the same table, but each table lives on an instance that was 'replicated' from the original. +

    How can we implement the ideal case?

    MySQL writes data (by default) to the directory +/var/lib/mysql/data. +

    We can use an +Union filesystem. And configure MySQL to use a different directory to read and write data. +

    That way we can have a feature/user-last-name 'branch' read and write data from a directory like +/app/user-last-name/mysql/data. +

    And a feature/user-address 'branch' read and write data from a directory like +/app/user-address/mysql/data. +

    Those branches can be mounted using fuse-overlayfs by executing the following commands: +

    +# Directory /app/base contains data from the original branch
    +
    +fuse-overlayfs -o lowerdir=/app/base,upperdir=/app/user-last-name,workdir=/tmp/user-last-name overlayfs /app/user-last-name
    +
    +fuse-overlayfs -o lowerdir=/app/base,upperdir=/app/user-address,workdir=/tmp/user-address overlayfs /app/user-address
    +
    +

    This means both 'branches' of the database are able to coexist and have different schemas during their lifetime. +

    Experimenting with a use case

    I had this idea in my head for months. I finally convinced myself that it was worth a shot. +

    I decided to do a little implementation using Docker and python FastAPI. +Exposing a simple interface so that it's easy to create and delete branches. +

    The project is live on github +branchable-mysql. +

    The container image is published on Docker Hub +branchable-mysql. +

    To start using the image let's create a docker-compose.yml file. +

    version: "3"
    +
    +services:
    +  mysql:
    +    image: mliezun/branchable-mysql
    +    platform: linux/amd64
    +    privileged: true
    +    restart: always
    +    volumes:
    +      - appdata:/app/
    +
    +volumes:
    +  appdata:
    +
    +

    Then you can execute +docker-compose up and the MySQL server should start running. +

    After that, connect easily to the db +docker compose exec mysql mysql -uroot -h127.0.0.1 --skip-password -P33061. You should enter to an interactive mysql console. +

    Let's create an initial schema, a table and insert some data so that we can see how branching works. +On the console that we just opened execute: +

    +mysql> create schema s1;
    +Query OK, 1 row affected (0.01 sec)
    +
    +mysql> use s1;
    +Database changed
    +mysql> create table people (id int primary key auto_increment, name varchar(255) not null);
    +Query OK, 0 rows affected (0.07 sec)
    +
    +mysql> insert into people select 0, 'Miguel';
    +Query OK, 1 row affected (0.02 sec)
    +Records: 1  Duplicates: 0  Warnings: 0
    +
    +mysql> select * from people;
    ++----+--------+
    +| id | name   |
    ++----+--------+
    +|  1 | Miguel |
    ++----+--------+
    +1 row in set (0.00 sec)
    +
    +

    That's enough for now, we're ready to start creating branches. +

    On a separate terminal, without closing the previous mysql interactive console, execute: +

    +docker compose exec mysql /app/scripts/create_branch.sh base feature/user-last-name
    +
    +{"branch_name":"feature/user-last-name","base_branch":"base","port":33062}
    +
    +

    Now you can login to the new database branch using port 33062 +docker compose exec mysql mysql -uroot -h127.0.0.1 --skip-password -P33062

    +mysql> use s1;
    +Reading table information for completion of table and column names
    +You can turn off this feature to get a quicker startup with -A
    +
    +Database changed
    +mysql> alter table people add column last_name varchar(255) not null;
    +Query OK, 0 rows affected (0.03 sec)
    +Records: 0  Duplicates: 0  Warnings: 0
    +
    +mysql> select * from people;
    ++----+--------+-----------+
    +| id | name   | last_name |
    ++----+--------+-----------+
    +|  1 | Miguel |           |
    ++----+--------+-----------+
    +1 row in set (0.00 sec)
    +
    +

    In a new terminal we can create a another branch: +

    +docker compose exec mysql /app/scripts/create_branch.sh base feature/user-address
    +
    +{"branch_name":"feature/user-address","base_branch":"base","port":33063}
    +
    +

    Then connect using port 33063 +docker compose exec mysql mysql -uroot -h127.0.0.1 --skip-password -P33063

    +mysql> use s1;
    +Reading table information for completion of table and column names
    +You can turn off this feature to get a quicker startup with -A
    +
    +Database changed
    +mysql> alter table people add column last_name varchar(255) not null;
    +Query OK, 0 rows affected (0.03 sec)
    +Records: 0  Duplicates: 0  Warnings: 0
    +
    +mysql> select * from people;
    ++----+--------+
    +| id | name   |
    ++----+--------+
    +|  1 | Miguel |
    ++----+--------+
    +1 row in set (0.00 sec)
    +
    +mysql> alter table people add column address varchar(255) not null;
    +Query OK, 0 rows affected (0.02 sec)
    +Records: 0  Duplicates: 0  Warnings: 0
    +
    +mysql> select * from people;
    ++----+--------+---------+
    +| id | name   | address |
    ++----+--------+---------+
    +|  1 | Miguel |         |
    ++----+--------+---------+
    +1 row in set (0.00 sec)
    +
    +

    As you can see, we have 3 servers running at the same time, each one with different schemas. +

    This is great for local development and for having branch-aware dev environments. +

    Final thoughts

    I hope you find this blogpost useful. +If you want to start using branchable-mysql go ahead. +If you encounter any issues please report them in the github repo or create a pull request. +

    \ No newline at end of file diff --git a/docs/2022/09/22/heroku-to-fly.html b/docs/2022/09/22/heroku-to-fly.html new file mode 100644 index 0000000..04ceea2 --- /dev/null +++ b/docs/2022/09/22/heroku-to-fly.html @@ -0,0 +1,116 @@ +Migrate from Heroku to Fly.io +

    Blog

    Reading time: +

    How to migrate from Heroku to Fly.io

    A couple weeks ago Heroku announced the removal. I have plenty of projects running on free dynos. + I have taken some time to move my code to Fly.io. And also I've written a little tutorial of how to perform the migration.

    I'll use one of my public repos as an example +https://github.com/mliezun/getmyip. It's a simple service that returns the IP from which you're making the request. It's useful when you want to know + your public IP. +

    That project ^ was covered in a previous +blogpost. +

    Migration instructions

    The first thing we need to do is to remove heroku from the remotes. +Inside your project run: +

    git remote remove heroku

    If you have a heroku.yml file, delete it. +

    rm -rf heroku.yml

    Then, we're ready to start using fly. +There are tutorials on the +official fly.io docs for many frameworks and languages. We're going to be following the one for a Docker app, + since it's the most general case. +

    First thing you need to do is create an account in +Fly.io if you don't have one yet. +

    Once you created your account, install the flyctl command line tool. + After that, login by running the following command: +

    flyctl auth login

    After you've logged in to your account, you're ready to launch your application. + Execute the next command and follow the interactive setup. +

    $ flyctl launch
    +Scanning source code
    +Detected a Dockerfile app
    +? App Name (leave blank to use an auto-generated name): 
    +Automatically selected personal organization: Miguel Liezun
    +? Select region: mia (Miami, Florida (US))
    +Created app morning-breeze-4255 in organization personal
    +Wrote config file fly.toml
    +? Would you like to set up a Postgresql database now? No
    +? Would you like to deploy now? Yes
    +Deploying morning-breeze-4255
    +==> Validating app configuration
    +--> Validating app configuration done
    +Services
    +TCP 80/443 -> 8080
    +Remote builder fly-builder-green-pond-8004 ready
    +==> Creating build context
    +--> Creating build context done
    +==> Building image with Docker
    +--> docker host: 20.10.12 linux x86_64
    +...
    +

    Make sure your app listens to port 8080, that's the default for fly apps. + You can change the port inside the file fly.toml if you want +, just search for the internal port and change it. Remember to run launch again if you change the port. +

    # fly.toml file generated for morning-breeze-4255 on 2022-09-21T21:50:20-03:00
    +
    +app = "morning-breeze-4255"
    +kill_signal = "SIGINT"
    +kill_timeout = 5
    +processes = []
    +
    +[env]
    +
    +[experimental]
    +  allowed_public_ports = []
    +  auto_rollback = true
    +
    +[[services]]
    +  http_checks = []
    +  internal_port = 8080 # <- Put your desired port here
    +# ...
    +

    Finally, you only need to open the app and enjoy! +You migrated your first app from heroku to fly :-) +

    $ flyctl open
    +opening http://morning-breeze-4255.fly.dev ...
    +

    Access the newly deployed 'getmyip' service using the link +http://morning-breeze-4255.fly.dev. +

    \ No newline at end of file diff --git a/docs/2022/11/26/grotsky-quine.html b/docs/2022/11/26/grotsky-quine.html new file mode 100644 index 0000000..3748f43 --- /dev/null +++ b/docs/2022/11/26/grotsky-quine.html @@ -0,0 +1,103 @@ +How to write a program that can replicate itself +

    Blog

    Reading time: +

    How to write a program that can replicate itself

    Grotsky is a toy programming language that I made for fun. Today we're visinting the concept of Quines, +a.k.a. self replicating programs. It's said that any turing-complete language should be able to write a program that replicates + itself. And grotsky is no exception.

    Read more about grotsky in previous blogposts: +

    Quines are very easy to write. The language that you're using needs to be able to do a couple things: +

    • Write to a file or stdout (print)
    • Support for string arrays
    • Translate numbers/integers to character ascii representation
    • Concatenate strings
    • Loop through arrays from arbitrary indexes

    Super simple quine: less than 30 lines of code

    let tabChar = 9
    +let quoteChar = 34
    +let commaChar = 44
    +let code = [
    +	"let tabChar = 9",
    +	"let quoteChar = 34",
    +	"let commaChar = 44",
    +	"let code = [",
    +	"]",
    +	"for let i = 0; i < 4; i = i+1 {",
    +	"	io.println(code[i])",
    +	"}",
    +	"for let i = 0; i < code.length; i = i+1 {",
    +	"	io.println(strings.chr(tabChar) + strings.chr(quoteChar) + code[i] + strings.chr(quoteChar) + strings.chr(commaChar))",
    +	"}",
    +	"for let i = 4; i < code.length; i = i+1 {",
    +	"	io.println(code[i])",
    +	"}",
    +]
    +for let i = 0; i < 4; i = i+1 {
    +	io.println(code[i])
    +}
    +for let i = 0; i < code.length; i = i+1 {
    +	io.println(strings.chr(tabChar) + strings.chr(quoteChar) + code[i] + strings.chr(quoteChar) + strings.chr(commaChar))
    +}
    +for let i = 4; i < code.length; i = i+1 {
    +	io.println(code[i])
    +}
    +
    +

    Now we can use grotksy cli to run the program and compare the output to the original source. +

    Save the original source to a file called +quine.gr then run the following commands: +

    +$ grotsky quine.gr > quine_copy.gr
    +$ cmp quine.gr quine_copy.gr
    +$ echo $?
    +0
    +
    +

    If you see a 0 as the final output that means the files are the same. + Otherwise if you saw an error message or a different output, that means something has gone wrong. +

    How exciting is this?!! +We've just written a program that gives itself as an output. +That sounds impossible when you hear it for the first time. But it was actually pretty easy! +

    Source code available here: +https://gist.github.com/mliezun/c750ba701608850bd86d646a3ebf700f. +

    Grotsky cli binary available here: +https://github.com/mliezun/grotsky/releases/tag/v0.0.6

    \ No newline at end of file diff --git a/docs/2023/04/08/redis-clone.html b/docs/2023/04/08/redis-clone.html new file mode 100644 index 0000000..942c6b1 --- /dev/null +++ b/docs/2023/04/08/redis-clone.html @@ -0,0 +1,601 @@ +Writing a Redis clone in Go from scratch +

    Blog

    Reading time: +

    Writing a Redis clone in Go from scratch

    In this post we're going to write a basic Redis clone in Go that implements the most simple commands: GET, +SET, DEL and QUIT. At the end you'll know how to parse a byte stream from a live TCP connection, and hopefully have a working +implementation of Redis.

    What's intersting about this project is that it's production ready (not really). + It's being used in production in an old Web app that I made for a client in 2017. + It has been running for a few months now without issues.

    I mantain that app to this day and I charge like 50 bucks a month for it. I do it because + Im friends with the person that uses the app.

    Long story short, the app's backend is written in PHP and uses Redis for caching, only GET, SET and DEL commands. + I asked my friend if I could replace it with my custom version and said yes, so I decided to give it a go.

    If you're looking for C/C++ implementation, go check out this book.

    What we'll be building

    If you go to the +command list on redis webpage you'll see that there are 463 commands to this day (maybe more if you're in the future). +

    That's a crazy number. Here, we're only implementing 4 commands: GET, SET, DEL, QUIT, + the other 459 commands are left as an exercise to the reader. +

    GET

    GET key
    +

    Returns the value referenced by key. + If the key does not exist then nil is returned. +

    SET

    SET command gains more features on newer versions of Redis. We're going to implement one that has all features + that were realeased up until version 6.0.0. +

    SET key value [NX | XX] [EX seconds | PX milliseconds]
    +

    Stores value as a string that is referenced by key. + Overwrites any data that was previously referenced by the key. +

    Options

    • EX seconds -- Set the specified expire time, in seconds.
    • PX milliseconds -- Set the specified expire time, in seconds.
    • NX -- Only set the key if it does not already exist.
    • XX -- Only set the key if it already exist.

    DEL

    DEL key [key ...]
    +

    Takes 'any' amount of keys as input and removes all of them from storage. If a key doesn't exist it is ignored. + Returns the amount of keys that were deleted. +

    QUIT

    QUIT
    +

    When receiving this command the server closes the connection. It's useful for interactive sessions. + For production environments the client should close the connection without sending any commands. +

    Examples

    Let's start an interactive session of redis to test some commands. + We can install redis-server with docker and run it locally. + Then we can use telnet to connect directly via TCP. + Open a terminal and execute the following instructions: +

    $ docker run -d --name redis-server -p 6379:6379 redis:alpine
    +
    +$ telnet 127.0.0.1 6379
    +Trying 127.0.0.1...
    +Connected to localhost.
    +Escape character is '^]'.
    +

    At this point the prompt should be waiting for you to write something. We're gonna test a couple of commands. + In the code boxes below the first line is the command, following lines are the response. +

    GET a
    +$-1
    +
    ^ That weird $-1 is the special nil value. Which means there's nothing stored here. +

    set a 1
    ++OK
    +
    ^ First thing to notice here is that we can use lowercase version of SET. + Also, when the command is successful returns +OK. +

    set b 2
    ++OK
    +

    SET c 3
    ++OK
    +
    ^ Just storing a couple more values. +

    GET a
    +$1
    +1
    +
    ^ Here the response is returned in two lines. First line is the length of the string. Second line + is the actual string. +

    get b
    +$1
    +2
    +
    ^ We can also use lowercase version of GET, I bet commands are case-insensitive. +

    get C
    +$-1
    +
    ^ Testing with uppercase C gives a nil. Keys seem to be case-sensitive, probably values too. + That makes sense. +

    del a b c
    +:3
    +
    ^ Deleting everything returns the amount of keys deleted. Integers are indicated by ':'. +

    quit
    ++OK
    +Connection closed by foreign host.
    +
    ^ When we send QUIT, the server closes the connection and we're back to our terminal session. +

    With those tests we have enough information to start building. We learned a little bit about the + redis protocol and what the responses look like. +

    Sending commands

    Until now we've been using the inline version of redis command. + There's another kind that follows the +RESP (Redis serialization protocol).

    The RESP protocol is quite similar to what we've seen in the examples above. +The most important addition is arrays. Let's see a Client<>Server interaction + using arrays. +

    Client

    *2
    +$3
    +GET
    +$1
    +a
    +
    Server
    $-1
    +
    The server response looks the same as in the inline version. + But what the client sends looks very different: +

    • In this case, the first thing the client sends is '*' followed by the number of elements in the array, + so '*2' indicates that there are 2 elements in the array and they would be found in the following lines. +
    • After that we have '$3' which means we're expecting the first element to be a string of length 3. + Next line is the actual string, in our case is the command 'GET'. +
    • The next value is also a string and is the key passed to the command. +

    That's almost everything we need to start building a client. There's one last thing: error responses. +

    -Example error message
    +-ERR unknown command 'foo'
    +-WRONGTYPE Operation against a key holding the wrong kind of value
    +
    A response that starts with a '-' is considered an error. The first word is the error type. + We'll only gonna be using 'ERR' as a generic response. +

    RESP protocol is what client libraries use to communicate with Redis. + With all that in our toolbox we're ready to start building. +

    Receiving connections

    A crucial part of our serve is the ability to receive client's information. + The way that this is done is that the server listens on a TCP port and waits + for client connections. Let's start building the basic structure. +

    Create a new go module, open main.go and create a main function as follows. +

    package main
    +
    +import (
    +	"bufio"
    +	"fmt"
    +	"log"
    +	"net"
    +	"strconv"
    +	"strings"
    +	"sync"
    +	"time"
    +)
    +
    +var cache sync.Map
    +
    +func main() {
    +	listener, err := net.Listen("tcp", ":6380")
    +	if err != nil {
    +		log.Fatal(err)
    +	}
    +	log.Println("Listening on tcp://0.0.0.0:6380")
    +
    +	for {
    +		conn, err := listener.Accept()
    +		log.Println("New connection", conn)
    +		if err != nil {
    +			log.Fatal(err)
    +		}
    +
    +		go startSession(conn)
    +	}
    +}
    +

    After declaring the package and imports, we create a global sync.Map that would be our cache. + That's where keys are gonna be stored and retrieved. +

    On the main function we start listening on port 6380. After that we have an infinite loop that accepts + new connections and spawns a goroutine to handle the session. +

    Session handling

    // startSession handles the client's session. Parses and executes commands and writes
    +// responses back to the client.
    +func startSession(conn net.Conn) {
    +	defer func() {
    +		log.Println("Closing connection", conn)
    +		conn.Close()
    +	}()
    +	defer func() {
    +		if err := recover(); err != nil {
    +			log.Println("Recovering from error", err)
    +		}
    +	}()
    +	p := NewParser(conn)
    +	for {
    +		cmd, err := p.command()
    +		if err != nil {
    +			log.Println("Error", err)
    +			conn.Write([]uint8("-ERR " + err.Error() + "\r\n"))
    +			break
    +		}
    +		if !cmd.handle() {
    +			break
    +		}
    +	}
    +}
    +

    It's super important that we close the connection when things are done. That's why we set a deferred function, + to close the connection when the session finishes. +

    After that we handle any panics using recover. We do this mainly because at some point we might be reading from + a connection that was closed by the client. And we don't want the entire server to die in case of an error. +

    Then we create a new parser and start trying to parse commands from the live connection. If we encounter an error + we write the error message back to the client and we finish the session. +

    When cmd.handle() returns false (signaling end of session) we break the loop and the session finishes. +

    Parsing commands

    Basic parser structure: +

    // Parser contains the logic to read from a raw tcp connection and parse commands.
    +type Parser struct {
    +	conn net.Conn
    +	r    *bufio.Reader
    +	// Used for inline parsing
    +	line []byte
    +	pos  int
    +}
    +
    +// NewParser returns a new Parser that reads from the given connection.
    +func NewParser(conn net.Conn) *Parser {
    +	return &Parser{
    +		conn: conn,
    +		r:    bufio.NewReader(conn),
    +		line: make([]byte, 0),
    +		pos:  0,
    +	}
    +}
    +

    This is pretty straight-forward. We store a reference to the connection, a reader and then some + attributes that will help us with parsing. +

    The NewParser() function should be used as a contructor for Parser objects. +

    We need some helper functions that will make parsing easier: +

    func (p *Parser) current() byte {
    +	if p.atEnd() {
    +		return '\r'
    +	}
    +	return p.line[p.pos]
    +}
    +
    +func (p *Parser) advance() {
    +	p.pos++
    +}
    +
    +func (p *Parser) atEnd() bool {
    +	return p.pos >= len(p.line)
    +}
    +
    +func (p *Parser) readLine() ([]byte, error) {
    +	line, err := p.r.ReadBytes('\r')
    +	if err != nil {
    +		return nil, err
    +	}
    +	if _, err := p.r.ReadByte(); err != nil {
    +		return nil, err
    +	}
    +	return line[:len(line)-1], nil
    +}
    +

    Also quite simple. +

    • current(): Returns the character being pointed at by pos inside the line.
    • advance(): Point to the next character in the line.
    • atEnd(): Indicates if we're at the end of the line.
    • readLine(): Reads the input from the connection up to the carriage return char. Skips the '\n' char.

    Parsing strings

    In Redis we can send commands like so: +

    SET text "quoted \"text\" here"
    +

    This means we need a way to handle \, " chars inside a string. +

    For that we need a special parsing function that will handle strings: +

    // consumeString reads a string argument from the current line.
    +func (p *Parser) consumeString() (s []byte, err error) {
    +	for p.current() != '"' && !p.atEnd() {
    +		cur := p.current()
    +		p.advance()
    +		next := p.current()
    +		if cur == '\\' && next == '"' {
    +			s = append(s, '"')
    +			p.advance()
    +		} else {
    +			s = append(s, cur)
    +		}
    +	}
    +	if p.current() != '"' {
    +		return nil, errors.New("unbalanced quotes in request")
    +	}
    +	p.advance()
    +	return
    +}
    +

    From the functions that we've declared up to this point it's pretty clear that our parser + will be reading the input line by line. And the consuming the line one char at a time. +

    The way consumeString() works is quite tricky. + It assumes that the initial " has been consumed before entering the function. + And it consumes all characters in the current line up until it reaches the closing quotes character + or the end of the line. +

    Inside the loop we can see that we're reading the current character and advancing the pointer, then + the next character. + When the user is sending an escaped quote inside the string we detect that by checking the current + and the next characters. + In this special case we end up advancing the pointer twice. Because we consumed two: chars the backslash + and the quote. But we added only one char to the output: ". +

    We append all other characters to the output buffer. +

    When the loop finishes, if we're not pointing to the end quote char, that means that the user + sent an invalid command and we return an error. +

    Otherwise we advance the pointer and return normally. +

    Parsing commands

    // command parses and returns a Command.
    +func (p *Parser) command() (Command, error) {
    +	b, err := p.r.ReadByte()
    +	if err != nil {
    +		return Command{}, err
    +	}
    +	if b == '*' {
    +		log.Println("resp array")
    +		return p.respArray()
    +	} else {
    +		line, err := p.readLine()
    +		if err != nil {
    +			return Command{}, err
    +		}
    +		p.pos = 0
    +		p.line = append([]byte{}, b)
    +		p.line = append(p.line, line...)
    +		return p.inline()
    +	}
    +}
    +

    We read the first character sent by the client. If it's an asterisk we handle it + using the RESP protocol. Otherwise we assume that it's an inline command. +

    Let's start by parsing the inline commands first. +

    // Command implements the behavior of the commands.
    +type Command struct {
    +	args []string
    +	conn net.Conn
    +}
    +
    +// inline parses an inline message and returns a Command. Returns an error when there's
    +// a problem reading from the connection or parsing the command.
    +func (p *Parser) inline() (Command, error) {
    +	// skip initial whitespace if any
    +	for p.current() == ' ' {
    +		p.advance()
    +	}
    +	cmd := Command{conn: p.conn}
    +	for !p.atEnd() {
    +		arg, err := p.consumeArg()
    +		if err != nil {
    +			return cmd, err
    +		}
    +		if arg != "" {
    +			cmd.args = append(cmd.args, arg)
    +		}
    +	}
    +	return cmd, nil
    +}
    +

    This is also quite easy to skim through. We skip any leading whitespace + in case the user sent something like ' GET a'. +

    We create a new Command object with a reference to the session connection. +

    While we're not at the end of the line we consume args and append them to the + arg list of the command object if they are not empty. +

    Consuming arguments

    // consumeArg reads an argument from the current line.
    +func (p *Parser) consumeArg() (s string, err error) {
    +	for p.current() == ' ' {
    +		p.advance()
    +	}
    +	if p.current() == '"' {
    +		p.advance()
    +		buf, err := p.consumeString()
    +		return string(buf), err
    +	}
    +	for !p.atEnd() && p.current() != ' ' && p.current() != '\r' {
    +		s += string(p.current())
    +		p.advance()
    +	}
    +	return
    +}
    +

    Same as before we consume any leading whitespace. +

    If we find a quoted string we call our function from before: consumeString(). +

    We append all characters to the output until we reach a carriage return \r, a whitespace + or the end of the line. +

    Parsing RESP protocol

    // respArray parses a RESP array and returns a Command. Returns an error when there's
    +// a problem reading from the connection.
    +func (p *Parser) respArray() (Command, error) {
    +	cmd := Command{}
    +	elementsStr, err := p.readLine()
    +	if err != nil {
    +		return cmd, err
    +	}
    +	elements, _ := strconv.Atoi(string(elementsStr))
    +	log.Println("Elements", elements)
    +	for i := 0; i < elements; i++ {
    +		tp, err := p.r.ReadByte()
    +		if err != nil {
    +			return cmd, err
    +		}
    +		switch tp {
    +		case ':':
    +			arg, err := p.readLine()
    +			if err != nil {
    +				return cmd, err
    +			}
    +			cmd.args = append(cmd.args, string(arg))
    +		case '$':
    +			arg, err := p.readLine()
    +			if err != nil {
    +				return cmd, err
    +			}
    +			length, _ := strconv.Atoi(string(arg))
    +			text := make([]byte, 0)
    +			for i := 0; len(text) <= length; i++ {
    +				line, err := p.readLine()
    +				if err != nil {
    +					return cmd, err
    +				}
    +				text = append(text, line...)
    +			}
    +			cmd.args = append(cmd.args, string(text[:length]))
    +		case '*':
    +			next, err := p.respArray()
    +			if err != nil {
    +				return cmd, err
    +			}
    +			cmd.args = append(cmd.args, next.args...)
    +		}
    +	}
    +	return cmd, nil
    +}
    +

    As we know, the leading asterisk has already been consumed from the connection input. + So, at this point, the first line contains the number of elements to be consumed. + We read that into an integer. +

    We create a for loop with that will parse all the elements in the array. + We consume the first character to detect which kind of element we need to consume: int, string or array. +

    The int case is quite simple, we just read until the rest of the line. +

    The array case is also quite simple, we call respArray() and append the args of the result, + to the current command object. +

    For strings we read the first line and get the size of the string. + We keep reading lines until we have read the indicated amount of characters. +

    Handling commands

    This is the 'fun' part of the implementation. Were our server becomes alive. + In this section we'll implement the actual functionality of the commands. +

    Let's start with the cmd.handle() function that we saw in handleSession(). +

    // handle Executes the command and writes the response. Returns false when the connection should be closed.
    +func (cmd Command) handle() bool {
    +	switch strings.ToUpper(cmd.args[0]) {
    +	case "GET":
    +		return cmd.get()
    +	case "SET":
    +		return cmd.set()
    +	case "DEL":
    +		return cmd.del()
    +	case "QUIT":
    +		return cmd.quit()
    +	default:
    +		log.Println("Command not supported", cmd.args[0])
    +		cmd.conn.Write([]uint8("-ERR unknown command '" + cmd.args[0] + "'\r\n"))
    +	}
    +	return true
    +}
    +

    Needs no further explanation. Let's implement the easiest command: QUIT. +

    // quit Used in interactive/inline mode, instructs the server to terminate the connection.
    +func (cmd *Command) quit() bool {
    +	if len(cmd.args) != 1 {
    +		cmd.conn.Write([]uint8("-ERR wrong number of arguments for '" + cmd.args[0] + "' command\r\n"))
    +		return true
    +	}
    +	log.Println("Handle QUIT")
    +	cmd.conn.Write([]uint8("+OK\r\n"))
    +	return false
    +}
    +

    If any extra arguments were passed to QUIT, it returns an error. +

    Otherwise write +OK to the client and return false. + Which if you remember handleSession() is the value to indicate that the session has finished. + After that the connection will be automatically closed. +

    The next easieast command is DEL +

    // del Deletes a key from the cache.
    +func (cmd *Command) del() bool {
    +	count := 0
    +	for _, k := range cmd.args[1:] {
    +		if _, ok := cache.LoadAndDelete(k); ok {
    +			count++
    +		}
    +	}
    +	cmd.conn.Write([]uint8(fmt.Sprintf(":%d\r\n", count)))
    +	return true
    +}
    +

    Iterates through all the keys passed, deletes the ones that exists and + writes back to the client the amount of keys deleted. +

    Returns true, which means the connection is kept alive. +

    Handling GET

    // get Fetches a key from the cache if exists.
    +func (cmd Command) get() bool {
    +	if len(cmd.args) != 2 {
    +		cmd.conn.Write([]uint8("-ERR wrong number of arguments for '" + cmd.args[0] + "' command\r\n"))
    +		return true
    +	}
    +	log.Println("Handle GET")
    +	val, _ := cache.Load(cmd.args[1])
    +	if val != nil {
    +		res, _ := val.(string)
    +		if strings.HasPrefix(res, "\"") {
    +			res, _ = strconv.Unquote(res)
    +		}
    +		log.Println("Response length", len(res))
    +		cmd.conn.Write([]uint8(fmt.Sprintf("$%d\r\n", len(res))))
    +		cmd.conn.Write(append([]uint8(res), []uint8("\r\n")...))
    +	} else {
    +		cmd.conn.Write([]uint8("$-1\r\n"))
    +	}
    +	return true
    +}
    +

    As before, we validate that the correct number of arguments were passed to the command. +

    We load the value from the global variable cache. +

    If the value is nil we write back to the client the special $-1. +

    When we have a value we cast it as string and unquote it in case it's quoted. + Then we write the length as the first line of the response and the string as the + second line of the response. +

    Handling SET

    This is the most complicated command that we'll implement. +

    // set Stores a key and value on the cache. Optionally sets expiration on the key.
    +func (cmd Command) set() bool {
    +	if len(cmd.args) < 3 || len(cmd.args) > 6 {
    +		cmd.conn.Write([]uint8("-ERR wrong number of arguments for '" + cmd.args[0] + "' command\r\n"))
    +		return true
    +	}
    +	log.Println("Handle SET")
    +	log.Println("Value length", len(cmd.args[2]))
    +	if len(cmd.args) > 3 {
    +		pos := 3
    +		option := strings.ToUpper(cmd.args[pos])
    +		switch option {
    +		case "NX":
    +			log.Println("Handle NX")
    +			if _, ok := cache.Load(cmd.args[1]); ok {
    +				cmd.conn.Write([]uint8("$-1\r\n"))
    +				return true
    +			}
    +			pos++
    +		case "XX":
    +			log.Println("Handle XX")
    +			if _, ok := cache.Load(cmd.args[1]); !ok {
    +				cmd.conn.Write([]uint8("$-1\r\n"))
    +				return true
    +			}
    +			pos++
    +		}
    +		if len(cmd.args) > pos {
    +			if err := cmd.setExpiration(pos); err != nil {
    +				cmd.conn.Write([]uint8("-ERR " + err.Error() + "\r\n"))
    +				return true
    +			}
    +		}
    +	}
    +	cache.Store(cmd.args[1], cmd.args[2])
    +	cmd.conn.Write([]uint8("+OK\r\n"))
    +	return true
    +}
    +

    As always, first thing we do is validate the number of arguments. + But in this case, SET is more tricky than the others. +

    When more than 3 arguments are passed we check for the NX or XX flags and handle them accordingly. +

    • NX -- Only set the key if it does not already exist.
    • XX -- Only set the key if it already exist.

    Then we parse the expiration flags if any. We'll see how that's done in a second. +

    After handling all those special cases we finally store the key and value in the cache, + write the +OK response and return true to keep the connection alive. +

    Expiration

    // setExpiration Handles expiration when passed as part of the 'set' command.
    +func (cmd Command) setExpiration(pos int) error {
    +	option := strings.ToUpper(cmd.args[pos])
    +	value, _ := strconv.Atoi(cmd.args[pos+1])
    +	var duration time.Duration
    +	switch option {
    +	case "EX":
    +		duration = time.Second * time.Duration(value)
    +	case "PX":
    +		duration = time.Millisecond * time.Duration(value)
    +	default:
    +		return fmt.Errorf("expiration option is not valid")
    +	}
    +	go func() {
    +		log.Printf("Handling '%s', sleeping for %v\n", option, duration)
    +		time.Sleep(duration)
    +		cache.Delete(cmd.args[1])
    +	}()
    +	return nil
    +}
    +

    We read the option and the expiration value, then we compute the duration + for each case and we spawn a new goroutine that sleeps for that amount of + time and the deletes the key from the cache. +

    This is not the most efficient way to do it, but it's simple and it works for us. +

    Working server

    At this point we have an usable implementation of Redis. +

    Let's start the server the server and test it. +

    $ go run main.go
    +2023/04/08 21:09:40 Listening on tcp://0.0.0.0:6380
    +

    On a different terminal connect to the server. +

    $ telnet 127.0.0.1 6380
    +GET a
    +$-1
    +set a "test \"quotes\" are working"
    ++OK
    +get a
    +$25
    +test "quotes" are working
    +

    It's alive!! Go have fun. +

    If you'd like to access the source code of this project there's a public gist + containing all of the code displayed here. +

    Link to source code

    \ No newline at end of file diff --git a/docs/2023/06/02/rewrite-grotsky-rust.html b/docs/2023/06/02/rewrite-grotsky-rust.html new file mode 100644 index 0000000..2074b55 --- /dev/null +++ b/docs/2023/06/02/rewrite-grotsky-rust.html @@ -0,0 +1,75 @@ +Rewrite my toy language interpreter in Rust +

    Blog

    Reading time: +

    Rewrite my toy language interpreter in Rust

    Im rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x.

    As of this writting the Rust implementation is 4x faster than the Golang implementation.

    I've rewritten the Lexer, Parser and part of the Runtime. Enough for run this code and measure + how long it takes for each implementation to finish:

    let a = 1
    +while a < 100000000 {
    +    a = a + 1
    +}
    +

    I was inspired by Mitchel Hashimoto's post: +My Approach to Building Large Technical Projects. And I want to create a roadmap of little projects to reach my goal of having a full-fledged interpreter in Rust. +

    Goal

    Until now Grotsky has been running on a Tree-based interpreter. My goal is that at the end of the rewrite I + will have a Bytecode interpreter. +

    First I want to rewrite the Tree-based interpreter in Rust and achieve at least 10x performance improvement. +

    Then figure out if I want to use a Register based or Stack based bytecode interpreter. +

    Also, I would like to have a stable bytecode representation to be able to compile programs to a binary format + that can be shipped as is or packaged into a binary. +

    Finally, it's time Grotsky gets a REPL. +

    Roadmap

    Believe it or not, Grotsky it's pretty big. It has support for reading and writting files, sockets and more + on the stdlib. Which means I have a huge task ahead of me. +

    First thing I want to do is to have some sort of automated testing setup that can compare Rust vs Go implementation. + Right now all test are written as Go unit test, I need to make them agnostic of language backend. +

    • Jun 9: Have a complete setup of automated tests for correctness and performance.
    • Jun 16: Language runtime (without stdlib) rewritten in Rust.
    • Jun 23: Finish migrating stdlib and publish results (new blog post). Use new implementation for blog engine.
    • Jun 30: Decide which kind of bytecode interpreter to use, then make a design and plan for the implementation.
    • Jul 7: Have a working bytecode interpreter that is able to run the program shown in this post ^. Just a simple while loop. Compare performance and share progress.
    • Jul 14: Add support for functions and closures.
    • Jul 21: Finish runtime without stdlib in bytecode interpreter.
    • Jul 28: Implement stdlib in bytecode interpreter. Share results.
    • Aug 5: Add ability to compile to bytecode and run from bytecode.
    • Aug 12: Add REPL and finish up project.

    Im gonna have lots of fun with this project and Im sure next time a post here I'll be a changed man. + Surely gonna learn a lot, Im excited about what lies ahead. +

    \ No newline at end of file diff --git a/docs/2023/07/15/the-end-of-a-side-project.html b/docs/2023/07/15/the-end-of-a-side-project.html new file mode 100644 index 0000000..98c1bd6 --- /dev/null +++ b/docs/2023/07/15/the-end-of-a-side-project.html @@ -0,0 +1,97 @@ +The end of a side project +

    Blog

    Reading time: +

    The end of a side project

    Cloud Outdated was a personalized digest of updates for cloud services. It's sad to see it go, + but it was a fun project to work on, learn some new stuff and collab with a friend. + There are some takeaways from this that I'd like to share. +

    Building something simple is hard

    From the beginning we were trying to build a simple product to get notified when new versions + of Python were supported in AWS Lambda. But we figured we could support other things too, like + all Lambda runtimes, then also GCP and Azure, then more services of each one. +Features started piling up pretty quickly. +

    When building stuff I try to think of every edge case, even the most improbable ones, and make + the software infalible. Of course, it's impossible to succeed at that, software will always be fallible. +And this premature optimization ends up making the project more complex than it should be. +

    We planned to work on this for a 1 or 2 months, and it ended up taking 6+ months :-). +

    My takeaway here is: start building the dumbest thing possible that gets the job done, then adjust as needed. +

    Getting users is hard

    We're killing this project because nobody uses it. +And nobody except us has used it since it was launched more than a year ago. +Some people subscribed but never even opened an email. +

    We tried to advertise in our social media and post it in different builders communities. +But that will get you so far if you're not an influencer that has the right audience. +

    We thought that we'll get more traffic from organic search or people telling their friends about this. +But in the end I think nobody really needed something like this that much. +

    My takeaway here is: building something that people really want and getting the product to the hands + of the people that want it is very complicated. You should think very deeply about what problem your + product is solving and how your users will find you. +

    The money

    This has costed like $200 (US dollars) since inception. For two people that's like $100 each. +It's not a lot, but for something that has no users that's quite expensive. +

    We used Lambdas to serve this project. +I feel like we were promised that the cloud and serverless are cheap and easy solution. +But in my opinion it doesn't seem to be the case. +It's definitely not easier nor cheaper than having a PHP website in this case. +Im sure there are other cases in which it makes more sense. +

    This is also a reason of why we're killing the project. +Our service started failing because after a deploy dependencies were updated and the code + was to big to fit on a Lambda. +It would have been a lot of work to fix it, so we decided to kill it and save some bucks every month. +

    For personal projects which you're not sure how they're going to scale, + I think serverless is probably not the right choice. +Serverless make sense if you're going to have huge burst of traffics and don't want + to handle a lot of infra. +

    My takeaway here is: beware of the cloud, if you're just building a small side project + or don't have huge infra needs stick with the cheapest providers (Hostinger, PythonAnywhere, Hetzner) + and avoid cloud providers (GCP, Azure, AWS). +

    Final thougths

    If I haven't made this clear enough, building a successful product is *hard*. +There are many things to think about when starting, and the technical stuff hopefully + is the easy part. +I think this are the 3 most important lessons that I've learned working on this: +

    • Build the dumbest thing that does the job, improve as needed.
    • Think deeply about what problem are you solving and how you're going to deliver the solution to the people that need it.
    • Beware of the cloud, if possible use a cheaper provider. It will save you money and headaches.

    \ No newline at end of file diff --git a/docs/2023/09/23/grotsky-rust-part2.html b/docs/2023/09/23/grotsky-rust-part2.html new file mode 100644 index 0000000..3ca2095 --- /dev/null +++ b/docs/2023/09/23/grotsky-rust-part2.html @@ -0,0 +1,75 @@ +Rewrite my toy language interpreter in Rust, an update +

    Blog

    Reading time: +

    Rewrite my toy language interpreter in Rust, an update

    Im rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x.

    In a +previous post + I've outlined a plan to migrate Grotsky to a Rust based platform. +

    I was suposed to finish it more than a month ago :'). +

    Of course, I wasn't able to do it. +

    I think my original estimation was actually very flawed, but I also didn't put enough work. +

    I failed my estimation, because I decided to skip the step of first writing a Tree-based interpreter in Rust. + To go directly to a Register-based virtual machine. +

    The reason that is taking me so long is that I travel a lot. I've been a digital nomad for more than a year now. + And when I finish working I prefer to go outside and explore the place where Im staying. + I go to a lot of interesting places: at this time Im in Turkey and heading to Hong Kong, after that Im going to South Korea. + So, it makes sense to actually experience the places where Im staying than to stay inside writing code. +

    Im here to propose a new roadmap that is more achivable with the time I have to work on this. + And the idea is to finish it before the end of the year. +

    Plus, I recently heard some advice that I think it's worth to try: Work on someone for 15 minutes every day. + I do have 15 minutes everyday where Im probably scrolling through social media or just consuming content. + I can make better use of that time by putting it into this project. +

    Updated Roadmap

    • Sep 30: Publish a blogpost about the memory model of the Rust implementation.
    • Oct 15: Migrate automated tests to the new backend and make sure they pass.
    • Oct 30: Implement stdlib in bytecode interpreter. Share results.
    • Nov 15: Add ability to compile to bytecode and run from bytecode.
    • Dec 30: Finish up everything and publish a blogpost update before end of year.

    This is gonna be rough, because of the traveling and being jetlaged all the time. + But I think the roadmap is easy enough so I can do it. +

    Thanks for reading. Please, leave a comment about your experience and struggle with side projects. +

    \ No newline at end of file diff --git a/docs/2023/11/23/grotsky-rust-part3.html b/docs/2023/11/23/grotsky-rust-part3.html new file mode 100644 index 0000000..307c3ae --- /dev/null +++ b/docs/2023/11/23/grotsky-rust-part3.html @@ -0,0 +1,187 @@ +I rewrote my toy language interpreter in Rust +

    Blog

    Reading time: +

    I rewrote my toy language interpreter in Rust

    Im rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x. This has been a serious of posts, this one is the latest one. Hopefully the best and most insightful +of them all.

    In previous posts: +

    I've outlined a plan to migrate Grotsky to a Rust based platform. +

    + Originally, my plan was very ambitious and I thought I would be able to finish the transition + in like two months. + In reality it took five months :-) + +

    Performance improvement

    I was aiming at a 10x improvement. In reality is not that much. + I ran various benchmarks and get at most a 4x improvement. + Which is not great, but also not that bad given that I know very little + of how to do high performance Rust. + The interpreter is written in the most dumbest and easiest way I managed to do it. +

    Let's look at some numbers for different programs. +

    Loop program

    let start = io.clock()
    +fn test() {
    +    let a = 1
    +    while a < 10000000 {
    +        a = a + 1
    +    }
    +}
    +test()
    +io.println(io.clock() - start)
    +

    The result is: +

    trial #1
    +build/grotsky   best 5.135s  3.877x time of best
    +build/grotsky-rs   best 1.325s  287.6800% faster
    +trial #2
    +build/grotsky   best 5.052s  3.814x time of best
    +build/grotsky-rs   best 1.325s  281.4182% faster
    +trial #3
    +build/grotsky   best 5.035s  3.802x time of best
    +build/grotsky-rs   best 1.325s  280.1663% faster
    +trial #4
    +build/grotsky   best 5.003s  3.777x time of best
    +build/grotsky-rs   best 1.325s  277.6831% faster
    +trial #5
    +build/grotsky   best 5.003s  3.777x time of best
    +build/grotsky-rs   best 1.325s  277.6831% faster
    +

    Recusive fibonacci

    let start = io.clock()
    +fn fib(n) {
    +	if n <= 2 {
    +		return n
    +	}
    +	return fib(n-2) + fib(n-1)
    +}
    +fib(28)
    +io.println(io.clock() - start)
    +

    The result is: +

    trial #1
    +build/grotsky   best 0.8409s  294.5155% faster
    +build/grotsky-rs   best 3.317s  3.945x time of best
    +trial #2
    +build/grotsky   best 0.8168s  271.3829% faster
    +build/grotsky-rs   best 3.033s  3.714x time of best
    +trial #3
    +build/grotsky   best 0.797s  245.6835% faster
    +build/grotsky-rs   best 2.755s  3.457x time of best
    +trial #4
    +build/grotsky   best 0.7784s  249.9964% faster
    +build/grotsky-rs   best 2.724s  3.5x time of best
    +trial #5
    +build/grotsky   best 0.7784s  249.9964% faster
    +build/grotsky-rs   best 2.724s  3.5x time of best
    +

    In this case is like 3.5x slower. This is due to function calls. + Im not very well versed in Rust, so on each call im copying a lot of + data over and over. In the go implementation everything is just pointers + so there's less copying. +

    Compile to bytecode

    With the Rust implementation, generating and compiling to bytecode was added. + Now it's possible to generate a bytecode file to later read it. + This is a way of distributing files without giving away source code and also + a little bit more performant because you skip parsing and compilation phases. +

    How it works: +

    grotsky compile example.gr  # Compile file
    +grotsky example.grc  # Run compiled file
    +

    Memory model

    Grotsky is a reference-counted language. We're using Rust's Rc and RefCell to keep track of values. +

    pub struct MutValue<T>(pub Rc<RefCell<T>>);
    +
    +impl<T> MutValue<T> {
    +    pub fn new(obj: T) -> Self {
    +        MutValue::<T>(Rc::new(RefCell::new(obj)))
    +    }
    +}
    +
    +pub enum Value {
    +    Class(MutValue<ClassValue>),
    +    Object(MutValue<ObjectValue>),
    +    Dict(MutValue<DictValue>),
    +    List(MutValue<ListValue>),
    +    Fn(MutValue<FnValue>),
    +    Native(NativeValue),
    +    Number(NumberValue),
    +    String(StringValue),
    +    Bytes(BytesValue),
    +    Bool(BoolValue),
    +    Slice(SliceValue),
    +    Nil,
    +}
    +
    +pub enum Record {
    +    Val(Value),
    +    Ref(MutValue<Value>),
    +}
    +
    +pub struct VM {
    +    pub activation_records: Vec<Record>,
    +}
    +

    Most of the simple values are just stored as-is: Native (builtin functions), Number, String, + Bytes, Bool, Slice and Nil. +

    For the other complex values we need to use 'pointers' which in this case are MutValue. +

    Then the Grotsky VM uses Records which can be a plain Value or a reference to a Value. + The records are registers, each function has up to 255 registers. + The reference to values are used to store upvalues. + A register is turned into an upvalue when a variable is closed by another function. +

    This implementation ends up being very slow, but easy to manage. Because Rust stdlib + does all the work. +

    Using Rust in this blogpost

    As you may know, this blog is powered by grotsky. + Im happy to say that I successfully migrated from grotsky to grostky-rs as the backend for the blog. + And what you're reading now is generated by the latest implementation of the language using Rust. +

    Even for local development the Rust version is used. Which means Im using a TCP server and an HTTP + implementation written in Grotsky. +

    Closing remarks

    This has been a great learning, Im happy to have finished because it required a lot of effort. + Im not gonna announce any new work on this interpreter but I would like to keep adding stuff. + Improving it further to make it more performant and more usable. +

    In the end I encourage everyone to try it and also start their own project. Is always cool to see + what everyone else is doing. +

    Thanks for reading and please leave a comment. +

    \ No newline at end of file diff --git a/docs/2023/12/25/favourite-advent-of-code-2023.html b/docs/2023/12/25/favourite-advent-of-code-2023.html new file mode 100644 index 0000000..2c759ac --- /dev/null +++ b/docs/2023/12/25/favourite-advent-of-code-2023.html @@ -0,0 +1,412 @@ +Day 20. My favourite problem from Advent of Code 2023 +

    Blog

    Reading time: +

    Day 20 +

    Im gonna briefly describe the problem here, but if you want to see the real thing go check it out https://adventofcode.com/2023/day/20. +

    I like it because it involves some simple electronic devices that are wired together and send pulses/signals to each other. In this problem you have to make sure to correctly propagate the signals and simulate the behaviour of the devices. +

    There are two devices that have a very distinct behaviour: +

  • Flip flops: similar to a T flip-flop electronic device.
  • +
  • Conjunctions: similar to a NAND gate with memory on its inputs.
  • +

    In this problem, Flip flops are initially off and whenever they reiceve a low pulse they toggle between on/off. Each time it toggles state it sends a pulse as an output. When turned off sends a low pulse, when turned on sends a high pulse. +

    Conjunction modules remember the most recent pulse on each input. By default it remembers a low pulse for all inputs. When a pulse is received it updates the memory for that input. Then, if it remembers high pulses for all inputs, it sends a low pulse; otherwise, it sends a high pulse. +

    There is also some "dummy" modules: +

  • Broadcaster: has 1 input and N outputs. It replicates the input in all its outputs.
  • +
  • Button: when pressed sends a low pulse. The button is always connected as the broadcaster input. This is similar to a normally closed switch.
  • +
  • Test module: module that receive and process inputs but has no output.
  • +

    One important thing to have in mind is that modules only send output pulses when they receive a pulse as input. +

    Problem input +

    The example input looks something like this: +
    +broadcaster -> a, b, c
    +%a -> b
    +%b -> c
    +%c -> inv
    +&inv -> a
    +
    +

    There will always be just one Broadcaster module called "broadcaster" that has the Button connected as input. In this case it has module's "a", "b" and "c" connected to its output. +

    The arrow -> indicates what modules are connected to the output of the module to the left. +

    Lines that start with % means the module is a Flip flop, for example: %a -> b indicates that there's a flip flop called "a" whose output is connected to module's "b" input. +

    Lines that start with & means the module is a Conjunction, for example: &inv -> a indicates that there's a conjunction called "inv" whose output is connected to module's "a" input. +

    Let's analyze how this circuit behaves once the button is pushed: +
    +button -0-> broadcaster
    +broadcaster -0-> a
    +broadcaster -0-> b
    +broadcaster -0-> c
    +a -1-> b
    +b -1-> c
    +c -1-> inv
    +inv -0-> a
    +a -0-> b
    +b -0-> c
    +c -0-> inv
    +inv -1-> a
    +
    +In this example 8 low (0) pulses and 4 high (1) pulses are sent. +

    Part 1 +

    To solve the first part we need to calculate the multiplication between high and low pulses sent between devices. +

    In the previous example that would be 8*4=32. +

    But this time we don't push the button only once, but we push it a 1000 times. Each time we push the button we wait until all signals propagate and the circuit settles into a state before pushing the button again. +

    Solution +

    First I started by modelling the devices as objects. Starting with a single base class that has most of the common behaviour. +
    +from abc import ABC
    +measure_pulses = {0: 0, 1: 0}
    +class Module(ABC):
    +    def __init__(self, name: str):
    +        self.name = name
    +        self.outputs = []
    +
    +    def receive_pulse(self, mod: "Module", pulse: int) -> list[tuple["Module", int]]:
    +        measure_pulses[pulse] += 1
    +        print(f"{mod and mod.name} -{pulse}-> {self.name}")
    +        return self.process_pulse(mod, pulse)
    +
    +    def connect_output(self, mod: "Module"):
    +        self.outputs.append(mod)
    +
    +    def propagate_pulse(self, pulse: int):
    +        mods = []
    +        for m in self.outputs:
    +            mods.append((m, pulse))
    +        return mods
    +
    +    def process_pulse(self, mod: "Module", pulse: int):
    +        raise NotImplementedError()
    +
    +    def __str__(self) -> str:
    +        return f"{self.__class__.__name__}(name={self.name})"
    +
    +    def __repr__(self) -> str:
    +        return str(self)
    +
    +

    What we see here is that we expect all modules to have a name and outputs. See __init__(), __str__(), __repr__() and connect_output(). +

    Each module can receive a pulse 0 or 1 from another module. See receive_pulse(). Each time we process a pulse we record it in a global dict called measure_pulses. +

    Also we leave process_pulse() to be defined by each particular module type. +

    We have a method that returns a list of all modules to which signals should be propagated. See propagate_pulse(). +

    Let's start by the easiest module type: +
    +class TestModule(Module):
    +    def process_pulse(self, mod: "Module", pulse: int):
    +        return []
    +
    +Give that it's a dummy module, it doesn't do anything when it receives an input. +
    +class Broadcaster(Module):
    +    def process_pulse(self, mod: "Module", pulse: int):
    +        return super().propagate_pulse(pulse)
    +
    +As expected the Broadcaster always propagates the received input to all its outputs. +
    +class FlipFlop(Module):
    +    def __init__(self, name: str):
    +        super().__init__(name)
    +        self.state = 0
    +
    +    def process_pulse(self, mod: "Module", pulse: int):
    +        if pulse == 0:
    +            self.state = (self.state + 1) % 2
    +            return super().propagate_pulse(self.state)
    +        return []
    +
    +

    The flip flop start initially turned off. See self.state = 0 in __init__(). +

    In process_pulse() we implement the behaviour: +

  • If receives a low pulse, toggles the state and sends a pulse equals to the state to all its outputs.
  • +
  • Otherwise it doesn't do anything.
  • +
    +class Conjunction(Module):
    +    def __init__(self, name: str):
    +        super().__init__(name)
    +        self.memory = {}
    +
    +    def remember_input(self, mod: Module):
    +        self.memory[mod.name] = 0
    +
    +    def process_pulse(self, mod: Module, pulse: int):
    +        self.memory[mod.name] = pulse
    +        if all(self.memory.values()):
    +            return self.propagate_pulse(0)
    +        return self.propagate_pulse(1)
    +
    +

    The conjunction initializes its memory as empty. See __init__(). +

    Each time a module is plugged in as an input it remembers it as OFF (0). See remember_input(). +

    The way it processes pulses is by first recording the pulse for the input in its memory. Then if all inputs are 1s it sends a 0 pulse to all its outputs. +

    Otherwise it sends a 1 pulse to all its outputs. +

    At this point we have all our building blocks for solving this problem. We only need to parse the input and something that pushes the button and makes sure signals are propagated to the end. +

    Parsing modules is straightforward: +
    +def parse_modules(modules: list) -> dict[str, Module]:
    +    modules_by_name = {}
    +    outputs_by_name = {}
    +
    +    # Parse all modules into their correspondig class and store
    +    # them in a dict.
    +    for m in modules:
    +        module_type = m[0]
    +        module_outputs = [o.strip() for o in m[1].split(",") if o.strip()]
    +        if module_type.startswith("broadcaster"):
    +            modules_by_name[module_type] = Broadcaster(module_type)
    +            outputs_by_name[module_type] = module_outputs
    +        elif module_type.startswith("%"):
    +            modules_by_name[module_type[1:]] = FlipFlop(module_type[1:])
    +            outputs_by_name[module_type[1:]] = module_outputs
    +        elif module_type.startswith("&"):
    +            modules_by_name[module_type[1:]] = Conjunction(module_type[1:])
    +            outputs_by_name[module_type[1:]] = module_outputs
    +    # Once all the modules are parsed use connect their outputs.
    +
    +    # If the module doesn't exist at this point is a TestModule.
    +    # If the module is a Conjunction, call remember_input().
    +    for name, outputs in outputs_by_name.items():
    +        for mod_name in outputs:
    +            mod = modules_by_name.get(mod_name, TestModule(mod_name))
    +            modules_by_name[name].connect_output(mod)
    +            if isinstance(mod, Conjunction):
    +                mod.remember_input(modules_by_name[name])
    +
    +    return modules_by_name
    +
    +

    If we parse our example using that function we will receive a dictionary as its output. Keys are module names and values are the objects representing the module. +

    If we parse the example we get something like this: +

    +example = """broadcaster -> a, b, c
    +%a -> b
    +%b -> c
    +%c -> inv
    +&inv -> a"""
    +example_modules = [m.split(" -> ") for m in example.splitlines() if m.strip()]
    +print(parse_modules(example_modules))
    +
    +# Output
    +{
    +    'broadcaster': Broadcaster(name=broadcaster),
    +    'a': FlipFlop(name=a),
    +    'b': FlipFlop(name=b),
    +    'c': FlipFlop(name=c),
    +    'inv': Conjunction(name=inv)
    +}
    +
    +Then we need a function that pushes the button and makes sure all signals are propagated: +
    +def push_button(modules_by_name: dict[str, Module]):
    +    broad = modules_by_name["broadcaster"]
    +    queue = [(broad, broad.receive_pulse(None, 0))]
    +    while queue:
    +        current, signals = queue.pop(0)
    +        for mod, pulse in signals:
    +            queue.append((mod, mod.receive_pulse(current, pulse)))
    +
    +

    Here, we lookup the broadcaster module by name. And send a pulse (note that we pass None as the module because we didn't implement a button class) to the broadcaster. +

    We store the current module (broadcaster) along with all the propagated signals (return value from receive_pulse()) in a queue to be processed. +

    While the signal queue to be processed is not empty we do the following: +

  • Extract the first element of the queue.
  • +
  • Go trough all the signals that this element is sending.
  • +
  • Send the pulses to each corresponding module and store them in the queue to be processed.
  • +

    This process will stop when all responses from receive_pulse() are empty and there are no more signals added to the queue. +

    If we run this for our example: +

    +example_modules = parse_modules(example_modules)
    +push_button(example_modules)
    +
    +# Output
    +None -0-> broadcaster
    +broadcaster -0-> a
    +broadcaster -0-> b
    +broadcaster -0-> c
    +a -1-> b
    +b -1-> c
    +c -1-> inv
    +inv -0-> a
    +a -0-> b
    +b -0-> c
    +c -0-> inv
    +inv -1-> a
    +
    +

    It looks the same as when we analyzed the example above!! +

    We're ready for processing our problems input. (Remeber to comment out the print statement inside receive_pulse()). +

    +modules = open("input.txt", "r").read().strip()
    +modules = [m.split(" -> ") for m in modules.splitlines() if m.strip()]
    +modules = parse_modules(modules)
    +
    +for _ in range(1000):
    +    push_button(modules)
    +
    +print("result:", measure_pulses[0] * measure_pulses[1])
    +
    +# Output
    +result: x
    +
    +Based on your problem input x will be the solution. +

    Part 2 +

    This part as always is much trickier than the first part. It doesn't involve much code changes, just figuring out a way of avoiding large computations. +

    For this part, the problem tells us that there's a module called rx. And we need to find out the lowest amount of button pulses that will make the rx module receive a low pulse. +

    As I have learned troughout this entire challenge, just nahively letting it run and see when the rx module gets a low signal will get me nowhere. It will run forever. +

    So, taking a look at the input and see what the rx module is actually connected to might provide some guidance. +

    Following is for my case (I don't know if all problem inputs are the same). Looking up "rx" in the input I find a single line: +
    +...
    +&lv -> rx
    +...
    +
    +

    That means rx is a TestModule (a dummy module that has nothing connected to its output). And that has only one input: a Conjunction called lv. +

    Ok, that feels like progress. Let's see what lv is connected to: +
    +...
    +&st -> lv
    +&tn -> lv
    +&hh -> lv
    +&dt -> lv
    +...
    +
    +

    Other 4 Conjunctions are connected as inputs of lv. That's interesting. Because lv is a Conjuction it means that to send the low pulse required by rx it should receive all high pulses from its inputs. +

    The solution from here is kind of intuitive at this point. If we figure out how many button pulses does it take for each of the input devices to send a 1 signal we can multiply them together and get the result. +

    I'll explain better. Let's say st sends a 1 on every button push, tn sends a 1 every second button push (this means you have to press the button twice to get tn to send a 1 as an output), hh sends a 1 every fourth button push and dt sends a 1 every eighth button push. +

    So it looks like this: +
    +module | pushes
    +---------------
    +  st   |   1
    +  tn   |   2
    +  hh   |   4
    +  dt   |   8
    +
    +

    In this example, if we push the button 8 times. They are all gonna send a high pulse. Because 8 is divisible by 1, 2, 4 and 8. +

    If the table were different: +
    +module | pushes
    +---------------
    +  st   |   1
    +  tn   |   3
    +  hh   |   5
    +  dt   |   7
    +
    +

    In this case there's no obvious number of times we should push the button. But if we multiply the numbers together we get a number that is divisible by every number in the table. Pushing the button 1 * 3 * 5 * 7 = 105 times will make all the outputs send a 1, and consequently rx will receive a 0. +

    Solution +

    What we need to do then is to figure after out how many button presses we get a 1 on each of those modules. +
    +from collections import defaultdict
    +
    +# Store number of button presses in a global variable
    +ITERATIONS = 0
    +# Store high pulses for target modules
    +OUT_PULSES = defaultdict(list)
    +
    +class Conjunction(Module):
    +    def __init__(self, name: str):
    +        super().__init__(name)
    +        self.memory = {}
    +
    +    def remember_input(self, mod: Module):
    +        self.memory[mod.name] = 0
    +
    +    def start_recording(self):
    +        self.recording = True
    +
    +    def record(self):
    +        if hasattr(self, "recording"):
    +            OUT_PULSES[self.name].append(ITERATIONS)
    +
    +    def process_pulse(self, mod: Module, pulse: int):
    +        self.memory[mod.name] = pulse
    +        if all(self.memory.values()):
    +            return self.propagate_pulse(0)
    +        self.record()
    +        return self.propagate_pulse(1)
    +
    +

    We introduced 2 new methods to the conjunction module: start_recording() and record(). The first just initializes a bool attribute. And the second makes sure to only record high pulses for objects that have been initialized (method start_recording() called). +

    Also introduced 2 global variables: ITERATIONS to keep track of button pushes and OUT_SIGNALS to track each time one of the modules outputs a high pulse. +

    Now we need to make those specific modules record their outputs: +
    +# Get the "lv" module by name
    +lv = modules["lv"]
    +lv_inputs = [modules[k] for k in lv.memory.keys()]
    +for m in lv_inputs:
    +    m.start_recording()
    +
    +I wasn't sure if the cycle was always going to be the same, so just to be sure I did 100_000 button pushes and recorded all the "1" outputs for those modules. +
    +for i in range(100_000):
    +    ITERATIONS += 1
    +    push_button(modules)
    +print(OUT_PULSES)
    +
    +# Output
    +{'hh': [3769, 7538, 11307, 15076, 18845, 22614, 26383, 30152, 33921, 37690, 41459, 45228, 48997, 52766, 56535, 60304, 64073, 67842, 71611, 75380, 79149, 82918, 86687, 90456, 94225, 97994], 'tn': [3863, 7726, 11589, 15452, 19315, 23178, 27041, 30904, 34767, 38630, 42493, 46356, 50219, 54082, 57945, 61808, 65671, 69534, 73397, 77260, 81123, 84986, 88849, 92712, 96575], 'st': [3929, 7858, 11787, 15716, 19645, 23574, 27503, 31432, 35361, 39290, 43219, 47148, 51077, 55006, 58935, 62864, 66793, 70722, 74651, 78580, 82509, 86438, 90367, 94296, 98225], 'dt': [4079, 8158, 12237, 16316, 20395, 24474, 28553, 32632, 36711, 40790, 44869, 48948, 53027, 57106, 61185, 65264, 69343, 73422, 77501, 81580, 85659, 89738, 93817, 97896]}
    +
    +We can observe that for each module we have a periodicity given by: +
    +hh = n*3769
    +tn = n*3863
    +st = n*3929
    +dt = n*4079
    +
    +

    This means we can just multiply the first element of each list for each module and we'll get our result. +

    In my case it was: +
    +accum = 1
    +for name, pulses in OUT_PULSES.items():
    +    accum *= pulses[0]
    +print("result:", accum)
    +
    +# Output
    +result: 233338595643977
    +
    +

    Edit: +As some people have pointed out in the +HN discussion, just multiplying the numbers together only works because the numbers are +coprimes + and the correct solution is to use +LCM. +

    Closing words +

    This problem is my favourite because it has a few characteristics that I personally enjoy: +

  • It's based on real world stuff. In this case electronic devices (which is also a plus because they're fun).
  • +
  • It can be easily translated to an OOP approach which makes it easy to implement and understand.
  • +
  • To solve the second part you need to look at the data and make a solution for your particular input.
  • +
  • It doesn't involve any Graph traversal or specific Math, Calculus or Algebra knowledge. Or any obscure CS algorithm.
  • +

    In the end this is one of my favourites because to solve it you just have to understand the problem and understand the data. +

    Link to my github project with the solutions https://github.com/mliezun/aoc. +

    \ No newline at end of file diff --git a/docs/2024/01/04/new-markdown-generator.html b/docs/2024/01/04/new-markdown-generator.html new file mode 100644 index 0000000..191b86a --- /dev/null +++ b/docs/2024/01/04/new-markdown-generator.html @@ -0,0 +1,97 @@ +Generating posts using markdown +

    Blog

    Reading time: +

    +

    +

    Generating posts using markdown +

    +

    This is pretty standard for Github pages. But in this case, the parser has been written by me. It takes some subset of markdown and compiles it to HTML. +

    Only what you see in this post is what's supported. +

    Code blocks +

    +

    Standard code block: +

    Hello, this is a code block!
    +

    +

    Syntax highlighting: +

    from functools import reduce, partial
    +import operator
    +
    +mul = partial(reduce, operator.mul)
    +print("Factorial of 5:", mul(range(1, 6)))
    +

    +

    Unordered list +

    +

    • This +
    • is an +
    • unordered list +

    Bolds, Italics and Inline code +

    +

    Some text can be bolded, while some other can be in Italics. +

    But the best is to have print("inline code"). +

    Links and images +

    +

    Link to the blog source code where you can see how the parser works (tldr: is awful). +

    Picture of NYC: +

    Picture of NYC +

    HEADINGS +

    +

    There's a thing you haven't noticed so far. There's support for different kinds of headings. You can see them in increasing order here: +

    Microscopic +

    +

    Small +

    +

    Good +

    +

    Pretty good +

    +

    Big +

    +

    Good bye! +

    +

    Thanks for checking out the blog. I've done this to reduce the complexity of creating new blogposts. It was a headache before. +

    Hopefully now I'll write more. Stay tuned. +

    +

    \ No newline at end of file diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css new file mode 100644 index 0000000..8a2ebea --- /dev/null +++ b/docs/assets/css/style.css @@ -0,0 +1,114 @@ +body { + font-family: "Ibarra Real Nova", serif; + font-size: 14pt; + color: #1d1d1d; + background-color: #eeeedd; +} + +.body-wrapper { + display: flex; + justify-content: center; +} + +.body-content { + padding: 20px; + width: 45%; +} + +@media (max-width: 1024px) { + .body-content { + width: 85%; + } +} + +@media (min-width: 1025px) and (max-width: 1440px) { + .body-content { + width: 65%; + } +} + +h1, +h2, +h3, +h4, +h5, +h6 { + color: #000000; +} + +h4 { + font-size: 1.07em; +} + +h5 { + font-size: 0.95em; +} + +article a { + font-size: 14.25pt; +} + + +a:link { + color: #000000; + font-weight: bold; +} + +a:visited { + color: #000000; + text-decoration: none; +} + +.triple-quote { + font-family: "PT Mono", monospace !important; + font-size: 13pt; + color: #444; + background-color: #002f5617; + padding: 30px; + border-radius: 6px; + overflow: auto; +} + +.single-quote { + font-family: "PT Mono", monospace !important; + font-size: 11pt; + overflow-wrap: break-word; + color: #444; + background-color: #002f5617; + /* padding: 2px; */ + border-radius: 6px; + padding-left: 6px; + padding-right: 6px; + padding-top: 2px; + padding-bottom: 2px; +} + +footer { + margin-top: 30px; +} + +.footer-wrapper { + display: flex; + justify-content: space-between; +} + +.footer-socials a { + padding: 5px; +} + +.aboutme { + font-size: 16px; + line-height: 24px; + border: 2px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + padding: 10px 20px; + width: 75%; + margin-left: auto; + margin-right: auto; + margin-top: 5vh; + margin-bottom: 2.5em; +} + +li { + margin-bottom: 10px; +} \ No newline at end of file diff --git a/docs/assets/images/branchable-mysql/diagram1.png b/docs/assets/images/branchable-mysql/diagram1.png new file mode 100644 index 0000000000000000000000000000000000000000..97f068c7a47e717ce33052ccea4158de4a826211 GIT binary patch literal 27143 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 literal 0 HcmV?d00001 diff --git a/docs/assets/images/branchable-mysql/diagram2.png b/docs/assets/images/branchable-mysql/diagram2.png new file mode 100644 index 0000000000000000000000000000000000000000..efb631650a3655ff5416f467802b83a9bc1e4376 GIT binary patch literal 35630 zcmeFYXIN8Rw>BDzG%3&6FQ-X9w2m(4xt1R2oNAZK&7f6qJUCj1uGyZy+i>K zP*6ds(u;)_YG}!~czoWo&pF@z@m<$BKlepQS!>oY#~kAx_Z%zB&KAnXa*hQA0c_#G*sY!n~4FqLJr*>0X}Fj4p1XdWxv2L5Qs@W+SD~VJT52%83>Y4HU9feLPaSU zg^89>HIYzJ@kOH*0z!NdQNH0Ag^0jt;1X~@92F1}6cQNlw~UICipm8gtqaOpjw;F$ zs)kDHz?+Juf~uC5``_}uh`@+{8&p?N0veFE@%0ahh(=+8B~*=o-xd+kfk@yJxD33* z;J}+J@UN`ot*q{?Ap?9hM5B>`E`felA;1{S)KxVURKdVyX)AL_8*2#_W8gb7BrFj4 zfCl=9p^mE<2S=g8fh%UpNB__bm z*D^>`!!Q=E`F9Snfl-(cRK$O#te~O*^nYAAIvySP_i8|3Y=}S5p^Aj6DIm^&M+FO_d^NLVbQL+xPi3qhaRmFrm zqLhOSOj~AsVO(E{0kze&%*|{zh(bzv$E!xe}IyOq(-%?W(21c1{DjA#m8Y;Vo zn7RZRY6V1FScXE8%H~!sNSIliQ7l}|PbnH6f(o;;3yP0(ad3}x4srq1-pB}n4YXBt zw8a8D9)XS1z`B_DyPILn6I|RJQL&Z|FqkPOGzx1Rr(tc207PID49L+4Yif=OKpG>Q zqYTyHs=?-QQOa<0ci#vXGZkeUJB*f!vj6cC`6FRU(7pj9`ISq;;&CF+3nr0}*Q)1dk6i4vvh9fE)Td#XDM{ zL%|NVks-=pgiWZ8jf=61t)G3At!<>6EpXWaiH@?jGd8oeQ}ef1cMo;OVzF@;`*1_$ z7!=&WCDO&nHUMpD7^Z1lWZNNyIU~pWZi=Vw&6xucdXx~iT&k7M?2*;p; zEwzB|Ea67S+W~mRT4=h40tH}pC_5Xqh>(PErBJ9_oP%S85i(E{Y~pVcsOhJapy?WP zyx+lwMrtTH#x2fX#nvJLscCHJao%-Jzm6=nic zb_hnPp>6#w5%G};XpE`3ze${}mV0QdO`wLUgH2SZx)s_Hs1jv`K}E;-E1CEsY!Vzy zq9d%F!PdwKRDz`?I6^5{9TsmMs1%Qgvoyp6#9M2s+6RQV1-Qq%McezjTRYoYTZ54% zMk>w$nogz;F20%?*2?bA;R#4I({Ku?xE2Bh%-R=_aac4m0w@u2EVdwYyeb+Ho>{1? zOLU~Yueqs>G79M*;OpX~;bLPJWbSOHjIlvF*jd@xA)MpE7#n*F6MJ>X5GU)Xz;H_~ zq*J(tS)83UMk&-f4roRb8)0r669@JS2nvgIQBg9rbH%y<*U|B|P$d_22OC(JLp;jP zCD1m^DL7bF!#qX_>zELuVxbC)3&&WXT{XiZOq8MMaHNWfX=I3z3(_&p324|z)5+dn z9c&VA9Pg*03=fM9Hn&NzGD|QH_YHN{bjB!~176q6H%>i1z!+_AZsTGUr*39q5{~eX zK-&1*1%)0@-o`lCK^YnmZs_W7mJnuS6XfKsiNOQ~scV=8!z@+f!?c1C5y((%l#_pO zj7vhSkt;X>91>`PQFC($(Q;FFf+Ya0Sce~5W+c=pRxK3psYXGX#|}spFezsL@m&A7 zG2r*Vz(CdX+OEV25J&`MVPfbQ=e1nQe9N(GltLKS>rh(0_dvde=>eys`R4|a?DuK% zp%dKdp&x_Viq5?4l#qz|QHZlb`8-E{Kp^Bu~+B z9=}p$#YIR}Dj9N(Vj{HvyhtuW|Nq~sTGjo`Mq~$_qe*1)5ljgJwHnJy9)>5el(&KW zLPp|}F5H;VcmRuQ6;1p;ImTl#PAH1?YG)<38|m!Ot$TpR(+4(|Uef5&OsjDm^{1ap zijz*c2r6+1;D?>4R!l+h?6oG8GG!VtGn*e;F%8eT;7=24@29l#jafue!ldh_XsT6H z20OxdhMt2axL5?C%thGuba7G(7h96lQmBm657#Np9oUcbXwDQEjqVvR{fHeIL|g+^ zza`10KtLmcM80>E^MtV(bN-RO3StsBaS40uce;*d9R6{&1`sr(*rS<~6 zbDCN>e2hM56A;n4OqnT+^yeu;nr~B?mq%*~VXt1NcC7N~u^+)ZUGKwRjOr{^E916d z6`52mkhJvF$*!wn{RXKIidqG;hiPWk1OlhkS#o&s*@7ps+32$&P!2EF69;i10SR;7 z>S#xS-m()Zk=2Z$U4-jdAv4lOVB8C`>UDvQiXu$FWxw7VR)>3D;B@Ipk1OI5$dT84 z6h>kUD1vq!9jtl>e(S6-evX>I$??vfGh3^|T~qAc7f$etk-^3GmO^nZjSO*KfgYdB z>1~b5NRii46oF4#I@SkLpvn`(U#G@y!tPO%&w|A^8@+ckQ<&n63})A|#CwtNt2!Df z9krcSq;@eLFZR;fSL?;=UhI*zOR$;gBm`{{pVz}Ug_CJljmTJ#4z16?7(=CU^(^6> zUKhTvx}tAVE#s*UmqXW-eqo;w%K{SQS?j}Z#6!(h&1}CYOiCP?LvS4RR_7T??$Lt_ zG}DM$QI!=x`EstHduI{8CG}~FvrTl_niWc#TkBqK!_MD)5}I{0RGpvCyrbhRQ5&m+Ymm6mDzos}nf$9lB+^?g0kQZ-~{ zHW9MsaZ}8?(JqLK>bdiX{b2J*AZ8k^ou=Np)XtYe0Qp>&i5bCrj+pzQO6uzQq=}y0 z#1J-yjioInsDftKyBL;h;|w{8)ig0tn!WFy(()tyv&7E_*m5pO)6+pi8pcs3kK6&5AC3zjHKfBe!x2R{D#?0_gTM`*;%Oo3E!LmBO~-#4=! zt8X!Ip3=DdYIb1jI?cMdCVju!qpOe#!v+SCi-#fn{AartUlrxhTKg_E(PyhxShsOS zJvc&|D3=Yt`;sc&A;YKeA(0(KE#vH88T>Ase*J`sjx6$gweoxWbGF>*yKmF2XJOu$ zO2X_X#d8fo(^fMHG|Ag}a_9g*6v(?j=3(X46ehAMplOAW-VioijpvVkQ3Qhm*PNJ^ zg8ai^6*T*us~ghnB~#@@GzFqJl9o^+>!W#wh<~?-0JAsTxVAZ1pD`OsSmI;!{4@~4 zwDW9`ZfJifmRKCPt)^R?yh^y%K(%x~Nx z?nPxs;!b>%^uoe&JxvcqyaA@>t@=)7?@b@xJrztkX@Gw z+E-nsDQWZy@8+36bMx+366pwYPdJ8|N2<8wASF|T- zwi2mJ6=|hxo=-kt?Hd|{Qm?LSZh2P{tIjtNZ-jCDqeE8!9WoM-P?akKX&BFH(c&|2fP;f#mD-{kpOb{j9-Y>nbu^q3F5RmZysP?bdzUK;$b7MZ_fxpGmZWL1WZ>F7P zGC#b1fklG2g4jEIw%uxghulh5e78nX?BQgFAm*CMze;~dY7UC);!+N_F_8V7E1q>h zxI(?5C*qMYJ6`>S&En+MR^rQoB-V-Hl#?g-en~4Dg>JkyI4G@F!pN)d6^)%H{AK%akKvX?=k`E$a+kIQM z3Pp1oM@Ufg`S}|4!DjM`_JGthgU$l0F4vvnJ@f#LHywmDm(0X-B7)tCIW+?wDJ~fy z;PlasnerQ%&=|tAyD=Tl$-*frLdwk)?<-+lRe;VcMPQcSjDMDx=q=QXOHO$IDL_;G z*s$xG5-xSOE;p)_@m~M&y8!VOgEfq}(xJBv&)o|YT9l^4?Xse-G;>DHor~}&{^T4H z(D8*KIimYhIypIlX#94`7CYgSsWTEeki_7eymRFpvH?>%(ajdqkS|sviWkTijQcjjNx5oaAEI!$*yM0dwW;`zmnGn3u40iXZ?ds8E zY}n(tvym($+j)_3PlG>vhiMKO5~$rEC&pnX=C9vv^MJwu+Rd46WIUw5!OuvV7L7R5 z#r8O^$^U+2&0E9q1}#o#>d5*ug=a!#&%-wn`I_O1?~yN%ljIAL_h^T)paS>Aq1N)SwkBsi!igR)S0 zbHYc)MW`owLtMl4fnsCVX2i^UtAGJj^@dU2&C~5nGvzK5d3o`7im{P{va8cQY6BeD z+W~E}_nls+d6%QdLssXA*pcw&#u*p=#3<5WOa`FS>@zOtp%->C4KI9L8%K!YVKVmw z3J@ngvE0xhF33?{B+4`_+eG5LopX3J7UAq~YyAQwx&+C{qB9W|AGz1!MA!)aB|Hq3 z93O(@a9ccADiI0+ltLeWYkZbe$?|XW0McS3bTdP1N z8a^pxkYzk4N^j^;9v9fCl)HYG9P6_zW~1LBHmZsNYm;-G#}|BVq`}Ad3fnbgq=Wp3 zfuAYN4C#48j{h{z&XCO&l|Uysif1u#<=!5Fmk~U>{MJO5^qGYpD=|M~oc6vao%4t| zgJU^mTE=FwUkY{Hd}O&ALu}<3x#2r@hvSazqTr;}mr#1NO@o~1rX1F3e)XE}t?UQp zLOgw*aQe~1u0-qcIC=0~koXm-BaFrH>(smvJ2DYnVN+9aePw~u_En(vULIv>@2tFd zz`&+RgZO#V;$H#D52jV8NZR}$KruC7`$)@%ZyK9?qe|>5fgM+taMkxe9PYRlRUpNm zU3#u`M*%?n(zifqgw|?D65_WT6(mh_0f&N#kKZ^*(Rr_WOKqoVBgX7LHugL-U~Fdx(*SlZ#5)F5TA zgm&iA`=Od1%ezR;VM_3b^vqq*&g=N^AQ*GNK;c8lWv4Z+othQDkI2ZVD{0~q0_yi& zqb$?j^_baL8j$Kch&)!!&X+Hlb1vM6Bns2*sjmAx=dOn+*-+$+jmi^DB zHuclI4yB(nWk^O`;d?C5hT8?iSDxVad!3<&*uNEjcQ1!DBBPM4 z&2P1wsv5A#cc;d%44E zJpef1Tk%R9xZA8RkTTkP*$_YlZ!VWcz7}yhbo0ghRCVD{;>F{nc3-IkcJB(W(L*TD9OZw8ejB$}QG=|h_4QOQfsd;W`%JMgdEg9|Q z+i=%7H22asEwQ|&KsDTQEVRRD_R%zE=<*VC^{e+S@6;LVB8y~QOT4CU!G9-Qk2dr_ z_se6f|Nfqv?*lErcWROJ>DSvmkMWq!=<}svNr6${B&#V!R(=|u1#8jb_J$-(UA}OS zsMFzDzAl1ZEI55s+WZq{i?>gvg5f7-zf2sZa z_M5$-E_cB>p_CWz94x@Hq^sR^%eZdY-YxILV!@8>s(+dXl7yhk521$1atHp2nRPSbe;{8BMhJMP24q z;Ah_1YC~H1ruFT`^sCqS`T9t_V=1}R$I?rQn0l*)VJE1rAMsoq&#%#=?jRRfg1|hQ z0|N~7guuDfR$&%CI(|+bE(UhK&17)U*vS>ZWlc(D%!l7IcX)JGv->b0oevXmNzZHS zeS8S}3PvrB_U-nm7oDSjYTl6yMh?n3G}VdNpsRys-DbSFTLp-m^OD0jwAUTXhj}2V zi<)au!BOTq${-ao!5X+3o`#_l`GVK;2ce!y-LI9}Nx=cxY%rroidsn>Gw7Xt8T3ai zp0y&XNGM8-pJB)LEQC)Y$_7LbcVQUoJ-qej>P;kNOkJKa_V-nBW@NyPJD+N|WcbDE zN{fP0WT``QvBjlxecs zyZiG(;<bNY8&gjeU$D7^lxLXZl%N2uLpX;BBfZbiF_BicBVTeWwC>ZjZ` z$C1tIa%)SwJ3H&*$@}kraIL)4Pd4hgj$W>a@=P7z9iLdN^SnO+?c6BFNAP_jUe8=s zxOrHdv5XG$-k7Q1)8#iZq!)u3%nHZz$)%b~5>Z|8BQTlV+65`>gmP}i@W{GPL%0Hd zNuN3qkkQ<#ydDjSn107VLsyS{gZozd>fK7f%1-wo*E^xm4%}Xn4u{p4Vqk5dnE3c| z%!0mHfeuTJa+ghUL*6aD3pdlh5GG2p4+PM30z#P&MQHP+036zK^=g?x#J!G5F$iYX zB2d>iM`s}NA(wN_GAHK1kzw^~_e;Z-oS-xTtuZg!I3_k2tX78*siDGhlDe3ay4(|3 zld<+_W#UfGcv+N`<_>GTnu2Lc^W(^2NQC=47*pdPevNcr?M87CY{5fwrPc|xvot+uVM_$pu+CIu1vAd6F(G#m{6)zgs`KqC=H<5fd1NJz( zy>&GdQ=S<2M~G8e8_>}&d1jQfSPyu_pWaF7dilqvHePG3_IFbeYq$KmS{G$xc?5QJ zPijRC331(GmNzYXV4yr}U-xl9cOo6Pv03DhZoPv>#U$qsd`fBfBvT*tI0dZ(7YcfJ zKQiKauaHXGCFnf|P0hrQqW-uCrF!(4t>|ohLf`w8$VKh1g*!F%1A;>hxwvwo;O`NT zZ&Uo|lXSZ=OR=wzkI>CN;D&})IXdd%9p=LvLi1cCqe%|WqtzRQ1HP(H>*lp<3a%@x z?!RA~*Lu-!c6zsFDbw^mw6hLNkHJCrEf2-LC1g^M11>(JmraBj%A|ng;(KkLr$gNB zu7;cUcz4Kg)f~GDOUWdpNcF7Z)`u@Und9)0hAK^{c$}v!^juuFCvIoGv_uZ^Ffe+o zt`hKO8UtRnycU%AtqWQlFcP98wRu(my^DJ7|XtdvGVH=9OR&$_$>AMcB^OkI73_EAI6bt|H~H$H&#Bl zgV#qs-}^Zz#2~cX`Chm_DM)%Nb7UvSrypp3&S?N5Jn*~h*|FVY(cn|h?-RcEexbX)0S*)s-;jDAHm~HojauOl!ed2zg`V7h{=jTuiU1Sz}IS zxZ=gpzY~~}Aq{lbHdV+1$m{vLPo_2jxY>dGh-@VnYm9Aa$IOmN`%WihIPYO27g3ao z1H2O7+^9cxyf$O)qa;OFJjM8@ZdKWu&)re(CG@_k?*PGg;Z7&Sr3CQJbr^g1eNmZf zgw34^=K{>G)RjN{MeIWLy_?^)#E5S|uTzZ=1rnB+Sozs?8?QeUFPWWwV)|QDe~0W> z_P$$-lxPGdEj3TF4&&B`APU#!Y0Yw6J%!pdhdE!RW#TKvlO!m6aZbCAY{0hp*3cBp zyg;j!%_bMszoIYHcIVyOy@;JhjZ8VSJzny-{AC%q{2A?pd1+kq9zA;@CQl%3oc$Z7 zQa@kMQ=oe;#NeW%r{kOg` z<%%99hiXd0q$Zwtme}7FxI<6HBS*vj1573%bpWkU8e3EUX<&4fwJI``Fv!&M{wd84 zaYeZcyy6*P4k_RHKJMTD(0g2`reKiKGdLtSu)E$EPme09MZXBE=SV0kS!(u{<#!Xg_AUYCm8T9w#7= zg~9(G$o$KsjAjL(r8Fbh`hN5~(Cv2DFLhw@&)Bd<)7A*_9v|uufL@X|()m3`nh^s8 zk8kA+5n+8tWI%7}{$Z8?R!j_-j4&rOR_rVUv>px`wXOcs`F#7yUq_w`AanhM$1Pye z#QrOp?&+}+t)T(`_{Igf`edj7_eegn=03H7{Q6YZmu#6{yP(5)@wJd3?)!^ZSyx7R zCF4CShu?O7Jd8Vq!%-F(Kkbg$ot2^$vx@?-!A z*s_yVr?}4G-Y@HCjid{UD(RXPc9Wgm<4#I8s2BW<+CF?PNn-Lj@n1(*6%yD`=~c+b zl$nxhUn2J+&x>_fQgHhfW{UIu2ZYsbUWT_aCo2RK2?Fv!*B61=eOp-D8UhX=WK+t2 zzrW%A8>_#0KmW2wgG76?+>(O-N1!;Y?M;+?!DC=;N+m={Yx0JA-n{g&WgF`=G_1J8 zJl0o4>$~`_d96I_5-tf^vFft`yi!SHnisiIGMB5-b~f)m1^M(MXV_EzN_CJdyO^Ee zalbQ&$2Hvav3FE9WkHbNopQ2sUx=O!)7d?Ij_I#z8~_zj1&Wp4uRuJ%L{xwWDWIx` z+dGJ5KivkxIo&&2y86Z_knOSG>5?rq0Wn@58Frp`-=AZYT)X(=^R-b$g|3@{Y{j~L zfAZfWOAl9WOFUn%Xhcb^1)hOC@ZOk`=;H_DDh25#-IZ2u%*3C4QhZEt-v#K&2S23d ztZaOce|}R&SGq8RA`Mk%n&~!>30N5Nk*GF;aDRdZsow@#8+zN5cc|*9O_o6=-j52 zQ!ke5N7GoZUrkJ)2+O00DQ(*phHd_<|RqVgB+UM25r;=6Y79L6PB(u+r^Bx>t z-#kKUqPIly$rZM2)ib^o?AlG!Fg-jkBj*!#VE6uBuXS(}NzK_gJj>QqV!VS8tn;tw zww>z8){z`lGUweHGEMot z$M+MdCrRsM0$4!PerE?A+j|dZ2i8^^xgaql;aw0Ac*_lWiYry4l4>djOcOj`bgP4O z01<-c2dEY+-{9*jKQApF>a4=_L}|r7TN=z(m59)q3hcD|uX==^B$M6Q->yM|{@8R9 z+A+41*-5_>@y|YEXzu+Ok%|FgGx|zVpShW99%V3h6qI}9r{;WNZfL)EqdTw}F9IP= z;3-b&2V{(K>76ATYhd_9QSy)zb!V{Ux$W6PfmMWg=^E?@f6B?D2=dbGd=d|q!ItAi zXs~VnK`jaW{^h`B9mdJ%vDF^?%i@MjFaMJZu(fvBXf}Q4mLTKO1+TUq_AA8_7YX}M zbu$f|j%oe0;@cmLn4_ad2(zfPAITX6i$mB5A~?z>g?DRcFF9hwe`ycCVtZ5x8Q0s{ zG|+LlQMOug5+p%M3*yz2qUAkyC-$p1UoA-9$R%U!g_c%FJL;ykFuaf@s?2YJ-lZwe zJyiXYjo@Vc=F!uTIUT9%zX%gsh@Oq@p4~CTN|L9J&oJZ2Q;^qO^jqIK#z2$6@Q=Or zv)-%n(?!6oS9CnBQ^&WwDqLsR-rxdCn0T%N4ZEJ-E;9$*G4$(8 z%Uev2ddHXwK|>Rqo6_mMXH0J*k`3$v`>khR9o1;tvfC|Qi+pJ~eS!b`+w*txfP|vH$)NId) z$VjUAsgb;YdL)0~^rbkPKay0|3m3wyS+CBL6fRu2{-=&L^iOPcA+3Lc1lI-enpUaF zH%sbQsVWQ5w)H|mz#lyZ&Nmd5 z)@Kb2(EANprWzqUG~{*gz7*2>*paDDXTL_3^JGDxN{+*5byMbb9oN?{B9BYKaSZc(~+Q6fuHXQk2FrGr$Tc=PT{b+}h z58xr~YiB8xfUz}PT010fX$_aL3n`1GURmGm{+c``b(j=U)(&G`!>g@O=9S2wt4jE{ zgw&PysFm+7g=8QdY`e>pn_*&;aW`q$IZ|F25vp2g&;&#D)TI!{snaUezmBly@{upJ zdv%M~YWA8Q#!Grk_7%9)F03Hi5pp`Cz3z22qj@T2RXQ;Qrg>nS5%|h$rnY!vrycx` zaAc~tZEYuo^7!=>m6LMR^?FL!dQWfrh$F2X#!h`R9F&e1zPTfGaPT&y9U?$UvKw2g zbx4L?-zChfF_!;8;i*PrYg9f;lEK(o?xVx9eF{FUeT`61IXW|y|8>$IkK!I$fi>*R z@4u{vJYCijk$C4wnu_o3$Y4|w*k6Ow7EkW~B#kHUeSzbX^%j1-6hC1AcHY5y6h2a2 zs%(cJxv4WyuMN*rS9AY-#TjgjAG+FHtOyxBqPy#y zbj?+_@*Aa8(tU{UiN3U~ZRrrymq|WxjgLV%^`CFi2s#5T3yWOP(1#!T};m;bU{SYURB`GEy zYzbVh*aym4#x|JEIFT={=zy2S3?~eiDLeX7@`pzW0C*!p0e>OfDpuSgI$cc8)eHMR zvW42zI+wDz_=B!!hQ84J^$2Vcyl|$|x$BaTC|>Q=2uA%ANoo+ccX$m=amsg&n**H{ z13)U*F+TSBX7$jBs6m5I$%}cJBjTjpfjrq|p}kHlNl{z&nAM5hNPEVi&1{#80Ma^k6%kLW zar4|+?a=-ww(!@W2JIuS4M(d(tDJ@o&L`E_Gj6|xp74wqP(tvL;}N8hnPouj=!SPtx35C64?-$Rqby=wV!*p zq%u}A=XTGIF#rbN+biEx%+3}NP1ILsPhNlw>Y^6?f=vT3cRX$?X3=ZJ*9Z2(ZR(oP z#jv5xs*;i?A;eyilc}35Fr|yfQ+o8ZlVD7lgKl*!sCvB@#7x=VTz2!m+^wK5B6FzN z-Yy$|Q4gQ1;&xE}ME}Vd7pH>z$J+DwSbJn`4ykIpEA#7=Wxm4MW4Lhh(a2QvSVYfM zLhQ>tGVWIUhLY*Fq0o(Jt8Rc9)#-mR1bb3lco)q$=^Zjlo%0~T6qY>zn9jTexoQ0? zgzv?y? zp0wuVoiDK8j-R~whzuaDdMX#buuNV$!iMhj6&sEPF%i;$>661@r;)w^-LPxoImNLt zyM_<(KfgZ8$)?_1V2k@Xk<|k`TE#CWmBftXgg-x!peX3M*-bp-V0l-b5d{RwOJa_c zKeEdUh%)u}D>-e;A1ggrOPvQ>t5lzB%TksX26Rzk3|lW@JLHu_2-SOb?c?<{vlsjK z?4lAXj{zE0C6lEYsz16K=3>pn6%?`iR!67y*(p*xxuG=jCpk47j9GxRgMc&;jQ@Sk zFdFpz`K^-9_IDd8EX@dZ{+(}EAWZ(qcRQcOgvj&cADWnP~$L0w&GagJ4q%R-e1adP6a^YHQ4!(*~E4p*D6Zi+dpFAhM;8Q1KJXnoeDgiyj zuGYEwNPcSf+Yo3A9O}WxJ^ORoSbwTfBPdz>%S0xdT!A$ZCk&Ps%pN5m4Cx((dH%PR zt2)!TG4)yvrkb@le?)3popu4KU&JkJC*IdMTXg$Vm`lm-@9?Rkm348&cm-#;M$SVH z^&bbKnO!H7!Z)BVqe+*C1YO?0DG(eWk0EZSk$Kgwy~K~K!~Tzw*Pq5 zt}pRrB<{kEpI*1FJZd?gIjk$3yRA+B2Nu1WdU>4A^KULxfK3|bnV5NA9m&wk_T_;c zsgJ=w*t;<_epc118HSj_VXmwK&eAN6(^nPEUMyM+eWy^aLtsfSRM)=N0(k7UF#xu; z+HO7;yO9T~5W5jPs&9T?KMZAl-hHQzD*XJZN2bZ9ETxp`67MlK%Oc9>kGPnme^v7_ zZFstI?H2i9NtHL2nkaVFgvd+tjj{3rR2K-}@a)dMy8}`+=JcMNc1hFOiJfz|fucVD zRrCTV)>MC%WO;amsXi8Lo+&UIu~RaDfi!P~XL0_U;5z|e&&IRA*pHbhz*r2X{2zDl z-;U)ot&hvRxKLH+6om14^}!Oi+xcS#D-SRQxwmv~pZS*n`(OiLqvE{PfA}I7dNsD6 z|IG~k+YbI`vNZp*vj3yT|6lj4FuM@_^wH;ATkpyeK5>KvrUCJm4D+Lcy{!|(XB?Im zZP}9-0G@2Q3{*z9E(Bz#k7%p?5(l+2D4ii9XkNskp2-7yS6VrBYk&HeQrizF;af$C zw_Ckt*D^X3PXKxE@2f=(o=cNsKtO(Xj&M*f3&6;#oU%)@WBBk*D6MlUSAh7P(tUrV z!oBl7(fRlG!OVF#QQAN;z#~}Sd#-gZs~s|l2%}nqE*j;7K}D5b?GW~x5@L2{w9hzt znVQc%yh%Qo+ur)#mT*KNn-ydlG%^E`fd)zTDh$YA?FbJeI6oa;D?|lcXS!9K^80cW zchv*(c}lWY@yjEaQGVT!XS;WQp1PP_&~byTEq5Q}1NL(Tk}jDv4jBvp!g7N<$1SR^ zhi2K%df%(~Cp5JBTpf0*b60MicL67_BxfzA42k&dU4;Mkh%<&WjlXDzo zB`AVhiz81tR)+m9dDEv*9}q;tY0%!Mik>fdE63Y1=2wmbq4ig$C;D~;PaFZOtgF{u z(3+*;6_S#_nb`Sf{O0wPf+n~c*Yum>fRYNpGspctbLX}OKwk(wasA9qdOr$!zZi*= z_@M&%?x118mSP!o2N`2yu$>ylAx-fEh@vIK$U+%aLhikFDR@MR7Qk|=$Z)VWnbMnp zQhw6~UY0&Cvp%Z;7j6Pcl1+I^O4Zp?8_{oh^H)MH<=H;tlA z{>x`M*sMC~cJ>d*^RKx^YQ%u9Jv*opBF|mS(g87lXmDVr5h0)LPQQ(YKfH==^s+#rBaokI=_t^~E;d?|z<;Kil(DQp>75$7JOA8+a97vf@nb+v5`c z8L1N^Vg7M}J)tEDM}s}@>8gGJ5kv8vEr1}O(*UA@{{Zrqwm(A|n*(hOs;8|X^XrGg zpG%Jq4Px&++rQVOE3`6Xc{t8Zl~J9dSCaQ8Az;N~189JZzN{C=zrjfzV(0)M_N~ON z2mamXsS8u8W$_T1LnNr$(BKsWS#KClvZj1{sEIth_pVboF6`}V<8-J_vs{droo4Dq z0GhGGUi@s4i6*e2B{o-QH!W!IHWVGDgf(=q+yK%)CeH55;6k?8qFqYp_o z@lLExOmY~h(hd}myuL~h5Usw>lq0{cpCdb=yn8_xpznKJ#@^FIPcEYh2jb2Gi*=G0 zFJBa!xp5h5>d3HX7(C+W{RH)s|1H zRiu|*Ta5CI<3S1cgFJ8+KChF|8qrUJkGQC-Ne$DYh010lx~AB_jK86&b3yUb9pwJX zm4gWtwh%TCR2N$bi5VA7ifj%MWg5z(PXLzhp`9#mGS|;c@^k(5!Hk+vAW}bWvtykH z!?ON6y}T|xg%{&>9J%mdbzY!w%O$ea-n8hqlg!*151_+$!F{Vm2jGaJ?Szd!+Ge8C znW=Y^55ZR4|t>G75=vEhs@Y_sX|2*(;f#j;d=PwRm9R#?j4Fr z)I|{ADv1iGTOu{0bFYJTexB8&B|g1mJ&PLVd$Qy`@$PF?tF6>Z7#d{c11$Pj+5T_@j#`T1sB%#XLrAw_tN3_VQ$u{wPa#;V=uAk(= z0|S5;ui6)3@yd-jcb;O(gKtjBt(re|c(z`*{SJA2ZC&f*fKXfM+LEgC&lOq&O2PgQ z{R&10D7$9pjixaIfiCK@FxvsNo+ul*$`tK*=p@{*dE2iaz-itlq@_#x2N&Be4i!#r z!loWc#k5VWvJunYo~_p>c-uaAbe7MJW+U1I>2GCq#J{?V%y12EOv5sp$A4;u5HQPU4}H_qmn=! zkCcz{cQH*V;!QA5iD32c|VtDaE@phIeLfk+I*o6ZCuOqq6YO!OH!64?Cmr%E$Ro z(v+uHcOCe)c?a*8#us+B1BMkj#LVLXNZUlOn_Q}&g+JTX{&_C8?{?aG%7WO-IN3ad zSH}{QSiJOeM4qJhkbF=UexA~XGv8@EZ-*pd?-V4?Iw#%Wc--3xomC{E#P7ZHYCPdq zP*?^quv^!7jrRcRM1lek<+nAu9Nz*~oOfCGP8)C_i9eBzx0=%~S~Po&bS-|}_SpBE zmgoYA>3)<<)cYsmRU@yog054F44%Xj~7Ao$(ufgByYQUhM}N!Uz@2vF8k9{`gtsH2+ENEoNF(c^cNfXcTK) zUy+E`H6QD~l7^>v-(#AOoX;DW#YDCtzB#sGI(WysFbB#QP`lWb|_;$CEU}fJM0H9_1eYRfbe0a!dt(95sCY zlMiyn^Nr8GC`5sBb=!d{cQ-C6Qk4=3re0XQIdbtJv}q@_nP=#`1a|FVgVZ?> zEKR_4KfZ^0_gvmcHTwmb;W*&z>-OW1GB;TyrXL=!_~(!FsiQy^EGPbM;}847$+F-G z-TORylK&UEz_a%KpEXJ3!hd75|6m_62plMjkYK0dB0dieZCyNnch~y7yIHj4!HCV} zQ0o;%t2Y@2h~>drv1#sqgpmqse7J$k1D>EE1CO-a`;$F{+Jp)veR9s!SP5|l3w#JD zGVyzodbUKy!tfMcnWH%zQIy0uOR$)=6i=i--^yt zZi`Xi!effQm)hUbyL8RIy|z~V6cx9e5Kshv~$}W;dcv5C!NyYGidD#@T%81&CXIEZsGZ%Wtbil>FJ0z%dU+ zN`Je!rMY}BC``#XR)ZEd5r0aflhdgY(Tu)f-Pf z&IC9M2aKbm+t&_GDLdw4^_aa=o!OI5hFTw@ir$EF**Z7f~_}WM`1YWb;QJeVpJd%0MSK<&!OX$kzH%5R|_o;f5Zb%u%`eZ zm`T&#A{Y=1N=o$_g>q5SYpU;J>@OgXGk7X_5C*WGBg#FK5Io{@b-Rv>ERc%s=21hP^K2U%a!-^MIC zba^%cQHf*aTEO1oada@B&FNj-_UM3E%Anb&3Doy(W_xGaonz6z318n+#8J5L2rtJ@ zP9UTZ5zBE@xp`bE;K~g2DAe|-pkw#tuhpNA#%Vk8KJ5v*(W{+xj*(vmP(dy7B$rga zR9h~)q|X*92@`TatQ0%(>c^MDQs$zNAB=0XQYWb_r|^=9#dvPzd#EIpKmiJM_`zc? zd{uVowWlUPfG7JUtVW9;@K!qU{_|gkk>4g>&wh%#n6w)~hn2@Wnd;$pTtHMeS}F3^)RvC3FaPc*NeNx3pcg(-z7n5?`u~#)(9z35+PE$y2f1~n((9ZAFs29d zpMKk1l|9f+9GGdN|E>?5QVaBqk1|b*5%2DMNeDA5U9jN*<2T;kG6`3D7Ju~n?8SXn zgYev!cGt`d0FVFrg?Ad@Dh(byM_#v+74uF~c5 zEPRg({}E%@(g%^D?4-RKYYOP-_+Rb4S6owH*De}5NJpedF9J$Os&u7@G!c>BL_`Dx zMClL^rB^|z5(EVaU8J|ro77N34NZ_<10f;FUi{zh{m!}g_PIO1eX(Dz_+_okx#k>W zjXBCQp1ivI7+!-Linfw3jnI=7)?SoVc-Jw~W*X7eCbBoK4}E_bk$Jz-O1*iwpXa2@ z70{h%*dEEDNITlDOWpcqr47C3W5g4>c4H(H$nKK0iEKwy)l|(&sDoGcs*6|&3xNfc zTIs+be=+h~H&fAm9i2H*#=m3}XdEK_R^*g;oGS>ZHiHI;g3zkF6`@Q^e-IBi3Byry zj2A$y+m4}YFX`*tZUqw8nM4g#C4fHvhaq_?5A^`0!v}1)j(+R($?R#+?aDrN*OT34 zxm`%V517-3B&jwjY;kWbseXz5+?NJQa8-HhroSfCZdQ$Gn@?-Qr~v^qd)+&U2kMW1 z;NhS}YQy|6$-_YvO97xV_g~qG*?b^7@#oNuC|Fr`GYMpL_CH)+^`0B3Q9>PcY5|HFM} zN@0+{sx(X2KpWpFRYk+>i4jDwcF>c01v3@^znGQ*$bgAtYTFucU~zp^Ekva>h7G*C z$O*Sl3(?C-A6!28&CRU^Cb03bMk6}tW#MloAU8w;D3o)UO@hgfjeQ7E1Hv80h%phF z116)MpXU%b)9^|b9~RUhef*b+XH>Z85o3}$P@;_Z&#nL2f&c8le|F$MJMf8O2qwoqnIrZ&J?<9z7 zgN~fw^N4p@xLUo&H8M~-ndSj&)cVbrmo*r`GkvIoh`j=QBNN7Un*$aqSs^7VY2y&6 zeqPo7SBY=a<4N} zpkbd9hPjCjK?m(+T5>ULY!5vTcDbHzCthcIa~t*(gf?R!g>z<=`~a#Q5AFlcOSQZ; znGGO2lz%ge?GOS+;)DIG*j4Hkq`dEkrQnF0s|HG2_pZT@H;uz`YshB60 zEjBX4ZfG*dD#_ZK08}N+nSf)$PcpeZ3!ugQGBhm^kYM^b^%JY7$#PB)B^{7oiG1f} zV9IV)n4W2a5f^p2XbKoB1({-KlY`-i)d|SU3_*#WaAab>TwMyK`x72gSY9p=@4Yv# z&;vp!zl<9e2%ezcor9&4=V9TR45U(pC?wHBxZWHY8(bas@vVhVFGpdo&mfHh&FPF# zWI>ce00EOH8J69l1GA<}&A(jo9X(#RkT4KQ;19LGf_ADBhiAg{m6Zgn_;Dt<`FU+5 z(Z@kaIDQvNtLUH=<~>J>{z2rnAJY2<9G(CIIbq2%R7BAJ7WD39OM8`!l`shN023g^ zV4yVBm!7sKN=}5jh|{cs`Bheqgm%Vxh?!%yx9mpMd8}^N;ecYsFln3^^lJUSl{Q0IdP@ks&JT%2HyKa*Q7hHEB zA8;w%&wt08|NhKOu!cw>YI&qy&4KRK^(iuhE!u|P(;-K=-4An4D0DoH`T=%z-i~gE zy@KW9B8+{v62ysA80Cq%UuS4(ixcxF3y0@VQGm#&O4RzUhL|l19s2$CWjVF1$h6%H znmMtCzwpAE!u(Dt52A`_$WcVTT)%ZR*3ZMZZE0E+Z$;uFDJ*8*M+$$=!kC-aCX$|P z6UT3IILx!OM}qvX!MZgW=Kn|)#{$S~UJAhV9N_;0#Iyh~=2tx+#=xO?+?;;lNS4mh zh%ra-L7XC*V6K)0+s~C^8|6SaKMdn@6(Bk_L)F02b;uOGo`uO7S`g-C;BODNrb-rG zhH(XwA&M2I-o~tM@=^*Z8Iv z6Dkn?%is0*W7WNv$TYO_o7#MqaFsxO_YZmt$qArZ7&@qV`3A;SekZ^N*L}{!WK^f+ ziO;6dM5p|>3myRX=7R>h|OrW=`5-U%2+-6)(9Z%FM)ws8CIQQ043=pt8Ao+l3 z@J}_1w7;$8Rmm6-qE_V}uy~jKv+s*(m#4kfq1P``-rUu^NHy!D~pMA&-!fy&m z*j9TB=2^2kOiU3)dI|E<=q6ABvxu)J_D%_9lTqy15El0Yw(QdXy+=U48%5NC$%_Lb z&yf_szOjd`fY11=dm;As-*_NEzk{oAx!}g)!KoT~;_{#lhjF^**+%_Q>87sA-muSI zWdM@}dRwz;N^-{MF|Gs{VED-de{vMMZi=GNt&_X}qoGvz4Tv9abkE@jZ{qzVq=In! z)P>aD1PEcMVgrJiLaMzwsE)dtQIFFx=dRNaQ>Bo-@hbG0Sv(KHkb8PHqQ$oqti7^7 zIs>Tb5d)C>84$We$w{(q(i}d#AgHKIFQ1+l7JjA5IDXI@h!4KeO=!-f+EIP#L~75& z(YZ*HWfe*|_^AlSt)O#x_Nq_M*x{!|Kd1xd)_~{DQ zh(K*CmH$aiM<9M<*uWuanZq`qqaJjXTwczeup}&Jb8z-&Aqf0yKVy&Z>A&~W9u@vm zuK}nZ3~JxeA0ZG_T~1qeXS6g9!7B-uBdMgpUfl!G>SqZf-BONAk?Y%g_Ew!Q=obq1 z$Mu>{{Pq6Fm5Qx^2UV((Wh2*R+_V9oF=smzK&Z9m~Fyc1U!} z{_E@JR$HjW;0FC%^}aAg{C3U#6-ZnmKvkto%t>W1MKQuID_^YUwGp!wZ3)aaGDLN} zZ@rNjqcEbMSIq8zY`T;x2a|3hS9EIwt=0|g<|5S`y-wAc5Y7iZ?}L5n*iN}d5wf`4 z8#VLv2hlCqkiaFh8uh>T^xt+Q+1hr%)$mjIC#iL$f|`fWB0Ao6_a_C>RH*{m;YMXJ zm)FXM6$gNOctxL59RQp6DsB1CnO$jSF+iyD(4SIFn+=UFUzV;pH za2pK`fdz)EqNf5K{{$|rP8O1&!rRYIql?uZO3Yuj1;l6a*CdZ%yGqah5W@?E$Zcfh zEfjtQJ@i94#h|%Hsxmye6^RcvuZLlY3})(T7%Sfz3nt;d9BB~Gc{Chm?(egAYR;A0 z6XLS%_6O{fupLt+bYDgZN5E>jhXM4z&2;z+*=r9@H@FS%e24J`=)dG zp1X%UG9s`M-y@JGR1IsviZi;fJ9>BtaB%-oX{b5s2wX|4G5epq!+SJss5b)R#0_@8}AZ$5|DgZL?UV+Yt=9&}Nm&-ua# z6oGMX#urdYb2jfbqBd*@56sp4F8D~MIh9!E?W;O+Bms#JHbB70=M1Z=vF+G`XBs?(1D239W_QZsQ7EZsKf_u8`wW(Iu?$U9p%1Q;fUn4yeljRr=8GU2f z_eE4?eszR867&EGg>c1t>$s^-j9Z-MjfS| zbnhzYtXtM6AnFSdQ^6ZQ7{!aYrGv}6+B96Yr5XF~T~I^mcgDZa>|Z)2<`0M)j?8ba zmjCrrxbfSm8YvZ|-i*j|VO{F`0(gNuOA9qG@YtZj_3zi%pPbhn-!zuKP6k=v2ONHi zA?vBH&&Re!r*qy6oA&1t{F2x+(Zgc`+@H|qZ$76Ua$o)XCd5IRYFRiGsIvL`Y8j*g z`+7gO#e?QF^Ds6>{fHbOIx95}tJvlIMY9uwe)or*zKuS3y#Bn`-=h<28HoaFmJb$) zq~I%ILlyh83NX!xcc_r2yls+dWbgU3#QkysmEHQ?A-^Q9bI%xX`0$hye{uwD9YrKn zN)LEJ8~*wLsE2Br%_)?0$$UG@0pARJ13psa9-_W<+ZB4q#8Gn5>a7zFQy&e_wnxZX z47YB(h0YBC6gQ76K&iFu7>})p@@j4ZXd1tHt@&^`>lw#8dzKvE`BOB0T6ZV~NT1IH zJ|=yw0DAC<2k~NVD}ginJz$Za3`J>3a1Cs5nSqm}*V946IENa`x7gqlD>UOYg2aB2 z59X~*@#2Nf;QV(X{DsV`kvU8;vZvDfnFE{*47Bp=wI>t>)9%sjRKFdq_fVKn#r)Q- zUq3ak{9E&1fA5YddXz94_DT+h7AKD1B5S_rfff#i zw&nQ_gVhWcl^jX}bAo2ehwNv-UbJBiU;h%S`}*PAKmdP7Bkvwc(+45kCsXQKU=}h; zMDcg3^wO>maQ0;Ak=KiXdej3eIjNYMZ01|*h&Qe1Ye>8 zVx(NfyU*S8BP&iDEZMY_Sc&9NP1*LbXr8um$(LB*`&+uun5`u*jy za*+x``HxWofG63s-L^h9pTLf#=|PS#d&1ls-1N$n4(#lV@||ZQedSXUD?2O4l|lJm z5@c+PB#5u9OxqmROssY|o1r1UmaOK3nKA1X1(*Fr72l#zW?VLr4G(F1v&@ZqV?eQj zIs7swpfMqOS++*r4CVqO*nFtMj_t6dZs0?3;oLiVn%YgO3;%sy^tcOgFamV4{WR;T z#znqcZk(;pVE)=+SWpu3x;f5$WPLa4EdQ@P#ZnJ1rQP3pvcS7Q3l}R;{RuwQLe20U z`Yri58F+%yNzkV`3coQB)tnz#2RW%wuQ25{Zt76f+(tWrjV?!ut*yoX?jWozpi;7& z_@EaU?6pH}{w3`+5g>sZVNmmRAWj{L`SC`9&8gN?I)jD&;FI;nje}}Ssr@e;)QN;K zffq03(VgF~UR7NV5g_=T-!3+1E$+vQ7 zu_4!&Mj4;iyRyn{-O6dT+unYHyaN&)Ye428x;d`peqI-nTH)7q$9Scue`Y@;&-S

    6Uw06d)Yd)p5tdKV?5n-6~ei&gY3z?W)XNB9)@4~{?l7VzX1cZ)>( zFIx)$o{CH7X8~{i51oYuDKr`I_O5&>7X-33fN+2cV+{B$*1nkapVosPcY#>*)|V+^1Lg+G4}sGv1AH7LAp?@V)%TPmvU`7C+$rHWm*!T07q3% z49*#o@2~Y1cls3rEoYhB4B>s360;FV)zacJl8bPwXz|+o zp8j;c-dj82?Qo7#{n=uW_il{}DbS(V_QM|~!}wP={PhII(9lqZTF4y>9vTjL8hh;Q zw3K59Y1dQf$>T4l4kvbb1}6NF#i0F`^*({5E?u6GwD{%XqTG%Ig~ezJZhw$uE1KVT z*0so}_X4a7{HJOR>Hd7LPs?N8Ja^&nswZ^mZ}67O754LpH))GZVSwx;6aR#pd?Zydd>?Fe1| zz`DXiKvU#~Vq7b~8!Z!172t%o+f&QmQjI!xW~!@#&tH2DIPA{W{GBS}kZ46ey*P#t zx(brivc_h&E85g%GJph1UuIw#<(rQhN?Rm}60fzY%q#5r6T71s1-yopHkQEn4e;X; zzj3fhuuW#Zmfg@3L`Boyork5Y5;oEPDv5KRZCt`RUU$c1J%d*{`g3v4>9&!33r(x- z6g*vP{i&$}WG=Y@LPn~n#{k*;#rNr00Pmhwt>%wIO?$7z2(JJlf`JKGd-tkt7U+il z=KPhAIvMR4&h)ZM(=PS%wbXAg`s=bUsu4*_>umD@8(;bz@#VHnLfYVoA13G%xbawp zz54%a=n}Ph)RcAVTMA|7X&WC}%;|0k8SMKTcd|C?6?^^-=UY3pw6y(Z^)|`JWu76T zN<6(}K@^-`8so8k$Pfjk>r$`R?*GnFx@~;*YSzE807|q{&I4&{Putt@s0vH*$x3ic z&JSQEDU@jiYUouSLPn`1B48={D=h|7bl)f*;dZI)Kg4%z;(K9>cw;ceH29#MqQ4=4 zZr|q+nap?Z0E;AW>wgwohT7zS@#pAazt)JtLLs_oN7UA2{YGvOOp@X!8L+JXXC8#_ zoQL&aq4Zx8Z&*Q9PlF!^-Hwi>zttKQ93C2X|>*T6ezvLGHf@Zz#V3Mo-lEf6wJ3O8>vR7M%qUsO1zA&p)68>gFO# zma3QSyGo>wb}twb)5u1Q}b=iVtSjt*g2Tb0BkgH)4C3HAPR~i@fQzHB<@#4ge{Td z40zzLg~Osc?&Entcm)nv>emI+&a+Z-4K?fs@l^=1Ll(RL$B{#j5e>w-*+5_y!x|>} zfhKct)!&yPteQxLK@B3##i_UCdM_C1@ztZ)Qo`&)oqB1$@(-RS%s%P@LwrBUk1>ou zt!uYOi~EeR!>pQs+9hplL;4dE^;kpt?dIf(Cgl@I``Vc3?tk9a>F;GL{E;TyEzKry}Q()W|ODn?iP4c}B zJ=Kg%3$`wmE>^k1Cos~tk}uWp!k$P<_!*3xoI_u1o^wGjsm@-prIY;nevrg&i|?~Y zyppPW-k0n0&9x%}JP&s)V@8YIVPn4XjeTdolBGXDXhnLo!BX_2p=4mn%F>d=A3JyW zt!?{;sfyGri@au&6I7;_#p)nb+nm0Yh=(pMgLhI^Aa=r4c72aO&$OczOO=1H!XMp{ z$_$0<8P}eTd;~T&$hUS{-{iZ)&$=FN!EQ*$z-p0V*D#G{71kJ0vT7q8L|~0vG4}rE zaNEl$_gy!O0NE^Elxd~Mhr5UBQp`-sf<1~>Ba=g%2t`MM5A1vRNyBfc0LpVY0&uDtKdXBuV3Nf(*ceVt7Vl{CwyW1Q7wM*@glL?bBy4l zd4U~VD;taKZ1?Be99o&_>=py>fsz1GU7xG`(Oc)e%hAPFw>?1$P z2X)afvxwq3pVW1p-o9tD#-I|kS+=qAil){@uRWMcLxq#YrAOc1{UgQ(ioRVP{~$Sv*a)nOaocVU-qh&^p(-H@>_ zg2CbW@`Ykl<|A_~D?dIbmEbqO9+y6FHWSl&*wK@ zkNHD1c{mK^y&S)`3@<8inB1Y%dzW%Qdb7EUkv+a!Xet&c90ac(pK={Y3X zSgrCJ35S3?$nsE~4R#$A6?uBwN?wdz5}vn2A8xQxaVP)v7NybG=OxscgBD;!W2OMK zIh{6mc-!Y$I+qA};>ypfj@`&a`PW(s)|TrJ)sL^}vLU?kj1CA&jp>_ybNwg6XF<>O zZgxR@%I#_3_cyB3jdaJxav;Xnz^61f?KpduPe2+h3501;!<8ryhm#$P_HLpV_z_zT~lB-pJxzQi40=4Vek{!yAm5l?)Pw>m6SO_ zSt449w}%V`$>T{7T2HqoGb9wb1uh048;*_CKh4hvxdb+r246BOY^!eu)wJ z4i9L^VVa4vT|)}^XYhJ#yo>6NFEspJyj>F1HCBm8HeLQRM!)8n5H22x5l;y9>>Q)6 zapSC&*|Q!(w)xq?rOQ{F%RRC`r^BcEY^Sw{Ig?uRd@w)uXA{Qy?N42Xd@8l@GJ>26 z#rNXwbM;oe8w}pwWN!)0-nnv=lu+Ly-;tGn!}^7$g4>!W6gDfFsi+yA zw@Ielht#h+k;oS6W*ioV$6Odf8i&hAc9$MlYb-VPgQvOe6f9o$y!593&^J9_Dumf} zD85`^bMpHj_XG+i{6aGl$ChZ3CCnkQ?e$8ePX%7JC9uuh)%@(B}<~qB`iA252xLvc>jelqpDXQf}Bqc9vlf?SOZ^E^be1}bEHE~ z)8tBgbI#~;#Tvvk;Ar5?;8%1?vige)yn0cJnE1)ITuJQZ!Kww|jo2X~IHRTvi zgOUB&Lhf(0hS}e2$%)NF~;Cbdou zt){M<$AkXVba0}4f1%*pd3*j} zS1F142r49+y#KmgK2oC(?4+R5NlW}+A03U{{%4f`f4>ULxrnJ{$l~kK`2AghpW4=< z$3@FdLV*;8N=jNSj>eS$x$h>2_7OQd5ja2hEK8P%dnzO_`5YfS-%#A)*Su1=mx`mR3Z z-X7%c%el~#{^)h12@_x_$xR%+R%M1-G#vQEx$0+-rMv~GG)$cK!yQ*-_0R7$k_hrZkb*wQ8YsTlb=b7G;O?1(;UJFBP&M`u zZ*SP`B`d)oTv-KYN<}erL%sp&=gE4pJyi#GBM8gwC=Qd6sV1l#rkDTSU+|9=*>2!T z@?`2%_f@!);`WL-9FYJ;Bp9k}rQhzebp;2WePiRXGW)d=Bq*_UqZ2(_eSccXzbP8U zXojLQL;ZkaBA}QYA-6aUu3mQk05hDPfb0F&oG;o^*KyHM%vWe&rz%6xm{v6p;$#PZ*O+3d}oNHRNY#GO3Y#5xsy|EAeiP$W2v$eVzfu)Iu@n&#{EGDV*&Z zAy*u?sH=E`fX@`+J{REJAml?at5kyg;6#}9<4Jq@qrh&WVp0>|w$e?3^weFQ(3 zyNc^rF({G(T8I4z-s#~pewe7LMC@~vPRA;)Z|_Pu7u*p2#t1)tk&oI`(plSA?26Q4 zEPY%%1;?v6S&}opO?oj6`Wbg&ND=I5){^%HbV%@xtK4uU?Bz5E=r51-+h;8_g1V4! z{G;7~Z*WHSXfU&l% zHBLgLd5V@=o*VT`9Qo~jcgtN}>6RPOOl%0E?0Pkye&M9Bw|JZ(TA|_aVBT|q@~du9 z=qm~9G|>mSscj;UwSpwcq{7`Q{h?5AdASyV=mKZV!+4ACNwtOlxY#uV0Ik=-qIER@vKx zU(i5jvzCx;_;j7z)hYS=e*|#GFQ&IFX;RussdNWcw2v_|q9Gv>YP+_A2S>5uM z8EJ_xg}*|H$5^=#lPdEz7L*VLVGqHr~IW77bL|l}dRk z?K)IiH>v4NjIo{8v8bZiDh$@z6Rel{;Uo$2}8X~A5ib{{l$ zdbpyLeFY@D%kJ@NhZ^||CbxBmK-B7UuA%b}35XQb!I!th<0$wql>!0L(K>GKJ6U6L z^s45G^u(G=fsD}$^>!*gwN${tlnv%guDy@UA~tCR50-PziS?xsC9l*O-P3}wkiLUC z^D;Dfv!5Nh>oOj-om&_9r~IA2PBlcVrX7>$!NU1kMD`kuhmQd@Fsuv%Fl;>5zqWw# z9I{)c*ZZ&2ke70v=5n5PIhTK*cwV)@0XcFy_08$uhvF;p@ET}n`75H>)k#bpPe|id zFm%{OIqPFxl4mgd?rvG!-#^Lp?Q*^3;ikNm-?mtryz9kn8tu1If;~#?k7%nS zbdX{!nlLWpfA7$^`!06<+KEZww0|iK zTqKlO@VGn#JzT*sM*qu-=oTxKp#D$Y(=uW z1;V|2?j3ip9Eu=1+bhp(uW2wUnJEjF&Mtl&O}75=OX!{X-jnLtT=+qXp?jbHw@|fB z$4|#Eu8iJvD80z;gz-I0(~VqB-b}H*hEgPlZYdQ#r#YB-X2@ndoFIL3>K^UbRtT=< zyYZy@l@D20!f!DrUbl2=BODhB=b@Q|^%;3Riq*>pi^nO3KSJM=CO>{~@eCRLjY)FaosytaP zu-3WTzbY{)YFd@#zAWi>RJPlEOC0)&^7i-Gbc}#g=khoO-8*RFUc01%rXav6FJjR} zH0C$`HQnLC4UcTf_{X@X3Oyt5d37sSTMBWS3h%k3c5gbQw48852lQ^BdO6OMTB{8o z#=HrC=MY&=IcdCKdS?H@%#?!po)XA?C?f0r+(-?t{^o0>YSGLCGjrYAm)XX6ac&eJ zH1s%~Edb{l7rU{BG()L`oz_Uor8FO9RV_ib<%}~R&lHbiEA9a3W!1&o`%f|H?6i@{Y1E{OqM+;aGqxH^Q3pn zRH4(hH}5XbT6t5Ucx};DW_O1%h_4TRIs+XHgh~d>M*YAH**~T4)0OjlYlK(PdH>w| ziyV=@;hmYM^4}Caw`TM;^&QWIYD8kS5dxny=_^mJbxbzf@COn647n@FB8L;u+SMut zM)*YMQUA4k-kC}WLxNMvEMuG zz{V`;A`|+MxSuRxWh`UabO)YIF$SG?+IZu>xSSzi!nJa`Sa&nKSnKyND>LbC;=U=cF07;J-B>v0|`@Y7+t>LrkmQA z^r0DV!?-{;l1y63ABBV|zQ5_~AFd6xxgX@{ahx)BlXhRJJNrX%yXO$r(zpPZDe#ocIN^F?v8!o_NyAAKMOKCfb6)bSN4S#Zy-~s&7 zL)GU+2$-!ex$n=9i!}`%Tp@Mq#Z2*DxB8;bdf1c2YiePQ0@S2I)@~(JHp;#8&ZKlV z2uCcTU2 zXj*1}bs0rpev}N({jUg{l9=Nj?^;_GNA5EDzdt&Mth4{UcCdqND=u6zLiTQ7ucf=m z{Ow6NG5;0piO2g>hPe?>j_=R)>A%v;ap)jXttA|l6@8;`6OSi;_SAlXni276e`R*b zl84-Ee2OieWdu%OA;61Kq?<$}5QXv)R=LY-+6u2}IJ?=s^x54%<0_&|dKqK7@y_Do zUDa6nn-fk^8mxN!UB~%P8U`CCpFI54ueLJg*TNJ3@%Wa^>sa5HmQHdEkDr)YS@NY7 zx4u8T6%KuIg_b<15^zWjFI_86XcY$^!JIGe?!s-W$E8-E@|A8OV?MSuYWF_4T5_ik zR`O2w=Mz*TR*uSASb(DRUEWsk0H^85RdFT({@{Wa-m4bhA#{y#$VDlXy+p_H=kGa_tcfgtS@G;jn9j^z zdBSbF>+b5*brTDXjN8o*T?tj`o`b@1E{&aN>Q_ag*68%gZ;Gw zDZ?Z~AI&s%opV#-p;Uo@x0$8WRs!?Ji*KT32D87^k1@D$UwWIXjwP9)Tx-VPACdRT z6=uGdQ~Z;0ti|i=PohEvC2*qgP4FU}M zd8Fa@ryCLDZ<7fDVm-B&fMb}>>2F*53lsC9+dcfHyqQ|m?K_KEV zJuS`0S7Utd7ava;dh~@^h&r_ADeYWtt#w=2tb@YhR*%K`Jw)Q@Wo`Du1O)l@uIy=IX0^-(r&`=8NA81{cSo|?~ z)BU(+qC9tI&0U;M>x!U45$#vivJi(YP>SEV;ds-dnSA}kaFNfP>poV23nP23)Tf`< zLQ{J0XTg=8-4x>uZpqwAXKwWfg$u=bdkUyJtzJY&i$9iC&ExC(zK>}LWL8a>(k=7> zUeHkX&U>?Z4&r1;Rw+B}sf;vl`y}2}7$Z8l_Mu5_RO)JIqi2pl$b{I^DyT6XUTB;{ zvQ1*3{X6?O(`bAr(#w;JW3qEB)f?Lyw*bUzlucF6N4lS%bP03@R8tObgw$N{2|d_C z&SUs=2y6kiGTGz%zPuJQt+kd02>6yy@!FW*@W{<&DX%x>{L9S~y2`Baz4I64z6(El z${mL~MW^4@t|-QJ?(hdZvv#Xo5ms7b;rquFF#0+Tu7Trrh8D{oZ0he@SAk=Ng7`kl zzx{ZcMcF4~QU9VHB1aBbU>hJjRfH(}yl!GVQio)N-HpYb$hv@IeFxmWJXLAXVL;p> zE(%UJ4k#;I9GP6DJFkemMe*-Lp`HdfL(;u@ZpZ#R_gcb8`G4jPJP+UCuH?z&NG%BX O)6+K6D!=RS?tcL+?pH(r literal 0 HcmV?d00001 diff --git a/docs/assets/images/favicon.ico b/docs/assets/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..f1e132660af520f3c259dcf43d1c61b4a9adf431 GIT binary patch literal 15406 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 H*FEqbB}>W( literal 0 HcmV?d00001 diff --git a/docs/assets/images/grotsky-part2/AST.png b/docs/assets/images/grotsky-part2/AST.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5261bc945883a81463a2f92065708e04dee9a9 GIT binary patch literal 6603 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 literal 0 HcmV?d00001 diff --git a/docs/assets/images/kandinsky.jpeg b/docs/assets/images/kandinsky.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..6356a6a8b9b7535e777f786f79a7d9810ba9175d GIT binary patch literal 58912 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

    y1M^qjEKUIfxUHup(S%FV#S2Fq>IjGos}2tz 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 z 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 eh_JZFi()kP&}CHhBB3coZX_)ilbQUv zaD%k%N7{;^#IiO@#AZ{SjZv&zxO3q`Q;e((k>$@k-`@9EXUBQo^E=1+ZLd7_ zx$%3?^L)Q&?|aUBo|h2+Nn{emL>W;*RHliFum)<4@FBd2XeB-peFF??wh)Cb!ZG6w z#E(G`uJWCz7Z!h!Y~oQ2gsVIZwM9k-aX$uPIWV!AQ3e`fAkxGMPW&tp+;4*|&;*{# zBAtO&56Qm~4~V10Hll=BN0br?;v(_dL!Aq%c-&Wivs6AOHWOoYGoy*cL=*ATv0jf* zR;6OcL~n@g#PAe_t1*YTdyM&Da# zsFDZQSPVSxS-2eYi7zSYsBtlX-B5mbhKgWG%WxDtIYf@qbQ$Ox#n(bZYYE3iGXGW#BE5 zr75&81>+{VHF*)WJ`EgTimdlw;0co(&qma%jg^7fOp!Vd2JqL-W%A$kVW5i`qDelN z_~pR>IL8=B_%LvXNq*duc<_}NrKz#Xhk*vhK$}McV4kK%z7GSnjDe1bF)l38)X4T> z;1FZrwr2yn?n90b1E(1SCq0VCa;e*A3Vj&3&KOwh(Lg&>W4R9lT}*Pk=e+P_poFPW z>B9ghU<#}d_6HG_t3toeAfirdRt6GG0mMoX94&2yV-q|@jAZgo6wxnqRtE6KI4rz2 zkGMf>l*W%Hwh}i*^r;G6i>D&!T4-A%QtUBkvN8ZRNY|H%pg*%;Qy5EM7n5U`L3A>( zD|h-f08!(mF$YD^7Fn$n-yM`;|z<<2oLuw!}xqg{B#j? z_)^u;udTAlVqg-yPV0I@*9_xkXvQoO!NJC+zPt)Lw-%p;#pSqS7?USmn-xxkOIN&K zYZ0#nuFYNCQkZq@%2;6@8?Ij}g7a>*!>lJkbN7#4REuLWnN+%Jy^b?3)2xa|6nIEH z5I`gQ<+45o0!`F$;t{y7+F~G*10VT}pIdPbZI6Li6@0pyWD=hYxWw1MI6M>3P$wM! z>jA!8;)7bxAP7fItTx9(5y3$eoTMTMjxG*@n<(fg6k6gU z+B&!if;w1Bb+D2&#oSne$hr7I&%2#N-seKjN$$O1M}!3|s?>#4#Ue`tH-@aRt7AsfFgpgww0dIYH#Z&00rhqGQ+7JKn|lhAgss(Zt377OfIDX z`>3%c4LI-twxt1g^myz$Da*g)T};>BAg8BIFIZxlMb7{1plJrIvFg#QldCDYb+;Xs zNzhM(LB`nO<*NWgY14|6dsK2=)H$Zt*U(Y&a)3i+-kZ_QnGC>WfYuS^NdgcQ8*+d+ zDJJOiWk+NH2AjM&l?M_^CB(f1U{;y?kWxF50Nf}~#Fg5fL>H+~j0S$-zd2}Lr2>CZ z0)VMDpbcmPejQ+(q(?O;S5b14pZ4S4>e2t(7im8PYCp!#YXzk3tAw&P`z!k@0b-Oi n*$*{`BTR;(=ygtkbYu1J*bC8Q&LV#(00000NkvXXu0mjf70AYt literal 0 HcmV?d00001 diff --git a/docs/assets/images/socials/twitter.png b/docs/assets/images/socials/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..419715e15f315d004d36b73be4222f6f5c8ec2e4 GIT binary patch literal 596 zcmV-a0;~OrP)nP)Dh4f*FtCNW5qu3wo0d_FTGS?`Rf`ra zbYYPQ1Fq89h~$Ktim3P+s)4lTxKMnIbI;?>J)7`9yL&m`cka3Ooc});0Q>|A6GM2^ zVub0&&t4Sr-81yz7NKBw^mgz9!4y8|wuTo7Iw5RTFouV(Hd*_PRlGo)2u=i&Hp2^A zXyz&7N3L(H1^-x9z<=_PbPl4583cj}8u_Fa404H02|h|Oi4mZc3qS>NwctKb%BrKF z4yVXxk#&|-f-(S#m`sWvb;O^gg=%iIr4n4;>)_+fu?H5kB8wtc)KgS{sKZr433qQu z@a3%Oe5U;g-O6}JbpLLNL=e?m2Qkhc>zc()g1je0r9|*YccG1tiFst{uH+h?(d$By zrz^OHyQtO`Ja;L0qAQ4!<4Q21zncz_VT{k=ugP}(k!(Xjn`u5+p@)DrK7(m9!50FY zHW+`CX-124-2i`v1@n|SPp-D$wY!`=$2Bed-6Wg3X3~!{#3Skc38nl>t*RL1q6A;U zMk-}Xh&!Z7@U@E3ClPEh%x6CEl19!+r^d%C>D)U$!7TSUrFgCBJm5zvm0g6O<42lD z53^pBj&UaG<1r0XaD__hXy>hq1LJ>CU0Xi{T0h2iSrv4EpNKX5H+%pEOj*L`A1wP$ i#ytI<^OcU>(eD7i-Lh${P963D00000?_C3KWL0Xj;aKuHHB9q-G9_tg*e-YMI;ZW*(1+34$PX z+{3%wu90K%e7#;97{(X8mj#EzVKQ*BSfto=I!&zL!KRAzy(_O3BWPA48oXWxxGgKj>MB1 z11q`o{sa&h1ndf6%%kl7_;>shkfS}HPl}?5X0sWEVMv$DCB??$F*Tb_I-O4BYhH}` zi)EKYg8o|-u-oM(mtE_el@s8vMD}w9XkF6ADkGrnZrZYQmyGxS>IiUfOfDrU+OXc< zjNteZ7_`@(^RlBGGRB?L@$8WQ(5~^K4ThI}GMS{9);YZh_-w6t=LD^DIuYOrWvv&v zc23Ybr*yRw?)Q5d31){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot call unknown function "+ident+", make sure it is exported");return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="mlisp.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["j"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["l"];addOnInit(Module["asm"]["k"]);removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){var result=WebAssembly.instantiate(binary,info);return result}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var tempDouble;var tempI64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto==="object"&&typeof crypto["getRandomValues"]==="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){var alignedSize=alignMemory(size,16384);var ptr=abort();while(size=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.indexOf("r")!==-1&&!(node.mode&292)){return 2}else if(perms.indexOf("w")!==-1&&!(node.mode&146)){return 2}else if(perms.indexOf("x")!==-1&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.indexOf(current.mount)!==-1){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=varargs?SYSCALLS.get():0;var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function abortOnCannotGrowMemory(requestedSize){abort("OOM")}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;abortOnCannotGrowMemory(requestedSize)}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"c":___sys_fcntl64,"h":___sys_ioctl,"i":___sys_open,"e":_emscripten_memcpy_big,"f":_emscripten_resize_heap,"b":_fd_close,"g":_fd_read,"d":_fd_seek,"a":_fd_write};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["k"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["m"]).apply(null,arguments)};var _mlisp_init=Module["_mlisp_init"]=function(){return(_mlisp_init=Module["_mlisp_init"]=Module["asm"]["n"]).apply(null,arguments)};var _mlisp_interpret=Module["_mlisp_interpret"]=function(){return(_mlisp_interpret=Module["_mlisp_interpret"]=Module["asm"]["o"]).apply(null,arguments)};var _mlisp_cleanup=Module["_mlisp_cleanup"]=function(){return(_mlisp_cleanup=Module["_mlisp_cleanup"]=Module["asm"]["p"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["q"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["r"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["s"]).apply(null,arguments)};Module["cwrap"]=cwrap;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); diff --git a/docs/assets/mlisp/mlisp.wasm b/docs/assets/mlisp/mlisp.wasm new file mode 100644 index 0000000000000000000000000000000000000000..a02cfcb19766a96dd7b7cd236578fc5c2dfef772 GIT binary patch literal 83808 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.github.io +https://mliezun.github.io/ +mliezun.github.io +2024-01-04 +I'm Miguel. Here I write mainly about programming and side projects. +Generating posts using markdown +https://mliezun.github.io/2024/01/04/new-markdown-generator.html +2024-01-04 +

    Reading time: +

    +

    +

    Generating posts using markdown +

    +

    This is pretty standard for Github pages. But in this case, the parser has been written by me. It takes some subset of markdown and compiles it to HTML. +

    Only what you see in this post is what's supported. +

    Code blocks +

    +

    Standard code block: +

    Hello, this is a code block!
    +

    +

    Syntax highlighting: +

    from functools import reduce, partial
    +import operator
    +
    +mul = partial(reduce, operator.mul)
    +print("Factorial of 5:", mul(range(1, 6)))
    +

    +

    Unordered list +

    +

    Bolds, Italics and Inline code +

    +

    Some text can be bolded, while some other can be in Italics. +

    But the best is to have print("inline code"). +

    Links and images +

    +

    Link to the blog source code where you can see how the parser works (tldr: is awful). +

    Picture of NYC: +

    Picture of NYC +

    HEADINGS +

    +

    There's a thing you haven't noticed so far. There's support for different kinds of headings. You can see them in increasing order here: +

    Microscopic +

    +

    Small +

    +

    Good +

    +

    Pretty good +

    +

    Big +

    +

    Good bye! +

    +

    Thanks for checking out the blog. I've done this to reduce the complexity of creating new blogposts. It was a headache before. +

    Hopefully now I'll write more. Stay tuned. +

    +

    Miguel LiezunCustom Markdown parser and HTML generator using Grotsky, my toy programming language that powers this blog. Up until now I've used a hacky HTML generator that relies on lists. Now Im integrating a simple MD parser that makes easier to write new articles. +Day 20. My favourite problem from Advent of Code 2023 +https://mliezun.github.io/2023/12/25/favourite-advent-of-code-2023.html +2023-12-25 +

    Reading time: +

    Day 20 +

    Im gonna briefly describe the problem here, but if you want to see the real thing go check it out https://adventofcode.com/2023/day/20. +

    I like it because it involves some simple electronic devices that are wired together and send pulses/signals to each other. In this problem you have to make sure to correctly propagate the signals and simulate the behaviour of the devices. +

    There are two devices that have a very distinct behaviour: +

  • Flip flops: similar to a T flip-flop electronic device.
  • +
  • Conjunctions: similar to a NAND gate with memory on its inputs.
  • +

    In this problem, Flip flops are initially off and whenever they reiceve a low pulse they toggle between on/off. Each time it toggles state it sends a pulse as an output. When turned off sends a low pulse, when turned on sends a high pulse. +

    Conjunction modules remember the most recent pulse on each input. By default it remembers a low pulse for all inputs. When a pulse is received it updates the memory for that input. Then, if it remembers high pulses for all inputs, it sends a low pulse; otherwise, it sends a high pulse. +

    There is also some "dummy" modules: +

  • Broadcaster: has 1 input and N outputs. It replicates the input in all its outputs.
  • +
  • Button: when pressed sends a low pulse. The button is always connected as the broadcaster input. This is similar to a normally closed switch.
  • +
  • Test module: module that receive and process inputs but has no output.
  • +

    One important thing to have in mind is that modules only send output pulses when they receive a pulse as input. +

    Problem input +

    The example input looks something like this: +
    +broadcaster -> a, b, c
    +%a -> b
    +%b -> c
    +%c -> inv
    +&inv -> a
    +
    +

    There will always be just one Broadcaster module called "broadcaster" that has the Button connected as input. In this case it has module's "a", "b" and "c" connected to its output. +

    The arrow -> indicates what modules are connected to the output of the module to the left. +

    Lines that start with % means the module is a Flip flop, for example: %a -> b indicates that there's a flip flop called "a" whose output is connected to module's "b" input. +

    Lines that start with & means the module is a Conjunction, for example: &inv -> a indicates that there's a conjunction called "inv" whose output is connected to module's "a" input. +

    Let's analyze how this circuit behaves once the button is pushed: +
    +button -0-> broadcaster
    +broadcaster -0-> a
    +broadcaster -0-> b
    +broadcaster -0-> c
    +a -1-> b
    +b -1-> c
    +c -1-> inv
    +inv -0-> a
    +a -0-> b
    +b -0-> c
    +c -0-> inv
    +inv -1-> a
    +
    +In this example 8 low (0) pulses and 4 high (1) pulses are sent. +

    Part 1 +

    To solve the first part we need to calculate the multiplication between high and low pulses sent between devices. +

    In the previous example that would be 8*4=32. +

    But this time we don't push the button only once, but we push it a 1000 times. Each time we push the button we wait until all signals propagate and the circuit settles into a state before pushing the button again. +

    Solution +

    First I started by modelling the devices as objects. Starting with a single base class that has most of the common behaviour. +
    +from abc import ABC
    +measure_pulses = {0: 0, 1: 0}
    +class Module(ABC):
    +    def __init__(self, name: str):
    +        self.name = name
    +        self.outputs = []
    +
    +    def receive_pulse(self, mod: "Module", pulse: int) -> list[tuple["Module", int]]:
    +        measure_pulses[pulse] += 1
    +        print(f"{mod and mod.name} -{pulse}-> {self.name}")
    +        return self.process_pulse(mod, pulse)
    +
    +    def connect_output(self, mod: "Module"):
    +        self.outputs.append(mod)
    +
    +    def propagate_pulse(self, pulse: int):
    +        mods = []
    +        for m in self.outputs:
    +            mods.append((m, pulse))
    +        return mods
    +
    +    def process_pulse(self, mod: "Module", pulse: int):
    +        raise NotImplementedError()
    +
    +    def __str__(self) -> str:
    +        return f"{self.__class__.__name__}(name={self.name})"
    +
    +    def __repr__(self) -> str:
    +        return str(self)
    +
    +

    What we see here is that we expect all modules to have a name and outputs. See __init__(), __str__(), __repr__() and connect_output(). +

    Each module can receive a pulse 0 or 1 from another module. See receive_pulse(). Each time we process a pulse we record it in a global dict called measure_pulses. +

    Also we leave process_pulse() to be defined by each particular module type. +

    We have a method that returns a list of all modules to which signals should be propagated. See propagate_pulse(). +

    Let's start by the easiest module type: +
    +class TestModule(Module):
    +    def process_pulse(self, mod: "Module", pulse: int):
    +        return []
    +
    +Give that it's a dummy module, it doesn't do anything when it receives an input. +
    +class Broadcaster(Module):
    +    def process_pulse(self, mod: "Module", pulse: int):
    +        return super().propagate_pulse(pulse)
    +
    +As expected the Broadcaster always propagates the received input to all its outputs. +
    +class FlipFlop(Module):
    +    def __init__(self, name: str):
    +        super().__init__(name)
    +        self.state = 0
    +
    +    def process_pulse(self, mod: "Module", pulse: int):
    +        if pulse == 0:
    +            self.state = (self.state + 1) % 2
    +            return super().propagate_pulse(self.state)
    +        return []
    +
    +

    The flip flop start initially turned off. See self.state = 0 in __init__(). +

    In process_pulse() we implement the behaviour: +

  • If receives a low pulse, toggles the state and sends a pulse equals to the state to all its outputs.
  • +
  • Otherwise it doesn't do anything.
  • +
    +class Conjunction(Module):
    +    def __init__(self, name: str):
    +        super().__init__(name)
    +        self.memory = {}
    +
    +    def remember_input(self, mod: Module):
    +        self.memory[mod.name] = 0
    +
    +    def process_pulse(self, mod: Module, pulse: int):
    +        self.memory[mod.name] = pulse
    +        if all(self.memory.values()):
    +            return self.propagate_pulse(0)
    +        return self.propagate_pulse(1)
    +
    +

    The conjunction initializes its memory as empty. See __init__(). +

    Each time a module is plugged in as an input it remembers it as OFF (0). See remember_input(). +

    The way it processes pulses is by first recording the pulse for the input in its memory. Then if all inputs are 1s it sends a 0 pulse to all its outputs. +

    Otherwise it sends a 1 pulse to all its outputs. +

    At this point we have all our building blocks for solving this problem. We only need to parse the input and something that pushes the button and makes sure signals are propagated to the end. +

    Parsing modules is straightforward: +
    +def parse_modules(modules: list) -> dict[str, Module]:
    +    modules_by_name = {}
    +    outputs_by_name = {}
    +
    +    # Parse all modules into their correspondig class and store
    +    # them in a dict.
    +    for m in modules:
    +        module_type = m[0]
    +        module_outputs = [o.strip() for o in m[1].split(",") if o.strip()]
    +        if module_type.startswith("broadcaster"):
    +            modules_by_name[module_type] = Broadcaster(module_type)
    +            outputs_by_name[module_type] = module_outputs
    +        elif module_type.startswith("%"):
    +            modules_by_name[module_type[1:]] = FlipFlop(module_type[1:])
    +            outputs_by_name[module_type[1:]] = module_outputs
    +        elif module_type.startswith("&"):
    +            modules_by_name[module_type[1:]] = Conjunction(module_type[1:])
    +            outputs_by_name[module_type[1:]] = module_outputs
    +    # Once all the modules are parsed use connect their outputs.
    +
    +    # If the module doesn't exist at this point is a TestModule.
    +    # If the module is a Conjunction, call remember_input().
    +    for name, outputs in outputs_by_name.items():
    +        for mod_name in outputs:
    +            mod = modules_by_name.get(mod_name, TestModule(mod_name))
    +            modules_by_name[name].connect_output(mod)
    +            if isinstance(mod, Conjunction):
    +                mod.remember_input(modules_by_name[name])
    +
    +    return modules_by_name
    +
    +

    If we parse our example using that function we will receive a dictionary as its output. Keys are module names and values are the objects representing the module. +

    If we parse the example we get something like this: +

    +example = """broadcaster -> a, b, c
    +%a -> b
    +%b -> c
    +%c -> inv
    +&inv -> a"""
    +example_modules = [m.split(" -> ") for m in example.splitlines() if m.strip()]
    +print(parse_modules(example_modules))
    +
    +# Output
    +{
    +    'broadcaster': Broadcaster(name=broadcaster),
    +    'a': FlipFlop(name=a),
    +    'b': FlipFlop(name=b),
    +    'c': FlipFlop(name=c),
    +    'inv': Conjunction(name=inv)
    +}
    +
    +Then we need a function that pushes the button and makes sure all signals are propagated: +
    +def push_button(modules_by_name: dict[str, Module]):
    +    broad = modules_by_name["broadcaster"]
    +    queue = [(broad, broad.receive_pulse(None, 0))]
    +    while queue:
    +        current, signals = queue.pop(0)
    +        for mod, pulse in signals:
    +            queue.append((mod, mod.receive_pulse(current, pulse)))
    +
    +

    Here, we lookup the broadcaster module by name. And send a pulse (note that we pass None as the module because we didn't implement a button class) to the broadcaster. +

    We store the current module (broadcaster) along with all the propagated signals (return value from receive_pulse()) in a queue to be processed. +

    While the signal queue to be processed is not empty we do the following: +

  • Extract the first element of the queue.
  • +
  • Go trough all the signals that this element is sending.
  • +
  • Send the pulses to each corresponding module and store them in the queue to be processed.
  • +

    This process will stop when all responses from receive_pulse() are empty and there are no more signals added to the queue. +

    If we run this for our example: +

    +example_modules = parse_modules(example_modules)
    +push_button(example_modules)
    +
    +# Output
    +None -0-> broadcaster
    +broadcaster -0-> a
    +broadcaster -0-> b
    +broadcaster -0-> c
    +a -1-> b
    +b -1-> c
    +c -1-> inv
    +inv -0-> a
    +a -0-> b
    +b -0-> c
    +c -0-> inv
    +inv -1-> a
    +
    +

    It looks the same as when we analyzed the example above!! +

    We're ready for processing our problems input. (Remeber to comment out the print statement inside receive_pulse()). +

    +modules = open("input.txt", "r").read().strip()
    +modules = [m.split(" -> ") for m in modules.splitlines() if m.strip()]
    +modules = parse_modules(modules)
    +
    +for _ in range(1000):
    +    push_button(modules)
    +
    +print("result:", measure_pulses[0] * measure_pulses[1])
    +
    +# Output
    +result: x
    +
    +Based on your problem input x will be the solution. +

    Part 2 +

    This part as always is much trickier than the first part. It doesn't involve much code changes, just figuring out a way of avoiding large computations. +

    For this part, the problem tells us that there's a module called rx. And we need to find out the lowest amount of button pulses that will make the rx module receive a low pulse. +

    As I have learned troughout this entire challenge, just nahively letting it run and see when the rx module gets a low signal will get me nowhere. It will run forever. +

    So, taking a look at the input and see what the rx module is actually connected to might provide some guidance. +

    Following is for my case (I don't know if all problem inputs are the same). Looking up "rx" in the input I find a single line: +
    +...
    +&lv -> rx
    +...
    +
    +

    That means rx is a TestModule (a dummy module that has nothing connected to its output). And that has only one input: a Conjunction called lv. +

    Ok, that feels like progress. Let's see what lv is connected to: +
    +...
    +&st -> lv
    +&tn -> lv
    +&hh -> lv
    +&dt -> lv
    +...
    +
    +

    Other 4 Conjunctions are connected as inputs of lv. That's interesting. Because lv is a Conjuction it means that to send the low pulse required by rx it should receive all high pulses from its inputs. +

    The solution from here is kind of intuitive at this point. If we figure out how many button pulses does it take for each of the input devices to send a 1 signal we can multiply them together and get the result. +

    I'll explain better. Let's say st sends a 1 on every button push, tn sends a 1 every second button push (this means you have to press the button twice to get tn to send a 1 as an output), hh sends a 1 every fourth button push and dt sends a 1 every eighth button push. +

    So it looks like this: +
    +module | pushes
    +---------------
    +  st   |   1
    +  tn   |   2
    +  hh   |   4
    +  dt   |   8
    +
    +

    In this example, if we push the button 8 times. They are all gonna send a high pulse. Because 8 is divisible by 1, 2, 4 and 8. +

    If the table were different: +
    +module | pushes
    +---------------
    +  st   |   1
    +  tn   |   3
    +  hh   |   5
    +  dt   |   7
    +
    +

    In this case there's no obvious number of times we should push the button. But if we multiply the numbers together we get a number that is divisible by every number in the table. Pushing the button 1 * 3 * 5 * 7 = 105 times will make all the outputs send a 1, and consequently rx will receive a 0. +

    Solution +

    What we need to do then is to figure after out how many button presses we get a 1 on each of those modules. +
    +from collections import defaultdict
    +
    +# Store number of button presses in a global variable
    +ITERATIONS = 0
    +# Store high pulses for target modules
    +OUT_PULSES = defaultdict(list)
    +
    +class Conjunction(Module):
    +    def __init__(self, name: str):
    +        super().__init__(name)
    +        self.memory = {}
    +
    +    def remember_input(self, mod: Module):
    +        self.memory[mod.name] = 0
    +
    +    def start_recording(self):
    +        self.recording = True
    +
    +    def record(self):
    +        if hasattr(self, "recording"):
    +            OUT_PULSES[self.name].append(ITERATIONS)
    +
    +    def process_pulse(self, mod: Module, pulse: int):
    +        self.memory[mod.name] = pulse
    +        if all(self.memory.values()):
    +            return self.propagate_pulse(0)
    +        self.record()
    +        return self.propagate_pulse(1)
    +
    +

    We introduced 2 new methods to the conjunction module: start_recording() and record(). The first just initializes a bool attribute. And the second makes sure to only record high pulses for objects that have been initialized (method start_recording() called). +

    Also introduced 2 global variables: ITERATIONS to keep track of button pushes and OUT_SIGNALS to track each time one of the modules outputs a high pulse. +

    Now we need to make those specific modules record their outputs: +
    +# Get the "lv" module by name
    +lv = modules["lv"]
    +lv_inputs = [modules[k] for k in lv.memory.keys()]
    +for m in lv_inputs:
    +    m.start_recording()
    +
    +I wasn't sure if the cycle was always going to be the same, so just to be sure I did 100_000 button pushes and recorded all the "1" outputs for those modules. +
    +for i in range(100_000):
    +    ITERATIONS += 1
    +    push_button(modules)
    +print(OUT_PULSES)
    +
    +# Output
    +{'hh': [3769, 7538, 11307, 15076, 18845, 22614, 26383, 30152, 33921, 37690, 41459, 45228, 48997, 52766, 56535, 60304, 64073, 67842, 71611, 75380, 79149, 82918, 86687, 90456, 94225, 97994], 'tn': [3863, 7726, 11589, 15452, 19315, 23178, 27041, 30904, 34767, 38630, 42493, 46356, 50219, 54082, 57945, 61808, 65671, 69534, 73397, 77260, 81123, 84986, 88849, 92712, 96575], 'st': [3929, 7858, 11787, 15716, 19645, 23574, 27503, 31432, 35361, 39290, 43219, 47148, 51077, 55006, 58935, 62864, 66793, 70722, 74651, 78580, 82509, 86438, 90367, 94296, 98225], 'dt': [4079, 8158, 12237, 16316, 20395, 24474, 28553, 32632, 36711, 40790, 44869, 48948, 53027, 57106, 61185, 65264, 69343, 73422, 77501, 81580, 85659, 89738, 93817, 97896]}
    +
    +We can observe that for each module we have a periodicity given by: +
    +hh = n*3769
    +tn = n*3863
    +st = n*3929
    +dt = n*4079
    +
    +

    This means we can just multiply the first element of each list for each module and we'll get our result. +

    In my case it was: +
    +accum = 1
    +for name, pulses in OUT_PULSES.items():
    +    accum *= pulses[0]
    +print("result:", accum)
    +
    +# Output
    +result: 233338595643977
    +
    +

    Edit: +As some people have pointed out in the +HN discussion, just multiplying the numbers together only works because the numbers are +coprimes + and the correct solution is to use +LCM. +

    Closing words +

    This problem is my favourite because it has a few characteristics that I personally enjoy: +

  • It's based on real world stuff. In this case electronic devices (which is also a plus because they're fun).
  • +
  • It can be easily translated to an OOP approach which makes it easy to implement and understand.
  • +
  • To solve the second part you need to look at the data and make a solution for your particular input.
  • +
  • It doesn't involve any Graph traversal or specific Math, Calculus or Algebra knowledge. Or any obscure CS algorithm.
  • +

    In the end this is one of my favourites because to solve it you just have to understand the problem and understand the data. +

    Link to my github project with the solutions https://github.com/mliezun/aoc. +

    Miguel LiezunAdvent of code 2023 has gone by, this is my first year participating. It's been fun and I want to share the problem that I enjoyed the most. It's based on simple electronic devices sending signals or pulses to each other. +
    I rewrote my toy language interpreter in Rust +https://mliezun.github.io/2023/11/23/grotsky-rust-part3.html +2023-11-23 +

    Reading time: +

    I rewrote my toy language interpreter in Rust

    Im rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x. This has been a serious of posts, this one is the latest one. Hopefully the best and most insightful +of them all.

    In previous posts: +

    I've outlined a plan to migrate Grotsky to a Rust based platform. +

    + Originally, my plan was very ambitious and I thought I would be able to finish the transition + in like two months. + In reality it took five months :-) + +

    Performance improvement

    I was aiming at a 10x improvement. In reality is not that much. + I ran various benchmarks and get at most a 4x improvement. + Which is not great, but also not that bad given that I know very little + of how to do high performance Rust. + The interpreter is written in the most dumbest and easiest way I managed to do it. +

    Let's look at some numbers for different programs. +

    Loop program

    let start = io.clock()
    +fn test() {
    +    let a = 1
    +    while a < 10000000 {
    +        a = a + 1
    +    }
    +}
    +test()
    +io.println(io.clock() - start)
    +

    The result is: +

    trial #1
    +build/grotsky   best 5.135s  3.877x time of best
    +build/grotsky-rs   best 1.325s  287.6800% faster
    +trial #2
    +build/grotsky   best 5.052s  3.814x time of best
    +build/grotsky-rs   best 1.325s  281.4182% faster
    +trial #3
    +build/grotsky   best 5.035s  3.802x time of best
    +build/grotsky-rs   best 1.325s  280.1663% faster
    +trial #4
    +build/grotsky   best 5.003s  3.777x time of best
    +build/grotsky-rs   best 1.325s  277.6831% faster
    +trial #5
    +build/grotsky   best 5.003s  3.777x time of best
    +build/grotsky-rs   best 1.325s  277.6831% faster
    +

    Recusive fibonacci

    let start = io.clock()
    +fn fib(n) {
    +	if n <= 2 {
    +		return n
    +	}
    +	return fib(n-2) + fib(n-1)
    +}
    +fib(28)
    +io.println(io.clock() - start)
    +

    The result is: +

    trial #1
    +build/grotsky   best 0.8409s  294.5155% faster
    +build/grotsky-rs   best 3.317s  3.945x time of best
    +trial #2
    +build/grotsky   best 0.8168s  271.3829% faster
    +build/grotsky-rs   best 3.033s  3.714x time of best
    +trial #3
    +build/grotsky   best 0.797s  245.6835% faster
    +build/grotsky-rs   best 2.755s  3.457x time of best
    +trial #4
    +build/grotsky   best 0.7784s  249.9964% faster
    +build/grotsky-rs   best 2.724s  3.5x time of best
    +trial #5
    +build/grotsky   best 0.7784s  249.9964% faster
    +build/grotsky-rs   best 2.724s  3.5x time of best
    +

    In this case is like 3.5x slower. This is due to function calls. + Im not very well versed in Rust, so on each call im copying a lot of + data over and over. In the go implementation everything is just pointers + so there's less copying. +

    Compile to bytecode

    With the Rust implementation, generating and compiling to bytecode was added. + Now it's possible to generate a bytecode file to later read it. + This is a way of distributing files without giving away source code and also + a little bit more performant because you skip parsing and compilation phases. +

    How it works: +

    grotsky compile example.gr  # Compile file
    +grotsky example.grc  # Run compiled file
    +

    Memory model

    Grotsky is a reference-counted language. We're using Rust's Rc and RefCell to keep track of values. +

    pub struct MutValue<T>(pub Rc<RefCell<T>>);
    +
    +impl<T> MutValue<T> {
    +    pub fn new(obj: T) -> Self {
    +        MutValue::<T>(Rc::new(RefCell::new(obj)))
    +    }
    +}
    +
    +pub enum Value {
    +    Class(MutValue<ClassValue>),
    +    Object(MutValue<ObjectValue>),
    +    Dict(MutValue<DictValue>),
    +    List(MutValue<ListValue>),
    +    Fn(MutValue<FnValue>),
    +    Native(NativeValue),
    +    Number(NumberValue),
    +    String(StringValue),
    +    Bytes(BytesValue),
    +    Bool(BoolValue),
    +    Slice(SliceValue),
    +    Nil,
    +}
    +
    +pub enum Record {
    +    Val(Value),
    +    Ref(MutValue<Value>),
    +}
    +
    +pub struct VM {
    +    pub activation_records: Vec<Record>,
    +}
    +

    Most of the simple values are just stored as-is: Native (builtin functions), Number, String, + Bytes, Bool, Slice and Nil. +

    For the other complex values we need to use 'pointers' which in this case are MutValue. +

    Then the Grotsky VM uses Records which can be a plain Value or a reference to a Value. + The records are registers, each function has up to 255 registers. + The reference to values are used to store upvalues. + A register is turned into an upvalue when a variable is closed by another function. +

    This implementation ends up being very slow, but easy to manage. Because Rust stdlib + does all the work. +

    Using Rust in this blogpost

    As you may know, this blog is powered by grotsky. + Im happy to say that I successfully migrated from grotsky to grostky-rs as the backend for the blog. + And what you're reading now is generated by the latest implementation of the language using Rust. +

    Even for local development the Rust version is used. Which means Im using a TCP server and an HTTP + implementation written in Grotsky. +

    Closing remarks

    This has been a great learning, Im happy to have finished because it required a lot of effort. + Im not gonna announce any new work on this interpreter but I would like to keep adding stuff. + Improving it further to make it more performant and more usable. +

    In the end I encourage everyone to try it and also start their own project. Is always cool to see + what everyone else is doing. +

    Thanks for reading and please leave a comment. +

    Miguel LiezunIm rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x. This has been a serious of posts, this one is the latest one. Hopefully the best and most insightful +of them all. +
    Rewrite my toy language interpreter in Rust, an update +https://mliezun.github.io/2023/09/23/grotsky-rust-part2.html +2023-09-23 +

    Reading time: +

    Rewrite my toy language interpreter in Rust, an update

    Im rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x.

    In a +previous post + I've outlined a plan to migrate Grotsky to a Rust based platform. +

    I was suposed to finish it more than a month ago :'). +

    Of course, I wasn't able to do it. +

    I think my original estimation was actually very flawed, but I also didn't put enough work. +

    I failed my estimation, because I decided to skip the step of first writing a Tree-based interpreter in Rust. + To go directly to a Register-based virtual machine. +

    The reason that is taking me so long is that I travel a lot. I've been a digital nomad for more than a year now. + And when I finish working I prefer to go outside and explore the place where Im staying. + I go to a lot of interesting places: at this time Im in Turkey and heading to Hong Kong, after that Im going to South Korea. + So, it makes sense to actually experience the places where Im staying than to stay inside writing code. +

    Im here to propose a new roadmap that is more achivable with the time I have to work on this. + And the idea is to finish it before the end of the year. +

    Plus, I recently heard some advice that I think it's worth to try: Work on someone for 15 minutes every day. + I do have 15 minutes everyday where Im probably scrolling through social media or just consuming content. + I can make better use of that time by putting it into this project. +

    Updated Roadmap

    • Sep 30: Publish a blogpost about the memory model of the Rust implementation.
    • Oct 15: Migrate automated tests to the new backend and make sure they pass.
    • Oct 30: Implement stdlib in bytecode interpreter. Share results.
    • Nov 15: Add ability to compile to bytecode and run from bytecode.
    • Dec 30: Finish up everything and publish a blogpost update before end of year.

    This is gonna be rough, because of the traveling and being jetlaged all the time. + But I think the roadmap is easy enough so I can do it. +

    Thanks for reading. Please, leave a comment about your experience and struggle with side projects. +

    Miguel LiezunIm rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x. +
    The end of a side project +https://mliezun.github.io/2023/07/15/the-end-of-a-side-project.html +2023-07-15 +

    Reading time: +

    The end of a side project

    Cloud Outdated was a personalized digest of updates for cloud services. It's sad to see it go, + but it was a fun project to work on, learn some new stuff and collab with a friend. + There are some takeaways from this that I'd like to share. +

    Building something simple is hard

    From the beginning we were trying to build a simple product to get notified when new versions + of Python were supported in AWS Lambda. But we figured we could support other things too, like + all Lambda runtimes, then also GCP and Azure, then more services of each one. +Features started piling up pretty quickly. +

    When building stuff I try to think of every edge case, even the most improbable ones, and make + the software infalible. Of course, it's impossible to succeed at that, software will always be fallible. +And this premature optimization ends up making the project more complex than it should be. +

    We planned to work on this for a 1 or 2 months, and it ended up taking 6+ months :-). +

    My takeaway here is: start building the dumbest thing possible that gets the job done, then adjust as needed. +

    Getting users is hard

    We're killing this project because nobody uses it. +And nobody except us has used it since it was launched more than a year ago. +Some people subscribed but never even opened an email. +

    We tried to advertise in our social media and post it in different builders communities. +But that will get you so far if you're not an influencer that has the right audience. +

    We thought that we'll get more traffic from organic search or people telling their friends about this. +But in the end I think nobody really needed something like this that much. +

    My takeaway here is: building something that people really want and getting the product to the hands + of the people that want it is very complicated. You should think very deeply about what problem your + product is solving and how your users will find you. +

    The money

    This has costed like $200 (US dollars) since inception. For two people that's like $100 each. +It's not a lot, but for something that has no users that's quite expensive. +

    We used Lambdas to serve this project. +I feel like we were promised that the cloud and serverless are cheap and easy solution. +But in my opinion it doesn't seem to be the case. +It's definitely not easier nor cheaper than having a PHP website in this case. +Im sure there are other cases in which it makes more sense. +

    This is also a reason of why we're killing the project. +Our service started failing because after a deploy dependencies were updated and the code + was to big to fit on a Lambda. +It would have been a lot of work to fix it, so we decided to kill it and save some bucks every month. +

    For personal projects which you're not sure how they're going to scale, + I think serverless is probably not the right choice. +Serverless make sense if you're going to have huge burst of traffics and don't want + to handle a lot of infra. +

    My takeaway here is: beware of the cloud, if you're just building a small side project + or don't have huge infra needs stick with the cheapest providers (Hostinger, PythonAnywhere, Hetzner) + and avoid cloud providers (GCP, Azure, AWS). +

    Final thougths

    If I haven't made this clear enough, building a successful product is *hard*. +There are many things to think about when starting, and the technical stuff hopefully + is the easy part. +I think this are the 3 most important lessons that I've learned working on this: +

    • Build the dumbest thing that does the job, improve as needed.
    • Think deeply about what problem are you solving and how you're going to deliver the solution to the people that need it.
    • Beware of the cloud, if possible use a cheaper provider. It will save you money and headaches.

    Miguel LiezunCloud Outdated was a personalized digest of updates for cloud services. It's sad to see it go, + but it was a fun project to work on, learn some new stuff and collab with a friend. + There are some takeaways from this that I'd like to share. +
    Rewrite my toy language interpreter in Rust +https://mliezun.github.io/2023/06/02/rewrite-grotsky-rust.html +2023-06-02 +

    Reading time: +

    Rewrite my toy language interpreter in Rust

    Im rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x.

    As of this writting the Rust implementation is 4x faster than the Golang implementation.

    I've rewritten the Lexer, Parser and part of the Runtime. Enough for run this code and measure + how long it takes for each implementation to finish:

    let a = 1
    +while a < 100000000 {
    +    a = a + 1
    +}
    +

    I was inspired by Mitchel Hashimoto's post: +My Approach to Building Large Technical Projects. And I want to create a roadmap of little projects to reach my goal of having a full-fledged interpreter in Rust. +

    Goal

    Until now Grotsky has been running on a Tree-based interpreter. My goal is that at the end of the rewrite I + will have a Bytecode interpreter. +

    First I want to rewrite the Tree-based interpreter in Rust and achieve at least 10x performance improvement. +

    Then figure out if I want to use a Register based or Stack based bytecode interpreter. +

    Also, I would like to have a stable bytecode representation to be able to compile programs to a binary format + that can be shipped as is or packaged into a binary. +

    Finally, it's time Grotsky gets a REPL. +

    Roadmap

    Believe it or not, Grotsky it's pretty big. It has support for reading and writting files, sockets and more + on the stdlib. Which means I have a huge task ahead of me. +

    First thing I want to do is to have some sort of automated testing setup that can compare Rust vs Go implementation. + Right now all test are written as Go unit test, I need to make them agnostic of language backend. +

    • Jun 9: Have a complete setup of automated tests for correctness and performance.
    • Jun 16: Language runtime (without stdlib) rewritten in Rust.
    • Jun 23: Finish migrating stdlib and publish results (new blog post). Use new implementation for blog engine.
    • Jun 30: Decide which kind of bytecode interpreter to use, then make a design and plan for the implementation.
    • Jul 7: Have a working bytecode interpreter that is able to run the program shown in this post ^. Just a simple while loop. Compare performance and share progress.
    • Jul 14: Add support for functions and closures.
    • Jul 21: Finish runtime without stdlib in bytecode interpreter.
    • Jul 28: Implement stdlib in bytecode interpreter. Share results.
    • Aug 5: Add ability to compile to bytecode and run from bytecode.
    • Aug 12: Add REPL and finish up project.

    Im gonna have lots of fun with this project and Im sure next time a post here I'll be a changed man. + Surely gonna learn a lot, Im excited about what lies ahead. +

    Miguel LiezunIm rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x. +
    Writing a Redis clone in Go from scratch +https://mliezun.github.io/2023/04/08/redis-clone.html +2023-04-08 +

    Reading time: +

    Writing a Redis clone in Go from scratch

    In this post we're going to write a basic Redis clone in Go that implements the most simple commands: GET, +SET, DEL and QUIT. At the end you'll know how to parse a byte stream from a live TCP connection, and hopefully have a working +implementation of Redis.

    What's intersting about this project is that it's production ready (not really). + It's being used in production in an old Web app that I made for a client in 2017. + It has been running for a few months now without issues.

    I mantain that app to this day and I charge like 50 bucks a month for it. I do it because + Im friends with the person that uses the app.

    Long story short, the app's backend is written in PHP and uses Redis for caching, only GET, SET and DEL commands. + I asked my friend if I could replace it with my custom version and said yes, so I decided to give it a go.

    If you're looking for C/C++ implementation, go check out this book.

    What we'll be building

    If you go to the +command list on redis webpage you'll see that there are 463 commands to this day (maybe more if you're in the future). +

    That's a crazy number. Here, we're only implementing 4 commands: GET, SET, DEL, QUIT, + the other 459 commands are left as an exercise to the reader. +

    GET

    GET key
    +

    Returns the value referenced by key. + If the key does not exist then nil is returned. +

    SET

    SET command gains more features on newer versions of Redis. We're going to implement one that has all features + that were realeased up until version 6.0.0. +

    SET key value [NX | XX] [EX seconds | PX milliseconds]
    +

    Stores value as a string that is referenced by key. + Overwrites any data that was previously referenced by the key. +

    Options

    • EX seconds -- Set the specified expire time, in seconds.
    • PX milliseconds -- Set the specified expire time, in seconds.
    • NX -- Only set the key if it does not already exist.
    • XX -- Only set the key if it already exist.

    DEL

    DEL key [key ...]
    +

    Takes 'any' amount of keys as input and removes all of them from storage. If a key doesn't exist it is ignored. + Returns the amount of keys that were deleted. +

    QUIT

    QUIT
    +

    When receiving this command the server closes the connection. It's useful for interactive sessions. + For production environments the client should close the connection without sending any commands. +

    Examples

    Let's start an interactive session of redis to test some commands. + We can install redis-server with docker and run it locally. + Then we can use telnet to connect directly via TCP. + Open a terminal and execute the following instructions: +

    $ docker run -d --name redis-server -p 6379:6379 redis:alpine
    +
    +$ telnet 127.0.0.1 6379
    +Trying 127.0.0.1...
    +Connected to localhost.
    +Escape character is '^]'.
    +

    At this point the prompt should be waiting for you to write something. We're gonna test a couple of commands. + In the code boxes below the first line is the command, following lines are the response. +

    GET a
    +$-1
    +
    ^ That weird $-1 is the special nil value. Which means there's nothing stored here. +

    set a 1
    ++OK
    +
    ^ First thing to notice here is that we can use lowercase version of SET. + Also, when the command is successful returns +OK. +

    set b 2
    ++OK
    +

    SET c 3
    ++OK
    +
    ^ Just storing a couple more values. +

    GET a
    +$1
    +1
    +
    ^ Here the response is returned in two lines. First line is the length of the string. Second line + is the actual string. +

    get b
    +$1
    +2
    +
    ^ We can also use lowercase version of GET, I bet commands are case-insensitive. +

    get C
    +$-1
    +
    ^ Testing with uppercase C gives a nil. Keys seem to be case-sensitive, probably values too. + That makes sense. +

    del a b c
    +:3
    +
    ^ Deleting everything returns the amount of keys deleted. Integers are indicated by ':'. +

    quit
    ++OK
    +Connection closed by foreign host.
    +
    ^ When we send QUIT, the server closes the connection and we're back to our terminal session. +

    With those tests we have enough information to start building. We learned a little bit about the + redis protocol and what the responses look like. +

    Sending commands

    Until now we've been using the inline version of redis command. + There's another kind that follows the +RESP (Redis serialization protocol).

    The RESP protocol is quite similar to what we've seen in the examples above. +The most important addition is arrays. Let's see a Client<>Server interaction + using arrays. +

    Client

    *2
    +$3
    +GET
    +$1
    +a
    +
    Server
    $-1
    +
    The server response looks the same as in the inline version. + But what the client sends looks very different: +

    • In this case, the first thing the client sends is '*' followed by the number of elements in the array, + so '*2' indicates that there are 2 elements in the array and they would be found in the following lines. +
    • After that we have '$3' which means we're expecting the first element to be a string of length 3. + Next line is the actual string, in our case is the command 'GET'. +
    • The next value is also a string and is the key passed to the command. +

    That's almost everything we need to start building a client. There's one last thing: error responses. +

    -Example error message
    +-ERR unknown command 'foo'
    +-WRONGTYPE Operation against a key holding the wrong kind of value
    +
    A response that starts with a '-' is considered an error. The first word is the error type. + We'll only gonna be using 'ERR' as a generic response. +

    RESP protocol is what client libraries use to communicate with Redis. + With all that in our toolbox we're ready to start building. +

    Receiving connections

    A crucial part of our serve is the ability to receive client's information. + The way that this is done is that the server listens on a TCP port and waits + for client connections. Let's start building the basic structure. +

    Create a new go module, open main.go and create a main function as follows. +

    package main
    +
    +import (
    +	"bufio"
    +	"fmt"
    +	"log"
    +	"net"
    +	"strconv"
    +	"strings"
    +	"sync"
    +	"time"
    +)
    +
    +var cache sync.Map
    +
    +func main() {
    +	listener, err := net.Listen("tcp", ":6380")
    +	if err != nil {
    +		log.Fatal(err)
    +	}
    +	log.Println("Listening on tcp://0.0.0.0:6380")
    +
    +	for {
    +		conn, err := listener.Accept()
    +		log.Println("New connection", conn)
    +		if err != nil {
    +			log.Fatal(err)
    +		}
    +
    +		go startSession(conn)
    +	}
    +}
    +

    After declaring the package and imports, we create a global sync.Map that would be our cache. + That's where keys are gonna be stored and retrieved. +

    On the main function we start listening on port 6380. After that we have an infinite loop that accepts + new connections and spawns a goroutine to handle the session. +

    Session handling

    // startSession handles the client's session. Parses and executes commands and writes
    +// responses back to the client.
    +func startSession(conn net.Conn) {
    +	defer func() {
    +		log.Println("Closing connection", conn)
    +		conn.Close()
    +	}()
    +	defer func() {
    +		if err := recover(); err != nil {
    +			log.Println("Recovering from error", err)
    +		}
    +	}()
    +	p := NewParser(conn)
    +	for {
    +		cmd, err := p.command()
    +		if err != nil {
    +			log.Println("Error", err)
    +			conn.Write([]uint8("-ERR " + err.Error() + "\r\n"))
    +			break
    +		}
    +		if !cmd.handle() {
    +			break
    +		}
    +	}
    +}
    +

    It's super important that we close the connection when things are done. That's why we set a deferred function, + to close the connection when the session finishes. +

    After that we handle any panics using recover. We do this mainly because at some point we might be reading from + a connection that was closed by the client. And we don't want the entire server to die in case of an error. +

    Then we create a new parser and start trying to parse commands from the live connection. If we encounter an error + we write the error message back to the client and we finish the session. +

    When cmd.handle() returns false (signaling end of session) we break the loop and the session finishes. +

    Parsing commands

    Basic parser structure: +

    // Parser contains the logic to read from a raw tcp connection and parse commands.
    +type Parser struct {
    +	conn net.Conn
    +	r    *bufio.Reader
    +	// Used for inline parsing
    +	line []byte
    +	pos  int
    +}
    +
    +// NewParser returns a new Parser that reads from the given connection.
    +func NewParser(conn net.Conn) *Parser {
    +	return &Parser{
    +		conn: conn,
    +		r:    bufio.NewReader(conn),
    +		line: make([]byte, 0),
    +		pos:  0,
    +	}
    +}
    +

    This is pretty straight-forward. We store a reference to the connection, a reader and then some + attributes that will help us with parsing. +

    The NewParser() function should be used as a contructor for Parser objects. +

    We need some helper functions that will make parsing easier: +

    func (p *Parser) current() byte {
    +	if p.atEnd() {
    +		return '\r'
    +	}
    +	return p.line[p.pos]
    +}
    +
    +func (p *Parser) advance() {
    +	p.pos++
    +}
    +
    +func (p *Parser) atEnd() bool {
    +	return p.pos >= len(p.line)
    +}
    +
    +func (p *Parser) readLine() ([]byte, error) {
    +	line, err := p.r.ReadBytes('\r')
    +	if err != nil {
    +		return nil, err
    +	}
    +	if _, err := p.r.ReadByte(); err != nil {
    +		return nil, err
    +	}
    +	return line[:len(line)-1], nil
    +}
    +

    Also quite simple. +

    • current(): Returns the character being pointed at by pos inside the line.
    • advance(): Point to the next character in the line.
    • atEnd(): Indicates if we're at the end of the line.
    • readLine(): Reads the input from the connection up to the carriage return char. Skips the '\n' char.

    Parsing strings

    In Redis we can send commands like so: +

    SET text "quoted \"text\" here"
    +

    This means we need a way to handle \, " chars inside a string. +

    For that we need a special parsing function that will handle strings: +

    // consumeString reads a string argument from the current line.
    +func (p *Parser) consumeString() (s []byte, err error) {
    +	for p.current() != '"' && !p.atEnd() {
    +		cur := p.current()
    +		p.advance()
    +		next := p.current()
    +		if cur == '\\' && next == '"' {
    +			s = append(s, '"')
    +			p.advance()
    +		} else {
    +			s = append(s, cur)
    +		}
    +	}
    +	if p.current() != '"' {
    +		return nil, errors.New("unbalanced quotes in request")
    +	}
    +	p.advance()
    +	return
    +}
    +

    From the functions that we've declared up to this point it's pretty clear that our parser + will be reading the input line by line. And the consuming the line one char at a time. +

    The way consumeString() works is quite tricky. + It assumes that the initial " has been consumed before entering the function. + And it consumes all characters in the current line up until it reaches the closing quotes character + or the end of the line. +

    Inside the loop we can see that we're reading the current character and advancing the pointer, then + the next character. + When the user is sending an escaped quote inside the string we detect that by checking the current + and the next characters. + In this special case we end up advancing the pointer twice. Because we consumed two: chars the backslash + and the quote. But we added only one char to the output: ". +

    We append all other characters to the output buffer. +

    When the loop finishes, if we're not pointing to the end quote char, that means that the user + sent an invalid command and we return an error. +

    Otherwise we advance the pointer and return normally. +

    Parsing commands

    // command parses and returns a Command.
    +func (p *Parser) command() (Command, error) {
    +	b, err := p.r.ReadByte()
    +	if err != nil {
    +		return Command{}, err
    +	}
    +	if b == '*' {
    +		log.Println("resp array")
    +		return p.respArray()
    +	} else {
    +		line, err := p.readLine()
    +		if err != nil {
    +			return Command{}, err
    +		}
    +		p.pos = 0
    +		p.line = append([]byte{}, b)
    +		p.line = append(p.line, line...)
    +		return p.inline()
    +	}
    +}
    +

    We read the first character sent by the client. If it's an asterisk we handle it + using the RESP protocol. Otherwise we assume that it's an inline command. +

    Let's start by parsing the inline commands first. +

    // Command implements the behavior of the commands.
    +type Command struct {
    +	args []string
    +	conn net.Conn
    +}
    +
    +// inline parses an inline message and returns a Command. Returns an error when there's
    +// a problem reading from the connection or parsing the command.
    +func (p *Parser) inline() (Command, error) {
    +	// skip initial whitespace if any
    +	for p.current() == ' ' {
    +		p.advance()
    +	}
    +	cmd := Command{conn: p.conn}
    +	for !p.atEnd() {
    +		arg, err := p.consumeArg()
    +		if err != nil {
    +			return cmd, err
    +		}
    +		if arg != "" {
    +			cmd.args = append(cmd.args, arg)
    +		}
    +	}
    +	return cmd, nil
    +}
    +

    This is also quite easy to skim through. We skip any leading whitespace + in case the user sent something like ' GET a'. +

    We create a new Command object with a reference to the session connection. +

    While we're not at the end of the line we consume args and append them to the + arg list of the command object if they are not empty. +

    Consuming arguments

    // consumeArg reads an argument from the current line.
    +func (p *Parser) consumeArg() (s string, err error) {
    +	for p.current() == ' ' {
    +		p.advance()
    +	}
    +	if p.current() == '"' {
    +		p.advance()
    +		buf, err := p.consumeString()
    +		return string(buf), err
    +	}
    +	for !p.atEnd() && p.current() != ' ' && p.current() != '\r' {
    +		s += string(p.current())
    +		p.advance()
    +	}
    +	return
    +}
    +

    Same as before we consume any leading whitespace. +

    If we find a quoted string we call our function from before: consumeString(). +

    We append all characters to the output until we reach a carriage return \r, a whitespace + or the end of the line. +

    Parsing RESP protocol

    // respArray parses a RESP array and returns a Command. Returns an error when there's
    +// a problem reading from the connection.
    +func (p *Parser) respArray() (Command, error) {
    +	cmd := Command{}
    +	elementsStr, err := p.readLine()
    +	if err != nil {
    +		return cmd, err
    +	}
    +	elements, _ := strconv.Atoi(string(elementsStr))
    +	log.Println("Elements", elements)
    +	for i := 0; i < elements; i++ {
    +		tp, err := p.r.ReadByte()
    +		if err != nil {
    +			return cmd, err
    +		}
    +		switch tp {
    +		case ':':
    +			arg, err := p.readLine()
    +			if err != nil {
    +				return cmd, err
    +			}
    +			cmd.args = append(cmd.args, string(arg))
    +		case '$':
    +			arg, err := p.readLine()
    +			if err != nil {
    +				return cmd, err
    +			}
    +			length, _ := strconv.Atoi(string(arg))
    +			text := make([]byte, 0)
    +			for i := 0; len(text) <= length; i++ {
    +				line, err := p.readLine()
    +				if err != nil {
    +					return cmd, err
    +				}
    +				text = append(text, line...)
    +			}
    +			cmd.args = append(cmd.args, string(text[:length]))
    +		case '*':
    +			next, err := p.respArray()
    +			if err != nil {
    +				return cmd, err
    +			}
    +			cmd.args = append(cmd.args, next.args...)
    +		}
    +	}
    +	return cmd, nil
    +}
    +

    As we know, the leading asterisk has already been consumed from the connection input. + So, at this point, the first line contains the number of elements to be consumed. + We read that into an integer. +

    We create a for loop with that will parse all the elements in the array. + We consume the first character to detect which kind of element we need to consume: int, string or array. +

    The int case is quite simple, we just read until the rest of the line. +

    The array case is also quite simple, we call respArray() and append the args of the result, + to the current command object. +

    For strings we read the first line and get the size of the string. + We keep reading lines until we have read the indicated amount of characters. +

    Handling commands

    This is the 'fun' part of the implementation. Were our server becomes alive. + In this section we'll implement the actual functionality of the commands. +

    Let's start with the cmd.handle() function that we saw in handleSession(). +

    // handle Executes the command and writes the response. Returns false when the connection should be closed.
    +func (cmd Command) handle() bool {
    +	switch strings.ToUpper(cmd.args[0]) {
    +	case "GET":
    +		return cmd.get()
    +	case "SET":
    +		return cmd.set()
    +	case "DEL":
    +		return cmd.del()
    +	case "QUIT":
    +		return cmd.quit()
    +	default:
    +		log.Println("Command not supported", cmd.args[0])
    +		cmd.conn.Write([]uint8("-ERR unknown command '" + cmd.args[0] + "'\r\n"))
    +	}
    +	return true
    +}
    +

    Needs no further explanation. Let's implement the easiest command: QUIT. +

    // quit Used in interactive/inline mode, instructs the server to terminate the connection.
    +func (cmd *Command) quit() bool {
    +	if len(cmd.args) != 1 {
    +		cmd.conn.Write([]uint8("-ERR wrong number of arguments for '" + cmd.args[0] + "' command\r\n"))
    +		return true
    +	}
    +	log.Println("Handle QUIT")
    +	cmd.conn.Write([]uint8("+OK\r\n"))
    +	return false
    +}
    +

    If any extra arguments were passed to QUIT, it returns an error. +

    Otherwise write +OK to the client and return false. + Which if you remember handleSession() is the value to indicate that the session has finished. + After that the connection will be automatically closed. +

    The next easieast command is DEL +

    // del Deletes a key from the cache.
    +func (cmd *Command) del() bool {
    +	count := 0
    +	for _, k := range cmd.args[1:] {
    +		if _, ok := cache.LoadAndDelete(k); ok {
    +			count++
    +		}
    +	}
    +	cmd.conn.Write([]uint8(fmt.Sprintf(":%d\r\n", count)))
    +	return true
    +}
    +

    Iterates through all the keys passed, deletes the ones that exists and + writes back to the client the amount of keys deleted. +

    Returns true, which means the connection is kept alive. +

    Handling GET

    // get Fetches a key from the cache if exists.
    +func (cmd Command) get() bool {
    +	if len(cmd.args) != 2 {
    +		cmd.conn.Write([]uint8("-ERR wrong number of arguments for '" + cmd.args[0] + "' command\r\n"))
    +		return true
    +	}
    +	log.Println("Handle GET")
    +	val, _ := cache.Load(cmd.args[1])
    +	if val != nil {
    +		res, _ := val.(string)
    +		if strings.HasPrefix(res, "\"") {
    +			res, _ = strconv.Unquote(res)
    +		}
    +		log.Println("Response length", len(res))
    +		cmd.conn.Write([]uint8(fmt.Sprintf("$%d\r\n", len(res))))
    +		cmd.conn.Write(append([]uint8(res), []uint8("\r\n")...))
    +	} else {
    +		cmd.conn.Write([]uint8("$-1\r\n"))
    +	}
    +	return true
    +}
    +

    As before, we validate that the correct number of arguments were passed to the command. +

    We load the value from the global variable cache. +

    If the value is nil we write back to the client the special $-1. +

    When we have a value we cast it as string and unquote it in case it's quoted. + Then we write the length as the first line of the response and the string as the + second line of the response. +

    Handling SET

    This is the most complicated command that we'll implement. +

    // set Stores a key and value on the cache. Optionally sets expiration on the key.
    +func (cmd Command) set() bool {
    +	if len(cmd.args) < 3 || len(cmd.args) > 6 {
    +		cmd.conn.Write([]uint8("-ERR wrong number of arguments for '" + cmd.args[0] + "' command\r\n"))
    +		return true
    +	}
    +	log.Println("Handle SET")
    +	log.Println("Value length", len(cmd.args[2]))
    +	if len(cmd.args) > 3 {
    +		pos := 3
    +		option := strings.ToUpper(cmd.args[pos])
    +		switch option {
    +		case "NX":
    +			log.Println("Handle NX")
    +			if _, ok := cache.Load(cmd.args[1]); ok {
    +				cmd.conn.Write([]uint8("$-1\r\n"))
    +				return true
    +			}
    +			pos++
    +		case "XX":
    +			log.Println("Handle XX")
    +			if _, ok := cache.Load(cmd.args[1]); !ok {
    +				cmd.conn.Write([]uint8("$-1\r\n"))
    +				return true
    +			}
    +			pos++
    +		}
    +		if len(cmd.args) > pos {
    +			if err := cmd.setExpiration(pos); err != nil {
    +				cmd.conn.Write([]uint8("-ERR " + err.Error() + "\r\n"))
    +				return true
    +			}
    +		}
    +	}
    +	cache.Store(cmd.args[1], cmd.args[2])
    +	cmd.conn.Write([]uint8("+OK\r\n"))
    +	return true
    +}
    +

    As always, first thing we do is validate the number of arguments. + But in this case, SET is more tricky than the others. +

    When more than 3 arguments are passed we check for the NX or XX flags and handle them accordingly. +

    • NX -- Only set the key if it does not already exist.
    • XX -- Only set the key if it already exist.

    Then we parse the expiration flags if any. We'll see how that's done in a second. +

    After handling all those special cases we finally store the key and value in the cache, + write the +OK response and return true to keep the connection alive. +

    Expiration

    // setExpiration Handles expiration when passed as part of the 'set' command.
    +func (cmd Command) setExpiration(pos int) error {
    +	option := strings.ToUpper(cmd.args[pos])
    +	value, _ := strconv.Atoi(cmd.args[pos+1])
    +	var duration time.Duration
    +	switch option {
    +	case "EX":
    +		duration = time.Second * time.Duration(value)
    +	case "PX":
    +		duration = time.Millisecond * time.Duration(value)
    +	default:
    +		return fmt.Errorf("expiration option is not valid")
    +	}
    +	go func() {
    +		log.Printf("Handling '%s', sleeping for %v\n", option, duration)
    +		time.Sleep(duration)
    +		cache.Delete(cmd.args[1])
    +	}()
    +	return nil
    +}
    +

    We read the option and the expiration value, then we compute the duration + for each case and we spawn a new goroutine that sleeps for that amount of + time and the deletes the key from the cache. +

    This is not the most efficient way to do it, but it's simple and it works for us. +

    Working server

    At this point we have an usable implementation of Redis. +

    Let's start the server the server and test it. +

    $ go run main.go
    +2023/04/08 21:09:40 Listening on tcp://0.0.0.0:6380
    +

    On a different terminal connect to the server. +

    $ telnet 127.0.0.1 6380
    +GET a
    +$-1
    +set a "test \"quotes\" are working"
    ++OK
    +get a
    +$25
    +test "quotes" are working
    +

    It's alive!! Go have fun. +

    If you'd like to access the source code of this project there's a public gist + containing all of the code displayed here. +

    Link to source code

    Miguel LiezunIn this post we're going to write a basic Redis clone in Go that implements the most simple commands: GET, +SET, DEL and QUIT. At the end you'll know how to parse a byte stream from a live TCP connection, and hopefully have a working +implementation of Redis. +
    How to write a program that can replicate itself +https://mliezun.github.io/2022/11/26/grotsky-quine.html +2022-11-26 +

    Reading time: +

    How to write a program that can replicate itself

    Grotsky is a toy programming language that I made for fun. Today we're visinting the concept of Quines, +a.k.a. self replicating programs. It's said that any turing-complete language should be able to write a program that replicates + itself. And grotsky is no exception.

    Read more about grotsky in previous blogposts: +

    Quines are very easy to write. The language that you're using needs to be able to do a couple things: +

    • Write to a file or stdout (print)
    • Support for string arrays
    • Translate numbers/integers to character ascii representation
    • Concatenate strings
    • Loop through arrays from arbitrary indexes

    Super simple quine: less than 30 lines of code

    let tabChar = 9
    +let quoteChar = 34
    +let commaChar = 44
    +let code = [
    +	"let tabChar = 9",
    +	"let quoteChar = 34",
    +	"let commaChar = 44",
    +	"let code = [",
    +	"]",
    +	"for let i = 0; i < 4; i = i+1 {",
    +	"	io.println(code[i])",
    +	"}",
    +	"for let i = 0; i < code.length; i = i+1 {",
    +	"	io.println(strings.chr(tabChar) + strings.chr(quoteChar) + code[i] + strings.chr(quoteChar) + strings.chr(commaChar))",
    +	"}",
    +	"for let i = 4; i < code.length; i = i+1 {",
    +	"	io.println(code[i])",
    +	"}",
    +]
    +for let i = 0; i < 4; i = i+1 {
    +	io.println(code[i])
    +}
    +for let i = 0; i < code.length; i = i+1 {
    +	io.println(strings.chr(tabChar) + strings.chr(quoteChar) + code[i] + strings.chr(quoteChar) + strings.chr(commaChar))
    +}
    +for let i = 4; i < code.length; i = i+1 {
    +	io.println(code[i])
    +}
    +
    +

    Now we can use grotksy cli to run the program and compare the output to the original source. +

    Save the original source to a file called +quine.gr then run the following commands: +

    +$ grotsky quine.gr > quine_copy.gr
    +$ cmp quine.gr quine_copy.gr
    +$ echo $?
    +0
    +
    +

    If you see a 0 as the final output that means the files are the same. + Otherwise if you saw an error message or a different output, that means something has gone wrong. +

    How exciting is this?!! +We've just written a program that gives itself as an output. +That sounds impossible when you hear it for the first time. But it was actually pretty easy! +

    Source code available here: +https://gist.github.com/mliezun/c750ba701608850bd86d646a3ebf700f. +

    Grotsky cli binary available here: +https://github.com/mliezun/grotsky/releases/tag/v0.0.6

    Miguel LiezunGrotsky is a toy programming language that I made for fun. Today we're visinting the concept of Quines, +a.k.a. self replicating programs. It's said that any turing-complete language should be able to write a program that replicates + itself. And grotsky is no exception. +
    Migrate from Heroku to Fly.io +https://mliezun.github.io/2022/09/22/heroku-to-fly.html +2022-09-22 +

    Reading time: +

    How to migrate from Heroku to Fly.io

    A couple weeks ago Heroku announced the removal. I have plenty of projects running on free dynos. + I have taken some time to move my code to Fly.io. And also I've written a little tutorial of how to perform the migration.

    I'll use one of my public repos as an example +https://github.com/mliezun/getmyip. It's a simple service that returns the IP from which you're making the request. It's useful when you want to know + your public IP. +

    That project ^ was covered in a previous +blogpost. +

    Migration instructions

    The first thing we need to do is to remove heroku from the remotes. +Inside your project run: +

    git remote remove heroku

    If you have a heroku.yml file, delete it. +

    rm -rf heroku.yml

    Then, we're ready to start using fly. +There are tutorials on the +official fly.io docs for many frameworks and languages. We're going to be following the one for a Docker app, + since it's the most general case. +

    First thing you need to do is create an account in +Fly.io if you don't have one yet. +

    Once you created your account, install the flyctl command line tool. + After that, login by running the following command: +

    flyctl auth login

    After you've logged in to your account, you're ready to launch your application. + Execute the next command and follow the interactive setup. +

    $ flyctl launch
    +Scanning source code
    +Detected a Dockerfile app
    +? App Name (leave blank to use an auto-generated name): 
    +Automatically selected personal organization: Miguel Liezun
    +? Select region: mia (Miami, Florida (US))
    +Created app morning-breeze-4255 in organization personal
    +Wrote config file fly.toml
    +? Would you like to set up a Postgresql database now? No
    +? Would you like to deploy now? Yes
    +Deploying morning-breeze-4255
    +==> Validating app configuration
    +--> Validating app configuration done
    +Services
    +TCP 80/443 -> 8080
    +Remote builder fly-builder-green-pond-8004 ready
    +==> Creating build context
    +--> Creating build context done
    +==> Building image with Docker
    +--> docker host: 20.10.12 linux x86_64
    +...
    +

    Make sure your app listens to port 8080, that's the default for fly apps. + You can change the port inside the file fly.toml if you want +, just search for the internal port and change it. Remember to run launch again if you change the port. +

    # fly.toml file generated for morning-breeze-4255 on 2022-09-21T21:50:20-03:00
    +
    +app = "morning-breeze-4255"
    +kill_signal = "SIGINT"
    +kill_timeout = 5
    +processes = []
    +
    +[env]
    +
    +[experimental]
    +  allowed_public_ports = []
    +  auto_rollback = true
    +
    +[[services]]
    +  http_checks = []
    +  internal_port = 8080 # -< Put your desired port here
    +# ...
    +

    Finally, you only need to open the app and enjoy! +You migrated your first app from heroku to fly :-) +

    $ flyctl open
    +opening http://morning-breeze-4255.fly.dev ...
    +

    Access the newly deployed 'getmyip' service using the link +http://morning-breeze-4255.fly.dev. +

    Miguel LiezunA couple weeks ago Heroku announced the removal. I have plenty of projects running on free dynos. + I have taken some time to move my code to Fly.io. And also I've written a little tutorial of how to perform the migration. +
    Branchable MySQL: Managing multiple dev environments +https://mliezun.github.io/2022/09/20/branchable-mysql.html +2022-09-20 +

    Reading time: +

    Branchable MySQL: Managing multiple dev environments

    When teams start to grow, having a single dev environment becomes an issue. People start stepping on each others toes. +A common problem is that two people want to apply incompatible migrations on the database. That problem is impossible +to fix if folks are working on parallel branches. +If we can have a database for each branch of a project, that will remove much of the pain of having multiple devs applying +changes to the db.

    There are already projects that solve this problem: +PlanetScale and +Neon. +

    A common case where this problem arises is when two devs want to add a column to the same table. +

    Two devs applying changes to the same table in the database.

    We have a +people table in the database. One of the devs wants to add the +last_name column and the other one wants to add the +address. +

    Dev1's code thinks the table will have 3 columns after he applies his operation: +id, name, last_name. +

    Dev2's code also thinks the table will have 3 columns: +id, name, address. +

    In reality the table will have 4 columns. +So neither of them will be able to run their code unless they talk to each other and figure out how to make this work. +

    This is far from ideal. +

    What we want instead, is that each one of them can develop their features independently. +

    Two devs applying changes to the same table in different database branches.

    They both apply to the same table, but each table lives on an instance that was 'replicated' from the original. +

    How can we implement the ideal case?

    MySQL writes data (by default) to the directory +/var/lib/mysql/data. +

    We can use an +Union filesystem. And configure MySQL to use a different directory to read and write data. +

    That way we can have a feature/user-last-name 'branch' read and write data from a directory like +/app/user-last-name/mysql/data. +

    And a feature/user-address 'branch' read and write data from a directory like +/app/user-address/mysql/data. +

    Those branches can be mounted using fuse-overlayfs by executing the following commands: +

    +# Directory /app/base contains data from the original branch
    +
    +fuse-overlayfs -o lowerdir=/app/base,upperdir=/app/user-last-name,workdir=/tmp/user-last-name overlayfs /app/user-last-name
    +
    +fuse-overlayfs -o lowerdir=/app/base,upperdir=/app/user-address,workdir=/tmp/user-address overlayfs /app/user-address
    +
    +

    This means both 'branches' of the database are able to coexist and have different schemas during their lifetime. +

    Experimenting with a use case

    I had this idea in my head for months. I finally convinced myself that it was worth a shot. +

    I decided to do a little implementation using Docker and python FastAPI. +Exposing a simple interface so that it's easy to create and delete branches. +

    The project is live on github +branchable-mysql. +

    The container image is published on Docker Hub +branchable-mysql. +

    To start using the image let's create a docker-compose.yml file. +

    version: "3"
    +
    +services:
    +  mysql:
    +    image: mliezun/branchable-mysql
    +    platform: linux/amd64
    +    privileged: true
    +    restart: always
    +    volumes:
    +      - appdata:/app/
    +
    +volumes:
    +  appdata:
    +
    +

    Then you can execute +docker-compose up and the MySQL server should start running. +

    After that, connect easily to the db +docker compose exec mysql mysql -uroot -h127.0.0.1 --skip-password -P33061. You should enter to an interactive mysql console. +

    Let's create an initial schema, a table and insert some data so that we can see how branching works. +On the console that we just opened execute: +

    +mysql> create schema s1;
    +Query OK, 1 row affected (0.01 sec)
    +
    +mysql> use s1;
    +Database changed
    +mysql> create table people (id int primary key auto_increment, name varchar(255) not null);
    +Query OK, 0 rows affected (0.07 sec)
    +
    +mysql> insert into people select 0, 'Miguel';
    +Query OK, 1 row affected (0.02 sec)
    +Records: 1  Duplicates: 0  Warnings: 0
    +
    +mysql> select * from people;
    ++----+--------+
    +| id | name   |
    ++----+--------+
    +|  1 | Miguel |
    ++----+--------+
    +1 row in set (0.00 sec)
    +
    +

    That's enough for now, we're ready to start creating branches. +

    On a separate terminal, without closing the previous mysql interactive console, execute: +

    +docker compose exec mysql /app/scripts/create_branch.sh base feature/user-last-name
    +
    +{"branch_name":"feature/user-last-name","base_branch":"base","port":33062}
    +
    +

    Now you can login to the new database branch using port 33062 +docker compose exec mysql mysql -uroot -h127.0.0.1 --skip-password -P33062

    +mysql> use s1;
    +Reading table information for completion of table and column names
    +You can turn off this feature to get a quicker startup with -A
    +
    +Database changed
    +mysql> alter table people add column last_name varchar(255) not null;
    +Query OK, 0 rows affected (0.03 sec)
    +Records: 0  Duplicates: 0  Warnings: 0
    +
    +mysql> select * from people;
    ++----+--------+-----------+
    +| id | name   | last_name |
    ++----+--------+-----------+
    +|  1 | Miguel |           |
    ++----+--------+-----------+
    +1 row in set (0.00 sec)
    +
    +

    In a new terminal we can create a another branch: +

    +docker compose exec mysql /app/scripts/create_branch.sh base feature/user-address
    +
    +{"branch_name":"feature/user-address","base_branch":"base","port":33063}
    +
    +

    Then connect using port 33063 +docker compose exec mysql mysql -uroot -h127.0.0.1 --skip-password -P33063

    +mysql> use s1;
    +Reading table information for completion of table and column names
    +You can turn off this feature to get a quicker startup with -A
    +
    +Database changed
    +mysql> alter table people add column last_name varchar(255) not null;
    +Query OK, 0 rows affected (0.03 sec)
    +Records: 0  Duplicates: 0  Warnings: 0
    +
    +mysql> select * from people;
    ++----+--------+
    +| id | name   |
    ++----+--------+
    +|  1 | Miguel |
    ++----+--------+
    +1 row in set (0.00 sec)
    +
    +mysql> alter table people add column address varchar(255) not null;
    +Query OK, 0 rows affected (0.02 sec)
    +Records: 0  Duplicates: 0  Warnings: 0
    +
    +mysql> select * from people;
    ++----+--------+---------+
    +| id | name   | address |
    ++----+--------+---------+
    +|  1 | Miguel |         |
    ++----+--------+---------+
    +1 row in set (0.00 sec)
    +
    +

    As you can see, we have 3 servers running at the same time, each one with different schemas. +

    This is great for local development and for having branch-aware dev environments. +

    Final thoughts

    I hope you find this blogpost useful. +If you want to start using branchable-mysql go ahead. +If you encounter any issues please report them in the github repo or create a pull request. +

    Miguel LiezunWhen teams start to grow, having a single dev environment becomes an issue. People start stepping on each others toes. +A common problem is that two people want to apply incompatible migrations on the database. That problem is impossible +to fix if folks are working on parallel branches. +If we can have a database for each branch of a project, that will remove much of the pain of having multiple devs applying +changes to the db. +
    Webscraping as a side project +https://mliezun.github.io/2022/07/20/cloud-outdated-release.html +2022-07-20 +

    Reading time: +

    Webscraping as a side project

    A friend and I were looking for a side project to work together. We realized we both faced a similar problem. +

    Let's use AWS Lambda Python runtime as an example. +AWS will send out emails when a version is at the end of life making it difficult to stay +on the latest if desired. +Plus, reacting to them usually means you are many many versions behind already. +

    Our journey started. +We made a list of providers for the MVP: AWS, GCP and Azure. +Then a list of the services that have versions (for example S3 doesn't have versions). +After that we realized that we could get some versions using APIs. +Other services exclusively require webscraping. +

    We support 46 services and counting. +Take a look at +Cloud Outdated and subscribe to get notified. +If you are looking for a service that's not there +contact us. +

    Picking a language, framework and platform

    We're both Python programmers. The choice was obvious. +"Let's use Python and Django framework for the job" we said. +We didn't want to spend our innovation tokens on new language/framework. +So we chose Boring Technology. +

    For the db we spent our first innovation token. +We decided to go with the flashy new serverless postgres-compatible +CockroachDB. +

    On the hosting side we're using AWS Lambda. Taking advantage of the free compute time. +Helps mantaining the costs down. +

    Make webscraping reliable

    A webpage that's being scraped can change at any time. First thing we did was account for those edge cases. +We created a custom exception that is triggered when something changed. So that we can react to that downstream. +

    +class ScrapingError(Exception):
    +    pass
    +
    +
    We wanted to keep the implementation simple. Each service is scraped by a single function. +The signature of the function is something like +aws_lambda_python() -> List[Version]. +All the implementations follow a similar pattern: +
    +def aws_lambda_python():
    +    # Read versions from aws docs website:
    +    # https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html
    +
    +    if not found_versions:
    +        raise ScrappingError
    +
    +    # Process and return versions
    +
    +
    That's ^ what we call a poll function. +

    We pass poll functions through a polling class that handles all the errors and results. +When we detect an scraping error we have a special case. We send an email with the details +of what happened. Because the problem is something that requires manual action. We receive +that email in our personal inboxes and fix the problem ASAP. +

    The poll class that handles all the magic behind cloud outdated is actually very simple: +

    +class PollService:
    +    def __init__(self, service: Service, poll_fn: Callable):
    +        self.poll_fn = poll_fn
    +        # Some other attributes...
    +
    +    def poll(self):
    +        try:
    +            results = self.poll_fn()
    +            self.process_results(results)
    +        except ScrapingError as e:
    +            notify_operator(
    +                f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e.__str__()}"
    +            )
    +
    +    def process_results(self, results):
    +        # if results contains new versions:
    +        #     save new versions to db
    +        # if results contains deprecated versions:
    +        #     set versions in db as depreacted
    +
    +

    That's the hearth of Cloud Outdated. +After that we have to send notifications to subscribed users. +That part is trivial. +We send an email that contains the difference between what was last sent to a user and what we +have stored in the db at the moment. +

    Last toughts

    Having a side project is usually a good idea. +For us has been a journey were we got to know some new stuff (CockroachDB). +We also learned about how to build a product and keep a MVP mentality. +The most difficult challenge we face is to bring more users to the platform. +

    We'd love to see more people subscribed. +If this blogpost sparked your interest go to +Cloud Outdated and subscribe to start getting emails. +

    See you next time! +

    Miguel LiezunCloud Outdated is a personalized digest of updates for cloud services. Works like a newsletter where you can choose + which services you want to get notified about. For example: Subscribe to AWS Lambda with Python runtime, and you'll get an email + when 3.10 is supported. +
    Blake3 hash plugin for MySQL written in Rust +https://mliezun.github.io/2022/05/05/rust-blake-mysql.html +2022-05-05 +

    Reading time: +

    Implementing a blake3 hash plugin for MySQL in Rust

    It's been long since I've written something, I wanted to bring you some new stuff, so here you have a short blog post. +I encourage you to try this new plugin that uses this +blake3 hash implementation. +Blake3 is secure, unlike MD5 and SHA-1. And secure against length extension, unlike SHA-2. +Start using it and create an issue in the github repo if you would like a feature implemented! +

    How to use

    Download and install MySQL plugin

    $ wget 'https://github.com/mliezun/blake-udf/releases/download/v0.1.0/libblake_udf.so'
    +$ mv libblake_udf.so /usr/lib/mysql/plugin/
    +

    Load UDF in MySQL

    $ mysql -uroot -p -e 'create function blake3_hash returns string soname "libblake_udf.so";'
    +

    Execute function

    $ mysql --binary-as-hex=0 -uroot -p -e 'select blake3_hash("a");'
    +
    Output: 17762fddd969a453925d65717ac3eea21320b66b54342fde15128d6caf21215f +
    Miguel LiezunUsing Rust to create a MySQL plugin that implements blake3 hash. +
    Playing with Javascript Proxies (getters/setters) +https://mliezun.github.io/2021/12/31/playing-with-js.html +2021-12-31 +

    Reading time: +

    Playing with Javascript Proxies (getters/setters)

    Overview

    Happy New Year!

    This is my final post for the 2021. This year I didn't post that much, but a lot of work was put into + the blog to rewrite it using +Grotksy. +I hope everyone has a great 2022 and that next year is much better than the last one. +

    The inspiration for this blog post comes from the idea of building a tiny db that feels more natural to Javscript. +All the databases that I've seen make a heavy use of methods like: +db.get(), +db.put(), +db.scan(), +db.query(). + And many others that Im sure you have seen. +I think it would be great to see something like: +

    const db = getDb("...")
    +// Create new user
    +const u = {username: "jdoe", email: "jdoe@example.com", id: 100}
    +// Store new user in the database
    +db.objects.users[u.username] = u
    +// Commit the changes to the database
    +db.actions.save()
    +

    In this blog post we will be building a much simpler version that stores everything in memory. Each change + made to the objects will be stored in a log (called layers) and the final object will be composed of all + the small layers present in the log. +

    Defining a proxy

    We need to implement some generic getters/setters. +

    const objects = new Proxy({}, {
    +    get: function(obj, prop) {
    +        validate(prop, null)
    +        // Implementation
    +    },
    +    set: function(obj, prop, value) {
    +        validate(prop, value)
    +        // Implementation
    +    }
    +})
    +

    Let's define the validation function. In this case we want the objects to be able to be serialized to JSON. +

    +const validate = (prop, value) => {
    +    // Make sure that the property and value are serializable
    +    // JSON.stringify throws an error if not serializable
    +    const l = {}
    +    l[prop] = value
    +    JSON.stringify(l)
    +    return l
    +}
    +
    +

    This empty proxy will validate that the values and prop are serializable and do nothing else. Now we can start +building on top of it. +

    Building a tree to hold everything together

    We need a root object where we will store all the changes that are applied to an object. +We will have a sort of tree structure to hold everything together. +It will look something like this: +

    +              rootObject({})  -> layers([{users: {jdoe: ...}}, {tokens: {tk1: ...}}])
    +                    |
    +        --------------------------
    +        |                        |
    + child(.users{})          child(.tokens{})
    +        |                        |
    +       ...                      ...
    +
    +

    The root object contains the layers with all the changes made from the beginning of the existence of the object. +Each time a property of the root object is accessed a child is returned that internally holds a reference to the root. +This way we can go through the entire chain of access and be able to reach the stored layers. +By chain of access I mean the following: objects.users.jdoe.metadata.login.ip. +As you can see, we need to traverse through many objects to be able to reach the ip field. But the layer that contains + the information is only stored in the root, so each child needs to mantain a reference to the parent to be able to reach + the root node. +

    Let's define a simple function to be able to create a new rootObject. +

    +const wrapObject = (parent, key, current) => {
    +    const rootObj = {
    +        parent: Object.freeze(parent),
    +        layers: [Object.freeze({'value': current, 'previous': null})],
    +        pushLayer (l) {}, // Push new layer
    +        getLayer (ks) {}, // Get layer where information is stored based on given keys
    +        getValue (k) {} // Get value that matches given key
    +    }
    +
    +    const rootProxy = {
    +        get: function(obj, prop) {
    +            validate(prop, null)
    +            const val = rootObj.getValue(prop)
    +            if (typeof val == 'object') {
    +                // If the value is an object we need to have a child instance
    +                // with a reference to the parent
    +                return wrapObject(rootObj, prop, val).objects
    +            }
    +            // If the value is other kind like a number or string we can safely return that
    +            return val
    +        },
    +        set: function(obj, prop, value) {
    +            const l = validate(prop, value)
    +            // Add new layer to the rootObj
    +            rootObj.pushLayer({'value': l})
    +        }
    +    }
    +
    +    return {
    +        actions: {
    +            revert () {
    +                // Deleting the last layer will revert the changes
    +                const pop = rootObj.layers[rootObj.layers.length-1]
    +                rootObj.layers.splice(rootObj.layers.length-1, rootObj.layers.length)
    +                return pop
    +            }
    +        },
    +        objects: new Proxy({}, rootProxy)
    +    }
    +}
    +
    +

    Handling layers

    The layer format: +

    +const layer = {
    +    value: {status: 'active'},
    +    previous: null // Reference to a previous layer that has the key 'status' in it
    +}
    +
    +
    The layers are stored in an array, each layer holds the value and a reference to the previous layer + that set a value for the same key (in this case the key was 'status'). Also the layers form a simple + linked list through the 'previous' reference. That way we have the entire history of a given key. +

    We would need a function to be able to tell if an object has a list of nested keys. Trust me for now, you'll see. +

    +const nestedExists = (obj, ks) => {
    +    for (let j = 0; j < ks.length; j++) {
    +        let k = ks[j];
    +        if (!(k in obj)) {
    +            return false
    +        }
    +        obj = obj[k]
    +    }
    +    return true
    +}
    +
    +
    In this function we receive an object and a list of keys, we start accessing the first internal object with the first key + and we keep doing the same till we make sure that all the keys are present. +

    Now we're almost done. Let's define the functions for handling the store and retrieval of layers. +

    +const rootObj = {
    +    parent: Object.freeze(parent),
    +    layers: [Object.freeze({'value': current, 'previous': null})],
    +    pushLayer (l) {
    +        // If this is a child object we need to build the entire chain of access
    +        // from the bottom up
    +        if (parent) {
    +            const ll = {}
    +            ll[key] = l['value']
    +            // Search for a previous layer modifying the same key
    +            const previous = parent.getLayer([key])
    +            // Tell the parent object to push the new layer
    +            parent.pushLayer(Object.freeze({'value': ll, previous}))
    +        } else {
    +            // We are in the root object, add the layer to the array
    +            this.layers.push(Object.freeze(l))
    +        }
    +    },
    +    getLayer (ks) {
    +        // Search through the entire list of layers to see if one contains all the keys
    +        // that we are looking for. Start from the end of the array (top of the stack)
    +        for (let i = this.layers.length - 1; i >= 0; i--) {
    +            let v = nestedExists(this.layers[i]['value'], ks)
    +            if (v) {
    +                return this.layers[i]
    +            }
    +        }
    +        if (parent) {
    +            // If we are in a child object, look through all the previous layers
    +            // and see if the key we're looking for is contained in one of them.
    +            let ll = parent.getLayer([key].concat(ks))
    +            while (ll) {
    +                let a = nestedExists(ll['value'][key], ks)
    +                if (a) {
    +                    return Object.freeze({'value': ll['value'][key]})
    +                }
    +                ll = ll.previous
    +            }
    +        }
    +    },
    +    getValue (k) {
    +        // Straightforward, get layer and return value
    +        const l = this.getLayer([k])
    +        if (l) {
    +            return Object.freeze(l['value'][k])
    +        }
    +    }
    +}
    +
    +
    That's all we need. We can create a new object and start adding and modifying properties. Each change will be added + to the end of the log and worked out when a property is accessed. +

    Wrapping Up

    Let's try the final result. The source code is loaded in this page, so you can open a dev console in the browser and + try for yourself. +

    +const store = wrapObject(null, null, {})
    +
    +// Create new user
    +const user = {username: 'jdoe', email: 'jdoe@example.com', name: 'John Doe', id: 100}
    +
    +// Add new user
    +store.objects.users = {}
    +store.objects.users[user.username] = user
    +
    +// Print user email
    +console.log(store.objects.users.jdoe.email)
    +
    +// Change user email and print
    +store.objects.users.jdoe.email = 'jdoe2@example.com'
    +console.log(store.objects.users.jdoe.email)
    +
    +// Revert last change and print email again
    +store.actions.revert()
    +console.log(store.objects.users.jdoe.email)
    +
    +

    That's it for now. We defined a Javascript object that contains the entire history of changes that were made to itself. +And at any point we can revert the changes and go back to a previous state. +Everything is stored in an array and is easily serializable. +If we wanted to take this to the next level, each change could be written to a persistence storage (s3, sqlite, mysql, ...) +

    The full source code is available in a +public gist. +

    Miguel LiezunIn this last post of the year I play with proxies in an attempt to create a Javascript object where changes are appended + to a log and can be reverted by deleting the last element of the log using getters and setters. +
    I created a programming language and this blog is powered by it +https://mliezun.github.io/2021/10/04/new-blog-engine.html +2021-10-04 +

    Reading time: +

    I created a programming language and this blog is powered by it

    Why did I do it?

    Mostly for fun.

    If you follow my blog or take a look at some of the posts that I made, you will see that I was +building a programming language called +Grotksy. Just a toy programming language that I made based on the book +Crafting Interpreters, which I totally recommend buying and reading if you haven't yet. +

    I wanted to build something interesting but simple enough that could be made with Grotsky. +I tought that replacing Jekyll with my own engine was a task worth a try. +There is nothing groundbreaking or innovative being made here, just a little experimentation. +

    I have to give credit to the project +lith, because the 'templating' engine for the blog is inspired by it. +

    How did I do it?

    That's a good question.

    Originally, this blog was powered by Jekyll, that translated markdown to html and hosted + by Github Pages. I decided that I was going to build a templating engine and generate html + to keep things simple. +

    But also, as a challenge I made a simple HTTP server to use as a dev server when trying the blog locally. +

    HTTP Server

    For the purpose of having a custom HTTP Server I had to add support for TCP sockets to the language. +I wrapped the go standard library in some functions and exposed that to the Grotsky language. +In grotsky looks something like this +

    +let socket = net.listenTcp(host + ":" + port)
    +let conn
    +while true {
    +	try {
    +		conn = socket.accept()
    +	} catch err {
    +		io.println("Error accepting new connection", err)
    +		continue
    +	}
    +	try {
    +		# Call function that handles connection
    +		handleConn(conn)
    +	} catch err {
    +		io.println("Error handling connection", err)
    +	}
    +	try {
    +		conn.close()
    +	} catch err {}
    +}
    +				
    +

    This means that the server listens on a socket, accepts connections, writes some text/bytes to the connection + and then closes the connection. +

    Template Engine

    The templating engine is built using the native support Grotsky provide for lists. +A regular page for the blog looks like this: +

    +let base = import("../base.gr")
    +# Create new Post Object
    +let post = base.Post(
    +	"Title",
    +	"Brief description of blog post.",
    +	"Author Name",
    +	"descriptive,tags",
    +	[
    +		[
    +			"h2",
    +			[],
    +			[
    +				"Title"
    +			]
    +		],
    +		[
    +			"div",
    +			["class", "content"],
    +			[
    +				"Content line 1",
    +				"Content line 2",
    +			]
    +		]
    +	]
    +)
    +			

    It's pretty straightforward: the first element of the list is the html tag, the second is +an array of properties for the tag and the last one is a list that contains what will be +the *content* of enclosed by the tags. +

    Resources

    If you want to take a peek, the source code for both projects is available on github: +

    Miguel LiezunI created my own programming language using Go, then I built a blog engine and used that engine to build this blog. +
    Mlisp: My own lisp implementation compiled to WASM +https://mliezun.github.io/2021/04/01/mlisp-wasm.html +2021-04-01 +

    Reading time: +

    + +

    Mlisp, My own lisp implementation +

    +Mlisp a tiny lispy language based on the book Build Your Own Lisp. + +The interpreter is written in C and compiled directly to WASM. You can try it in this page by openning the developer console of your browser and typing Mlisp.interpret("+ 2 2") or using the repl shown below. + + +

    Interface +

    +To be able to access C functions from your browser you have to export them. Let's see how we can define a function that is exported. + +
    +#if __EMSCRIPTEN__
    +EMSCRIPTEN_KEEPALIVE
    +#endif
    +int mlisp_init();
    +
    + +When compilen with emcc the emscripten compiler to wasm, you have to add EMSCRIPTEN_KEEPALIVE macro before your function so it doesn't get optimized away. + +The exported functions in this project are: + +
    +int mlisp_init();
    +char *mlisp_interpret(char *input);
    +void mlisp_cleanup();
    +
    + +The project is then compiled with: + +
    +emcc -std=c99  -Wall -O3 -s WASM=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]'
    +
    + +That means that you would be able to access the exported functions using a cwrap that let's you wrap a C function call from a Javascript function call. + +This compilation generates two files mlisp.js and mlisp.wasm. + +The javascript file defines a Module that provides useful tool to access exported functions. + + +

    Let's start using it +

    +
    +const Mlisp = {
    +    init: Module.cwrap('mlisp_init', 'number', []),
    +    interpret: Module.cwrap('mlisp_interpret', 'string', ['string']),
    +    cleanup: Module.cwrap('mlisp_cleanup', 'void', []),
    +};
    +
    +// Init interpreter
    +Mlisp.init();
    +
    +// Run some commands
    +console.log(Mlisp.interpret("+ 2 2"));
    +
    +// Cleanup interpreter
    +Mlisp.cleanup();
    +
    + + +

    Automated Build & Release from github +

    +I made a github workflow for this project to automatically build and release so you can retrieve them from Github. + + + +

    REPL +

    + + + + +
    +
    + + Input some commands"> +
    +
    + + + + + + +

    Interesting commands to try out +

    +
  • foldl: Fold left (same as reduce left) + - (foldl + 0 {1 2 3 4 5}): Sum of elements +
  • filter + - (filter (\ {e} {> e 3}) {1 2 3 4 5 6}): Elements bigger than 3 +
  • map + - (foldl * 1 (map (\ {e} {* e 2}) {1 1 1 1 1})): Multiply elements by 2 and then multiply all elements + + +
  • Miguel LiezunLisp implementation written in C that compiles to WASM with emscripten. +
    Grotsky Part 4: Writing a service to get your public IP +https://mliezun.github.io/2020/12/17/grotsky-getmyip.html +2020-12-17 +

    Reading time: +

    + +

    Writing a service to get your public IP +

    +Grotsky (my toy programming language) finally can be used to make something useful. + +In this post I want to show you how I made a service that let's your retrieve your public IP as a response to a HTTP Request. + + +

    Show me the code +

    +Let's start by building the http request handler. + +The service will be deployed to heroku. Heroku passes the port that the http server has to listen as an environment variable named PORT. + + +
    Let's get the server up and running +
    +
    +let listen = ":8092"
    +let port = env.get("PORT")
    +if port != "" {
    +    listen = ":" + port
    +}
    +
    +io.println("Listen " + listen)
    +http.listen(listen)
    +
    + +We listen by default at the port 8092 and if the environment variable is given we change it. + +Then we print what is the port and start the server with http.listen. That blocks the execution and starts the server. + +Grotsky interpreter is written in Go, and uses Go's standard http server. Each requests is handled by a goroutine, but because Grotsky is single threaded only one goroutine executes at any given point in time. + +When a request is received the goroutine has to hold the GIL (Global Interrupt Lock) to be able to give control to the interpreter. + + +
    Now lets add some code to handle requests +
    +
    +fn getIP(rq, rs) {
    +    io.println("Request from --> " + rq.address)
    +    rs.write(200, rq.address)
    +}
    +
    +http.handler("/", getIP)
    +
    +let listen = ":8092"
    +let port = env.get("PORT")
    +if port != "" {
    +    listen = ":" + port
    +}
    +
    +io.println("Listen " + listen)
    +http.listen(listen)
    +
    + +Now we have something interesting to try out! + +What we've done is to log and write back as response the address of the device that is doing the request. + +To try it out you need to download grotsky. + +
    +$ go get github.com/mliezun/grotsky/cmd/grotsky
    +
    + +Save the Grotsky code under a filed called getip.g and the execute it using the grotsky interpreter: + +
    +$ go run $(go env GOPATH)/src/github.com/mliezun/grotsky/cmd/grotsky getip.g
    +
    + +Output: +
    +Listen :8092
    +
    + +Now you can make a request to see if it is working + +
    +$ curl localhost:8092
    +
    + +Output: +
    +[::1]:43464
    +
    + +We see that the address contains the port we want to split it and show just the IP. + + + +
    Let's write a couple functions to do that +
    +
    +fn findReversed(string, char) {
    +    for let i = string.length-1; i > -1; i = i - 1 {
    +        if string[i] == char {
    +            return i
    +        }
    +    }
    +    return -1
    +}
    +
    +fn parseIP(address) {
    +    let ix = findReversed(address, ":")
    +    return address[:ix]
    +}
    +
    + +The function findReversed finds the first index where char appears in string starting from the end. + +The function parseIP uses findReversed to obtain the index where ":" splits the IP and the PORT and uses that index to return just the IP address. + + +
    Now we can send just the IP address +
    +
    +fn getIP(rq, rs) {
    +    let address = parseIP(rq.address)
    +    io.println("Request from --> " + address)
    +    rs.write(200, address)
    +}
    +
    + +Add the two functions at the beginning of the file and modify the getIP function. + +Restart the server and now if you make a request you should get just the IP. + +
    +$ curl localhost:8092
    +[::1]
    +
    + +Voila! + + + +
    We have just one last issue: Proxies! +
    +Our service will probably sit behind a proxy, so we need to read the address from a special header X-Forwarded-For. + +Let's implement that! + +
    +fn getIP(rq, rs) {
    +    let address = parseIP(rq.address)
    +    let forwarded = rq.headers["X-Forwarded-For"]
    +    if forwarded != nil {
    +        address = forwarded[0]
    +    }
    +    io.println("Request from --> " + address)
    +    rs.write(200, address)
    +}
    +
    + +We read the header from the request and if X-Forwarded-For is present we sent that as a response to the user. + + +
    Our work is complete. Let's try it! +
    +
    +$ curl localhost:8092 -H 'X-Forwarded-For: 8.8.8.8'
    +8.8.8.8
    +
    +$ curl localhost:8092
    +[::1]
    +
    + +Well done. Now you can deploy it to Heroku (that's up to you) or any other cloud platform. + +I have my own version running under: https://peaceful-lowlands-45821.herokuapp.com/ + + + +

    Deployed to Fly.io after Heroku killed the free plan: +http://morning-breeze-4255.fly.dev +

    Try it from your command line +
    +
    +$ curl http://morning-breeze-4255.fly.dev
    +
    + + +
    Miguel LiezunPart 4 of building my own language series. This time I write and deploy a service to Heroku that let's your retrieve your pulbic IP. +
    Executing python code from MySQL Server +https://mliezun.github.io/2020/04/19/mysql-python.html +2020-04-19 +

    Reading time: +

    + +

    Executing python code from MySQL Server +

    + +

    Trying py_eval +

    + +
    Generate a list of integers from 0 to 10 +
    +
    +> select py_eval('[i for i in range(10)]') list;
    ++--------------------------------+
    +| list                           |
    ++--------------------------------+
    +| [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
    ++--------------------------------+
    +
    + + +
    Generate a dictionary (json object) from a list of dicts +
    +
    +> select replace(py_eval('{ str(user["id"]) : user for user in [{"id": 33, "name": "John"}, {"id": 44, "name": "George"}] }'), "'", '"') dict;
    ++------------------------------------------------------------------------+
    +| dict                                                                   |
    ++------------------------------------------------------------------------+
    +| {"33": {"id": 33, "name": "John"}, "44": {"id": 44, "name": "George"}} |
    ++------------------------------------------------------------------------+
    +
    + +Replace is needed here, because python uses single quotes for dictionaries. + + +
    Make a function that receives a json array and a key and sorts the array by key +
    +
    +DROP function IF EXISTS sort_by_key;
    +DELIMITER $$
    +CREATE FUNCTION sort_by_key (arr json, k text)
    +RETURNS json
    +BEGIN
    +    RETURN replace(py_eval(CONCAT("sorted(", arr, ", key=lambda e: e['", k, "'])")), "'", '"');
    +END$$
    +DELIMITER ;
    +
    + +Test + +
    +> select sort_by_key('[{"a":2}, {"a":1}, {"a": 722}, {"a": 0}]', 'a') sorted;
    ++--------------------------------------------+
    +| sorted                                     |
    ++--------------------------------------------+
    +| [{"a": 0}, {"a": 1}, {"a": 2}, {"a": 722}] |
    ++--------------------------------------------+
    +
    + + +

    How to write a MySQL UDF +

    +There is a pretty good guide at the MySQL 8.0 Reference Manual. I'll give you a brief explanation so you can start quickly, but reading the full guide is highly recomended. + +MySQL's UDFs are written in C++ and need to follow certain conventions so they can be recognized as such. + +In our case, we want our MySQL function to be called py_eval, so we have to define the following C++ functions: + +
  • py_eval_init or py_eval_deinit +
  • py_eval + +**py_eval_init**: (Optional) Initializes memory and data structures for the function execution. + +**py_eval**: Executes the actual function, in our case evaluates a python expression. + +**py_eval_deinit**: (Optional) If any memory was allocated in the init function, this is the place where we free it. + +For py_eval we only need **py_eval_init** and **py_eval**. + + +
  • Functions signatures +

    +
    +bool py_eval_init(UDF_INIT *initid, UDF_ARGS *args,
    +                             char *message);
    +
    +char *py_eval(UDF_INIT *, UDF_ARGS *args, char *result,
    +                         unsigned long *res_length, unsigned char *null_value,
    +                         unsigned char *);
    +
    + +These are the standard definitions for MySQL functions that return string, as is the case of py_eval. To be able to declare this functions, you need to have the definition of UDF_INIT and UDF_ARGS, you can find that at the source code of mysql server -> right here. + + +

    Evaluating python expression +

    +For evaluating python expression, we'll be using pybind11. That gives us the ability to directly access the python interpreter and execute code. + + +
    Example +
    +Make sure you have g++ installed. Try executing: g++ --help. And some version of python running of your system, for this tutorial I'll be using version _3.8_. + +
    +$ mkdir py_eval && cd py_eval
    +$ git clone https://github.com/pybind/pybind11
    +
    + +Create a new file called main.cpp with the following content: + +
    +#include "pybind11/include/pybind11/embed.h"
    +#include "pybind11/include/pybind11/eval.h"
    +#include 
    +
    +namespace py = pybind11;
    +
    +py::scoped_interpreter guard{}; // We need this to keep the interpreter alive
    +
    +int main(void) {
    +    auto obj = py::eval("[i for i in range(10)]");
    +    std::cout << std::string(py::str(obj)) << std::endl;
    +}
    +
    + +To run the example we have to compile the file. + +First, we need the compilation flags. + +
    +$ pkg-config python-3.8 --libs --cflags
    +-I/usr/include/python3.8
    +
    + +Then, we can compile and run our code with the following. + +
    +$ g++ main.cpp -I/usr/include/python3.8 -lpython3.8
    +$ ./a.out
    +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    +
    + + +

    Putting all together +

    +Download udf types to the project folder. + +
    +$ wget https://raw.githubusercontent.com/mysql/mysql-server/8.0/include/mysql/udf_registration_types.h
    +
    + +Create a new file called py_eval.cpp, with the following content: + +
    c++
    +#include "pybind11/include/pybind11/embed.h"
    +#include "pybind11/include/pybind11/eval.h"
    +#include "udf_registration_types.h"
    +#include 
    +
    +namespace py = pybind11;
    +
    +py::scoped_interpreter guard{}; // We need this to keep the interpreter alive
    +
    +extern "C" bool py_eval_init(UDF_INIT *initid, UDF_ARGS *args,
    +                             char *message)
    +{
    +    // Here we can check if we received one argument
    +    if (args->arg_count != 1)
    +    {
    +        // The function returns true if there is an error,
    +        // the error message is copied to the message arg.
    +        strcpy(message, "py_eval must have one argument");
    +        return true;
    +    }
    +    // Cast the passed argument to string
    +    args->arg_type[0] = STRING_RESULT;
    +    initid->maybe_null = true; /* The result may be null */
    +    return false;
    +}
    +
    +extern "C" char *py_eval(UDF_INIT *, UDF_ARGS *args, char *result,
    +                         unsigned long *res_length, unsigned char *null_value,
    +                         unsigned char *)
    +{
    +    // Evaluate the argument as a python expression
    +    auto obj = py::eval(args->args[0]);
    +    // Cast the result to std::string
    +    std::string res_str = std::string(py::str(obj));
    +
    +    // Copy the output string from py::eval to the result argument
    +    strcpy(result, res_str.c_str());
    +
    +    // Set the length of the result string
    +    *res_length = res_str.length();
    +
    +    return result;
    +}
    +
    + +Then, we have to compile the project as a shared library, and move it to the plugin folder of mysql (in your case, it could be located in some other directory). + +
    +$ g++ -I/usr/include/python3.8 -lpython3.8 -shared -fPIC -o py_eval.so py_eval.cpp
    +$ sudo cp py_eval.so /usr/lib/mysql/plugin/
    +
    + +Now, it's time to try it from mysql. + +First, connect to your server as root. + +
    +$ sudo mysql -uroot
    +
    + +Create and test the function. + +
    +> create function py_eval returns string soname 'py_eval.so';
    +Query OK, 0 rows affected (0.029 sec)
    +
    +> select py_eval('[i for i in range(10)]') list;
    ++--------------------------------+
    +| list                           |
    ++--------------------------------+
    +| [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
    ++--------------------------------+
    +1 row in set (0.001 sec)
    +
    + + +

    Future +

    +There is a lot to do, for example there is no error control on the function execution. The python expression that we are trying to evaluate could fail causing a server reboot. Also, there is some extra work to do to be able to use import. And there are many concerns regarding concurrency issues. + +If you want to contribute to improve execution of python code on mysql server, please go to my github project and make a PR. + +I hope you enjoyed this tutorial and come back soon for more. + +
    Miguel LiezunEvaluate python expressions inside MySQL using a UDF that binds to python interpreter. +
    Writing your own C malloc and free +https://mliezun.github.io/2020/04/11/custom-malloc.html +2020-04-11 +

    Reading time: +

    + +

    Writing your own C malloc and free +

    + +

    Challenge +

    +This challenge comes from the book Crafting Interpreters by Bob Nystrom. And can be found in Chapter 14 - Challenge 3. + +The challenge goes: + +> You are allowed to call malloc() once, at the beginning of the interpreters execution, to allocate a single big block of memory which your reallocate() function has access to. It parcels out blobs of memory from that single region, your own personal heap. Its your job to define how it does that. + + +

    Check out this +comparison of malloc() to S3.

    Solution +

    +As stated in the challenge I'll be using a big chunk of _contiguous_ memory. The main idea of my solution is to store the blocks of memory in the array prepending a header with metadata. + +
    + _______________________________________________
    +|head_0|block_0 ... |head_1|block_1    ...      |
    + 
    +
    + +The structure of the header is pretty similar to that of a linked list. + +
    +struct block_meta
    +{
    +    size_t size;
    +    struct block_meta *next;
    +    int free;
    +};
    +
    +#define META_SIZE sizeof(struct block_meta)
    +
    + +It stores the size of the block, a pointer to the next block and a flag to mark wether it's free or not. + +Then, a function to traverse the list of blocks and find if there is any freed block is needed: + +
    +void *first_block = NULL;
    +
    +struct block_meta *find_free_block(struct block_meta **last, size_t size)
    +{
    +    struct block_meta *current = first_block;
    +    while (current && !(current->free && current->size >= size))
    +    {
    +        *last = current;
    +        current = current->next;
    +    }
    +    return current;
    +}
    +
    + +This function receives a double pointer to a block_meta struct called last that at the end of the execution should be pointing to the last node of the list and a size_t variable that indicates the minimum size that the block needs to be. + + +
    Memory initialization +
    +Two functions are needed to handle the big chunk of memory, one to initialize and the other to free it. + +
    +void initMemory();
    +void freeMemory();
    +
    + +To implement initMemory I've decided that I would ask for the maximum amount of memory that I could get from the OS. + +
    +#define MINREQ 0x20000
    +
    +// Big block of memory
    +void *memory = NULL;
    +// Position where the last block ends
    +size_t endpos = 0;
    +
    +void initMemory()
    +{
    +    size_t required = PTRDIFF_MAX;
    +    while (memory == NULL)
    +    {
    +        memory = malloc(required);
    +        if (required < MINREQ)
    +        {
    +            if (memory)
    +            {
    +                free(memory);
    +            }
    +            printf("Cannot allocate enough memory\n");
    +            exit(ENOMEM);
    +        }
    +        required >>= 1;
    +    }
    +}
    +
    +void freeMemory()
    +{
    +    free(memory);
    +}
    +
    + +As you can see, initMemory starts trying to allocate the maximum amount a memory allowed, and starts to divide that amount by 2 every time the allocation fails. If there isn't at least 128KB of memory available the program crashes with ENOMEM. + +Now that we have our chunk of memory ready to go, we can start to start giving blocks away. + +
    +struct block_meta *request_block(size_t size)
    +{
    +    struct block_meta *last = NULL;
    +    struct block_meta *block = find_free_block(&last, size);
    +    if (block)
    +    {
    +        block->free = 0;
    +        return block;
    +    }
    +    // Append new block to list
    +    block = memory + endpos;
    +    endpos += META_SIZE + size;
    +    if (last)
    +    {
    +        last->next = block;
    +    }
    +    else
    +    {
    +        first_block = block;
    +    }
    +    block->free = 0;
    +    block->next = NULL;
    +    block->size = size;
    +    return block;
    +}
    +
    + +How request_block works: + +1. Tries to find a free block with enough space. If there is one, it is set as occupied and returns that block. +2. If there isn't a free block available. It adds a new block with enough space at the end of memory (the big chunk). +3. If this is the first call, points the head of the list to the recently created block, else point the last node to the block. +4. Set the new block as occupied, set the size and next to null. Then return the new block. + +With this function, implementing malloc and free is pretty easy: + +
    +void *my_malloc(size_t size)
    +{
    +    struct block_meta *block = request_block(size);
    +    return block + 1;
    +}
    +
    +void my_free(void *ptr)
    +{
    +    struct block_meta *block = ptr - META_SIZE;
    +    block->free = 1;
    +}
    +
    + +To finish the challenge, I have to implement realloc, that is a little bit more tricky. + +
    +void *my_realloc(void *ptr, size_t size)
    +{
    +    if (!ptr)
    +    {
    +        return my_malloc(size);
    +    }
    +    struct block_meta *block = ptr - META_SIZE;
    +    if (block->size >= size)
    +    {
    +        return block + 1;
    +    }
    +    uint8_t *newptr = my_malloc(size);
    +    size_t i;
    +    for (i = 0; i < (block->size < size ? block->size : size); i++)
    +    {
    +        newptr[i] = ((uint8_t *)ptr)[i];
    +    }
    +    block->free = 1;
    +    return newptr;
    +}
    +
    + +How realloc works: + +
  • If the pointer to reallocate is null, works just like malloc. +
  • If the given size is bigger than the prior size, it allocates a bigger block and copies all data from the original block to the new block. +
  • If the given size is smaller than the prior size, it allocates a smaller block and copies just the data that fits into the smaller block. + + +
  • New challenge +

    +In my implementation I used a linked list where each node holds a pointer to the next, but given that I have control over the _entire_ memory this actualy isn't necessary. + +My challenge to you is that you remove the pointer to next from the block_meta struct. + + +

    Resources +

    +
  • https://danluu.com/malloc-tutorial/ +
  • http://www.craftinginterpreters.com/chunks-of-bytecode.html + +
  • Miguel LiezunChallenge for writing your own implementation of malloc and free. +
    Grotsky Part 3: Interpreting +https://mliezun.github.io/2020/04/01/grotsky-part3.html +2020-04-01 +

    Reading time: +

    + +

    Grotsky Part 3: Interpreting +

    + +

    It's slow! +

    +My interpreter it's really, really, wait for it... _Really slow_. + +An example of a bad performing grotsky code: + +
    +# fib: calculates the n-th fibonacci number recursively
    +fn fib(n) begin
    +    if n < 2 return n
    +    return fib(n-2) + fib(n-1)
    +end
    +println(fib(30))
    +
    + + +
    Running the code +
    +
    +$ time ./grotsky examples/fib.g
    +
    + +Gives a wooping result of: + +
    +832040
    +
    +real    0m11,154s
    +user    0m11,806s
    +sys     0m0,272s
    +
    + +Almost twelve seconds!!! + +Comparing with a similar python code + +
    +def fib(n):
    +    if n < 2: return n
    +    return fib(n-2) + fib(n-1)
    +print(fib(30))
    +
    + +Gives a result of: + +
    +832040
    +
    +real    0m0,423s
    +user    0m0,387s
    +sys     0m0,021s
    +
    + +That means, my interpreter is at least 20 times slower than Cpython. + + +
    Why is it so slow? +
    +Here is an explanation. + +As the person from the first comment states, go garbage collector is not well suited for this kind of scenario with heavy allocation of objects. + +> Go's GC is not generational, so allocation requires (comparatively speaking) much more work. It's also tuned for low latency (smallest pause when GC has to stop the program) at the expense of throughput (i.e. total speed). This is the right trade-off for most programs but doesn't perform optimally on micro-benchmarks that measure throughtput. + +Setting the gc percent at 800 (100 by default) more than halves the time that the function takes to compute: + +
    +$ time GOGC=800 ./grotsky examples/fib.g
    +832040
    +
    +real    0m5,110s
    +user    0m5,182s
    +sys     0m0,061s
    +
    + + +

    Interpreting functions +

    +Callable interface + +
    +type callable interface {
    +	arity() int
    +	call(exec *exec, arguments []interface{}) interface{}
    +}
    +
    + +_All grotsky functions must be an object that implements the callable interface._ + +For that I defined two kind of structs: + +
    +type function struct {
    +	declaration   *fnStmt
    +	closure       *env
    +	isInitializer bool
    +}
    +
    +type nativeFn struct {
    +	arityValue int
    +	callFn  func(exec *exec, arguments []interface{}) interface{}
    +}
    +
    + + +
    nativeFn +
    +Let's you define standard functions available on all grotsky interpreters. Line println. + +
    +func (n *nativeFn) arity() int {
    +	return n.arityValue
    +}
    +
    +func (n *nativeFn) call(exec *exec, arguments []interface{}) interface{} {
    +	return n.callFn(exec, arguments)
    +}
    +
    + +From that, println would be pretty straight forward: + +
    +...
    +
    +var println nativeFn
    +println.arityValue = 1
    +println.callFn = func(exec *exec, arguments []interface{}) interface{} {
    +    fmt.Println(arguments[0])
    +    return nil
    +}
    +...
    +
    + + +
    Ordinary grotsky functions +
    +For ordinary grotsky functions the things are a little bit messier. + +First I got to introduce the environment that is an object that holds map[string]interface{} as a dictionary for variables in the local scope and a pointer to another environment that contains variables for the outer scope. + +
    +type env struct {
    +	state *state
    +
    +	enclosing *env
    +	values    map[string]interface{}
    +}
    +
    +func newEnv(state *state, enclosing *env) *env {
    +	return &env{
    +		state:     state,
    +		enclosing: enclosing,
    +		values:    make(map[string]interface{}),
    +	}
    +}
    +
    +func (e *env) get(name *token) interface{} {
    +	if value, ok := e.values[name.lexeme]; ok {
    +		return value
    +	}
    +	if e.enclosing != nil {
    +		return e.enclosing.get(name)
    +	}
    +	e.state.runtimeErr(errUndefinedVar, name)
    +	return nil
    +}
    +
    +func (e *env) define(name string, value interface{}) {
    +	e.values[name] = value
    +}
    +
    + +As you can see, the define method creates a variable on the local scope, and the get methods tries to retrieve a variable first from the local scope and then from the outer scope. + +Let's see how functions are implemented. + +
    +func (f *function) arity() int {
    +	return len(f.declaration.params)
    +}
    +
    +func (f *function) call(exec *exec, arguments []interface{}) (result interface{}) {
    +	env := newEnv(exec.state, f.closure)
    +	for i := range f.declaration.params {
    +		env.define(f.declaration.params[i].lexeme, arguments[i])
    +	}
    +
    +	defer func() {
    +		if r := recover(); r != nil {
    +			if returnVal, isReturn := r.(returnValue); isReturn {
    +				result = returnVal
    +			} else {
    +				panic(r)
    +			}
    +		}
    +	}()
    +
    +	exec.executeBlock(f.declaration.body, env)
    +
    +	return nil
    +}
    +
    + +Function arity is pretty simple. + +The function call takes an exec object, that is no more than an instance of the interpreter, and the arguments to the function as an array of objects. Then creates a new environment the is surrounded by the environment local to the function definition and defines all the function parameters. Then comes the tricky part, first there is a deferred call to an anonymous function, let's ignore that for a moment, in the end, the function executeBlock gets called. Let's see what that function does: + +
    +func (e *exec) executeBlock(stmts []stmt, env *env) {
    +	previous := e.env
    +	defer func() {
    +		e.env = previous
    +	}()
    +	e.env = env
    +	for _, s := range stmts {
    +		e.execute(s)
    +	}
    +}
    +
    + +What's happening here is that the interpreter steps into the new environment, saving the previous environment in a variable, and execute all given statements, after that it restores the environment to the previous one. Exactly as a function does. + + +
    What happens when you hit a return +
    +
    +type returnValue interface{}
    +
    +...
    +
    +func (e *exec) visitReturnStmt(stmt *returnStmt) R {
    +	if stmt.value != nil {
    +		panic(returnValue(stmt.value.accept(e)))
    +	}
    +	return nil
    +}
    +
    + +When you get to a return node in the ast, the nodes panics with a return value. This has to do with the fact that you need to go up the call stack and finish the execution of the function, otherwise the function will keep it's execution. + +That's the reason of the deferred function we forgot a couple seconds ago: + +
    +func (f *function) call(exec *exec, arguments []interface{}) (result interface{}) {
    +    ...
    +
    +    defer func() {
    +		if r := recover(); r != nil {
    +			if returnVal, isReturn := r.(returnValue); isReturn {
    +				result = returnVal
    +			} else {
    +				panic(r)
    +			}
    +		}
    +    }()
    +
    +    ...
    +}
    +
    + +This function recovers from a panic. If the value recovered is of type returnValue it recovers successfully and sets the result value of the function call to the return value, else it panics again. + + +

    Hasta la vista, baby +

    +That's it for now. There are a lot of nifty stuff to keep talking about. But I think it's enough for now. + +Remember to check out the source code. And stay tuned for more. + +
    Miguel LiezunPart 3 of building my own language series. Interpreting expressions and statement, traversing the Abstract Syntax Tree. +
    Grotsky Part 2: Parsing expressions +https://mliezun.github.io/2020/03/15/grotsky-part2.html +2020-03-15 +

    Reading time: +

    + +

    Grotsky Part 2: Parsing expressions +

    + +

    Expressions +

    +Parsing an expression like 1+2*3 requires a complex representation on memory. Just looking at it we think that it's pretty simple, but there is some hidden hierarchy that we have to pay attention to, like the fact that first we have to compute 2*3 and then add 1 to it. + +To represent that in a data structure the best thing we can come up to is a tree, as seen in the next figure: + +![image](/assets/images/grotsky-part2/AST.png) + +As you can see the leaves of the tree are literals and the root and intermediate nodes are operations that have to be applied from the bottom up. That means that we traverse the tree until we reach the bottom and start computing the results by going up. + + +

    Defining node types +

    +> Not all operations are created equal. + +We have to define how each node fits into the tree. + +I'll use the following syntax: Binary -> left expr, operator token, right expr. Which means that a binary operation (as we have seen in the image before) links to 2 expressions (left and right) and stores 1 value (operator). + + +
    Let's define all posible operations on literals +
    +
    +Literal -> value object
    +# 1, "asd", 5.2, true, false
    +
    +Binary -> left expr, operator token, right expr
    +# 1+2, 3*3, 4^2+1
    +
    +Grouping -> expression expr
    +# (1+2)
    +
    +Logical -> left expr, operator token, right expr
    +# true or false, false and true
    +
    +Unary: operator token, right expr
    +# not true, -5
    +
    +List -> elements []expr
    +# [1, 2, 3, [4], "asd"]
    +
    +Dictionary -> elements []expr
    +# {"a": 1, "b": 2, 3: 4}
    +
    +Access -> object expr, slice expr
    +# [1, 2, 3][0], {"a":1}["a"]
    +
    +Slice -> first expr, second expr, third expr
    +# [1, 2, 3, 4, 5, 6][1:4:2]
    +
    + + +

    Traversing the abstract syntax tree +

    +To traverse the syntax tree we need a pattern that's uniform and easily scalable when we have to add other types of expressions and statements. + +For that we'll use the Visitor Pattern. + + +

    Visitor Pattern +

    +First we need an interface for the expression that allows a visitor to visit it. + +
    +type expr interface {
    +    accept(exprVisitor) interface{}
    +}
    +
    + +An expression visitor should have a method for each kind of expression it has to visit. + +
    +type exprVisitor interface {
    +    visitLiteralExpr(expr expr) interface{}
    +    visitBinaryExpr(expr expr) interface{}
    +    visitGroupingExpr(expr expr) interface{}
    +    visitLogicalExpr(expr expr) interface{}
    +    visitUnaryExpr(expr expr) interface{}
    +    visitListExpr(expr expr) interface{}
    +    visitDictionaryExpr(expr expr) interface{}
    +    visitAccessExpr(expr expr) interface{}
    +    visitSliceExpr(expr expr) interface{}
    +}
    +
    + +Then we have to define a type for each kind of expression that implements expr interface. For example, this is the implementation for a binary expression: + +
    +type binaryExpr struct {
    +    left expr
    +    operator *token
    +    right expr
    +}
    +
    +func (s *binaryExpr) accept(visitor exprVisitor) R {
    +    return visitor.visitBinaryExpr(s)
    +}
    +
    + +For all other expressions the definition is practically the same. + + +

    String Visitor +

    +To finish this chapter, let's define a visitor that allows you to print the syntax tree in a lisp-like syntax, ex: (+ 1 2). + +Here is the implementation of the string visitor for a binary expression: + +
    +type stringVisitor struct{}
    +
    +func (v stringVisitor) visitBinaryExpr(expr expr) R {
    +    binary := expr.(*binaryExpr)
    +    return fmt.Sprintf("(%s %v %v)", binary.operator.lexeme, binary.left.accept(v), binary.right.accept(v))
    +}
    +
    + + +

    Grotsky expression +

    +You can check out the state of the Grotsky project right here: https://github.com/mliezun/grotsky. + +Grotsky it's able to parse and print all types of expressions defined in this article right now. + + +

    Expressions +

    +Examples of operations supported: + +
    +# Math operations
    +1+2*3^2-(4+123)/2.6
    +=> (- (+ 1 (* 2 (^ 3 2))) (/ (+ 4 123) 2.6))
    +
    +# Logical operations
    +true or false
    +=> (or true false)
    +
    +# Comparisons
    +1 == 1 and (1 > 3 or 11/5.5 <= 3+2^2 and 1 != 2)
    +=> (and (== 1 1) (or (> 1 3) (and (<= (/ 11 5.5) (+ 3 (^ 2 2))) (!= 1 2))))
    +
    +# Lists
    +[1, 2, [3], "asd"]
    +=> (list 1 2 (list 3) "asd")
    +
    +# List slicing
    +[1,2,3,4][1:3][::2][0]
    +=> (#0 (#::2 (#1:3 (list 1 2 3 4))))
    +
    +# Dictionary
    +{
    +    1: 2,
    +    3: 4,
    +    "asd": 3.14
    +}
    +=> (dict 1=>2 3=>4 "asd"=>3.14)
    +
    +# Dictionary key lookup
    +{"key":0.6}["key"]
    +=> (#"key" (dict "key"=>0.6))
    +
    + +That's it for now. In the next chapter we'll traverse the tree but instead of printing we'll execute the operations listed before. + +If you have questions or suggestions please get in touch. + +
    Miguel LiezunPart 2 of building my own language series. Parsing expressions, traversing and printing the Abstract Syntax Tree. +
    Grotsky Part 1: Syntax +https://mliezun.github.io/2020/02/21/grotsky-part1.html +2020-02-21 +

    Reading time: +

    + +

    Grotsky Part 1: Syntax +

    + +

    Syntax Restrictions +

    +
  • No use of semicolon ; +
  • Block statements delimited by begin and end +
  • Function definition using fn keyword +
  • Logic operators in plain english or, and, not +
  • Conditional statements use the following keywords: if, elif, else +
  • There is no switch statement +
  • Class definition with class keyword +
  • Arithmetic operations: *, /, -, +, ^ +
  • Grouping with parentheses () +
  • Native support for python-like lists and dictionaries: [], {} +
  • Support for enhanced for loop: for i, el in array +
  • Keywords and identifiers can only use alphabethic characters + + +
  • Primitives +

    +
  • nil +
  • Integers +
  • Floats +
  • Booleans +
  • Strings +
  • Lists +
  • Dictionaries + + +
  • Example of functions and operations +

    +
    +## Arithmethic
    +print(2^10 - 2323*3)
    +# Output: -5945
    +print(2^(12*3+400/-4+10*5/2))
    +# Output: 1.8189894035458565e-12
    +
    +## Logic
    +print(true or false)
    +# Output: true (short circuit)
    +print(false and true)
    +# Output: false (short circuit)
    +
    +## Conditionals
    +if 3 > 2 or (1 < 3 and 2 == 2) begin
    +    print('Condition is true')
    +end
    +elif 3 == 4 begin
    +    print('Condition 2 is true')
    +end
    +else begin
    +    print('Conditions are false')
    +end
    +
    +## Lists
    +for i in [1, 2, 3, 4] begin
    +    print(i)
    +end
    +
    +let lst = [1, 2, 3, 4]
    +lst[0] = -1
    +print(lst) # Output: [-1, 2, 3, 4]
    +print(lst[1:3]) # Output: [2, 3]
    +
    +## Dictionaries
    +# (dictionaries and lists not allowed as keys)
    +let dct = {
    +    "Key1": "Val1",
    +    2: "Val2",
    +    true: false
    +}
    +for key, val in dct begin
    +    print(key, val)
    +end
    +
    +## Functions
    +fn square(x)
    +begin
    +    return x^2
    +end
    +
    +fn operate(x, operation)
    +begin
    +    return operation(x)
    +end
    +
    +## Clojure
    +fn makeCounter()
    +begin
    +    let n = 0
    +    return fn() begin
    +        n = n+1
    +        return n
    +    end
    +end
    +
    +## Classes
    +class Counter
    +begin
    +    init(start) begin
    +        self.start = start
    +    end
    +    count() begin
    +        self.start = self.start+1
    +        return self.start
    +    end
    +end
    +
    +class CounterTwo
    +begin
    +    count() begin
    +        return super.count()*2
    +    end
    +end
    +
    + + +

    Syntax definition +

    +Let's build a syntax definition in backus naur format that will be easy to parse with a recursive descent parser. + + +
    Expresions +
    +
    +expression       assignment;
    +list             "[" arguments? "]";
    +dictionary       "{" dict_elements? "}";
    +dict_elements    keyval ("," keyval)*;
    +keyval           expression ":" expression;
    +assignment       (call ".")? IDENTIFIER "=" assignment | access;
    +access           logic_or ("[" slice "]")*;
    +logic_or         logic_and ("or" logic_and)*;
    +logic_and        equality ("and" equality)*;
    +equality         comparison (("!=" | "==") comparison)*;
    +comparison       addition ((">" | ">=" | "<" | "<=") addition)*;
    +addition         multiplication (("-" | "+") multiplication)*;
    +multiplication   power (("/" | "*") power)*;
    +power            unary ("^" unary)*;
    +unary            ("not" | "-") unary | call;
    +call             primary ("(" arguments? ")" | "." IDENTIFIER)*;
    +arguments        expression ("," expression)*;
    +slice            (":" expression)
    +                | (":" expression ":" expression)
    +                | (":" ":" expression)
    +                | expression
    +                | (expression ":")
    +                | (expression ":" expression)
    +                | (expression ":" ":" expression)
    +                | (expression ":" expression ":" expression);
    +primary          NUMBER
    +                | STRING
    +                | "false"
    +                | "true"
    +                | "nil"
    +                | IDENTIFIER
    +                | "(" expression ")"
    +                | fnAnon
    +                | list
    +                | dictionary;
    +fnAnon           "fn" "(" parameters? ")" block;
    +
    + + +
    Statements +
    +
    +program         declaration* EOF;
    +declaration     classDecl | funDecl | varDecl | statement;
    +classDecl       "class" IDENTIFIER ( "<" IDENTIFIER )? "begin" methodDecl* "end" NEWLINE;
    +methodDecl      "class"? function;
    +funDecl         "fn" function ;
    +function        IDENTIFIER "(" parameters? ")" block ;
    +parameters      IDENTIFIER ( "," IDENTIFIER )* ;
    +varDecl         "let" IDENTIFIER ("=" expression)? NEWLINE;
    +statement       forStmt
    +                | ifStmt
    +                | returnStmt
    +                | whileStmt
    +                | exprStmt
    +                | block;
    +exprStmt        expression NEWLINE;
    +forStmt         "for"  (classicFor | newFor) statement;
    +classicFor      (varDecl | exprStmt | ",") expression? "," expression?;
    +newFor          IDENTIFIER ("," IDENTIFIER)? "in" expression;
    +ifStmt          "if" expression statement ("elif" expression statement)* ("else" statement)?;
    +returnStmt      "return" expression? NEWLINE;
    +whileStmt       "while" expression statement;
    +block           "begin" NEWLINE declaration* "end" NEWLINE;
    +
    + +That's it! The next step is to build a lexer and a parser. + +
    Miguel LiezunPart 1 of building my own language series. Defining the syntax of grotsky toy language. +
    Sudoku Solver +https://mliezun.github.io/2020/02/18/sudoku-solver.html +2020-02-18 +

    Reading time: +

    + +

    Sudoku Solver +

    +I wanted to make my own sudoku solver to challenge myself. + +Im not a sudoku player so my approach is a brute force scan of possible combinations sort-of. + +I just know the basic rules: + +
  • Numbers 1-9 are allowed. +
  • Numbers in the same row cannot be repeated. +
  • Numbers in the same column cannot be repeated. +
  • Numbers in the 3x3 square cannot be repeated. + +The first thing i did was to build a some classes that calculates the possible values a cell can have if it's empty, based on the constraints. + +I came up with 3 classes: + +
  • Board that stores the entire board. +
  • BoardSlice that stores a slice of a board. An object of this type is returned when a Board is sliced (method __getitem__). +
  • Cell that stores the value of a single cell and calculates all possible values a cell can take. + +The class Cell receives a board, the coordinates on the board, and the value that holds. Also has the method options that uses python set data structure to calculate the posibilites. + +If you look at the following snippet you can see that the method options +generates the sets: options that contains all possible options (1-9), row that contains all the numbers that are in the same row, column that contains all the numbers that are in the same column and square that contains all the numbers that are in the same 3x3 square. The return value is options without all the used values. + +
    +class Cell:
    +    def __init__(self, b, i, j, value):
    +        self.b = b
    +        self.value = value
    +        self.i = i
    +        self.j = j
    +
    +    def options(self):
    +        if self.value != 0:
    +            return {self.value}
    +        options = set(range(1, 10))
    +        row = set(map(lambda x: x.value, self.b[self.i]))
    +        column = set(map(lambda x: x.value, self.b[:][self.j]))
    +        def to_square(k): return slice((k // 3) * 3, (k // 3) * 3 + 3)
    +        square = set(
    +            map(lambda x: x.value,
    +                self.b[to_square(self.i)][to_square(self.j)]))
    +        return options - row - column - square - {0}
    +
    + +To make easier the implementation of the square I used the class BoardSlice that contains a slice of a board and implements the magic method __getitem__. + +
    +class BoardSlice:
    +    def __init__(self, board_slice):
    +        self.board_slice = board_slice
    +
    +    def __getitem__(self, items):
    +        if type(items) == slice:
    +            return (el for row in self.board_slice for el in row[items])
    +        if type(items) == int:
    +            return (row[items] for row in self.board_slice)
    +        raise KeyError
    +
    + +The base class: Board contains the board and a copy method that copies all the values and creates a new Board object. This is necessary to avoid messing with object references and have a clean object when needed. + +
    +class Board:
    +    def __init__(self, board):
    +        self.board = [[Cell(self, i, j, value)
    +                       for (j, value) in enumerate(row)] for (i, row) in enumerate(board)]
    +
    +    def copy(self):
    +        return Board(((cell.value for cell in row) for row in self.board))
    +
    +    def __getitem__(self, items):
    +        if type(items) == int:
    +            return self.board[items]
    +        if type(items) == slice:
    +            return BoardSlice(self.board[items])
    +        raise KeyError
    +
    +    def __repr__(self):
    +        return repr(self.board)
    +
    + +With these tools the next step is to solve the problem! + +My idea was to generate a mixed iterative-recursive algorithm. + +The first pass will be iterative, and if needed, the second pass will be recursive. + + +
  • Iterative pass +
    +Iterates over the whole board and calculates the options that each cell can have. If a cell has only one option set that option on the cell and set a flag to repeat the iterative pass, if has 0 options return None meaning that the board has no solutions, and if has more than one option store the options for the recursive pass. + +If the loop ends and we found that no cell has more than one option then we solved the board! + +The idea of this first step is to solve an _easy_ board quickly. + + +
    Recursive pass +
    +If the iterative pass ends and we found that a cell has more than one option then we try all that options and call solve again! + +If solve returns a board that means we've found the solution! + +If solve returns None (back at the iterative passs) we have to try with another options. + + +
    BoardSolver +
    +The class is pretty straightforward. + +
    +class SudokuSolver:
    +    @staticmethod
    +    def solve(board):
    +        b = board.copy()
    +        # First pass: Iterative
    +        board_map = {}
    +        exhaust = False
    +        while not exhaust:
    +            exhaust = True
    +            for i in range(9):
    +                for j in range(9):
    +                    cell = b[i][j]
    +                    if cell.value == 0:
    +                        options = cell.options()
    +                        if len(options) == 1:
    +                            cell.value = options.pop()
    +                            exhaust = False
    +                        elif len(options) == 0:
    +                            return None
    +                        elif len(board_map) == 0:
    +                            board_map[(i, j)] = options
    +
    +        # Second pass: Recursive
    +        for ((i, j), options) in board_map.items():
    +            for op in options:
    +                b[i][j].value = op
    +                solved = SudokuSolver.solve(b)
    +                if solved:
    +                    return solved
    +            return None
    +
    +        return b
    +
    + + +
    Conclusions +
    +Actually my implementation is not a brute force algorithm, is a search algorithm, that searches the path to solving a board. Because it doesn't try all values on all cells nonsensically, it rather tries _some_ options for a given cell and advances to the next option as _soon_ as it detects that it's not the correct path. + + +

    Source +

    +Take a look at the source code. + +
    Miguel LiezunIterative + recursive sudoku solver using python magic methods. +
    Crafting interpreters +https://mliezun.github.io/2020/02/12/crafting-interpreters.html +2020-02-12 +

    Reading time: +

    + +

    Crafting interpreters +

    +I've just finished the section 2 of the book _Crafting Interpreters_, and I wanted to upload it to github right away. + +Take a look at the source code. + +Beside the lox specification I've added: + +
  • The keyword until that is a variation of while loops (as in ruby). also +
  • print is a function instead of a statement. +
  • eval function that let's you eval source code in runtime. +
  • Class methods. + +I'll implement another language interpreter, this time using golang and with a syntax similar to ruby. + +
  • Miguel LiezunLox language interpreter based on the book craftinginterpreters.com by Bob Nystrom. +
    Reinventing the Wheel: PHP Generators +https://mliezun.github.io/2020/01/24/php-generator.html +2020-01-24 +

    Reading time: +

    + +

    Reinventing the Wheel: PHP Generators +

    + +

    First thing first. How a generator works? +

    + +

    Starting back at C +

    +Let's create a function that each time we call it we get the next number of the fibonacci sequence. + +
    +int fibonacci()
    +{
    +    static int a = 0;
    +    static int b = 1;
    +    int aux = b;
    +    b = a + b;
    +    a = aux;
    +    return a;
    +}
    +
    + +If we call fibonacci(), the first time we'll get 1, the second time 1, the third 2, the fourth 3, and so on... + +This happens because we declared variables a, b to be static. This means that they mantain the value after the function returns. Normally, what happens (if we don't declare a variable as static) is that the variables inside the function don't mantain the values of the last execution. + + +

    First generator for PHP +

    +The equivalent function in PHP is pretty similar to C's approach. + +
    +
    +
    +function fibonacci()
    +{
    +    static $a = 0;
    +    static $b = 1;
    +    $aux = $b;
    +    $b = $a + $b;
    +    $a = $aux;
    +    return $a;
    +}
    +
    +$out = [];
    +
    +for ($i = 1; $i <= 10; $i++) {
    +    $out[] = fibonacci();
    +}
    +
    +echo implode(', ', $out) . "\n";
    +
    +/*
    +Output: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
    +*/
    +
    + +Let's compare this to the real PHP version using yield. + +
    +
    +
    +function fibonacci($N)
    +{
    +    $a = 0;
    +    $b = 1;
    +    for ($i = 0; $i < $N; $i++) {
    +        $aux = $b;
    +        $b = $a + $b;
    +        $a = $aux;
    +        yield $a;
    +    }
    +}
    +
    +$out = [];
    +
    +foreach (fibonacci(10) as $fib) {
    +    $out[] = $fib;
    +}
    +
    +echo implode(', ', $out) . "\n";
    +
    +/*
    +Output: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
    +*/
    +
    + + +

    Creating a custom version of PHP yield +

    +This is my own version using the library parallel and channels (probably uses yield internally). + +
    +
    +
    +class MyGenerator implements Iterator
    +{
    +    private $chan;
    +    private $current;
    +    private $iteratorFn;
    +    private $runtime;
    +    private $key = -1;
    +    private $valid = true;
    +
    +    public function __construct($iteratorFn)
    +    {
    +        $this->iteratorFn = $iteratorFn;
    +        $this->runtime = new \parallel\Runtime();
    +        $channel = new \parallel\Channel();
    +
    +        $this->runtime->run(function() use ($iteratorFn, $channel) {
    +            $iteratorFn(function ($val) use ($channel) {
    +                $channel->send($val);
    +            });
    +            $channel->close();
    +        });
    +
    +        $this->chan = $channel;
    +        $this->next();
    +    }
    +
    +    public function current()
    +    {
    +        return $this->current;
    +    }
    +
    +    public function next()
    +    {
    +        try {
    +            ++$this->key;
    +            $val = $this->chan->recv();
    +            $this->current = $val;
    +        } catch (\parallel\Channel\Error\Closed $e) {
    +            $this->valid = false;
    +        }
    +        return $this->current;
    +    }
    +
    +    public function key() {return $this->key;}
    +    public function valid() {return $this->valid;}
    +    public function rewind() {}
    +}
    +
    +
    +function fibonacci($N)
    +{
    +    return new MyGenerator(function ($yield) use ($N) {
    +        $a = 0;
    +        $b = 1;
    +        for ($i = 0; $i < $N; $i++) {
    +            $aux = $b;
    +            $b = $a + $b;
    +            $a = $aux;
    +            $yield($a);
    +        }
    +    });
    +}
    +
    +$out = [];
    +
    +foreach (fibonacci(10) as $fib) {
    +    $out[] = $fib;
    +}
    +
    +echo implode(', ', $out) . "\n";
    +
    + + +

    Performance comparison: PHP vs Custom +

    + +
    Tested code +
    +
    +for ($i = 0; $i < 1000; ++$i) {
    +    foreach (fibonacci(100) as $fib) {
    +        $out[] = $fib;
    +    }
    +}
    +
    + + +
    yield version +
    +
    +real    0m0,083s
    +user    0m0,059s
    +sys     0m0,023s
    +
    + + +
    MyGenerator version +
    +
    +real    0m2,138s
    +user    0m1,426s
    +sys     0m1,363s
    +
    + +So, it's aproximately 26 times slower :-) + +
    Miguel LiezunAttempt of a lunatic to recreate functionalities that a language already has using the same language, and failing. +
    \ No newline at end of file diff --git a/docs/googlee7507b09f10f12d1.html b/docs/googlee7507b09f10f12d1.html new file mode 100644 index 0000000..e474a5f --- /dev/null +++ b/docs/googlee7507b09f10f12d1.html @@ -0,0 +1 @@ +google-site-verification: googlee7507b09f10f12d1.html \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..1aac893 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,45 @@ +Blog | mliezun.github.io + +

    Blog

    Hi folks!

    I'm Miguel. Here I write mainly about programming and side projects.

    I've written my own programming language called Grotsky, and im +rewriting it in Rust. +

    Generating posts using markdown

    2024-01-04
    Custom Markdown parser and HTML generator using Grotsky, my toy programming language that powers this blog. Up until now I've used a hacky HTML generator that relies on lists. Now Im integrating a simple MD parser that makes easier to write new articles.

    Day 20. My favourite problem from Advent of Code 2023

    2023-12-25
    Advent of code 2023 has gone by, this is my first year participating. It's been fun and I want to share the problem that I enjoyed the most. It's based on simple electronic devices sending signals or pulses to each other.

    I rewrote my toy language interpreter in Rust

    2023-11-23
    Im rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x. This has been a serious of posts, this one is the latest one. Hopefully the best and most insightful +of them all.

    Rewrite my toy language interpreter in Rust, an update

    2023-09-23
    Im rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x.

    The end of a side project

    2023-07-15
    Cloud Outdated was a personalized digest of updates for cloud services. It's sad to see it go, + but it was a fun project to work on, learn some new stuff and collab with a friend. + There are some takeaways from this that I'd like to share.

    Rewrite my toy language interpreter in Rust

    2023-06-02
    Im rewriting Grotsky (my toy programming language) in Rust, the previous implementation +was done in Go. The goal of the rewrite is to improve my Rust skills, and to improve the performance of Grotsky, +by at least 10x.

    Writing a Redis clone in Go from scratch

    2023-04-08
    In this post we're going to write a basic Redis clone in Go that implements the most simple commands: GET, +SET, DEL and QUIT. At the end you'll know how to parse a byte stream from a live TCP connection, and hopefully have a working +implementation of Redis.

    How to write a program that can replicate itself

    2022-11-26
    Grotsky is a toy programming language that I made for fun. Today we're visinting the concept of Quines, +a.k.a. self replicating programs. It's said that any turing-complete language should be able to write a program that replicates + itself. And grotsky is no exception.

    Migrate from Heroku to Fly.io

    2022-09-22
    A couple weeks ago Heroku announced the removal. I have plenty of projects running on free dynos. + I have taken some time to move my code to Fly.io. And also I've written a little tutorial of how to perform the migration.

    Branchable MySQL: Managing multiple dev environments

    2022-09-20
    When teams start to grow, having a single dev environment becomes an issue. People start stepping on each others toes. +A common problem is that two people want to apply incompatible migrations on the database. That problem is impossible +to fix if folks are working on parallel branches. +If we can have a database for each branch of a project, that will remove much of the pain of having multiple devs applying +changes to the db.

    Webscraping as a side project

    2022-07-20
    Cloud Outdated is a personalized digest of updates for cloud services. Works like a newsletter where you can choose + which services you want to get notified about. For example: Subscribe to AWS Lambda with Python runtime, and you'll get an email + when 3.10 is supported.

    Playing with Javascript Proxies (getters/setters)

    2021-12-31
    In this last post of the year I play with proxies in an attempt to create a Javascript object where changes are appended + to a log and can be reverted by deleting the last element of the log using getters and setters.

    Grotsky Part 3: Interpreting

    2020-04-01
    Part 3 of building my own language series. Interpreting expressions and statement, traversing the Abstract Syntax Tree.

    Grotsky Part 1: Syntax

    2020-02-21
    Part 1 of building my own language series. Defining the syntax of grotsky toy language.

    Sudoku Solver

    2020-02-18
    Iterative + recursive sudoku solver using python magic methods.

    Crafting interpreters

    2020-02-12
    Lox language interpreter based on the book craftinginterpreters.com by Bob Nystrom.
    \ No newline at end of file