> ## Documentation Index
> Fetch the complete documentation index at: https://leetcode-py.wisl.dev/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> leetcode-py is a Python LeetCode practice environment generator with one CLI: lcpy. It is not a service or platform.
> Each problem is a directory under leetcode/ with README.md, solution.py, test_solution.py, helpers.py, and playground.ipynb. lcpy gen creates them from JSON templates bundled with the package.
> Examples are backed by tests; copy them verbatim.

# Basic Calculator Python Solution with Tests

> Tested Python solution for LeetCode 224 with 24 pytest cases. Generate a practice environment with lcpy.

LeetCode 224, Hard. Topics: Math, String, Stack, Recursion. [View on LeetCode](https://leetcode.com/problems/basic-calculator/description/).

Generate this problem as a practice environment: tested reference solution, 24 [parametrized pytest cases](/practice/testing), and a playground notebook:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
lcpy gen -n 224   # by problem number
lcpy gen -s basic_calculator   # by problem name
```

## Problem

Given a string `s` representing a valid expression, implement a basic calculator to evaluate it, and return the result of the evaluation.

**Note:** You are **not** allowed to use any built-in function which evaluates strings as mathematical expressions, such as `eval()`.

### Examples

```
Input: s = "1 + 1"
Output: 2
```

```
Input: s = " 2-1 + 2 "
Output: 3
```

```
Input: s = "(1+(4+5+2)-3)+(6+8)"
Output: 23
```

### Constraints

* `1 <= s.length <= 3 * 10^5`
* `s` consists of digits, `'+'`, `'-'`, `'('`, `')'`, and `' '`.
* `s` represents a valid expression.
* `'+'` is **not** used as a unary operation (i.e., `"+1"` and `"+(2 + 3)"` is invalid).
* `'-'` could be used as a unary operation (i.e., `"-1"` and `"-(2 + 3)"` is valid).
* There will be no two consecutive operators in the input.
* Every number and running calculation will fit in a signed 32-bit integer.

## Solution

Reference implementation from [solution.py on GitHub](https://github.com/wislertt/leetcode-py/blob/main/leetcode/basic_calculator/solution.py), full suite in [test\_solution.py](https://github.com/wislertt/leetcode-py/blob/main/leetcode/basic_calculator/test_solution.py):

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
class Solution:
    # Time: O(n)
    # Space: O(n)
    def calculate(self, s: str) -> int:
        stack = []
        num = 0
        sign = 1
        result = 0

        for char in s:
            if char.isdigit():
                num = num * 10 + int(char)
            elif char in "+-":
                result += sign * num
                num = 0
                sign = 1 if char == "+" else -1
            elif char == "(":
                stack.append(result)
                stack.append(sign)
                result = 0
                sign = 1
            elif char == ")":
                if len(stack) < 2:
                    raise ValueError("Mismatched parentheses")
                result += sign * num
                num = 0
                result *= stack.pop()
                result += stack.pop()
            elif char != " ":
                raise ValueError(f"Invalid character: '{char}'")

        if stack:
            raise ValueError("Mismatched parentheses")

        return result + sign * num


# Example walkthrough: "(1+(4+5+2)-3)+(6+8)" = 23
#
# char | num | sign | result | stack      | action
# -----|-----|------|--------|------------|------------------
# '('  | 0   | 1    | 0      | [0, 1]     | push result=0, sign=1
# '1'  | 1   | 1    | 0      | [0, 1]     | build num=1
# '+'  | 0   | 1    | 1      | [0, 1]     | result += 1*1 = 1
# '('  | 0   | 1    | 0      | [0,1,1,1]  | push result=1, sign=1
# '4'  | 4   | 1    | 0      | [0,1,1,1]  | build num=4
# '+'  | 0   | 1    | 4      | [0,1,1,1]  | result += 1*4 = 4
# '5'  | 5   | 1    | 4      | [0,1,1,1]  | build num=5
# '+'  | 0   | 1    | 9      | [0,1,1,1]  | result += 1*5 = 9
# '2'  | 2   | 1    | 9      | [0,1,1,1]  | build num=2
# ')'  | 0   | 1    | 11     | [0, 1]     | result=11*1+1 = 12
# '-'  | 0   | -1   | 12     | [0, 1]     | sign = -1
# '3'  | 3   | -1   | 12     | [0, 1]     | build num=3
# ')'  | 0   | 1    | 9      | []         | result=9*1+0 = 9
# '+'  | 0   | 1    | 9      | []         | sign = 1
# '('  | 0   | 1    | 0      | [9, 1]     | push result=9, sign=1
# '6'  | 6   | 1    | 0      | [9, 1]     | build num=6
# '+'  | 0   | 1    | 6      | [9, 1]     | result += 1*6 = 6
# '8'  | 8   | 1    | 6      | [9, 1]     | build num=8
# ')'  | 0   | 1    | 14     | []         | result=14*1+9 = 23
# end  | 0   | 1    | 14     | []         | return 14+1*0 = 23
```

## Complexity

| Time | Space |
| ---- | ----- |
| O(n) | O(n)  |

## Tags

[Grind 75](/catalog/grind-75), [Grind](/catalog/grind).
