This release is a pre-release and may not be stable for production use.
mvin: Minimum Viable Interpreter for Excel Formulas
mvin is a lightweight, dependency-free interpreter for evaluating single Excel-like formulas from tokenized input.
It is built around a shunting-yard parser with a small, extensible function/operator surface.
If this library saved your team hours of manual formatting, consider buying me a coffee! ☕ Donations help prioritize support for new Excel formulas and complex CSS mapping.
Why mvin
- No runtime dependencies.
- Works with tokenizer output (for example,
openpyxltokens). - Supports numeric, comparison, and string-concatenation operators.
- Allows custom function maps and operator maps.
- Dual licensed under MIT or Apache-2.0.
Installation
pip install mvin
Python support: >=3.9,<4.0.
Quick Start
from mvin import TokenNumber, TokenOperator
from mvin.interpreter import get_interpreter
tokens = [
TokenNumber(1),
TokenOperator("+"),
TokenNumber(2),
]
run = get_interpreter(tokens)
result = run({}) if run else None
assert result == 3
get_interpreter(...) returns a callable that evaluates the expression. Inputs for cell references are passed as a dictionary.
Token Contract
mvin accepts any token object with these attributes:
type: strsubtype: strvalue: Any
Built-in token classes are available in mvin (TokenNumber, TokenString, TokenBool, TokenOperator, etc.), but third-party tokenizers are supported if they follow the same shape.
Supported Operators
| Operator | Meaning |
|---|---|
+ |
Addition |
- |
Subtraction |
* |
Multiplication |
/ |
Division |
^ |
Exponentiation |
& |
String concatenation |
= / == |
Equality |
<> / != |
Inequality |
< |
Less than |
<= |
Less than or equal |
> |
Greater than |
>= |
Greater than or equal |
Built-in Functions
Built-ins are defined in DEFAULT_FUNCTIONS in src/mvin/functions/excel_lib.py.
| Function | Notes |
|---|---|
NOT(value) |
Accepts logical or numeric values. |
ISERROR(value) |
Returns whether value is an error token. |
SEARCH(find_text, within_text, [start_num]) |
1-based index; defaults start_num to 1. |
LEFT(text, [num_chars]) |
Defaults num_chars to 1. |
RIGHT(text, [num_chars]) |
Defaults num_chars to 1. |
LEN(text) |
Length of text representation. |
Working with References (Ranges)
If a token has type="OPERAND" and subtype="RANGE", its value is treated as an input key.
- Required keys are exposed as
run.inputs. - Inputs should map reference name to token objects.
from mvin import BaseToken, TokenNumber
from mvin.interpreter import get_interpreter
class RefToken(BaseToken):
def __init__(self, ref: str):
super().__init__()
self._value = ref
self._type = "OPERAND"
self._subtype = "RANGE"
tokens = [RefToken("A1")]
run = get_interpreter(tokens)
assert run is not None
assert run.inputs == {"A1"}
assert run({"A1": TokenNumber(10)}) == 10
Customizing Functions
Pass a custom function map through proposed_functions.
Function keys follow tokenizer function-open values (for example, "MYFUNC(").
from mvin import BaseToken, TokenNumber
from mvin.interpreter import get_interpreter
from mvin.functions.excel_lib import DEFAULT_FUNCTIONS
class T(BaseToken):
def __init__(self, value: str, token_type: str, subtype: str):
super().__init__()
self._value = value
self._type = token_type
self._subtype = subtype
def excel_double(value):
if value and value.type == "OPERAND" and value.subtype == "NUMBER":
return TokenNumber(value.value * 2)
return value
custom_functions = dict(DEFAULT_FUNCTIONS)
custom_functions["DOUBLE("] = ([None], excel_double)
tokens = [
T("DOUBLE(", "FUNC", "OPEN"),
TokenNumber(21),
T(")", "FUNC", "CLOSE"),
]
run = get_interpreter(tokens, proposed_functions=custom_functions)
assert run is not None
assert run({}) == 42
Customizing Operators
Pass a custom operator dictionary through registered_ops (or use register_op / register_numeric_op).
Pragmatically, overriding behavior of existing operator symbols is the safest path.
Development
Setup
uv sync --dev
Run tests
uv run pytest
Test files are grouped by feature in tests/ (for example, interpreter core/syntax/execution, operators, search, and text functions).
Build
uv build
Limitations
- This library evaluates tokenized formulas, not full workbooks.
- No built-in tokenizer is included.
- Workbook/sheet dependency graphs are out of scope.
License
Licensed under either of:
- Apache License, Version 2.0 (
LICENSE_APACHEor https://www.apache.org/licenses/LICENSE-2.0) - MIT license (
LICENSE_MITor https://opensource.org/licenses/MIT)
at your option.
Metadata
Release files for mvin 0.13.0b6021601
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mvin-0.13.0b6021601.tar.gz | 20.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mvin-0.13.0b6021601-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 37.8 kB
Release files / mvin-0.13.0b6021601.tar.gz
| Download URL | mvin-0.13.0b6021601.tar.gz |
|---|---|
| Size | 20.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c8b3f9d7586678a5d07a612f674997fbb09e08e12220f503b1704bb04ee07126
|
|
BLAKE2b-256 checksum How to use checksums |
9800766479cf4d1786f40e09eb1158b941f97c034cffc07553d8750ec2bbf0fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
pdm/2.26.6 CPython/3.14.3 Darwin/24.6.0
|
Release files / mvin-0.13.0b6021601-py3-none-any.whl
| Download URL | mvin-0.13.0b6021601-py3-none-any.whl |
|---|---|
| Size | 17.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
342f24bea1218fc9fd684a5bd4d295fd0b4a79ba4d7bce258065768f9e592f13
|
|
BLAKE2b-256 checksum How to use checksums |
af6add9d1cbd7773e01fef51e57aa0a4526b30254630b935b1a249d18cfa156c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
pdm/2.26.6 CPython/3.14.3 Darwin/24.6.0
|