Install & Compatibility
Where this runs
tested against v0.5 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.030s · 18MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 1.5s · import 0.026s · 18MB
16MB installed
● package 16MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
Lexer
✓ from sly import Lexer
Lexer is used to break input text into tokens.
Parser
✓ from sly import Parser
Parser is used to recognize language syntax from a stream of tokens.
This quickstart demonstrates a simple calculator using SLY. It defines a lexer to tokenize input (numbers, IDs, operators) and a parser to build an abstract syntax tree and evaluate expressions, including variable assignments.
from sly import Lexer, Parser
class CalcLexer(Lexer):
tokens = { NUMBER, ID, PLUS, MINUS, TIMES, DIVIDE, ASSIGN, LPAREN, RPAREN }
literals = { '=', '+', '-', '*', '/', '(', ')' }
# String containing ignored characters
ignore = ' \t'
# Regular expression rules for tokens
NUMBER = r'\d+'
ID = r'[a-zA-Z_][a-zA-Z0-9_]*'
# Special rules for tokens
def NUMBER(self, t):
t.value = int(t.value)
return t
def ID(self, t):
t.value = str(t.value)
return t
def error(self, t):
print(f"Illegal character '{t.value[0]}' at line {t.lineno}")
self.index += 1
class CalcParser(Parser):
tokens = CalcLexer.tokens
precedence = (
('left', PLUS, MINUS),
('left', TIMES, DIVIDE)
)
def __init__(self):
self.names = { }
@_('ID ASSIGN expr')
def statement(self, p):
self.names[p.ID] = p.expr
return p.expr
@_('expr')
def statement(self, p):
return p.expr
@_('expr PLUS expr')
def expr(self, p):
return p.expr0 + p.expr1
@_('expr MINUS expr')
def expr(self, p):
return p.expr0 - p.expr1
@_('expr TIMES expr')
def expr(self, p):
return p.expr0 * p.expr1
@_('expr DIVIDE expr')
def expr(self, p):
return p.expr0 / p.expr1
@_('LPAREN expr RPAREN')
def expr(self, p):
return p.expr
@_('NUMBER')
def expr(self, p):
return p.NUMBER
@_('ID')
def expr(self, p):
try:
return self.names[p.ID]
except LookupError:
print(f"Undefined name '{p.ID}'")
return 0
def error(self, p):
if p:
print(f"Syntax error at token {p.type}, value '{p.value}'")
else:
print("Syntax error at EOF")
if __name__ == '__main__':
lexer = CalcLexer()
parser = CalcParser()
while True:
try:
text = input('calc > ')
if text.lower() == 'quit':
break
result = parser.parse(lexer.tokenize(text))
if result is not None: # Only print if there was a calculable result
print(result)
except EOFError:
break
except Exception as e:
print(f"Error: {e}")
Debug
Known issues
breakingThe SLY project was officially retired by its author on December 21, 2025. No further maintenance or development is expected. Users are advised to consider other parsing libraries or fork the project for continued use.fixMigrate to an actively maintained parsing library (e.g., PLY, Lark) or fork SLY's source code if continued use is necessary.
affects: All versions (from December 21, 2025, onwards)
gotchaSLY is a modernization of the PLY project, but code written for PLY is generally not compatible with SLY. Direct migration of PLY code to SLY without modifications will likely fail.fixRewrite PLY-based lexers and parsers to conform to SLY's API and conventions. Consult SLY's documentation for correct usage.
affects: All versions
gotchaSLY requires Python 3.6 or newer. It is not compatible with older Python versions.fixEnsure your project uses Python 3.6 or a more recent version.
affects: <0.5 (older Python versions)
gotchaLexer classes *must* define a `tokens` set specifying all possible token type names. Token names should generally be in all-caps. Incorrectly defined tokens or regex patterns are a common source of parsing issues.fixAlways define `tokens` as a set of all-capitalized token names. Carefully review regular expressions and ensure they match the intended input. Utilize the `error` method in the Lexer for debugging unmatched characters.
affects: All versions
gotchaWhen defining parser rules, the `_()` decorator is crucial. Accessing parts of a rule (e.g., `expr PLUS expr`) requires careful indexing (e.g., `p.expr0`, `p.expr1`) if a symbol appears multiple times in a rule's right-hand side, or by name (e.g., `p.NUMBER`) otherwise.fixRefer to rule components using `p.symbolname` for unique symbols or `p.symbolnameN` (where N is a 0-indexed number) for repeated symbols. Consult the documentation for examples of `_()` decorator usage.
affects: All versions
Errors
Common errors & fixes
ModuleNotFoundError: No module named 'sly'
The 'sly' library is not installed in the current Python environment.
fixRun `pip install sly` in your terminal to install the library.
ImportError: cannot import name 'Lexer' from 'sly'
The `Lexer` class is part of the `sly.lex` submodule, and `Parser` is from `sly.yacc`, not directly available under the top-level 'sly' package.
fixChange your import statements to `from sly.lex import Lexer` and `from sly.yacc import Parser`.
SyntaxError: Expected a list of tokens called 'tokens'
Every `sly.lex.Lexer` subclass must define a class attribute named `tokens`, which is a list of strings representing all valid token names.
fixAdd a `tokens = [...]` class attribute to your `Lexer` subclass, listing all token types (e.g., `tokens = ['NUMBER', 'PLUS']`).
SyntaxError: Expected a rule for token 'TOKEN_NAME'
A token name listed in the `tokens` list of your `Lexer` class does not have a corresponding regular expression rule (e.g., `t_TOKEN_NAME`) or a `t_ignore_TOKEN_NAME` rule defined for it.
fixDefine a `t_TOKEN_NAME = r'...'` attribute in your `Lexer` class for the missing token, or remove the token from the `tokens` list if it's not intended to be used.
SyntaxError: Parsing error. Unexpected token TOKEN_TYPE (VALUE) at line X, column Y
The `sly.yacc.Parser` encountered an input token that does not match any of the defined grammar rules (`p_NAME` methods) at the current parsing state.
fixReview your parser's grammar rules (`p_NAME` methods) and the input string for any discrepancies, or implement a custom `p_error` method in your `Parser` class to handle or recover from syntax errors.
Upgrade
Version history
0.5latest on PyPI · released Oct 25, 2022
Audit
Dependencies
No dependency data recorded yet.