loxxy 0.0.14 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +82 -0
- data/README.md +106 -53
- data/lib/loxxy/ast/all_lox_nodes.rb +5 -1
- data/lib/loxxy/ast/ast_builder.rb +36 -3
- data/lib/loxxy/ast/ast_visitor.rb +46 -2
- data/lib/loxxy/ast/lox_binary_expr.rb +1 -1
- data/lib/loxxy/ast/lox_grouping_expr.rb +23 -0
- data/lib/loxxy/ast/lox_logical_expr.rb +28 -0
- data/lib/loxxy/ast/lox_unary_expr.rb +27 -0
- data/lib/loxxy/back_end/engine.rb +49 -3
- data/lib/loxxy/datatype/builtin_datatype.rb +55 -0
- data/lib/loxxy/datatype/false.rb +22 -0
- data/lib/loxxy/datatype/lx_string.rb +3 -4
- data/lib/loxxy/datatype/nil.rb +22 -0
- data/lib/loxxy/datatype/number.rb +113 -14
- data/lib/loxxy/datatype/true.rb +8 -0
- data/lib/loxxy/front_end/grammar.rb +2 -2
- data/lib/loxxy/version.rb +1 -1
- data/spec/datatype/lx_string_spec.rb +5 -0
- data/spec/datatype/number_spec.rb +10 -0
- data/spec/front_end/parser_spec.rb +4 -4
- data/spec/interpreter_spec.rb +184 -6
- metadata +5 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: eced55bddfd7918cbc7aea8eff5f7ecd37aa88baa6e5a6f7350f5bd7606e99af
         | 
| 4 | 
            +
              data.tar.gz: f9b8104a0f3a273466ff568a014e345437f4c568621828de8da44961fb52f0e5
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d5b23defe380228bda4e32d9cb94909767fdc42242111f00be214bfbead6323a5e6acda0d78dcaf4e1c2e1fa7d13cd0363661af959a62b093bfcc50c34c290a0
         | 
| 7 | 
            +
              data.tar.gz: 96a6a77824cd97b248ad6a78d66d4757c81549fa199021f446883d690f2702dbf4d9879950505d9aa54f45376bd7e6204be8b054e27b43a5576aca744be40047
         | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,85 @@ | |
| 1 | 
            +
            ## [0.0.18] - 2021-01-14
         | 
| 2 | 
            +
            - The interpreter supports expressions between parentheses (grouping).
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            ## Added
         | 
| 5 | 
            +
            - Class `Ast::LoxLogicalExpr`
         | 
| 6 | 
            +
            - Method `Ast::ASTBuilder#reduce_grouping_expr` as semantic action for grouping expression
         | 
| 7 | 
            +
            - Method `Ast::ASTVisitor#visit_grouping_expr` for visiting grouping expressions
         | 
| 8 | 
            +
            - Method `Engine::after_grouping_expr`for the evaluation of grouping expressions
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            ## [0.0.18] - 2021-01-13
         | 
| 11 | 
            +
            - The interpreter can evaluate `and`, `or`expressions.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ## Added
         | 
| 14 | 
            +
            - Class `Ast::LoxLogicalExpr`
         | 
| 15 | 
            +
            - Method `Ast::ASTBuilder#reduce_logical_expr` for the semantic action require for `and`, `or`
         | 
| 16 | 
            +
            - Method `Ast::ASTVisitor#visit_logical_expr` for visiting logical expressions
         | 
| 17 | 
            +
            - Method `Backend::Engine#after_logical_expr` implements the evaluation of the logical expressions
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## [0.0.17] - 2021-01-12
         | 
| 20 | 
            +
            - The interpreter can evaluate all arithmetic and comparison operations.
         | 
| 21 | 
            +
            - It implements `==`, `!=` and the unary operations `!`, `-`
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ## Added
         | 
| 24 | 
            +
            - Class `Ast::LoxUnaryExpr`
         | 
| 25 | 
            +
            - Method `Ast::ASTBuilder#reduce_unary_expr` to support the evaluation of `!` and ``-@`
         | 
| 26 | 
            +
            - Method `Ast::ASTVisitor#visit_unnary_expr` for visiting unary expressions
         | 
| 27 | 
            +
            - Method `Backend::Engine#after_unary_expr` evaluating an unary expression
         | 
| 28 | 
            +
            - In class `Datatype::BuiltinDatatype` the methods `falsey?`, `truthy?`, `!`, `!=`
         | 
| 29 | 
            +
            - In class `Datatype::Number`the methods `<`, `<=`, ´>´, `>=` and `-@`
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            ## Changed
         | 
| 32 | 
            +
            - File `README.md` updated.
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            ## [0.0.16] - 2021-01-11
         | 
| 35 | 
            +
            - The interpreter can evaluate product and division of two numbers.
         | 
| 36 | 
            +
            - It also implements equality `==` and inequality `!=` operators
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            ## Added
         | 
| 39 | 
            +
            - Method `Datatype::False#==` for equality testing
         | 
| 40 | 
            +
            - Method `Datatype::False#!=` for inequality testing
         | 
| 41 | 
            +
            - Method `Datatype::LXString#==` for equality testing
         | 
| 42 | 
            +
            - Method `Datatype::LXString#!=` for inequality testing
         | 
| 43 | 
            +
            - Method `Datatype::Nil#==` for equality testing
         | 
| 44 | 
            +
            - Method `Datatype::Nil#!=` for inequality testing
         | 
| 45 | 
            +
            - Method `Datatype::Number#==` for equality testing
         | 
| 46 | 
            +
            - Method `Datatype::Number#!=` for inequality testing
         | 
| 47 | 
            +
            - Method `Datatype::Number#*` for multiply operator
         | 
| 48 | 
            +
            - Method `Datatype::Number#/` for divide operator
         | 
| 49 | 
            +
            - Method `Datatype::True#==` for equality testing
         | 
| 50 | 
            +
            - Method `Datatype::True#!=` for inequality testing
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            ## Changed
         | 
| 53 | 
            +
            - Method `BackEnd::Engine#after_binary_expr` to allow `*`, `/`, `==`, `!=` operators
         | 
| 54 | 
            +
            - File `README.md` updated for the newly implemented operators
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            ## [0.0.15] - 2021-01-11
         | 
| 57 | 
            +
            - The interpreter can evaluate substraction between two numbers.
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            ## Added
         | 
| 60 | 
            +
            - Method `Datatype::Number#-` implmenting the subtraction operation
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            ## Changed
         | 
| 63 | 
            +
            - File `README.md` minor editorial changes.
         | 
| 64 | 
            +
            - File `lx_string_spec.rb` Added test for string concatentation
         | 
| 65 | 
            +
            - File `number_spec.rb` Added tests for addition and subtraction operations
         | 
| 66 | 
            +
            - File `interpreter_spec.rb` Added tests for subtraction operation
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            ## [0.0.14] - 2021-01-10
         | 
| 69 | 
            +
            - The interpreter can evaluate addition of numbers and string concatenation
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            ## Added
         | 
| 72 | 
            +
            - Method `Ast::ASTVisitor#visit_binary_expr` for visiting binary expressions
         | 
| 73 | 
            +
            - Method `Ast::LoxBinaryExpr#accept` for visitor pattern
         | 
| 74 | 
            +
            - Method `BackEnd::Engine#after_binary_expr` to trigger execution of binary operator
         | 
| 75 | 
            +
            - `Boolean` class hierarchy: added methos `true?` and `false?` to ease spec test writing
         | 
| 76 | 
            +
            - Method `Datatype::LXString#+` implementation of the string concatenation
         | 
| 77 | 
            +
            - Method `Datatype::Number#+` implementation of the addition of numbers
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            ## Changed
         | 
| 80 | 
            +
            - File `interpreter_spec.rb` Added tests for addition operation and string concatenation
         | 
| 81 | 
            +
             | 
| 82 | 
            +
             | 
| 1 83 | 
             
            ## [0.0.13] - 2021-01-10
         | 
| 2 84 | 
             
            - The interpreter can evaluate directly simple literals.
         | 
| 3 85 |  | 
    
        data/README.md
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 | 
             
            [](https://badge.fury.io/rb/loxxy)
         | 
| 3 3 | 
             
            [](https://github.com/famished-tiger/loxxy/blob/main/LICENSE.txt)
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 5 | 
            +
            ### What is loxxy?
         | 
| 6 6 | 
             
            A Ruby implementation of the [Lox programming language](https://craftinginterpreters.com/the-lox-language.html ),
         | 
| 7 7 | 
             
            a simple language used in Bob Nystrom's online book [Crafting Interpreters](https://craftinginterpreters.com/ ).
         | 
| 8 8 |  | 
| @@ -16,7 +16,7 @@ a simple language used in Bob Nystrom's online book [Crafting Interpreters](http | |
| 16 16 | 
             
            The project is still in inception and the interpreter is being implemented...  
         | 
| 17 17 | 
             
            Currently it can execute a tiny subset of __Lox__ language.
         | 
| 18 18 |  | 
| 19 | 
            -
             | 
| 19 | 
            +
            But the __loxxy__ gem hosts also a parser class `RawPaser` that can parse, in principle, any valid Lox input.
         | 
| 20 20 |  | 
| 21 21 | 
             
            ## What's the fuss about Lox?
         | 
| 22 22 | 
             
            ... Nothing...  
         | 
| @@ -74,7 +74,7 @@ require 'loxxy' | |
| 74 74 |  | 
| 75 75 | 
             
            lox = Loxxy::Interpreter.new
         | 
| 76 76 |  | 
| 77 | 
            -
            lox_program = ' | 
| 77 | 
            +
            lox_program = '47 - 5; // THE answer'
         | 
| 78 78 | 
             
            result = lox.evaluate(lox_program) # => Loxxy::Datatype::Number
         | 
| 79 79 |  | 
| 80 80 | 
             
            # `result` is a Ruby object, so let's use it...
         | 
| @@ -128,17 +128,20 @@ program | |
| 128 128 | 
             
            ```
         | 
| 129 129 |  | 
| 130 130 | 
             
            ## Suppported Lox language features
         | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 131 | 
            +
            On one hand, the parser covers the complete Lox grammar and should therefore, in principle,
         | 
| 132 | 
            +
            parse any valid Lox program.
         | 
| 133 | 
            +
             | 
| 134 | 
            +
            On the other hand, the interpreter is under development and currently it can evaluate only a tiny subset of __Lox__.
         | 
| 135 | 
            +
            But the situation is changing almost daily, stay tuned...
         | 
| 133 136 |  | 
| 134 137 | 
             
            Here are the language features currently supported by the interpreter:
         | 
| 135 138 |  | 
| 136 139 | 
             
            - [Comments](#comments)
         | 
| 137 140 | 
             
            - [Keywords](#keywords)
         | 
| 138 | 
            -
            - [Operators and Special Chars](#operators-and-special-chars)
         | 
| 139 141 | 
             
            - [Datatypes](#datatypes)
         | 
| 140 | 
            -
            - [Statements](#statements)
         | 
| 141 | 
            -
            - | 
| 142 | 
            +
            - [Statements](#statements)  
         | 
| 143 | 
            +
              -[Expressions](#expressions)  
         | 
| 144 | 
            +
              -[Print Statement](#print-statement)
         | 
| 142 145 |  | 
| 143 146 | 
             
            ### Comments
         | 
| 144 147 |  | 
| @@ -149,66 +152,116 @@ Loxxy supports single line C-style comments. | |
| 149 152 | 
             
            ```
         | 
| 150 153 |  | 
| 151 154 | 
             
            ### Keywords
         | 
| 152 | 
            -
             | 
| 153 | 
            -
            The parser knows all the __Lox__ reserved keywords:
         | 
| 155 | 
            +
            Loxxy implements the following __Lox__ reserved keywords:
         | 
| 154 156 | 
             
            ```lang-none
         | 
| 155 | 
            -
            and,  | 
| 156 | 
            -
            print, return, super, this, true, var, while
         | 
| 157 | 
            +
            and, false, nil, or, print, true
         | 
| 157 158 | 
             
            ```
         | 
| 158 | 
            -
            Of these, the interpreter implements: `false`, `nil`, `true`
         | 
| 159 | 
            -
             | 
| 160 | 
            -
            ### Operators and Special Chars
         | 
| 161 | 
            -
            #### Operators
         | 
| 162 | 
            -
            The parser recognizes all the __Lox__ operators, delimiters and separators:
         | 
| 163 | 
            -
            - Arithmetic operators: `+`, `-`, `*`, `/`
         | 
| 164 | 
            -
            - Comparison operators: `>`, `>=`, `<`, `<=`
         | 
| 165 | 
            -
            - Equality operators: `==`, `!=`
         | 
| 166 | 
            -
             | 
| 167 | 
            -
            Of these, the interpreter implements: 
         | 
| 168 | 
            -
            `+` (addition of two numbers or string concatenation)
         | 
| 169 | 
            -
             | 
| 170 | 
            -
            #### Delimiters
         | 
| 171 | 
            -
            The parser knows all the __Lox__ grouping delimiters:  
         | 
| 172 | 
            -
            (`, ), `{`, `}`
         | 
| 173 | 
            -
             | 
| 174 | 
            -
            These aren't yet implemented in the interpreter.
         | 
| 175 | 
            -
             | 
| 176 | 
            -
            The other characters that have a special meaning in __Lox__ are:
         | 
| 177 | 
            -
            - `,` Used in parameter list
         | 
| 178 | 
            -
            - `.` For the dot notation (i.e. calling a method)
         | 
| 179 | 
            -
            - `;` The semicolon is used to terminates expressions
         | 
| 180 | 
            -
            - `=` Assignment
         | 
| 181 | 
            -
             | 
| 182 | 
            -
            The parser recognizes them all but the interpreter accepts the semicolons only.
         | 
| 183 159 |  | 
| 184 160 | 
             
            ### Datatypes
         | 
| 185 161 |  | 
| 186 | 
            -
             | 
| 162 | 
            +
            loxxy supports  all the standard __Lox__ datatypes:
         | 
| 187 163 | 
             
            - `Boolean`: Can be `true` or `false`
         | 
| 188 164 | 
             
            - `Number`: Can be an integer or a floating-point numbers. For example: `123, 12.34, -45.67`
         | 
| 189 165 | 
             
            - `String`: Sequence of characters surrounded by `"`. For example: `"Hello!"`
         | 
| 190 166 | 
             
            - `Nil`: Used to define a null value, denoted by the `nil` keyword
         | 
| 191 167 |  | 
| 192 | 
            -
             | 
| 168 | 
            +
            ### Statements
         | 
| 169 | 
            +
             | 
| 170 | 
            +
            Loxxy supports the following statements:  
         | 
| 171 | 
            +
            -[Expressions](#expressions)  
         | 
| 172 | 
            +
              -[Arithmetic expressions](#arithmetic-expressions)  
         | 
| 173 | 
            +
              -[String concatenation](#string-concatenation)  
         | 
| 174 | 
            +
              -[Comparison expressions](#comparison-expressions)  
         | 
| 175 | 
            +
              -[Logical expressions](#logical-expressions)  
         | 
| 176 | 
            +
              -[Grouping expressions](#grouping-expressions)  
         | 
| 177 | 
            +
            -[Print Statement](#print-statement)
         | 
| 178 | 
            +
             | 
| 179 | 
            +
            #### Expressions
         | 
| 180 | 
            +
             | 
| 181 | 
            +
            ##### Arithmetic expressions
         | 
| 182 | 
            +
            Loxxy supports the following operators for arithmetic expressions:
         | 
| 183 | 
            +
             | 
| 184 | 
            +
            - `+`: Adds of two numbers. Both operands must be of the type Number  
         | 
| 185 | 
            +
              E.g. `37 + 5; // => 42`  
         | 
| 186 | 
            +
              `7 + -3; // => 4`
         | 
| 187 | 
            +
            - `-`: (Binary) Subtracts right operand from left operand. Both operands must be numbers.  
         | 
| 188 | 
            +
              E.g. `47 - 5; // => 42`
         | 
| 189 | 
            +
            - `-`: (Unary) Negates (= changes the sign) of the given number operand.  
         | 
| 190 | 
            +
              E.g. `- -3; // => 3`
         | 
| 191 | 
            +
            - `*`: Multiplies two numbers  
         | 
| 192 | 
            +
              E.g. `2 * 3; // => 6`
         | 
| 193 | 
            +
            - `/`: Divides two numbers  
         | 
| 194 | 
            +
              E.g. `8 / 2; // => 4`
         | 
| 195 | 
            +
              `5 / 2; // => 2.5`
         | 
| 196 | 
            +
             | 
| 197 | 
            +
            ##### String concatenation
         | 
| 198 | 
            +
            - `+`: Concatenates two strings. Both operands must be of the String type.  
         | 
| 199 | 
            +
              E.g. `"Hello" + ", " + "world! // => "Hello, world!"`
         | 
| 200 | 
            +
             | 
| 201 | 
            +
            ##### Comparison expressions
         | 
| 202 | 
            +
             | 
| 203 | 
            +
            - `==`: Returns `true` if left operand is equal to right operand, otherwise `false`  
         | 
| 204 | 
            +
              E.g. `false == false; // => true`  
         | 
| 205 | 
            +
              `5 + 2 == 3 + 4; // => true`  
         | 
| 206 | 
            +
              `"" == ""; // => true`
         | 
| 207 | 
            +
            - `!=`: Returns `true` if left operand is not equal to right operand, otherwise `false`  
         | 
| 208 | 
            +
              E.g. `false != "false"; // => true`  
         | 
| 209 | 
            +
              `5 + 2 != 4 + 3; // => false`
         | 
| 210 | 
            +
            - `<`: Returns `true` if left operand is less than right operand, otherwise `false`. Both operands must be numbers  
         | 
| 211 | 
            +
              E.g. `1 < 3; // => true`  
         | 
| 212 | 
            +
              `1 < 0; // => false`  
         | 
| 213 | 
            +
              `2 < 2; // => false`
         | 
| 214 | 
            +
            - `<=`: Returns `true` if left operand is equal to right operand, otherwise `false`. Both operands must be numbers  
         | 
| 215 | 
            +
              E.g. `1 <= 3; // => true`  
         | 
| 216 | 
            +
              `1 <= 0; // => false`  
         | 
| 217 | 
            +
              `2 <= 2; // => true`
         | 
| 218 | 
            +
            - `>`: Returns `true` if left operand is equal to right operand, otherwise `false`. Both operands must be numbers  
         | 
| 219 | 
            +
              E.g. `1 > 3; // => false`  
         | 
| 220 | 
            +
              `1 > 0; // => true`  
         | 
| 221 | 
            +
              `2 > 2; // => false`
         | 
| 222 | 
            +
            - `>=`: Returns `true` if left operand is equal to right operand, otherwise `false`. Both operands must be numbers  
         | 
| 223 | 
            +
              E.g. `1 > 3; // => false`  
         | 
| 224 | 
            +
              `1 > 0; // => true`  
         | 
| 225 | 
            +
              `2 > 2; // => false`
         | 
| 226 | 
            +
             | 
| 227 | 
            +
            ##### Logical expressions
         | 
| 228 | 
            +
             | 
| 229 | 
            +
            REMINDER: In __Lox__, `false` and `nil` are considered falsey, everything else is truthy.
         | 
| 230 | 
            +
             | 
| 231 | 
            +
            - `and`: When both operands are booleans, then returns `true` if both left and right operands are truthy, otherwise `false`.   
         | 
| 232 | 
            +
              If at least one operand isn't a boolean then returns first falsey operand else (both operands are truthy) returns the second operand.
         | 
| 233 | 
            +
              truthy returns the second operand.  
         | 
| 234 | 
            +
              E.g. `false and true; // => false`  
         | 
| 235 | 
            +
              `true and nil; // => nil`  
         | 
| 236 | 
            +
              `0 and true and ""; // => ""`
         | 
| 237 | 
            +
            - `or`: When both operands are booleans, then returns `true` if left or right operands are truthy, otherwise `false`.  
         | 
| 238 | 
            +
              If at least one operand isn't a boolean then returns first truthy operand else (both operands are truthy) returns the second operand.
         | 
| 239 | 
            +
              E.g. `false or true; // => true`  
         | 
| 240 | 
            +
              `true or nil; // => nil`  
         | 
| 241 | 
            +
              `false or nil; // => nil`  
         | 
| 242 | 
            +
              `0 or true or ""; // => 0`
         | 
| 243 | 
            +
            - `!`: Performs a logical negation on its operand  
         | 
| 244 | 
            +
              E.g. `!false; // => true`  
         | 
| 245 | 
            +
              `!!true; // => true`  
         | 
| 246 | 
            +
              `!0; // => false`
         | 
| 247 | 
            +
             | 
| 248 | 
            +
            #####  Grouping expressions
         | 
| 249 | 
            +
            Use parentheses `(` `)` for a better control in expression/operator precedence.
         | 
| 250 | 
            +
             | 
| 251 | 
            +
            ``` javascript
         | 
| 252 | 
            +
            print 3 + 4 * 5;  // => 23
         | 
| 253 | 
            +
            print (3 + 4) * 5; // => 35
         | 
| 254 | 
            +
            ```
         | 
| 193 255 |  | 
| 194 | 
            -
            ## Statements
         | 
| 195 | 
            -
            ### Implemented expressions
         | 
| 196 | 
            -
            Loxxy implements expressions:
         | 
| 197 | 
            -
            - Consisting of literals only; or,
         | 
| 198 | 
            -
            - Addition of two numbers; or,
         | 
| 199 | 
            -
            - Concatenation of two strings
         | 
| 200 256 |  | 
| 201 | 
            -
             | 
| 202 | 
            -
            Loxxy implements the following statements:
         | 
| 203 | 
            -
            - Expressions (see above sub-section)
         | 
| 204 | 
            -
            - Print statement
         | 
| 257 | 
            +
            #####  Print Statement
         | 
| 205 258 |  | 
| 206 | 
            -
             | 
| 207 | 
            -
            // Print statement with nested concatenation
         | 
| 208 | 
            -
            print "Hello" + ", " + "world!";
         | 
| 209 | 
            -
            ```
         | 
| 259 | 
            +
            The statement print + expression + ; prints the result of the expression to stdout.
         | 
| 210 260 |  | 
| 261 | 
            +
            ``` javascript
         | 
| 262 | 
            +
            print "Hello, world!";  // Output: Hello, world!
         | 
| 211 263 |  | 
| 264 | 
            +
            ```
         | 
| 212 265 |  | 
| 213 266 | 
             
            ## Installation
         | 
| 214 267 |  | 
| @@ -1,5 +1,9 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative 'lox_print_stmt'
         | 
| 4 3 | 
             
            require_relative 'lox_literal_expr'
         | 
| 5 4 | 
             
            require_relative 'lox_noop_expr'
         | 
| 5 | 
            +
            require_relative 'lox_grouping_expr'
         | 
| 6 | 
            +
            require_relative 'lox_unary_expr'
         | 
| 7 | 
            +
            require_relative 'lox_binary_expr'
         | 
| 8 | 
            +
            require_relative 'lox_logical_expr'
         | 
| 9 | 
            +
            require_relative 'lox_print_stmt'
         | 
| @@ -15,7 +15,6 @@ module Loxxy | |
| 15 15 | 
             
                    # @return [Hash{String => String}]
         | 
| 16 16 | 
             
                    Name2special = {
         | 
| 17 17 | 
             
                      'AND' => 'and',
         | 
| 18 | 
            -
                      'BANG' => '!',
         | 
| 19 18 | 
             
                      'BANG_EQUAL' => '!=',
         | 
| 20 19 | 
             
                      'COMMA' =>  ',',
         | 
| 21 20 | 
             
                      'DOT' => '.',
         | 
| @@ -36,6 +35,11 @@ module Loxxy | |
| 36 35 | 
             
                      'SLASH' =>  '/',
         | 
| 37 36 | 
             
                      'STAR' => '*'
         | 
| 38 37 | 
             
                    }.freeze
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    Name2unary = {
         | 
| 40 | 
            +
                      'BANG' => '!',
         | 
| 41 | 
            +
                      'MINUS' => '-@'
         | 
| 42 | 
            +
                    }.freeze
         | 
| 39 43 | 
             
                  end # defined
         | 
| 40 44 |  | 
| 41 45 | 
             
                  attr_reader :strict
         | 
| @@ -92,6 +96,17 @@ module Loxxy | |
| 92 96 | 
             
                    node
         | 
| 93 97 | 
             
                  end
         | 
| 94 98 |  | 
| 99 | 
            +
                  def reduce_logical_expr(_production, _range, tokens, theChildren)
         | 
| 100 | 
            +
                    operand1 = theChildren[0]
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    # Second child is array with couples [operator, operand2]
         | 
| 103 | 
            +
                    theChildren[1].each do |(operator, operand2)|
         | 
| 104 | 
            +
                      operand1 = LoxLogicalExpr.new(tokens[0].position, operator, operand1, operand2)
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                    operand1
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 95 110 | 
             
                  # rule('lhs' => 'nonterm_i nonterm_k_plus')
         | 
| 96 111 | 
             
                  def reduce_binary_operator(_production, _range, tokens, theChildren)
         | 
| 97 112 | 
             
                    operand1 = theChildren[0]
         | 
| @@ -119,6 +134,11 @@ module Loxxy | |
| 119 134 | 
             
                    [[operator, operand2]]
         | 
| 120 135 | 
             
                  end
         | 
| 121 136 |  | 
| 137 | 
            +
                  # Return the AST node corresponding to the second symbol in the rhs
         | 
| 138 | 
            +
                  def reduce_keep_symbol2(_production, _range, _tokens, theChildren)
         | 
| 139 | 
            +
                    theChildren[1]
         | 
| 140 | 
            +
                  end
         | 
| 141 | 
            +
             | 
| 122 142 | 
             
                  #####################################
         | 
| 123 143 | 
             
                  #  SEMANTIC ACTIONS
         | 
| 124 144 | 
             
                  #####################################
         | 
| @@ -145,7 +165,7 @@ module Loxxy | |
| 145 165 |  | 
| 146 166 | 
             
                  # rule('logic_or' => 'logic_and disjunct_plus')
         | 
| 147 167 | 
             
                  def reduce_logic_or_plus(production, range, tokens, theChildren)
         | 
| 148 | 
            -
                     | 
| 168 | 
            +
                    reduce_logical_expr(production, range, tokens, theChildren)
         | 
| 149 169 | 
             
                  end
         | 
| 150 170 |  | 
| 151 171 | 
             
                  # rule('disjunct_plus' => 'disjunct_plus OR logic_and')
         | 
| @@ -160,7 +180,7 @@ module Loxxy | |
| 160 180 |  | 
| 161 181 | 
             
                  # rule('logic_and' => 'equality conjunct_plus')
         | 
| 162 182 | 
             
                  def reduce_logic_and_plus(production, range, tokens, theChildren)
         | 
| 163 | 
            -
                     | 
| 183 | 
            +
                    reduce_logical_expr(production, range, tokens, theChildren)
         | 
| 164 184 | 
             
                  end
         | 
| 165 185 |  | 
| 166 186 | 
             
                  # rule('conjunct_plus' => 'conjunct_plus AND equality')
         | 
| @@ -231,6 +251,19 @@ module Loxxy | |
| 231 251 | 
             
                    reduce_binary_plus_end(production, range, tokens, theChildren)
         | 
| 232 252 | 
             
                  end
         | 
| 233 253 |  | 
| 254 | 
            +
                  # rule('unary' => 'unaryOp unary')
         | 
| 255 | 
            +
                  def reduce_unary_expr(_production, _range, tokens, theChildren)
         | 
| 256 | 
            +
                    operator = Name2unary[theChildren[0].symbol.name].to_sym
         | 
| 257 | 
            +
                    operand = theChildren[1]
         | 
| 258 | 
            +
                    LoxUnaryExpr.new(tokens[0].position, operator, operand)
         | 
| 259 | 
            +
                  end
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                  # rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN')
         | 
| 262 | 
            +
                  def reduce_grouping_expr(_production, _range, tokens, theChildren)
         | 
| 263 | 
            +
                    subexpr = theChildren[1]
         | 
| 264 | 
            +
                    LoxGroupingExpr.new(tokens[0].position, subexpr)
         | 
| 265 | 
            +
                  end
         | 
| 266 | 
            +
             | 
| 234 267 | 
             
                  # rule('primary' => 'FALSE' | TRUE').as 'literal_expr'
         | 
| 235 268 | 
             
                  def reduce_literal_expr(_production, _range, _tokens, theChildren)
         | 
| 236 269 | 
             
                    first_child = theChildren.first
         | 
| @@ -51,7 +51,7 @@ module Loxxy | |
| 51 51 | 
             
                    broadcast(:after_ptree, aParseTree)
         | 
| 52 52 | 
             
                  end
         | 
| 53 53 |  | 
| 54 | 
            -
                  # Visit event. The visitor is about to visit a print statement | 
| 54 | 
            +
                  # Visit event. The visitor is about to visit a print statement.
         | 
| 55 55 | 
             
                  # @param aPrintStmt [AST::LOXPrintStmt] the print statement node to visit
         | 
| 56 56 | 
             
                  def visit_print_stmt(aPrintStmt)
         | 
| 57 57 | 
             
                    broadcast(:before_print_stmt, aPrintStmt)
         | 
| @@ -59,7 +59,21 @@ module Loxxy | |
| 59 59 | 
             
                    broadcast(:after_print_stmt, aPrintStmt)
         | 
| 60 60 | 
             
                  end
         | 
| 61 61 |  | 
| 62 | 
            -
                  # Visit event. The visitor is about to visit a  | 
| 62 | 
            +
                  # Visit event. The visitor is about to visit a logical expression.
         | 
| 63 | 
            +
                  # Since logical expressions may take shorcuts by not evaluating all their
         | 
| 64 | 
            +
                  # sub-expressiosns, they are responsible for visiting or not their children.
         | 
| 65 | 
            +
                  # @param aBinaryExpr [AST::LOXBinaryExpr] the logical expression node to visit
         | 
| 66 | 
            +
                  def visit_logical_expr(aLogicalExpr)
         | 
| 67 | 
            +
                    broadcast(:before_logical_expr, aLogicalExpr)
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    # As logical connectors may take a shortcut only the first argument is visited
         | 
| 70 | 
            +
                    traverse_given_subnode(aLogicalExpr, 0)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    # The second child could be visited: this action is deferred in handler
         | 
| 73 | 
            +
                    broadcast(:after_logical_expr, aLogicalExpr, self)
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  # Visit event. The visitor is about to visit a binary expression.
         | 
| 63 77 | 
             
                  # @param aBinaryExpr [AST::LOXBinaryExpr] the binary expression node to visit
         | 
| 64 78 | 
             
                  def visit_binary_expr(aBinaryExpr)
         | 
| 65 79 | 
             
                    broadcast(:before_binary_expr, aBinaryExpr)
         | 
| @@ -67,6 +81,22 @@ module Loxxy | |
| 67 81 | 
             
                    broadcast(:after_binary_expr, aBinaryExpr)
         | 
| 68 82 | 
             
                  end
         | 
| 69 83 |  | 
| 84 | 
            +
                  # Visit event. The visitor is about to visit an unary expression.
         | 
| 85 | 
            +
                  # @param anUnaryExpr [AST::anUnaryExpr] unary expression node to visit
         | 
| 86 | 
            +
                  def visit_unary_expr(anUnaryExpr)
         | 
| 87 | 
            +
                    broadcast(:before_unary_expr, anUnaryExpr)
         | 
| 88 | 
            +
                    traverse_subnodes(anUnaryExpr)
         | 
| 89 | 
            +
                    broadcast(:after_unary_expr, anUnaryExpr)
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  # Visit event. The visitor is about to visit a grouping expression.
         | 
| 93 | 
            +
                  # @param aGroupingExpr [AST::LoxGroupingExpr] grouping expression to visit
         | 
| 94 | 
            +
                  def visit_grouping_expr(aGroupingExpr)
         | 
| 95 | 
            +
                    broadcast(:before_grouping_expr, aGroupingExpr)
         | 
| 96 | 
            +
                    traverse_subnodes(aGroupingExpr)
         | 
| 97 | 
            +
                    broadcast(:after_grouping_expr, aGroupingExpr)
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 70 100 | 
             
                  # Visit event. The visitor is visiting the
         | 
| 71 101 | 
             
                  # given terminal node containing a datatype object.
         | 
| 72 102 | 
             
                  # @param aLiteralExpr [AST::LoxLiteralExpr] the leaf node to visit.
         | 
| @@ -98,6 +128,20 @@ module Loxxy | |
| 98 128 | 
             
                    broadcast(:after_subnodes, aParentNode, subnodes)
         | 
| 99 129 | 
             
                  end
         | 
| 100 130 |  | 
| 131 | 
            +
                  # Visit event. The visitor is about to visit one given subnode of a non
         | 
| 132 | 
            +
                  # terminal node.
         | 
| 133 | 
            +
                  # @param aParentNode [Ast::LocCompoundExpr] the parent node.
         | 
| 134 | 
            +
                  # @param index [integer] index of child subnode
         | 
| 135 | 
            +
                  def traverse_given_subnode(aParentNode, index)
         | 
| 136 | 
            +
                    subnode = aParentNode.subnodes[index]
         | 
| 137 | 
            +
                    broadcast(:before_given_subnode, aParentNode, subnode)
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                    # Now, let's proceed with the visit of that subnode
         | 
| 140 | 
            +
                    subnode.accept(self)
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                    broadcast(:after_given_subnode, aParentNode, subnode)
         | 
| 143 | 
            +
                  end
         | 
| 144 | 
            +
             | 
| 101 145 | 
             
                  # Send a notification to all subscribers.
         | 
| 102 146 | 
             
                  # @param msg [Symbol] event to notify
         | 
| 103 147 | 
             
                  # @param args [Array] arguments of the notification.
         | 
| @@ -5,7 +5,7 @@ require_relative 'lox_compound_expr' | |
| 5 5 | 
             
            module Loxxy
         | 
| 6 6 | 
             
              module Ast
         | 
| 7 7 | 
             
                class LoxBinaryExpr < LoxCompoundExpr
         | 
| 8 | 
            -
                  # @return [Symbol]
         | 
| 8 | 
            +
                  # @return [Symbol] message name to be sent to receiver
         | 
| 9 9 | 
             
                  attr_reader :operator
         | 
| 10 10 |  | 
| 11 11 | 
             
                  # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'lox_compound_expr'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Loxxy
         | 
| 6 | 
            +
              module Ast
         | 
| 7 | 
            +
                class LoxGroupingExpr < LoxCompoundExpr
         | 
| 8 | 
            +
                  # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
         | 
| 9 | 
            +
                  # @param subExpr [Loxxy::Ast::LoxNode]
         | 
| 10 | 
            +
                  def initialize(aPosition, subExpr)
         | 
| 11 | 
            +
                    super(aPosition, [subExpr])
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  # Part of the 'visitee' role in Visitor design pattern.
         | 
| 15 | 
            +
                  # @param visitor [Ast::ASTVisitor] the visitor
         | 
| 16 | 
            +
                  def accept(visitor)
         | 
| 17 | 
            +
                    visitor.visit_grouping_expr(self)
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  alias operands subnodes
         | 
| 21 | 
            +
                end # class
         | 
| 22 | 
            +
              end # module
         | 
| 23 | 
            +
            end # module
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'lox_compound_expr'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Loxxy
         | 
| 6 | 
            +
              module Ast
         | 
| 7 | 
            +
                class LoxLogicalExpr < LoxCompoundExpr
         | 
| 8 | 
            +
                  # @return [Symbol] message name to be sent to receiver
         | 
| 9 | 
            +
                  attr_reader :operator
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
         | 
| 12 | 
            +
                  # @param operand1 [Loxxy::Ast::LoxNode]
         | 
| 13 | 
            +
                  # @param operand2 [Loxxy::Ast::LoxNode]
         | 
| 14 | 
            +
                  def initialize(aPosition, anOperator, operand1, operand2)
         | 
| 15 | 
            +
                    super(aPosition, [operand1, operand2])
         | 
| 16 | 
            +
                    @operator = anOperator
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  # Part of the 'visitee' role in Visitor design pattern.
         | 
| 20 | 
            +
                  # @param visitor [Ast::ASTVisitor] the visitor
         | 
| 21 | 
            +
                  def accept(visitor)
         | 
| 22 | 
            +
                    visitor.visit_logical_expr(self)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  alias operands subnodes
         | 
| 26 | 
            +
                end # class
         | 
| 27 | 
            +
              end # module
         | 
| 28 | 
            +
            end # module
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'lox_compound_expr'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Loxxy
         | 
| 6 | 
            +
              module Ast
         | 
| 7 | 
            +
                class LoxUnaryExpr < LoxCompoundExpr
         | 
| 8 | 
            +
                  # @return [Symbol] message name to be sent to receiver
         | 
| 9 | 
            +
                  attr_reader :operator
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
         | 
| 12 | 
            +
                  # @param operand [Loxxy::Ast::LoxNode]
         | 
| 13 | 
            +
                  def initialize(aPosition, anOperator, operand)
         | 
| 14 | 
            +
                    super(aPosition, [operand])
         | 
| 15 | 
            +
                    @operator = anOperator
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  # Part of the 'visitee' role in Visitor design pattern.
         | 
| 19 | 
            +
                  # @param visitor [Ast::ASTVisitor] the visitor
         | 
| 20 | 
            +
                  def accept(visitor)
         | 
| 21 | 
            +
                    visitor.visit_unary_expr(self)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  alias operands subnodes
         | 
| 25 | 
            +
                end # class
         | 
| 26 | 
            +
              end # module
         | 
| 27 | 
            +
            end # module
         | 
| @@ -22,7 +22,7 @@ module Loxxy | |
| 22 22 | 
             
                    @stack = []
         | 
| 23 23 | 
             
                  end
         | 
| 24 24 |  | 
| 25 | 
            -
                  # Given an abstract syntax parse tree visitor,  | 
| 25 | 
            +
                  # Given an abstract syntax parse tree visitor, launch the visit
         | 
| 26 26 | 
             
                  # and execute the visit events in the output stream.
         | 
| 27 27 | 
             
                  # @param aVisitor [AST::ASTVisitor]
         | 
| 28 28 | 
             
                  # @return [Loxxy::Datatype::BuiltinDatatype]
         | 
| @@ -40,12 +40,43 @@ module Loxxy | |
| 40 40 | 
             
                    @ostream.print tos.to_str
         | 
| 41 41 | 
             
                  end
         | 
| 42 42 |  | 
| 43 | 
            +
                  def after_logical_expr(aLogicalExpr, visitor)
         | 
| 44 | 
            +
                    op = aLogicalExpr.operator
         | 
| 45 | 
            +
                    operand1 = stack.pop # only first operand was evaluated
         | 
| 46 | 
            +
                    result = nil
         | 
| 47 | 
            +
                    if ((op == :and) && operand1.falsey?) || ((op == :or) && operand1.truthy?)
         | 
| 48 | 
            +
                      result = operand1
         | 
| 49 | 
            +
                    else
         | 
| 50 | 
            +
                      raw_operand2 = aLogicalExpr.subnodes[1]
         | 
| 51 | 
            +
                      raw_operand2.accept(visitor) # Visit means operand2 is evaluated
         | 
| 52 | 
            +
                      operand2 = stack.pop
         | 
| 53 | 
            +
                      result = logical_2nd_arg(operand2)
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    stack.push result
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  def logical_2nd_arg(operand2)
         | 
| 60 | 
            +
                    case operand2
         | 
| 61 | 
            +
                       when false
         | 
| 62 | 
            +
                         False.instance # Convert to Lox equivalent
         | 
| 63 | 
            +
                       when nil
         | 
| 64 | 
            +
                         Nil.instance # Convert to Lox equivalent
         | 
| 65 | 
            +
                       when true
         | 
| 66 | 
            +
                         True.instance # Convert to Lox equivalent
         | 
| 67 | 
            +
                       when Proc
         | 
| 68 | 
            +
                         # Second operand wasn't yet evaluated...
         | 
| 69 | 
            +
                         operand2.call
         | 
| 70 | 
            +
                       else
         | 
| 71 | 
            +
                         operand2
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 43 75 | 
             
                  def after_binary_expr(aBinaryExpr)
         | 
| 44 76 | 
             
                    op = aBinaryExpr.operator
         | 
| 45 77 | 
             
                    operand2 = stack.pop
         | 
| 46 78 | 
             
                    operand1 = stack.pop
         | 
| 47 | 
            -
                     | 
| 48 | 
            -
                    if implemented && operand1.respond_to?(op)
         | 
| 79 | 
            +
                    if operand1.respond_to?(op)
         | 
| 49 80 | 
             
                      stack.push operand1.send(op, operand2)
         | 
| 50 81 | 
             
                    else
         | 
| 51 82 | 
             
                      msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
         | 
| @@ -53,6 +84,21 @@ module Loxxy | |
| 53 84 | 
             
                    end
         | 
| 54 85 | 
             
                  end
         | 
| 55 86 |  | 
| 87 | 
            +
                  def after_unary_expr(anUnaryExpr)
         | 
| 88 | 
            +
                    op = anUnaryExpr.operator
         | 
| 89 | 
            +
                    operand = stack.pop
         | 
| 90 | 
            +
                    if operand.respond_to?(op)
         | 
| 91 | 
            +
                      stack.push operand.send(op)
         | 
| 92 | 
            +
                    else
         | 
| 93 | 
            +
                      msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
         | 
| 94 | 
            +
                      raise StandardError, msg1
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  def after_grouping_expr(_groupingExpr)
         | 
| 99 | 
            +
                    # Do nothing: work was already done by visiting /evaluating the subexpression
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
             | 
| 56 102 | 
             
                  # @param literalExpr [Ast::LoxLiteralExpr]
         | 
| 57 103 | 
             
                  def before_literal_expr(literalExpr)
         | 
| 58 104 | 
             
                    stack.push(literalExpr.literal)
         | 
| @@ -14,6 +14,48 @@ module Loxxy | |
| 14 14 | 
             
                    @value = validated_value(aValue)
         | 
| 15 15 | 
             
                  end
         | 
| 16 16 |  | 
| 17 | 
            +
                  # Is the value considered falsey in Lox?
         | 
| 18 | 
            +
                  # Rule: false and nil are falsey and everything else is truthy.
         | 
| 19 | 
            +
                  # This test used in conditional statements (i.e. if, while)
         | 
| 20 | 
            +
                  def falsey?
         | 
| 21 | 
            +
                    false # Default implementation
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  # Is the value considered truthy in Lox?
         | 
| 25 | 
            +
                  # Rule: false and nil are falsey and everything else is truthy.
         | 
| 26 | 
            +
                  # This test used in conditional statements (i.e. if, while)
         | 
| 27 | 
            +
                  def truthy?
         | 
| 28 | 
            +
                    true # Default implementation
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  # Check for inequality of this object with another Lox object
         | 
| 32 | 
            +
                  # @param other [Datatype::BuiltinDatatype, Object]
         | 
| 33 | 
            +
                  # @return [Datatype::Boolean]
         | 
| 34 | 
            +
                  def !=(other)
         | 
| 35 | 
            +
                    !(self == other)
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  # Negation ('not')
         | 
| 39 | 
            +
                  # Returns a boolean with opposite truthiness value.
         | 
| 40 | 
            +
                  # @return [Datatype::Boolean]
         | 
| 41 | 
            +
                  def !
         | 
| 42 | 
            +
                    falsey? ? True.instance : False.instance
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  # Returns the first falsey argument (if any),
         | 
| 46 | 
            +
                  # otherwise returns the last truthy argument.
         | 
| 47 | 
            +
                  # @param operand2 [Loxxy::Datatype::BuiltinDatatype, Proc]
         | 
| 48 | 
            +
                  def and(operand2)
         | 
| 49 | 
            +
                    falsey? ? self : logical_2nd_arg(operand2)
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  # Returns the first truthy argument (if any),
         | 
| 53 | 
            +
                  # otherwise returns the last falsey argument.
         | 
| 54 | 
            +
                  # @param operand2 [Loxxy::Datatype::BuiltinDatatype, Proc]
         | 
| 55 | 
            +
                  def or(operand2)
         | 
| 56 | 
            +
                    truthy? ? self : logical_2nd_arg(operand2)
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 17 59 | 
             
                  # Method called from Lox to obtain the text representation of the boolean.
         | 
| 18 60 | 
             
                  # @return [String]
         | 
| 19 61 | 
             
                  def to_str
         | 
| @@ -25,6 +67,19 @@ module Loxxy | |
| 25 67 | 
             
                  def validated_value(aValue)
         | 
| 26 68 | 
             
                    aValue
         | 
| 27 69 | 
             
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  def logical_2nd_arg(operand2)
         | 
| 72 | 
            +
                    case operand2
         | 
| 73 | 
            +
                      when false
         | 
| 74 | 
            +
                        False.instance # Convert to Lox equivalent
         | 
| 75 | 
            +
                      when nil
         | 
| 76 | 
            +
                        Nil.instance # Convert to Lox equivalent
         | 
| 77 | 
            +
                      when true
         | 
| 78 | 
            +
                        True.instance # Convert to Lox equivalent
         | 
| 79 | 
            +
                      else
         | 
| 80 | 
            +
                        operand2
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                  end
         | 
| 28 83 | 
             
                end # class
         | 
| 29 84 | 
             
              end # module
         | 
| 30 85 | 
             
            end # module
         | 
    
        data/lib/loxxy/datatype/false.rb
    CHANGED
    
    | @@ -19,6 +19,28 @@ module Loxxy | |
| 19 19 | 
             
                  def false?
         | 
| 20 20 | 
             
                    true
         | 
| 21 21 | 
             
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  # Is the value considered falsey in Lox?
         | 
| 24 | 
            +
                  # Rule: false and nil are falsey and everything else is truthy.
         | 
| 25 | 
            +
                  # This test used in conditional statements (i.e. if, while)
         | 
| 26 | 
            +
                  def falsey?
         | 
| 27 | 
            +
                    true
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # Is the value considered truthy in Lox?
         | 
| 31 | 
            +
                  # Rule: false and nil are falsey and everything else is truthy.
         | 
| 32 | 
            +
                  # This test used in conditional statements (i.e. if, while)
         | 
| 33 | 
            +
                  def truthy?
         | 
| 34 | 
            +
                    false
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  # Check for equality of a Lox False with another Lox object
         | 
| 38 | 
            +
                  # @param other [Datatype::BuiltinDatatype, FalseClass, Object]
         | 
| 39 | 
            +
                  # @return [Datatype::Boolean]
         | 
| 40 | 
            +
                  def ==(other)
         | 
| 41 | 
            +
                    falsey = other.kind_of?(False) || other.kind_of?(FalseClass)
         | 
| 42 | 
            +
                    falsey ? True.instance : False.instance
         | 
| 43 | 
            +
                  end
         | 
| 22 44 | 
             
                end # class
         | 
| 23 45 |  | 
| 24 46 | 
             
                False.instance.freeze # Make the sole instance immutable
         | 
| @@ -7,8 +7,8 @@ module Loxxy | |
| 7 7 | 
             
              module Datatype
         | 
| 8 8 | 
             
                # Class for representing a Lox string of characters value.
         | 
| 9 9 | 
             
                class LXString < BuiltinDatatype
         | 
| 10 | 
            -
                  #  | 
| 11 | 
            -
                  # @param other [Datatype::LxString, String]
         | 
| 10 | 
            +
                  # Check the equality of a Lox String with another Lox object.
         | 
| 11 | 
            +
                  # @param other [Datatype::LxString, String, Object]
         | 
| 12 12 | 
             
                  # @return [Datatype::Boolean]
         | 
| 13 13 | 
             
                  def ==(other)
         | 
| 14 14 | 
             
                    case other
         | 
| @@ -17,8 +17,7 @@ module Loxxy | |
| 17 17 | 
             
                    when String
         | 
| 18 18 | 
             
                      (value == other) ? True.instance : False.instance
         | 
| 19 19 | 
             
                    else
         | 
| 20 | 
            -
                       | 
| 21 | 
            -
                      raise StandardError, err_msg
         | 
| 20 | 
            +
                      False.instance
         | 
| 22 21 | 
             
                    end
         | 
| 23 22 | 
             
                  end
         | 
| 24 23 |  | 
    
        data/lib/loxxy/datatype/nil.rb
    CHANGED
    
    | @@ -14,6 +14,28 @@ module Loxxy | |
| 14 14 | 
             
                    super(nil)
         | 
| 15 15 | 
             
                  end
         | 
| 16 16 |  | 
| 17 | 
            +
                  # Check the equality with another object.
         | 
| 18 | 
            +
                  # @param other [Datatype::BuiltinDatatype, NilClass, Object]
         | 
| 19 | 
            +
                  # @return [Datatype::Boolean]
         | 
| 20 | 
            +
                  def ==(other)
         | 
| 21 | 
            +
                    is_nil = other.kind_of?(Nil) || other.kind_of?(NilClass)
         | 
| 22 | 
            +
                    is_nil ? True.instance : False.instance
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  # Is the value considered falsey in Lox?
         | 
| 26 | 
            +
                  # Rule: false and nil are falsey and everything else is truthy.
         | 
| 27 | 
            +
                  # This test used in conditional statements (i.e. if, while)
         | 
| 28 | 
            +
                  def falsey?
         | 
| 29 | 
            +
                    true
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  # Is the value considered truthy in Lox?
         | 
| 33 | 
            +
                  # Rule: false and nil are falsey and everything else is truthy.
         | 
| 34 | 
            +
                  # This test used in conditional statements (i.e. if, while)
         | 
| 35 | 
            +
                  def truthy?
         | 
| 36 | 
            +
                    false
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 17 39 | 
             
                  # Method called from Lox to obtain the text representation of nil.
         | 
| 18 40 | 
             
                  # @return [String]
         | 
| 19 41 | 
             
                  def to_str
         | 
| @@ -7,8 +7,78 @@ module Loxxy | |
| 7 7 | 
             
              module Datatype
         | 
| 8 8 | 
             
                # Class for representing a Lox numeric value.
         | 
| 9 9 | 
             
                class Number < BuiltinDatatype
         | 
| 10 | 
            -
                  #  | 
| 11 | 
            -
                  #  | 
| 10 | 
            +
                  # Perform the addition of two Lox numbers or
         | 
| 11 | 
            +
                  # one Lox number and a Ruby Numeric
         | 
| 12 | 
            +
                  # @param other [Loxxy::Datatype::Number, Numeric]
         | 
| 13 | 
            +
                  # @return [Loxxy::Datatype::Number]
         | 
| 14 | 
            +
                  def +(other)
         | 
| 15 | 
            +
                    case other
         | 
| 16 | 
            +
                    when Number
         | 
| 17 | 
            +
                      self.class.new(value + other.value)
         | 
| 18 | 
            +
                    when Numeric
         | 
| 19 | 
            +
                      self.class.new(value + other)
         | 
| 20 | 
            +
                    else
         | 
| 21 | 
            +
                      err_msg = "`+': #{other.class} can't be coerced into #{self.class} (TypeError)"
         | 
| 22 | 
            +
                      raise TypeError, err_msg
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  # Perform the subtraction of two Lox numbers or
         | 
| 27 | 
            +
                  # one Lox number and a Ruby Numeric
         | 
| 28 | 
            +
                  # @param other [Loxxy::Datatype::Number, Numeric]
         | 
| 29 | 
            +
                  # @return [Loxxy::Datatype::Number]
         | 
| 30 | 
            +
                  def -(other)
         | 
| 31 | 
            +
                    case other
         | 
| 32 | 
            +
                    when Number
         | 
| 33 | 
            +
                      self.class.new(value - other.value)
         | 
| 34 | 
            +
                    when Numeric
         | 
| 35 | 
            +
                      self.class.new(value - other)
         | 
| 36 | 
            +
                    else
         | 
| 37 | 
            +
                      err_msg = "`-': #{other.class} can't be coerced into #{self.class} (TypeError)"
         | 
| 38 | 
            +
                      raise TypeError, err_msg
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  # Perform the multiplication of two Lox numbers or
         | 
| 43 | 
            +
                  # one Lox number and a Ruby Numeric
         | 
| 44 | 
            +
                  # @param other [Loxxy::Datatype::Number, Numeric]
         | 
| 45 | 
            +
                  # @return [Loxxy::Datatype::Number]
         | 
| 46 | 
            +
                  def *(other)
         | 
| 47 | 
            +
                    case other
         | 
| 48 | 
            +
                    when Number
         | 
| 49 | 
            +
                      self.class.new(value * other.value)
         | 
| 50 | 
            +
                    when Numeric
         | 
| 51 | 
            +
                      self.class.new(value * other)
         | 
| 52 | 
            +
                    else
         | 
| 53 | 
            +
                      err_msg = "'*': Operands must be numbers."
         | 
| 54 | 
            +
                      raise TypeError, err_msg
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  # Perform the division of two Lox numbers or
         | 
| 59 | 
            +
                  # one Lox number and a Ruby Numeric
         | 
| 60 | 
            +
                  # @param other [Loxxy::Datatype::Number, Numeric]
         | 
| 61 | 
            +
                  # @return [Loxxy::Datatype::Number]
         | 
| 62 | 
            +
                  def /(other)
         | 
| 63 | 
            +
                    case other
         | 
| 64 | 
            +
                    when Number
         | 
| 65 | 
            +
                      self.class.new(value / other.value)
         | 
| 66 | 
            +
                    when Numeric
         | 
| 67 | 
            +
                      self.class.new(value / other)
         | 
| 68 | 
            +
                    else
         | 
| 69 | 
            +
                      err_msg = "'/': Operands must be numbers."
         | 
| 70 | 
            +
                      raise TypeError, err_msg
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  # Unary minus (return value with changed sign)
         | 
| 75 | 
            +
                  # @return [Loxxy::Datatype::Number]
         | 
| 76 | 
            +
                  def -@
         | 
| 77 | 
            +
                    self.class.new(-value)
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  # Check the equality of a Lox number object with another object
         | 
| 81 | 
            +
                  # @param other [Datatype::BuiltinDatatype, Numeric, Object]
         | 
| 12 82 | 
             
                  # @return [Datatype::Boolean]
         | 
| 13 83 | 
             
                  def ==(other)
         | 
| 14 84 | 
             
                    case other
         | 
| @@ -17,27 +87,56 @@ module Loxxy | |
| 17 87 | 
             
                    when Numeric
         | 
| 18 88 | 
             
                      (value == other) ? True.instance : False.instance
         | 
| 19 89 | 
             
                    else
         | 
| 20 | 
            -
                       | 
| 21 | 
            -
                      raise StandardError, err_msg
         | 
| 90 | 
            +
                      False.instance
         | 
| 22 91 | 
             
                    end
         | 
| 23 92 | 
             
                  end
         | 
| 24 93 |  | 
| 25 | 
            -
                  #  | 
| 26 | 
            -
                  #  | 
| 27 | 
            -
                  # @ | 
| 28 | 
            -
                   | 
| 29 | 
            -
             | 
| 30 | 
            -
                    case numeric
         | 
| 94 | 
            +
                  # Check whether this Lox number has a greater value than given argument.
         | 
| 95 | 
            +
                  # @param other [Datatype::Number, Numeric]
         | 
| 96 | 
            +
                  # @return [Datatype::Boolean]
         | 
| 97 | 
            +
                  def >(other)
         | 
| 98 | 
            +
                    case other
         | 
| 31 99 | 
             
                    when Number
         | 
| 32 | 
            -
                       | 
| 100 | 
            +
                      (value > other.value) ? True.instance : False.instance
         | 
| 33 101 | 
             
                    when Numeric
         | 
| 34 | 
            -
                       | 
| 102 | 
            +
                      (value > other) ? True.instance : False.instance
         | 
| 35 103 | 
             
                    else
         | 
| 36 | 
            -
                       | 
| 37 | 
            -
                      raise  | 
| 104 | 
            +
                      msg = "'>': Operands must be numbers."
         | 
| 105 | 
            +
                      raise StandardError, msg
         | 
| 38 106 | 
             
                    end
         | 
| 39 107 | 
             
                  end
         | 
| 40 108 |  | 
| 109 | 
            +
                  # Check whether this Lox number has a greater or equal value
         | 
| 110 | 
            +
                  # than given argument.
         | 
| 111 | 
            +
                  # @param other [Datatype::Number, Numeric]
         | 
| 112 | 
            +
                  # @return [Datatype::Boolean]
         | 
| 113 | 
            +
                  def >=(other)
         | 
| 114 | 
            +
                    case other
         | 
| 115 | 
            +
                    when Number
         | 
| 116 | 
            +
                      (value >= other.value) ? True.instance : False.instance
         | 
| 117 | 
            +
                    when Numeric
         | 
| 118 | 
            +
                      (value >= other) ? True.instance : False.instance
         | 
| 119 | 
            +
                    else
         | 
| 120 | 
            +
                      msg = "'>': Operands must be numbers."
         | 
| 121 | 
            +
                      raise StandardError, msg
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                  # Check whether this Lox number has a lesser value than given argument.
         | 
| 126 | 
            +
                  # @param other [Datatype::Number, Numeric]
         | 
| 127 | 
            +
                  # @return [Datatype::Boolean]
         | 
| 128 | 
            +
                  def <(other)
         | 
| 129 | 
            +
                    !(self >= other)
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  # Check whether this Lox number has a lesser or equal value
         | 
| 133 | 
            +
                  # than given argument.
         | 
| 134 | 
            +
                  # @param other [Datatype::Number, Numeric]
         | 
| 135 | 
            +
                  # @return [Datatype::Boolean]
         | 
| 136 | 
            +
                  def <=(other)
         | 
| 137 | 
            +
                    !(self > other)
         | 
| 138 | 
            +
                  end
         | 
| 139 | 
            +
             | 
| 41 140 | 
             
                  protected
         | 
| 42 141 |  | 
| 43 142 | 
             
                  def validated_value(aValue)
         | 
    
        data/lib/loxxy/datatype/true.rb
    CHANGED
    
    | @@ -19,6 +19,14 @@ module Loxxy | |
| 19 19 | 
             
                  def true?
         | 
| 20 20 | 
             
                    true
         | 
| 21 21 | 
             
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  # Check for equality of a Lox True with another Lox object / Ruby true
         | 
| 24 | 
            +
                  # @param other [Datatype::True, TrueClass, Object]
         | 
| 25 | 
            +
                  # @return [Datatype::Boolean]
         | 
| 26 | 
            +
                  def ==(other)
         | 
| 27 | 
            +
                    thruthy = other.kind_of?(True) || other.kind_of?(TrueClass)
         | 
| 28 | 
            +
                    thruthy ? True.instance : False.instance
         | 
| 29 | 
            +
                  end
         | 
| 22 30 | 
             
                end # class
         | 
| 23 31 |  | 
| 24 32 | 
             
                True.instance.freeze # Make the sole instance immutable
         | 
| @@ -121,7 +121,7 @@ module Loxxy | |
| 121 121 | 
             
                  rule('multiplicative_plus' => 'multOp unary').as 'multiplicative_plus_end'
         | 
| 122 122 | 
             
                  rule('multOp' => 'SLASH')
         | 
| 123 123 | 
             
                  rule('multOp' => 'STAR')
         | 
| 124 | 
            -
                  rule('unary' => 'unaryOp unary')
         | 
| 124 | 
            +
                  rule('unary' => 'unaryOp unary').as 'unary_expr'
         | 
| 125 125 | 
             
                  rule('unary' => 'call')
         | 
| 126 126 | 
             
                  rule('unaryOp' => 'BANG')
         | 
| 127 127 | 
             
                  rule('unaryOp' => 'MINUS')
         | 
| @@ -138,7 +138,7 @@ module Loxxy | |
| 138 138 | 
             
                  rule('primary' => 'NUMBER').as 'literal_expr'
         | 
| 139 139 | 
             
                  rule('primary' => 'STRING').as 'literal_expr'
         | 
| 140 140 | 
             
                  rule('primary' => 'IDENTIFIER')
         | 
| 141 | 
            -
                  rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN')
         | 
| 141 | 
            +
                  rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN').as 'grouping_expr'
         | 
| 142 142 | 
             
                  rule('primary' => 'SUPER DOT IDENTIFIER')
         | 
| 143 143 |  | 
| 144 144 | 
             
                  # Utility rules
         | 
    
        data/lib/loxxy/version.rb
    CHANGED
    
    
| @@ -51,6 +51,11 @@ module Loxxy | |
| 51 51 | 
             
                      result = subject == ''
         | 
| 52 52 | 
             
                      expect(result).to be_false
         | 
| 53 53 | 
             
                    end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    it 'performs the concatenation with another string' do
         | 
| 56 | 
            +
                      concatenation = LXString.new('str') + LXString.new('ing')
         | 
| 57 | 
            +
                      expect(concatenation == 'string').to be_true
         | 
| 58 | 
            +
                    end
         | 
| 54 59 | 
             
                  end
         | 
| 55 60 | 
             
                end # describe
         | 
| 56 61 | 
             
              end # module
         | 
| @@ -41,6 +41,16 @@ module Loxxy | |
| 41 41 | 
             
                    it 'should give its display representation' do
         | 
| 42 42 | 
             
                      expect(subject.to_str).to eq(sample_value.to_s)
         | 
| 43 43 | 
             
                    end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    it 'should compute the addition with another number' do
         | 
| 46 | 
            +
                      addition = subject + Number.new(10)
         | 
| 47 | 
            +
                      expect(addition == -2.34).to be_true
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    it 'should compute the subtraction with another number' do
         | 
| 51 | 
            +
                      subtraction = subject - Number.new(10)
         | 
| 52 | 
            +
                      expect(subtraction == -22.34).to be_true
         | 
| 53 | 
            +
                    end
         | 
| 44 54 | 
             
                  end
         | 
| 45 55 | 
             
                end # describe
         | 
| 46 56 | 
             
              end # module
         | 
| @@ -262,12 +262,12 @@ LOX_END | |
| 262 262 | 
             
                  end # context
         | 
| 263 263 |  | 
| 264 264 | 
             
                  context 'Parsing logical expressions' do
         | 
| 265 | 
            -
                    it 'should parse the logical operations  | 
| 265 | 
            +
                    it 'should parse the logical operations between two sub-expression' do
         | 
| 266 266 | 
             
                      %w[or and].each do |connector|
         | 
| 267 267 | 
             
                        input = "5 > 2 #{connector} 3 <= 4;"
         | 
| 268 268 | 
             
                        ptree = subject.parse(input)
         | 
| 269 269 | 
             
                        expr = ptree.root
         | 
| 270 | 
            -
                        expect(expr).to be_kind_of(Ast:: | 
| 270 | 
            +
                        expect(expr).to be_kind_of(Ast::LoxLogicalExpr)
         | 
| 271 271 | 
             
                        expect(expr.operator).to eq(connector.to_sym)
         | 
| 272 272 | 
             
                        expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
         | 
| 273 273 | 
             
                        expect(expr.operands[0].operator).to eq(:>)
         | 
| @@ -284,9 +284,9 @@ LOX_END | |
| 284 284 | 
             
                      input = '4 > 3 and 1 < 2 or 4 >= 5;'
         | 
| 285 285 | 
             
                      ptree = subject.parse(input)
         | 
| 286 286 | 
             
                      expr = ptree.root
         | 
| 287 | 
            -
                      expect(expr).to be_kind_of(Ast:: | 
| 287 | 
            +
                      expect(expr).to be_kind_of(Ast::LoxLogicalExpr)
         | 
| 288 288 | 
             
                      expect(expr.operator).to eq(:or) # or has lower precedence than and
         | 
| 289 | 
            -
                      expect(expr.operands[0]).to be_kind_of(Ast:: | 
| 289 | 
            +
                      expect(expr.operands[0]).to be_kind_of(Ast::LoxLogicalExpr)
         | 
| 290 290 | 
             
                      expect(expr.operands[0].operator).to eq(:and)
         | 
| 291 291 | 
             
                      conjuncts = expr.operands[0].operands
         | 
| 292 292 | 
             
                      expect(conjuncts[0]).to be_kind_of(Ast::LoxBinaryExpr)
         | 
    
        data/spec/interpreter_spec.rb
    CHANGED
    
    | @@ -25,6 +25,32 @@ module Loxxy | |
| 25 25 | 
             
                  end
         | 
| 26 26 | 
             
                end # context
         | 
| 27 27 |  | 
| 28 | 
            +
                context 'Evaluating arithmetic operations code:' do
         | 
| 29 | 
            +
                  it 'should evaluate an addition of two numbers' do
         | 
| 30 | 
            +
                    result = subject.evaluate('123 + 456; // => 579')
         | 
| 31 | 
            +
                    expect(result).to be_kind_of(Loxxy::Datatype::Number)
         | 
| 32 | 
            +
                    expect(result == 579).to be_true
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  it 'should evaluate a subtraction of two numbers' do
         | 
| 36 | 
            +
                    result = subject.evaluate('4 - 3; // => 1')
         | 
| 37 | 
            +
                    expect(result).to be_kind_of(Loxxy::Datatype::Number)
         | 
| 38 | 
            +
                    expect(result == 1).to be_true
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  it 'should evaluate a multiplication of two numbers' do
         | 
| 42 | 
            +
                    result = subject.evaluate('5 * 3; // => 15')
         | 
| 43 | 
            +
                    expect(result).to be_kind_of(Loxxy::Datatype::Number)
         | 
| 44 | 
            +
                    expect(result == 15).to be_true
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  it 'should evaluate a division of two numbers' do
         | 
| 48 | 
            +
                    result = subject.evaluate('8 / 2; // => 4')
         | 
| 49 | 
            +
                    expect(result).to be_kind_of(Loxxy::Datatype::Number)
         | 
| 50 | 
            +
                    expect(result == 4).to be_true
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end # context
         | 
| 53 | 
            +
             | 
| 28 54 | 
             
                context 'Evaluating Lox code:' do
         | 
| 29 55 | 
             
                  let(:hello_world) { 'print "Hello, world!";' }
         | 
| 30 56 |  | 
| @@ -33,18 +59,170 @@ module Loxxy | |
| 33 59 | 
             
                    expect(result).to be_kind_of(Loxxy::Datatype::True)
         | 
| 34 60 | 
             
                  end
         | 
| 35 61 |  | 
| 36 | 
            -
                  it 'should evaluate arithmetic operation' do
         | 
| 37 | 
            -
                    result = subject.evaluate('123 + 456; // => 579')
         | 
| 38 | 
            -
                    expect(result).to be_kind_of(Loxxy::Datatype::Number)
         | 
| 39 | 
            -
                    expect(result == 579).to be_true
         | 
| 40 | 
            -
                  end
         | 
| 41 | 
            -
             | 
| 42 62 | 
             
                  it 'should evaluate string concatenation' do
         | 
| 43 63 | 
             
                    result = subject.evaluate('"str" + "ing"; // => "string"')
         | 
| 44 64 | 
             
                    expect(result).to be_kind_of(Loxxy::Datatype::LXString)
         | 
| 45 65 | 
             
                    expect(result == 'string').to be_true
         | 
| 46 66 | 
             
                  end
         | 
| 47 67 |  | 
| 68 | 
            +
                  it 'should perform the equality tests of two values' do
         | 
| 69 | 
            +
                    [
         | 
| 70 | 
            +
                      ['nil == nil;', true],
         | 
| 71 | 
            +
                      ['true == true;', true],
         | 
| 72 | 
            +
                      ['true == false;', false],
         | 
| 73 | 
            +
                      ['1 == 1;', true],
         | 
| 74 | 
            +
                      ['1 == 2;', false],
         | 
| 75 | 
            +
                      ['0 == 0;', true],
         | 
| 76 | 
            +
                      ['"str" == "str";', true],
         | 
| 77 | 
            +
                      ['"str" == "ing";', false],
         | 
| 78 | 
            +
                      ['"" == "";', true],
         | 
| 79 | 
            +
                      ['nil == false;', false],
         | 
| 80 | 
            +
                      ['false == 0;', false],
         | 
| 81 | 
            +
                      ['0 == "0";', false]
         | 
| 82 | 
            +
                    ].each do |(source, predicted)|
         | 
| 83 | 
            +
                      lox = Loxxy::Interpreter.new
         | 
| 84 | 
            +
                      result = lox.evaluate(source)
         | 
| 85 | 
            +
                      expect(result.value == predicted).to be_truthy
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  it 'should perform the inequality test of two values' do
         | 
| 90 | 
            +
                    [
         | 
| 91 | 
            +
                      ['nil != nil;', false],
         | 
| 92 | 
            +
                      ['true != true;', false],
         | 
| 93 | 
            +
                      ['true != false;', true],
         | 
| 94 | 
            +
                      ['1 != 1;', false],
         | 
| 95 | 
            +
                      ['1 != 2;', true],
         | 
| 96 | 
            +
                      ['0 != 0;', false],
         | 
| 97 | 
            +
                      ['"str" != "str";', false],
         | 
| 98 | 
            +
                      ['"str" != "ing";', true],
         | 
| 99 | 
            +
                      ['"" != "";', false],
         | 
| 100 | 
            +
                      ['nil != false;', true],
         | 
| 101 | 
            +
                      ['false != 0;', true],
         | 
| 102 | 
            +
                      ['0 != "0";', true]
         | 
| 103 | 
            +
                    ].each do |(source, predicted)|
         | 
| 104 | 
            +
                      lox = Loxxy::Interpreter.new
         | 
| 105 | 
            +
                      result = lox.evaluate(source)
         | 
| 106 | 
            +
                      expect(result.value == predicted).to be_truthy
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  it 'should evaluate a comparison of two numbers' do
         | 
| 111 | 
            +
                    [
         | 
| 112 | 
            +
                      ['1 < 2;', true],
         | 
| 113 | 
            +
                      ['2 < 2;', false],
         | 
| 114 | 
            +
                      ['2 < 1;', false],
         | 
| 115 | 
            +
                      ['1 <= 2;', true],
         | 
| 116 | 
            +
                      ['2 <= 2;', true],
         | 
| 117 | 
            +
                      ['2 <= 1;', false],
         | 
| 118 | 
            +
                      ['1 > 2;', false],
         | 
| 119 | 
            +
                      ['2 > 2;', false],
         | 
| 120 | 
            +
                      ['2 > 1;', true],
         | 
| 121 | 
            +
                      ['1 >= 2;', false],
         | 
| 122 | 
            +
                      ['2 >= 2;', true],
         | 
| 123 | 
            +
                      ['2 >= 1;', true],
         | 
| 124 | 
            +
                      ['0 < -0;', false],
         | 
| 125 | 
            +
                      ['-0 < 0;', false],
         | 
| 126 | 
            +
                      ['0 > -0;', false],
         | 
| 127 | 
            +
                      ['-0 > 0;', false],
         | 
| 128 | 
            +
                      ['0 <= -0;', true],
         | 
| 129 | 
            +
                      ['-0 <= 0;', true],
         | 
| 130 | 
            +
                      ['0 >= -0;', true],
         | 
| 131 | 
            +
                      ['-0 >= 0;', true]
         | 
| 132 | 
            +
                    ].each do |(source, predicted)|
         | 
| 133 | 
            +
                      lox = Loxxy::Interpreter.new
         | 
| 134 | 
            +
                      result = lox.evaluate(source)
         | 
| 135 | 
            +
                      expect(result.value == predicted).to be_truthy
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                  it 'should evaluate the change sign of a number' do
         | 
| 140 | 
            +
                    [
         | 
| 141 | 
            +
                      ['- 3;', -3],
         | 
| 142 | 
            +
                      ['- - 3;', 3],
         | 
| 143 | 
            +
                      ['- - - 3;', -3]
         | 
| 144 | 
            +
                    ].each do |(source, predicted)|
         | 
| 145 | 
            +
                      lox = Loxxy::Interpreter.new
         | 
| 146 | 
            +
                      result = lox.evaluate(source)
         | 
| 147 | 
            +
                      expect(result.value == predicted).to be_truthy
         | 
| 148 | 
            +
                    end
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                  it 'should evaluate the negation of an object' do
         | 
| 152 | 
            +
                    [
         | 
| 153 | 
            +
                      ['!true;', false],
         | 
| 154 | 
            +
                      ['!false;', true],
         | 
| 155 | 
            +
                      ['!!true;', true],
         | 
| 156 | 
            +
                      ['!123;', false],
         | 
| 157 | 
            +
                      ['!0;', false],
         | 
| 158 | 
            +
                      ['!nil;', true],
         | 
| 159 | 
            +
                      ['!"";', false]
         | 
| 160 | 
            +
                    ].each do |(source, predicted)|
         | 
| 161 | 
            +
                      lox = Loxxy::Interpreter.new
         | 
| 162 | 
            +
                      result = lox.evaluate(source)
         | 
| 163 | 
            +
                      expect(result.value == predicted).to be_truthy
         | 
| 164 | 
            +
                    end
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                  it 'should evaluate the "conjunction" of two values' do
         | 
| 168 | 
            +
                    [
         | 
| 169 | 
            +
                      # Return the first falsey argument
         | 
| 170 | 
            +
                      ['false and 1;', false],
         | 
| 171 | 
            +
                      ['nil and 1;', nil],
         | 
| 172 | 
            +
                      ['true and 1;', 1],
         | 
| 173 | 
            +
                      ['1 and 2 and false;', false],
         | 
| 174 | 
            +
                      ['1 and 2 and nil;', nil],
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                      # Return the last argument if all are truthy
         | 
| 177 | 
            +
                      ['1 and true;', true],
         | 
| 178 | 
            +
                      ['0 and true;', true],
         | 
| 179 | 
            +
                      ['"false" and 0;', 0],
         | 
| 180 | 
            +
                      ['1 and 2 and 3;', 3]
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                      # TODO test short-circuit at first false argument
         | 
| 183 | 
            +
                    ].each do |(source, predicted)|
         | 
| 184 | 
            +
                      lox = Loxxy::Interpreter.new
         | 
| 185 | 
            +
                      result = lox.evaluate(source)
         | 
| 186 | 
            +
                      expect(result.value == predicted).to be_truthy
         | 
| 187 | 
            +
                    end
         | 
| 188 | 
            +
                  end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                  it 'should evaluate the "disjunction" of two values' do
         | 
| 191 | 
            +
                    [
         | 
| 192 | 
            +
                      # Return the first truthy argument
         | 
| 193 | 
            +
                      ['1 or true;', 1],
         | 
| 194 | 
            +
                      ['false or 1;', 1],
         | 
| 195 | 
            +
                      ['nil or 1;', 1],
         | 
| 196 | 
            +
                      ['false or false or true;', true],
         | 
| 197 | 
            +
                      ['1 and 2 and nil;', nil],
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                      # Return the last argument if all are falsey
         | 
| 200 | 
            +
                      ['false or false;', false],
         | 
| 201 | 
            +
                      ['nil or false;', false],
         | 
| 202 | 
            +
                      ['false or nil;', nil],
         | 
| 203 | 
            +
                      ['false or false or false;', false],
         | 
| 204 | 
            +
                      ['false or false or nil;', nil]
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                      # TODO test short-circuit at first false argument
         | 
| 207 | 
            +
                    ].each do |(source, predicted)|
         | 
| 208 | 
            +
                      lox = Loxxy::Interpreter.new
         | 
| 209 | 
            +
                      result = lox.evaluate(source)
         | 
| 210 | 
            +
                      expect(result.value == predicted).to be_truthy
         | 
| 211 | 
            +
                    end
         | 
| 212 | 
            +
                  end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                  it 'should supprt expressions between parentheses' do
         | 
| 215 | 
            +
                    [
         | 
| 216 | 
            +
                      ['3 + 4 * 5;', 23],
         | 
| 217 | 
            +
                      ['(3 + 4) * 5;', 35],
         | 
| 218 | 
            +
                      ['(5 - (3 - 1)) + -(1);', 2]
         | 
| 219 | 
            +
                    ].each do |(source, predicted)|
         | 
| 220 | 
            +
                      lox = Loxxy::Interpreter.new
         | 
| 221 | 
            +
                      result = lox.evaluate(source)
         | 
| 222 | 
            +
                      expect(result.value == predicted).to be_truthy
         | 
| 223 | 
            +
                    end
         | 
| 224 | 
            +
                  end
         | 
| 225 | 
            +
             | 
| 48 226 | 
             
                  it 'should print the hello world message' do
         | 
| 49 227 | 
             
                    expect { subject.evaluate(hello_world) }.not_to raise_error
         | 
| 50 228 | 
             
                    expect(sample_cfg[:ostream].string).to eq('Hello, world!')
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: loxxy
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.19
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Dimitri Geshef
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2021-01- | 
| 11 | 
            +
            date: 2021-01-14 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rley
         | 
| @@ -89,10 +89,13 @@ files: | |
| 89 89 | 
             
            - lib/loxxy/ast/ast_visitor.rb
         | 
| 90 90 | 
             
            - lib/loxxy/ast/lox_binary_expr.rb
         | 
| 91 91 | 
             
            - lib/loxxy/ast/lox_compound_expr.rb
         | 
| 92 | 
            +
            - lib/loxxy/ast/lox_grouping_expr.rb
         | 
| 92 93 | 
             
            - lib/loxxy/ast/lox_literal_expr.rb
         | 
| 94 | 
            +
            - lib/loxxy/ast/lox_logical_expr.rb
         | 
| 93 95 | 
             
            - lib/loxxy/ast/lox_node.rb
         | 
| 94 96 | 
             
            - lib/loxxy/ast/lox_noop_expr.rb
         | 
| 95 97 | 
             
            - lib/loxxy/ast/lox_print_stmt.rb
         | 
| 98 | 
            +
            - lib/loxxy/ast/lox_unary_expr.rb
         | 
| 96 99 | 
             
            - lib/loxxy/back_end/engine.rb
         | 
| 97 100 | 
             
            - lib/loxxy/datatype/all_datatypes.rb
         | 
| 98 101 | 
             
            - lib/loxxy/datatype/boolean.rb
         |