# Encountering Error for test case Booleans & Nil #sc2

**URL:** <https://forum.codecrafters.io/t/encountering-error-for-test-case-booleans-nil-sc2/3792>\
**Category:** Challenges\
**Tags:** challenge:interpreter\
**Created:** [December 29, 2024, 12:05pm UTC](https://forum.codecrafters.io/t/encountering-error-for-test-case-booleans-nil-sc2/3792 "2024-12-29T12:05:32Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![ksv-py](https://yyz1.discourse-cdn.com/flex003/user_avatar/forum.codecrafters.io/ksv-py/32/3812_2.png) [@ksv-py](https://forum.codecrafters.io/u/ksv-py)\
**Post date:** [December 29, 2024, 12:05pm UTC](https://forum.codecrafters.io/t/encountering-error-for-test-case-booleans-nil-sc2/3792/1 "2024-12-29T12:05:32Z")

</div>

I’m stuck on Stage #Booleans & Nil #sc2.

Here are my logs:

```auto
remote: [tester::#PQ5] [test-3] [test.lox] var greeting = "Hello"
remote: [tester::#PQ5] [test-3] [test.lox] if (greeting == "Hello") {
remote: [tester::#PQ5] [test-3] [test.lox] return true
remote: [tester::#PQ5] [test-3] [test.lox] } else {
remote: [tester::#PQ5] [test-3] [test.lox] return false
remote: [tester::#PQ5] [test-3] [test.lox] }
remote: [tester::#PQ5] [test-3] $ ./your_program.sh tokenize test.lox
remote: [your_program] VAR var null
remote: [your_program] IDENTIFIER greeting null
remote: [your_program] EQUAL = null
remote: [your_program] STRING "Hello" Hello
remote: [your_program] IF if null
remote: [your_program] LEFT_PAREN ( null
remote: [your_program] IDENTIFIER greeting null
remote: [your_program] EQUAL_EQUAL == null
remote: [your_program] STRING "Hello" Hello
remote: [your_program] RIGHT_PAREN ) null
remote: [your_program] LEFT_BRACE { null
remote: [your_program] RETURN return null
remote: [your_program] TRUE true null
remote: [your_program] RIGHT_BRACE } null
remote: [your_program] ELSE else null
remote: [your_program] LEFT_BRACE { null
remote: [your_program] RETURN return null
remote: [your_program] FALSE false null
remote: [your_program] RIGHT_BRACE } null
remote: [your_program] EOF null
remote: [tester::#PQ5] [test-3] ✓ 20 line(s) match on stdout
remote: [tester::#PQ5] [test-3] ✓ Received exit code 0.
remote: [tester::#PQ5] [test-4] Running test case: 4
remote: [tester::#PQ5] [test-4] Writing contents to ./test.lox:
remote: [tester::#PQ5] [test-4] [test.lox] var result = (a + b) > 7 or "Success" != "Failure" or x >= 5
remote: [tester::#PQ5] [test-4] [test.lox] while (result) {
remote: [tester::#PQ5] [test-4] [test.lox] var counter = 0
remote: [tester::#PQ5] [test-4] [test.lox] counter = counter + 1
remote: [tester::#PQ5] [test-4] [test.lox] if (counter == 10) {
remote: [tester::#PQ5] [test-4] [test.lox] return nil
remote: [tester::#PQ5] [test-4] [test.lox] }
remote: [tester::#PQ5] [test-4] [test.lox] }
remote: [tester::#PQ5] [test-4] $ ./your_program.sh tokenize test.lox
remote: [your_program] VAR var null
remote: [your_program] IDENTIFIER result null
remote: [your_program] EQUAL = null
remote: [your_program] LEFT_PAREN ( null
remote: [your_program] IDENTIFIER a null
remote: [your_program] PLUS + null
remote: [your_program] IDENTIFIER b null
remote: [your_program] GREATER > null
remote: [your_program] NUMBER 7 null
remote: [your_program] OR or null
remote: [your_program] STRING "Success" Success
remote: [your_program] BANG_EQUAL != null
remote: [your_program] STRING "Failure" Failure
remote: [your_program] OR or null
remote: [your_program] IDENTIFIER x null
remote: [your_program] GREATER_EQUAL >= null
remote: [your_program] NUMBER 5 null
remote: [your_program] WHILE while null
remote: [your_program] LEFT_PAREN ( null
remote: [your_program] IDENTIFIER result null
remote: [your_program] LEFT_BRACE { null
remote: [your_program] VAR var null
remote: [your_program] IDENTIFIER counter null
remote: [your_program] EQUAL = null
remote: [your_program] NUMBER 0 null
remote: [your_program] IDENTIFIER counter null
remote: [your_program] EQUAL = null
remote: [your_program] IDENTIFIER counter null
remote: [your_program] PLUS + null
remote: [your_program] NUMBER 1 null
remote: [your_program] IF if null
remote: [your_program] LEFT_PAREN ( null
remote: [your_program] IDENTIFIER counter null
remote: [your_program] EQUAL_EQUAL == null
remote: [your_program] NUMBER 10 null
remote: [your_program] LEFT_BRACE { null
remote: [your_program] RETURN return null
remote: [your_program] NIL nil null
remote: [your_program] RIGHT_BRACE } null
remote: [your_program] RIGHT_BRACE } null
remote: [your_program] EOF null
remote: [tester::#PQ5] [test-4] ✓ VAR var null
remote: [tester::#PQ5] [test-4] ✓ IDENTIFIER result null
remote: [tester::#PQ5] [test-4] ✓ EQUAL = null
remote: [tester::#PQ5] [test-4] ✓ LEFT_PAREN ( null
remote: [tester::#PQ5] [test-4] ✓ IDENTIFIER a null
remote: [tester::#PQ5] [test-4] ✓ PLUS + null
remote: [tester::#PQ5] [test-4] ✓ IDENTIFIER b null
remote: [tester::#PQ5] [test-4] 𐄂 GREATER > null
remote: [tester::#PQ5] [test-4] Expected line #8 on stdout to be "RIGHT_PAREN ) null", got "GREATER > null"
remote: [tester::#PQ5] [test-4] Test failed

```

And here’s a snippet of my code:

```python
import sys

def tokenize_file(file_contents):
    # Reserved words in the language (keywords)
    reserved_words = {
        "and", "class", "else", "false", "for", "fun", "if", "nil", "or", "print", 
        "return", "super", "this", "true", "var", "while"
    }

    # Mapping of single-character symbols to token types
    token_map = {
        "(": "LEFT_PAREN",
        ")": "RIGHT_PAREN",
        "{": "LEFT_BRACE",
        "}": "RIGHT_BRACE",
        ",": "COMMA",
        ".": "DOT",
        "-": "MINUS",
        "+": "PLUS",
        ";": "SEMICOLON",
        "*": "STAR"
    }

    tokens = [] # List to hold all the tokens
    error = False # Flag to indicate errors during tokenization
    i = 0 # Pointer to iterate over the file contents

    while i < len(file_contents): # Iterate through the file contents
        c = file_contents[i] # Current character in the file

        # Skip whitespace characters (space, carriage return, tab, newline)
        if c in " \r\t\n":
            i += 1 # Move to the next character
            continue

        # Handle single-character symbols (e.g., parentheses, operators)
        elif c in "(){},.-+;*":
            token = {"type": token_map[c], "value": c}
            tokens.append(token) # Add the token to the list

        # Handle operators and other special symbols (e.g., ==, !=)
        elif c == "=":
            if i + 1 < len(file_contents) and file_contents[i + 1] == "=":
                token = {"type": "EQUAL_EQUAL", "value": "=="}
                i += 1 # Skip the second '=' character
            else:
                token = {"type": "EQUAL", "value": "="}
            tokens.append(token) # Add the token to the list

        # Handle reserved words (keywords) and identifiers
        # Add logic for reserved words, identifiers, strings, numbers, etc.

        # Move to the next character
        # Handle less-than or less-than-equal (<=)
        elif c == "<":
            if i + 1 < len(file_contents) and file_contents[i + 1] == "=":
                token = {"type": "LESS_EQUAL", "value": "<="}
                i += 1 # Skip the '=' character
            else:
                token = {"type": "LESS", "value": "<"}
            tokens.append(token) # Add the token to the list

        # Handle greater-than or greater-than-equal (>=)
        elif c == ">":
            if i + 1 < len(file_contents) and file_contents[i + 1] == "=":
                token = {"type": "GREATER_EQUAL", "value": ">="}
                i += 1 # Skip the '=' character
            else:
                token = {"type": "GREATER", "value": ">"}
            tokens.append(token) # Add the token to the list

        # Handle not-equal (!=) or not (!) 
        elif c == "!":
            if i + 1 < len(file_contents) and file_contents[i + 1] == "=":
                token = {"type": "BANG_EQUAL", "value": "!="}
                i += 1 # Skip the '=' character
            else:
                token = {"type": "BANG", "value": "!"}
            tokens.append(token) # Add the token to the list

        # Handle comments (//) – Skip the rest of the line after '//'
        elif c == "/":
            if i + 1 < len(file_contents) and file_contents[i + 1] == "/":
                while i < len(file_contents) and file_contents[i] != "\n":
                    i += 1 # Skip characters until the end of the line
            else:
                token = {"type": "SLASH", "value": "/"}
                tokens.append(token) # Add the token to the list

        # Handle string literals (enclosed in " or ')
        elif c in "\"'":
            quote_type = c # Keep track of whether it's a single or double quote
            i += 1 # Move to the next character after the opening quote
            string_value = "" # Initialize an empty string for the literal value
            while i < len(file_contents) and file_contents[i] != quote_type:
                string_value += file_contents[i] # Append characters inside the string literal
                i += 1
            if i == len(file_contents): # Error: Unterminated string literal
                error = True
                line_no = file_contents.count("\n", 0, i) + 1
                print(f"[line {line_no}] Error: Unterminated string.", file=sys.stderr)
            else:
                token = {"type": "STRING", "value": string_value} # Create a token for the string
                tokens.append(token) # Add the token to the list
        
        # Handle numbers (integers and floats)
        elif c.isdigit():
            start_idx = i # Mark the starting index of the number
            while i < len(file_contents) and (file_contents[i].isdigit() or file_contents[i] == "."):
                i += 1 # Move until the end of the number
            lexeme = file_contents[start_idx:i] # Extract the lexeme (the number string)
            # Check for invalid number literals (multiple decimal points or non-numeric characters)
            if lexeme.count(".") > 1 or not lexeme.replace(".", "", 1).isdigit():
                error = True
                line_no = file_contents.count("\n", 0, i) + 1
                print(f"[line {line_no}] Error: Invalid number literal.", file=sys.stderr)
            else:
                literal_value = float(lexeme) # Convert the lexeme to a float
                token = {"type": "NUMBER", "value": lexeme} # Create a token for the number
                tokens.append(token) # Add the token to the list

        # Handle identifiers (variable names, function names, etc.)
        elif c.isalpha() or c == "_":
            start_idx = i # Mark the starting index of the identifier
            while i < len(file_contents) and (file_contents[i].isalnum() or file_contents[i] == "_"):
                i += 1 # Move until the end of the identifier
            lexeme = file_contents[start_idx:i] # Extract the lexeme (the identifier)
            if lexeme in reserved_words: # Check if it's a reserved word (keyword)
                token = {"type": lexeme.upper(), "value": lexeme} # Reserved word token
                tokens.append(token) # Add the token to the list
            else:
                token = {"type": "IDENTIFIER", "value": lexeme} # Regular identifier token
                tokens.append(token) # Add the token to the list

        # Handle unexpected characters (errors)
        else:
            error = True
            line_no = file_contents.count("\n", 0, i) + 1
            print(f"[line {line_no}] Error: Unexpected character: {c}", file=sys.stderr)

        i += 1 # Move to the next character

    # Add an EOF (End of File) token
    tokens.append({"type": "EOF", "value": "null"})

    # If there was an error, exit with code 65
    if error:
        exit(65)
    else:
        return tokens # Return the list of tokens

def print_tokens(tokens):
    for token in tokens:
        if token['type'] == 'EOF':
            print(f"{token['type']} {token['value']}")
        elif token['type'] == 'STRING':
            print(f'{token['type']} "{token['value']}" {token['value']}')
        else:
            print(f"{token['type']} {token['value']} null")
 
def literals(value):
    if value == None:
        return 'nil'
    return str(value).lower()

def unary(operator,right):
    return {'operator':operator, 'right':right}

def binary(left, operator, right):
    return {'left':left,'operator':operator,'right':right}

def grouping(expression):
    return {'expression':expression}

class Parser:
    def __init__ (self, tokens):
        self.tokens = tokens
        self.current = 0 # Points to the current token

    def parse(self):
        return self.expression() # Start parsing from the highest level rule

    def expression(self):
        return self.equality()

    def equality(self):
        expr = self.comparison()
        while self.match("BANG_EQUAL", "EQUAL_EQUAL"):
            operator = self.previous()
            right = self.comparison()
            expr = binary(expr, operator["value"], right["value"])
        return expr

    def comparison(self):
        expr = self.term()
        while self.match("GREATER", "GREATER_EQUAL", "LESS", "LESS_EQUAL"):
            operator = self.previous()
            right = self.term()
            expr = binary(expr, operator["value"], right["value"])
        return expr

    def term(self):
        expr = self.factor()
        while self.match("MINUS", "PLUS"):
            operator = self.previous()
            right = self.factor()
            expr = binary(expr, operator["value"], right["value"])
        return expr

    def factor(self):
        expr = self.unary()
        while self.match("SLASH", "STAR"):
            operator = self.previous()
            right = self.unary()
            expr = binary(expr, operator["value"], right["value"])
        return expr

    def unary(self):
        if self.match("BANG", "MINUS"):
            operator = self.previous()
            right = self.unary()
            return unary(operator["value"], right["value"])
        return self.primary()

    def primary(self):
        if self.match("FALSE"):
            return literals(False)
        if self.match("TRUE"):
            return literals(True)
        if self.match("NIL"):
            return literals(None)
        if self.match("NUMBER", "STRING"):
            return self.previous()["value"]
        if self.match("LEFT_PAREN"):
            expr = self.expression()
            self.consume("RIGHT_PAREN", "Expect ')' after expression.")
            return grouping(expr)
        if self.match("IDENTIFIER"):
            return {"type": "IDENTIFIER", "value": self.previous()["value"]}
        self.error("Expect expression.")

    def print_ast(self, expr):
        if isinstance(expr, dict): # If it's a binary operation
            return f"({expr['operator']} {self.print_ast(expr['left'])} {self.print_ast(expr['right'])})"
        elif isinstance(expr, str): # If it's a literal
            return expr
        return ""

    # Utility Methods
    def match(self, *types):
        for token_type in types:
            if self.check(token_type):
                self.advance()
                return True
        return False

    def check(self, token_type):
        if self.is_at_end():
            return False
        return self.peek()["type"] == token_type

    def advance(self):
        if not self.is_at_end():
            self.current += 1
        return self.previous()

    def is_at_end(self):
        return self.peek()["type"] == "EOF"

    def peek(self):
        return self.tokens[self.current]

    def previous(self):
        return self.tokens[self.current - 1]

    def consume(self, token_type, message):
        if self.check(token_type):
            return self.advance()
        self.error(message)

    def error(self, message):
        token = self.peek()
        line = token.get("line", "unknown")
        raise SyntaxError(f"[line {line}] Error: {message}")

def main():
    if len(sys.argv) < 3:
        print("Usage: ./your_program.sh <command> <filename>", file=sys.stderr)
        exit(1)

    command = sys.argv[1]
    filename = sys.argv[2]

    try:
        with open(filename) as file:
            file_contents = file.read()
    except FileNotFoundError:
        print(f"Error: File '{filename}' not found.", file=sys.stderr)
        exit(1)

    if command == "tokenize":
        tokens = tokenize_file(file_contents)
        print_tokens(tokens)

    elif command == "parse":
        tokens = tokenize_file(file_contents)
        parser = Parser(tokens)
        try:
            expression = parser.parse() # Generate the AST
            print(parser.print_ast(expression)) # Print the AST representation
        except SyntaxError as e:
            print(e, file=sys.stderr)
            exit(65)
    else:
        print(f"Unknown command: {command}", file=sys.stderr)
        exit(1)
  
if __name__ == " __main__":
    main()

```

---

<div class="post-metadata">

**Author:** ![andy1li](https://yyz1.discourse-cdn.com/flex003/user_avatar/forum.codecrafters.io/andy1li/32/10_2.png) [@andy1li](https://forum.codecrafters.io/u/andy1li)\
**Post date:** [December 29, 2024, 1:46pm UTC](https://forum.codecrafters.io/t/encountering-error-for-test-case-booleans-nil-sc2/3792/3 "2024-12-29T13:46:41Z")

</div>

Hi @ksv-py, I tried running your code against the previous stages, but it’s actually no longer passing the stage **#EA6 (Scanning: Lexical errors)**.

 ![image](https://canada1.discourse-cdn.com/flex003/uploads/codecrafters/original/2X/6/6d526573973495da986fae251de2f2a5882f664c.png)

Suggestions:

1. Use [our CLI](https://docs.codecrafters.io/cli/installation) to test against previous stages by running:

```auto
codecrafters test --previous

```

1. Focus on fixing the early stages first, as later stages depend on them.

---

<div class="post-metadata">

**Author:** ![andy1li](https://yyz1.discourse-cdn.com/flex003/user_avatar/forum.codecrafters.io/andy1li/32/10_2.png) [@andy1li](https://forum.codecrafters.io/u/andy1li)\
**Post date:** [January 5, 2025, 11:53pm UTC](https://forum.codecrafters.io/t/encountering-error-for-test-case-booleans-nil-sc2/3792/5 "2025-01-05T23:53:19Z")

</div>

Closing this thread due to inactivity. If you still need assistance, feel free to reopen or start a new discussion!

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex003/uploads/codecrafters/original/3X/7/0/700657133935c15703e22c7c8870394f6e6dc27d.svg) [@system](https://forum.codecrafters.io/u/system)\
**Post date:** [January 10, 2025, 11:54pm UTC](https://forum.codecrafters.io/t/encountering-error-for-test-case-booleans-nil-sc2/3792/6 "2025-01-10T23:54:16Z")

</div>

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.
