.. sly documentation master file, created by
   sphinx-quickstart on Wed Sep  7 13:23:26 2016.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.

SLY (Sly Lex Yacc)
==================

THIS IS A WORK IN PROGRESS.  NO OFFICIAL RELEASE HAS BEEN MADE.
USE AT YOUR OWN RISK.

Requirements
============

SLY requires the use of Python 3.6 or greater.  Older versions
of Python are not supported.

Overview
========

SLY is a 100% Python implementation of the lex and yacc tools
commonly used to write parsers and compilers.  Parsing is
based on the same LALR(1) algorithm used by many yacc tools.
Here are a few notable features:

 -  SLY provides *very* extensive error reporting and diagnostic 
    information to assist in parser construction.  The original
    implementation was developed for instructional purposes.  As
    a result, the system tries to identify the most common types
    of errors made by novice users.  

 -  SLY provides full support for empty productions, error recovery,
    precedence specifiers, and moderately ambiguous grammars.

 -  SLY uses various Python metaprogramming features to specify
    lexers and parsers.  There are no generated files or extra
    steps involved. You simply write Python code and run it.

 -  SLY can be used to build parsers for "real" programming languages.
    Although it is not ultra-fast due to its Python implementation,
    SLY can be used to parse grammars consisting of several hundred
    rules (as might be found for a language like C).  

SLY originates from the PLY project (http://www.dabeaz.com/ply/index.html).
However, it's been modernized a bit.  In fact, don't expect any code
previously written for PLY to work. That said, most of the things 
that were possible in PLY are also possible in SLY. 

An Example
==========

SLY is probably best illustrated by an example.  Here's what it
looks like to write a parser that can evaluate simple arithmetic
expressions and store variables::

    # -----------------------------------------------------------------------------
    # calc.py
    # -----------------------------------------------------------------------------

    from sly import Lexer, Parser

    class CalcLexer(Lexer):
        tokens = { NAME, NUMBER }
        ignore = ' \t'
        literals = { '=', '+', '-', '*', '/', '(', ')' }

        # Tokens
        NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'

        @_(r'\d+')
        def NUMBER(self, t):
            t.value = int(t.value)
            return t

        @_(r'\n+')
        def newline(self, t):
            self.lineno += t.value.count('\n')

        def error(self, t):
            print("Illegal character '%s'" % t.value[0])
            self.index += 1

    class CalcParser(Parser):
        tokens = CalcLexer.tokens

        precedence = (
            ('left', '+', '-'),
            ('left', '*', '/'),
            ('right', 'UMINUS'),
            )

        def __init__(self):
            self.names = { }

        @_('NAME "=" expr')
        def statement(self, p):
            self.names[p.NAME] = p.expr

        @_('expr')
        def statement(self, p):
            print(p.expr)

        @_('expr "+" expr')
        def expr(self, p):
            return p.expr0 + p.expr1

        @_('expr "-" expr')
        def expr(self, p):
            return p.expr0 - p.expr1

        @_('expr "*" expr')
        def expr(self, p):
            return p.expr0 * p.expr1

        @_('expr "/" expr')
        def expr(self, p):
            return p.expr0 / p.expr1

        @_('"-" expr %prec UMINUS')
        def expr(self, p):
            return -p.expr

        @_('"(" expr ")"')
        def expr(self, p):
            return p.expr

        @_('NUMBER')
        def expr(self, p):
            return p.NUMBER

        @_('NAME')
        def expr(self, p):
            try:
                return self.names[p.NAME]
            except LookupError:
                print("Undefined name '%s'" % p.NAME)
                return 0

    if __name__ == '__main__':
        lexer = CalcLexer()
        parser = CalcParser()
        while True:
            try:
                text = input('calc > ')
            except EOFError:
                break
            if text:
                parser.parse(lexer.tokenize(text))
 

More Documentation
==================

Contents:

.. toctree::
   :maxdepth: 2

   sly

Resources
=========

For a detailed overview of parsing theory, consult the excellent
book "Compilers : Principles, Techniques, and Tools" by Aho, Sethi, and
Ullman.  The topics found in "Lex & Yacc" by Levine, Mason, and Brown
may also be useful.

The GitHub page for SLY can be found at:

     https://github.com/dabeaz/sly

Please direct bug reports and pull requests to the GitHub page.
To contact me directly, send email to dave@dabeaz.com or contact
me on Twitter (@dabeaz).