コード例 #1
0
ファイル: script.py プロジェクト: prataprc/tayra
def lexical( pa, options ):
    from tayra.lexer import TTLLexer
    stats = {}
    setts = { 'optimize' : 0 }
    compiler = pa.qp( pa, ISettings, 'tayra.ttlcompiler', settings=setts )
    ttllex = TTLLexer( compiler )
    ttllex.build( ttlfile=options.ttlfile )
    ttllex.input( open( options.ttlfile, encoding='utf-8-sig' ).read() )
    tok = fetchtoken( ttllex, stats )
    while tok :
        tok = fetchtoken( ttllex, stats )
コード例 #2
0
def lexical(pa, options):
    from tayra.lexer import TTLLexer
    stats = {}
    setts = {'optimize': 0}
    compiler = pa.qp(pa, ISettings, 'tayra.ttlcompiler', settings=setts)
    ttllex = TTLLexer(compiler)
    ttllex.build(ttlfile=options.ttlfile)
    ttllex.input(open(options.ttlfile, encoding='utf-8-sig').read())
    tok = fetchtoken(ttllex, stats)
    while tok:
        tok = fetchtoken(ttllex, stats)
コード例 #3
0
class TTLParser(object):
    """Parser class for tayra template language. This class is initialized
    with :class:`tayra.compiler.TTLCompiler` instance, and  configuration
    options like, ``optimize``, ``lex_debug``, ``lextab``, ``yacc_debug``,
    ``yacctab``, ``yacc_outputdir``.

    It might be a costly operation to create a TTLParser instance every time,
    and to avoid that use _initialize() method before starting off with a new
    template.
    """
    def __init__(self, compiler):
        self.compiler = compiler

    def _initialize(self, ttlfile=None):
        optimize = self.compiler['optimize']
        lex_debug = self.compiler['lex_debug']
        lextab = self.compiler['lextab']
        yacc_debug = self.compiler['yacc_debug']
        yacctab = self.compiler['yacctab']
        yacc_outputdir = self.compiler['yacc_outputdir']

        self.ttlfile = ttlfile

        # Build Lexer
        self.ttllex = TTLLexer(self.compiler)
        kwargs = {'ttlfile': ttlfile}
        kwargs.update(optimize=optimize) if optimize else None
        kwargs.update(debug=lex_debug)
        kwargs.update(lextab=lextab) if lextab else None
        self.ttllex.build(**kwargs)
        self.tokens = self.ttllex.tokens  # Important for YACCer
        self.ttllex.reset_lineno()

        # Build Yaccer
        kwargs = {'optimize': optimize} if optimize else {}
        kwargs.update(debug=yacc_debug)
        kwargs.update(outputdir=yacc_outputdir) if yacc_outputdir else None
        kwargs.update(tabmodule=yacctab)
        self.parser = ply.yacc.yacc(module=self, **kwargs)
        self.parser.ttlparser = self  # For AST nodes to access `this`
        self.parser.compiler = self.compiler

        self.prolog = True

    # ------------------------- Private functions -----------------------------

    def _coord(self, lineno, column=None):
        return Coord(file=self.ttllex.ttlfile, line=lineno, column=column)

    def _parse_error(self, msg, coord):
        raise Exception("%s: %s" % (coord, msg))

    def _buildterms(self, p, terms):
        """Convenience function to construct Terminal nodes from parser object
        ``p``."""
        rc = []
        for t in terms:
            if t == None:
                rc.append(None)
                continue
            elif isinstance(t, tuple):
                cls, (val, lineno) = t[0], p[t[1]]
                rc.append(cls(p.parser, lineno, val))
            else:
                rc.append(t)
        return rc

    #---- API methods.

    def parse(self, text, ttlfile=None, debuglevel=0):
        """Parse tayra templage text, ``text`` and return an AST tree. AST
        tree starts with a root node :class:`tayra.ast.Template`. Every time
        the parser is invoked the lexer, yaccer, app options will be reused.

        ``ttlfile``
            Name of the file being parsed (for meaningful error messages)

        ``debuglevel``
            Debug level to yacc
        """
        # Parser Initialize
        self._initialize(ttlfile=ttlfile)
        # parse and get the Translation Unit
        self.ast = self.parser.parse(text, lexer=self.ttllex, debug=debuglevel)
        return self.ast

    # ---------- Precedence and associativity of operators --------------------

    precedence = ()

    def p_template_1(self, p):
        """template : script"""
        p[0] = Template(p.parser, p[1])

    def p_template_2(self, p):
        """template : """
        p[0] = Template(p.parser, None)

    def p_script_1(self, p):
        """script : doctype
                  | body
                  | importas
                  | inherit
                  | implement
                  | use
                  | statement
                  | tagline
                  | commentline
                  | tagblock
                  | commentblock
                  | textblock
                  | textspan
                  | filterblock
                  | functionblock
                  | interfaceblock
                  | ifelfiblock
                  | forblock
                  | whileblock"""
        p[0] = Script(p.parser, None, p[1])

    def p_script_2(self, p):
        """script : script doctype
                  | script body
                  | script importas
                  | script inherit
                  | script implement
                  | script use
                  | script statement
                  | script tagline
                  | script commentline
                  | script tagblock
                  | script commentblock
                  | script textblock
                  | script textspan
                  | script filterblock
                  | script functionblock
                  | script interfaceblock
                  | script ifelfiblock
                  | script forblock
                  | script whileblock"""
        p[0] = Script(p.parser, p[1], p[2])

    #-- Prolog

    def p_docytpe(self, p):
        """doctype  : DOCTYPE NEWLINES"""
        terms = [(DOCTYPE, 1), (NEWLINES, 2)]
        p[0] = DocType(p.parser, *self._buildterms(p, terms))

    def p_body(self, p):
        """body     : BODY NEWLINES"""
        terms = [(BODY, 1), (NEWLINES, 2)]
        p[0] = Body(p.parser, *self._buildterms(p, terms))

    def p_importas(self, p):
        """importas : IMPORT NEWLINES"""
        terms = [(IMPORT, 1), (NEWLINES, 2)]
        p[0] = ImportAs(p.parser, *self._buildterms(p, terms))

    def p_inherit(self, p):
        """inherit  : INHERIT NEWLINES"""
        terms = [(INHERIT, 1), (NEWLINES, 2)]
        p[0] = Inherit(p.parser, *self._buildterms(p, terms))

    def p_implement(self, p):
        """implement : IMPLEMENT NEWLINES"""
        terms = [(IMPLEMENT, 1), (NEWLINES, 2)]
        p[0] = Implement(p.parser, *self._buildterms(p, terms))

    def p_use(self, p):
        """use      : USE NEWLINES"""
        terms = [(USE, 1), (NEWLINES, 2)]
        p[0] = Use(p.parser, *self._buildterms(p, terms))

    def p_commentline(self, p):
        """commentline  : COMMENTLINE NEWLINES"""
        terms = [(COMMENTLINE, 1), (NEWLINES, 2)]
        p[0] = CommentLine(p.parser, *self._buildterms(p, terms))

    def p_commentblock(self, p):
        """commentblock : COMMENTOPEN COMMENTTEXT COMMENTCLOSE NEWLINES
                        | COMMENTOPEN COMMENTCLOSE NEWLINES"""
        if len(p) == 5:
            terms = [(COMMENTOPEN, 1), (COMMENTTEXT, 2), (COMMENTCLOSE, 3),
                     (NEWLINES, 4)]
        else:
            terms = [(COMMENTOPEN, 1), None, (COMMENTCLOSE, 2), (NEWLINES, 3)]
        p[0] = CommentBlock(p.parser,
                            *self._buildterms(p, terms),
                            prolog=self.prolog)

    #---- Script lines

    def p_statement(self, p):
        """statement    : STATEMENT NEWLINES"""
        terms = [(STATEMENT, 1), (NEWLINES, 2)]
        p[0] = Statement(p.parser, *self._buildterms(p, terms))

    def p_tagline(self, p):
        """tagline      : tagspans NEWLINES"""
        p[1] = p[1].right_recursive(None, p[1].TAGBEGIN, p[1].text)
        terms = [p[1], (NEWLINES, 2)]
        p[0] = TagLine(p.parser, *self._buildterms(p, terms))

    def p_tagspans_1(self, p):
        """tagspans     : TAGBEGIN
                        | TAGBEGIN text"""
        if len(p) == 2:
            terms = [None, (TAGBEGIN, 1), None]
        else:
            terms = [None, (TAGBEGIN, 1), p[2]]
        p[0] = TagSpans(p.parser, *self._buildterms(p, terms))

    def p_tagspans_2(self, p):
        """tagspans     : tagspans TAGBEGIN 
                        | tagspans TAGBEGIN text"""
        if len(p) == 3:
            terms = [p[1], (TAGBEGIN, 2), None]
        else:
            terms = [p[1], (TAGBEGIN, 2), p[3]]
        p[0] = TagSpans(p.parser, *self._buildterms(p, terms))

    def p_text(self, p):
        """text         : TEXT
                        | text TEXT"""
        terms = [None, (TEXT, 1)] if len(p) == 2 else [p[1], (TEXT, 2)]
        p[0] = Text(p.parser, *self._buildterms(p, terms))

    def p_textspan(self, p):
        """textspan     : text tagspans NEWLINES"""
        p[2] = p[2].right_recursive(None, p[2].TAGBEGIN, p[2].text)
        terms = [p[1], p[2], (NEWLINES, 3)]
        p[0] = TextSpan(p.parser, *self._buildterms(p, terms))

    #---- Script blocks

    def p_tagblock_1(self, p):
        """tagblock     : tagline INDENT script DEDENT"""
        terms = [p[1], (INDENT, 2), p[3], (DEDENT, 4)]
        p[0] = TagBlock(p.parser, *self._buildterms(p, terms))

    def p_tagblock_2(self, p):
        """tagblock     : tagline INDENT DEDENT"""
        terms = [p[1], (INDENT, 2), None, (DEDENT, 3)]
        p[0] = TagBlock(p.parser, *self._buildterms(p, terms))

    def p_textblock_1(self, p):
        """textblock    : text NEWLINES"""
        terms = [None, p[1], (NEWLINES, 2), None, None, None]
        p[0] = TextBlock(p.parser, *self._buildterms(p, terms))

    def p_textblock_2(self, p):
        """textblock    : textblock text NEWLINES"""
        terms = [p[1], p[2], (NEWLINES, 3), None, None, None]
        p[0] = TextBlock(p.parser, *self._buildterms(p, terms))

    def p_textblock_3(self, p):
        """textblock    : textblock INDENT script DEDENT"""
        terms = [p[1], None, None, (INDENT, 2), p[3], (DEDENT, 4)]
        p[0] = TextBlock(p.parser, *self._buildterms(p, terms))

    def p_textblock_4(self, p):
        """textblock    : textblock INDENT DEDENT"""
        terms = [p[1], None, None, (INDENT, 2), None, (DEDENT, 3)]
        p[0] = TextBlock(p.parser, *self._buildterms(p, terms))

    def p_filterblock_1(self, p):
        """filterblock : FILTEROPEN NEWLINES filtertext FILTERCLOSE NEWLINES"""
        terms = [(FILTEROPEN, 1), (NEWLINES, 2), p[3], (FILTERCLOSE, 4),
                 (NEWLINES, 5)]
        assert p[1][0].startswith(p[4][0].strip(' \t'))
        p[0] = FilterBlock(p.parser, *self._buildterms(p, terms))

    def p_filterblock_2(self, p):
        """filterblock : FILTEROPEN NEWLINES FILTERCLOSE NEWLINES"""
        terms = [(FILTEROPEN, 1), (NEWLINES, 2), None, (FILTERCLOSE, 3),
                 (NEWLINES, 4)]
        p[0] = FilterBlock(p.parser, *self._buildterms(p, terms))

    def p_filtertext(self, p):
        """filtertext       : FILTERTEXT NEWLINES
                            | filtertext FILTERTEXT NEWLINES"""
        if len(p) == 4:
            p[1].append(FILTERTEXT(p.parser, p[2][1], p[2][0] + p[3][0]))
            p[0] = p[1]
        else:
            p[0] = [FILTERTEXT(p.parser, p[1][1], p[1][0] + p[2][0])]

    def p_functionblock_1(self, p):
        """functionblock : DECORATOR NEWLINES FUNCTION NEWLINES INDENT script DEDENT"""
        terms = [(DECORATOR, 1), (NEWLINES, 2), (FUNCTION, 3), (NEWLINES, 4),
                 (INDENT, 5), p[6], (DEDENT, 7)]
        p[0] = FunctionBlock(p.parser, *self._buildterms(p, terms))

    def p_functionblock_2(self, p):
        """functionblock : DECORATOR NEWLINES FUNCTION NEWLINES INDENT DEDENT"""
        terms = [(DECORATOR, 1), (NEWLINES, 2), (FUNCTION, 3), (NEWLINES, 4),
                 (INDENT, 5), None, (DEDENT, 6)]
        p[0] = FunctionBlock(p.parser, *self._buildterms(p, terms))

    def p_functionblock_3(self, p):
        """functionblock : FUNCTION NEWLINES INDENT script DEDENT"""
        terms = [
            None, None, (FUNCTION, 1), (NEWLINES, 2), (INDENT, 3), p[4],
            (DEDENT, 5)
        ]
        p[0] = FunctionBlock(p.parser, *self._buildterms(p, terms))

    def p_functionblock_4(self, p):
        """functionblock : FUNCTION NEWLINES INDENT DEDENT"""
        terms = [
            None, None, (FUNCTION, 1), (NEWLINES, 2), (INDENT, 3), None,
            (DEDENT, 4)
        ]
        p[0] = FunctionBlock(p.parser, *self._buildterms(p, terms))

    def p_interfaceblock_1(self, p):
        """interfaceblock   : INTERFACE NEWLINES INDENT script DEDENT"""
        terms = [(INTERFACE, 1), (NEWLINES, 2), (INDENT, 3), p[4], (DEDENT, 5)]
        p[0] = InterfaceBlock(p.parser, *self._buildterms(p, terms))

    def p_interfaceblock_2(self, p):
        """interfaceblock   : INTERFACE NEWLINES INDENT DEDENT"""
        terms = [(INTERFACE, 1), (NEWLINES, 2), (INDENT, 3), None, (DEDENT, 4)]
        p[0] = InterfaceBlock(p.parser, *self._buildterms(p, terms))

    #-- Control blocks  ( if-elif-else / for / while )

    def p_ifelfiblock_1(self, p):
        """ifelfiblock  : ifblock"""
        p[0] = IfelfiBlock(p.parser, None, p[1], None, None)

    def p_ifelfiblock_2(self, p):
        """ifelfiblock  : ifelfiblock elifblock"""
        p[0] = IfelfiBlock(p.parser, p[1], None, p[2], None)

    def p_ifelfiblock_3(self, p):
        """ifelfiblock  : ifelfiblock elseblock"""
        p[0] = IfelfiBlock(p.parser, p[1], None, None, p[2])

    def p_ifblock_1(self, p):
        """ifblock      : IF NEWLINES INDENT script DEDENT"""
        terms = [(IF, 1), (NEWLINES, 2), (INDENT, 3), p[4], (DEDENT, 5)]
        p[0] = IfBlock(p.parser, *self._buildterms(p, terms))

    def p_ifblock_2(self, p):
        """ifblock      : IF NEWLINES INDENT DEDENT"""
        terms = [(IF, 1), (NEWLINES, 2), (INDENT, 3), None, (DEDENT, 4)]
        p[0] = IfBlock(p.parser, *self._buildterms(p, terms))

    def p_elifblock_1(self, p):
        """elifblock    : ELIF NEWLINES INDENT script DEDENT"""
        terms = [(ELIF, 1), (NEWLINES, 2), (INDENT, 3), p[4], (DEDENT, 5)]
        p[0] = ElifBlock(p.parser, *self._buildterms(p, terms))

    def p_elifblock_2(self, p):
        """elifblock    : ELIF NEWLINES INDENT DEDENT"""
        terms = [(ELIF, 1), (NEWLINES, 2), (INDENT, 3), None, (DEDENT, 4)]
        p[0] = ElifBlock(p.parser, *self._buildterms(p, terms))

    def p_elseblock_1(self, p):
        """elseblock    : ELSE NEWLINES INDENT script DEDENT"""
        terms = [(ELSE, 1), (NEWLINES, 2), (INDENT, 3), p[4], (DEDENT, 5)]
        p[0] = ElseBlock(p.parser, *self._buildterms(p, terms))

    def p_elseblock_2(self, p):
        """elseblock    : ELSE NEWLINES INDENT DEDENT"""
        terms = [(ELSE, 1), (NEWLINES, 2), (INDENT, 3), None, (DEDENT, 4)]
        p[0] = ElseBlock(p.parser, *self._buildterms(p, terms))

    def p_forblock_1(self, p):
        """forblock     : FOR NEWLINES INDENT script DEDENT"""
        terms = [(FOR, 1), (NEWLINES, 2), (INDENT, 3), p[4], (DEDENT, 5)]
        p[0] = ForBlock(p.parser, *self._buildterms(p, terms))

    def p_forblock_2(self, p):
        """forblock     : FOR NEWLINES INDENT DEDENT"""
        terms = [(FOR, 1), (NEWLINES, 2), (INDENT, 3), None, (DEDENT, 4)]
        p[0] = ForBlock(p.parser, *self._buildterms(p, terms))

    def p_whileblock_1(self, p):
        """whileblock   : WHILE NEWLINES INDENT script DEDENT"""
        terms = [(WHILE, 1), (NEWLINES, 2), (INDENT, 3), p[4], (DEDENT, 5)]
        p[0] = WhileBlock(p.parser, *self._buildterms(p, terms))

    def p_whileblock_2(self, p):
        """whileblock   : WHILE NEWLINES INDENT DEDENT"""
        terms = [(WHILE, 1), (NEWLINES, 2), (INDENT, 3), None, (DEDENT, 4)]
        p[0] = WhileBlock(p.parser, *self._buildterms(p, terms))

    def p_error(self, p):
        if p:
            column = self.ttllex._find_tok_column(p)
            self._parse_error('before: %s ' % (p.value, ),
                              self._coord(p.lineno, column))
        else:
            self._parse_error('At end of input', '')
コード例 #4
0
ファイル: parser.py プロジェクト: prataprc/tayra
class TTLParser( object ):
    """Parser class for tayra template language. This class is initialized
    with :class:`tayra.compiler.TTLCompiler` instance, and  configuration
    options like, ``optimize``, ``lex_debug``, ``lextab``, ``yacc_debug``,
    ``yacctab``, ``yacc_outputdir``.

    It might be a costly operation to create a TTLParser instance every time,
    and to avoid that use _initialize() method before starting off with a new
    template.
    """

    def __init__( self, compiler ) :
        self.compiler = compiler

    def _initialize( self, ttlfile=None ) :
        optimize = self.compiler['optimize']
        lex_debug = self.compiler['lex_debug']
        lextab = self.compiler['lextab']
        yacc_debug = self.compiler['yacc_debug']
        yacctab = self.compiler['yacctab']
        yacc_outputdir = self.compiler['yacc_outputdir']

        self.ttlfile = ttlfile

        # Build Lexer
        self.ttllex = TTLLexer( self.compiler )
        kwargs = { 'ttlfile' : ttlfile }
        kwargs.update( optimize=optimize ) if optimize else None
        kwargs.update( debug=lex_debug )
        kwargs.update( lextab=lextab ) if lextab else None
        self.ttllex.build( **kwargs )
        self.tokens = self.ttllex.tokens    # Important for YACCer
        self.ttllex.reset_lineno()

        # Build Yaccer
        kwargs = {'optimize' : optimize} if optimize else {}
        kwargs.update( debug=yacc_debug )
        kwargs.update( outputdir=yacc_outputdir ) if yacc_outputdir else None
        kwargs.update( tabmodule=yacctab )
        self.parser = ply.yacc.yacc( module=self, **kwargs )
        self.parser.ttlparser = self     # For AST nodes to access `this`
        self.parser.compiler = self.compiler

        self.prolog = True

    # ------------------------- Private functions -----------------------------

    def _coord( self, lineno, column=None ):
        return Coord( file=self.ttllex.ttlfile, line=lineno, column=column )
    
    def _parse_error(self, msg, coord):
        raise Exception("%s: %s" % (coord, msg))

    def _buildterms( self, p, terms ) :
        """Convenience function to construct Terminal nodes from parser object
        ``p``."""
        rc = []
        for t in terms :
            if t == None : 
                rc.append( None )
                continue
            elif isinstance(t, tuple) :
                cls, (val, lineno) = t[0], p[ t[1] ]
                rc.append( cls( p.parser, lineno, val ))
            else :
                rc.append(t)
        return rc
 
    #---- API methods.

    def parse( self, text, ttlfile=None, debuglevel=0 ):
        """Parse tayra templage text, ``text`` and return an AST tree. AST
        tree starts with a root node :class:`tayra.ast.Template`. Every time
        the parser is invoked the lexer, yaccer, app options will be reused.

        ``ttlfile``
            Name of the file being parsed (for meaningful error messages)

        ``debuglevel``
            Debug level to yacc
        """
        # Parser Initialize
        self._initialize( ttlfile=ttlfile )
        # parse and get the Translation Unit
        self.ast = self.parser.parse(text, lexer=self.ttllex, debug=debuglevel)
        return self.ast

    # ---------- Precedence and associativity of operators --------------------

    precedence = (
    )

    def p_template_1( self, p ) :
        """template : script"""
        p[0] = Template( p.parser, p[1] )

    def p_template_2( self, p ) :
        """template : """
        p[0] = Template( p.parser, None )

    def p_script_1( self, p ) :
        """script : doctype
                  | body
                  | importas
                  | inherit
                  | implement
                  | use
                  | statement
                  | tagline
                  | commentline
                  | tagblock
                  | commentblock
                  | textblock
                  | textspan
                  | filterblock
                  | functionblock
                  | interfaceblock
                  | ifelfiblock
                  | forblock
                  | whileblock"""
        p[0] = Script( p.parser, None, p[1] )

    def p_script_2( self, p ) :
        """script : script doctype
                  | script body
                  | script importas
                  | script inherit
                  | script implement
                  | script use
                  | script statement
                  | script tagline
                  | script commentline
                  | script tagblock
                  | script commentblock
                  | script textblock
                  | script textspan
                  | script filterblock
                  | script functionblock
                  | script interfaceblock
                  | script ifelfiblock
                  | script forblock
                  | script whileblock"""
        p[0] = Script( p.parser, p[1], p[2] )

    #-- Prolog

    def p_docytpe( self, p ) :
        """doctype  : DOCTYPE NEWLINES"""
        terms = [ (DOCTYPE, 1), (NEWLINES, 2) ]
        p[0] = DocType( p.parser, *self._buildterms( p, terms ))

    def p_body( self, p ) :
        """body     : BODY NEWLINES"""
        terms = [ (BODY, 1), (NEWLINES, 2) ]
        p[0] = Body( p.parser, *self._buildterms( p, terms ))

    def p_importas( self, p ) :
        """importas : IMPORT NEWLINES"""
        terms = [ (IMPORT, 1), (NEWLINES, 2) ]
        p[0] = ImportAs( p.parser, *self._buildterms( p, terms ))

    def p_inherit( self, p ) :
        """inherit  : INHERIT NEWLINES"""
        terms = [ (INHERIT, 1), (NEWLINES, 2) ]
        p[0] = Inherit( p.parser, *self._buildterms( p, terms ))

    def p_implement( self, p ) :
        """implement : IMPLEMENT NEWLINES"""
        terms = [ (IMPLEMENT, 1), (NEWLINES, 2) ]
        p[0] = Implement( p.parser, *self._buildterms( p, terms ))

    def p_use( self, p ) :
        """use      : USE NEWLINES"""
        terms = [ (USE, 1), (NEWLINES, 2) ]
        p[0] = Use( p.parser, *self._buildterms( p, terms ))

    def p_commentline( self, p ) :
        """commentline  : COMMENTLINE NEWLINES"""
        terms = [ (COMMENTLINE, 1), (NEWLINES, 2) ]
        p[0] = CommentLine( p.parser, *self._buildterms( p, terms ))

    def p_commentblock( self, p ) :
        """commentblock : COMMENTOPEN COMMENTTEXT COMMENTCLOSE NEWLINES
                        | COMMENTOPEN COMMENTCLOSE NEWLINES"""
        if len(p) == 5 :
            terms = [ (COMMENTOPEN,1), (COMMENTTEXT,2), (COMMENTCLOSE,3),
                      (NEWLINES,4) ]
        else :
            terms = [ (COMMENTOPEN,1), None, (COMMENTCLOSE,2), (NEWLINES,3) ]
        p[0] = CommentBlock( p.parser, *self._buildterms( p, terms ), 
                             prolog=self.prolog )

    #---- Script lines

    def p_statement( self, p ) :
        """statement    : STATEMENT NEWLINES"""
        terms = [ (STATEMENT, 1), (NEWLINES, 2) ]
        p[0] = Statement( p.parser, *self._buildterms(p, terms) )

    def p_tagline( self, p ) :
        """tagline      : tagspans NEWLINES"""
        p[1] = p[1].right_recursive( None, p[1].TAGBEGIN, p[1].text )
        terms = [ p[1], (NEWLINES, 2) ]
        p[0] = TagLine( p.parser, *self._buildterms(p, terms) )

    def p_tagspans_1( self, p ):
        """tagspans     : TAGBEGIN
                        | TAGBEGIN text"""
        if len(p) == 2 :
            terms = [ None, (TAGBEGIN,1), None ]
        else :
            terms = [ None, (TAGBEGIN,1), p[2] ]
        p[0] = TagSpans( p.parser, *self._buildterms(p, terms) )

    def p_tagspans_2( self, p ):
        """tagspans     : tagspans TAGBEGIN 
                        | tagspans TAGBEGIN text"""
        if len(p) == 3 :
            terms = [ p[1], (TAGBEGIN,2), None ]
        else :
            terms = [ p[1], (TAGBEGIN,2), p[3] ]
        p[0] = TagSpans( p.parser, *self._buildterms(p, terms) )

    def p_text( self, p ):
        """text         : TEXT
                        | text TEXT"""
        terms = [ None, (TEXT,1) ] if len(p) == 2 else [ p[1], (TEXT,2) ]
        p[0] = Text( p.parser, *self._buildterms(p, terms) )

    def p_textspan( self, p ):
        """textspan     : text tagspans NEWLINES"""
        p[2] = p[2].right_recursive( None, p[2].TAGBEGIN, p[2].text )
        terms = [ p[1], p[2], (NEWLINES,3) ] 
        p[0] = TextSpan( p.parser, *self._buildterms(p, terms) )

    #---- Script blocks

    def p_tagblock_1( self, p ) :
        """tagblock     : tagline INDENT script DEDENT"""
        terms = [ p[1], (INDENT,2), p[3], (DEDENT,4) ]
        p[0] = TagBlock( p.parser, *self._buildterms( p, terms ) )

    def p_tagblock_2( self, p ) :
        """tagblock     : tagline INDENT DEDENT"""
        terms = [ p[1], (INDENT,2), None, (DEDENT,3) ]
        p[0] = TagBlock( p.parser, *self._buildterms( p, terms ) )

    def p_textblock_1( self, p ) :
        """textblock    : text NEWLINES"""
        terms = [ None, p[1], (NEWLINES, 2), None, None, None ]
        p[0] = TextBlock( p.parser, *self._buildterms(p, terms) )

    def p_textblock_2( self, p ):
        """textblock    : textblock text NEWLINES"""
        terms = [ p[1], p[2], (NEWLINES, 3), None, None, None ]
        p[0] = TextBlock( p.parser, *self._buildterms(p, terms) )

    def p_textblock_3( self, p ) :
        """textblock    : textblock INDENT script DEDENT"""
        terms = [ p[1], None, None, (INDENT,2), p[3], (DEDENT,4) ]
        p[0] = TextBlock( p.parser, *self._buildterms(p, terms) )

    def p_textblock_4( self, p ) :
        """textblock    : textblock INDENT DEDENT"""
        terms = [ p[1], None, None, (INDENT,2), None, (DEDENT,3) ]
        p[0] = TextBlock( p.parser, *self._buildterms(p, terms) )

    def p_filterblock_1( self, p ) :
        """filterblock : FILTEROPEN NEWLINES filtertext FILTERCLOSE NEWLINES"""
        terms = [ (FILTEROPEN,1), (NEWLINES, 2), p[3], (FILTERCLOSE,4),
                  (NEWLINES,5) ]
        assert p[1][0].startswith( p[4][0].strip(' \t') )
        p[0] = FilterBlock( p.parser, *self._buildterms( p, terms ) )

    def p_filterblock_2( self, p ) :
        """filterblock : FILTEROPEN NEWLINES FILTERCLOSE NEWLINES"""
        terms = [ (FILTEROPEN,1), (NEWLINES,2), None, (FILTERCLOSE,3),
                  (NEWLINES,4) ]
        p[0] = FilterBlock( p.parser, *self._buildterms( p, terms ) )

    def p_filtertext( self, p ):
        """filtertext       : FILTERTEXT NEWLINES
                            | filtertext FILTERTEXT NEWLINES"""
        if len(p) == 4 :
            p[1].append( FILTERTEXT( p.parser, p[2][1], p[2][0]+p[3][0] ))
            p[0] = p[1]
        else :
            p[0] = [ FILTERTEXT( p.parser, p[1][1], p[1][0]+p[2][0] ) ]

    def p_functionblock_1( self, p ) :
        """functionblock : DECORATOR NEWLINES FUNCTION NEWLINES INDENT script DEDENT"""
        terms = [ (DECORATOR, 1), (NEWLINES, 2), (FUNCTION,3), (NEWLINES, 4),
                  (INDENT,5), p[6], (DEDENT,7) ]
        p[0] = FunctionBlock( p.parser, *self._buildterms(p, terms) )

    def p_functionblock_2( self, p ) :
        """functionblock : DECORATOR NEWLINES FUNCTION NEWLINES INDENT DEDENT"""
        terms = [ (DECORATOR,1), (NEWLINES, 2), (FUNCTION,3), (NEWLINES, 4),
                  (INDENT,5), None, (DEDENT,6) ]
        p[0] = FunctionBlock( p.parser, *self._buildterms(p, terms) )

    def p_functionblock_3( self, p ) :
        """functionblock : FUNCTION NEWLINES INDENT script DEDENT"""
        terms = [ None, None, (FUNCTION,1), (NEWLINES, 2), (INDENT,3), 
                  p[4], (DEDENT,5) ]
        p[0] = FunctionBlock( p.parser, *self._buildterms(p, terms) )

    def p_functionblock_4( self, p ) :
        """functionblock : FUNCTION NEWLINES INDENT DEDENT"""
        terms = [ None, None, (FUNCTION,1), (NEWLINES, 2), (INDENT,3),
                  None, (DEDENT,4) ]
        p[0] = FunctionBlock( p.parser, *self._buildterms(p, terms) )

    def p_interfaceblock_1( self, p ) :
        """interfaceblock   : INTERFACE NEWLINES INDENT script DEDENT"""
        terms = [ (INTERFACE,1), (NEWLINES, 2), (INDENT,3), p[4], (DEDENT,5) ]
        p[0] = InterfaceBlock( p.parser, *self._buildterms(p, terms) )

    def p_interfaceblock_2( self, p ) :
        """interfaceblock   : INTERFACE NEWLINES INDENT DEDENT"""
        terms = [ (INTERFACE,1), (NEWLINES, 2), (INDENT,3), None, (DEDENT,4) ]
        p[0] = InterfaceBlock( p.parser, *self._buildterms(p, terms) )

    #-- Control blocks  ( if-elif-else / for / while )

    def p_ifelfiblock_1( self, p ) :
        """ifelfiblock  : ifblock"""
        p[0] = IfelfiBlock( p.parser, None, p[1], None, None )

    def p_ifelfiblock_2( self, p ) :
        """ifelfiblock  : ifelfiblock elifblock"""
        p[0] = IfelfiBlock( p.parser, p[1], None, p[2], None )

    def p_ifelfiblock_3( self, p ) :
        """ifelfiblock  : ifelfiblock elseblock"""
        p[0] = IfelfiBlock( p.parser, p[1], None, None, p[2] )

    def p_ifblock_1( self, p ) :
        """ifblock      : IF NEWLINES INDENT script DEDENT"""
        terms = [ (IF,1), (NEWLINES, 2), (INDENT,3), p[4], (DEDENT,5) ]
        p[0] = IfBlock( p.parser, *self._buildterms(p, terms) )

    def p_ifblock_2( self, p ) :
        """ifblock      : IF NEWLINES INDENT DEDENT"""
        terms = [ (IF,1), (NEWLINES, 2), (INDENT,3), None, (DEDENT,4) ]
        p[0] = IfBlock( p.parser, *self._buildterms(p, terms) )

    def p_elifblock_1( self, p ) :
        """elifblock    : ELIF NEWLINES INDENT script DEDENT"""
        terms = [ (ELIF,1), (NEWLINES,2), (INDENT,3), p[4], (DEDENT,5) ]
        p[0] = ElifBlock( p.parser, *self._buildterms(p, terms) )

    def p_elifblock_2( self, p ) :
        """elifblock    : ELIF NEWLINES INDENT DEDENT"""
        terms = [ (ELIF,1), (NEWLINES,2), (INDENT,3), None, (DEDENT,4) ]
        p[0] = ElifBlock( p.parser, *self._buildterms(p, terms) )

    def p_elseblock_1( self, p ) :
        """elseblock    : ELSE NEWLINES INDENT script DEDENT"""
        terms = [ (ELSE,1), (NEWLINES,2), (INDENT,3), p[4], (DEDENT,5) ]
        p[0] = ElseBlock( p.parser, *self._buildterms(p, terms) )

    def p_elseblock_2( self, p ) :
        """elseblock    : ELSE NEWLINES INDENT DEDENT"""
        terms = [ (ELSE,1), (NEWLINES,2), (INDENT,3), None, (DEDENT,4) ]
        p[0] = ElseBlock( p.parser, *self._buildterms(p, terms) )

    def p_forblock_1( self, p ) :
        """forblock     : FOR NEWLINES INDENT script DEDENT"""
        terms = [ (FOR,1), (NEWLINES,2), (INDENT,3), p[4], (DEDENT,5) ]
        p[0] = ForBlock( p.parser, *self._buildterms(p, terms) )

    def p_forblock_2( self, p ) :
        """forblock     : FOR NEWLINES INDENT DEDENT"""
        terms = [ (FOR,1), (NEWLINES,2), (INDENT,3), None, (DEDENT,4) ]
        p[0] = ForBlock( p.parser, *self._buildterms(p, terms) )

    def p_whileblock_1( self, p ) :
        """whileblock   : WHILE NEWLINES INDENT script DEDENT"""
        terms = [ (WHILE,1), (NEWLINES,2), (INDENT,3), p[4], (DEDENT,5) ]
        p[0] = WhileBlock( p.parser, *self._buildterms(p, terms) )

    def p_whileblock_2( self, p ) :
        """whileblock   : WHILE NEWLINES INDENT DEDENT"""
        terms = [ (WHILE,1), (NEWLINES,2), (INDENT,3), None, (DEDENT,4) ]
        p[0] = WhileBlock( p.parser, *self._buildterms(p, terms) )

    def p_error( self, p ):
        if p:
            column = self.ttllex._find_tok_column( p )
            self._parse_error( 'before: %s ' % (p.value,),
                               self._coord(p.lineno, column) )
        else:
            self._parse_error( 'At end of input', '' )