Code generation

As with semantic analysis, code generation is a tree traversal process, where methods are called by node name. So we can define what «code» should be generated for a particular node.

The code:

#!/usr/bin/env python
 
import spark
from helper import getValue
 
device = 'htb0'
 
root_setup = """#
#
# "%s":
tc qdisc add dev %s root handle %s: htb
tc class add dev %s parent %s classid %s htb rate %skbit ceil %skbit"""
 
class_setup = """
#
# Client "%s":
tc class add dev %s parent %s classid %s htb rate %skbit ceil %skbit"""
 
 
class Interpret(spark.GenericASTTraversal):
    def __init__(self, ast):
        spark.GenericASTTraversal.__init__(self, ast)
        self.postorder()
 
    def n_root(self, node):
        # generate code:
        handle = node.classid.attr.split(':')[0]
        node.code = root_setup % (
            getValue(node, 'descr'),
            device,
            handle,
            device,
            '%s:%s' % (handle, 0),
            node.classid,
            getValue(node, 'rate'),
            getValue(node, 'ceil') or getValue(node, 'rate')
        )
 
    def n_classdef(self, node):
        # generate code:
        node.code = class_setup % (
            getValue(node, 'descr'),
            device,
            node.parent,
            node.classid,
            getValue(node, 'rate'),
            getValue(node, 'ceil') or getValue(node, 'rate')
        )
 
    def default(self, node):
        # "default" will be called for all
        # unmentioned nodes.
        # In our case -- for "classdefs",
        # which contain other classes.
 
        # generate code:
        node.code = '\n'.join([ x.code for x in node.kids ])

default method will be called for all unmentioned nodes. In our case — for «classdefs» and «classes«, which contain HTB classes definitions.

And now our complete script looks like:

#!/usr/bin/env python
 
import spark
import scanner
import parser
import semantic as semant
import generate as generat
 
def scan(f):
    input = f.read()
    scnr = scanner.SimpleScanner()
    return scnr.tokenize(input)
 
def parse(tokens):
    prsr = parser.SimpleParser()
    return prsr.parse(tokens)
 
def semantic(ast):
    semant.HTBCheckTree(ast)
    return ast
 
def generate(ast):
    generat.Interpret(ast)
    return ast
 
 
f = open('test.confg')
 
print generate(semantic(parse(scan(f)))).code

Generated code:

#
#
# "Root_as_root":
tc qdisc add dev htb0 root handle 1: htb
tc class add dev htb0 parent 1:0 classid 1:5 htb rate 10240 ceil 20480
 
#
# Client "My_favorite_client":
tc class add dev htb0 parent 1:5 classid 1:50 htb rate 1024kbit ceil 2048kbit
 
#
# Client "My_other_client":
tc class add dev htb0 parent 1:50 classid 1:53 htb rate 1024kbit ceil 2048kbit

*Still* making errors

Surely — this example is incomplete.

If we will write classes” configurations not in order, in which they must appear in HTB starting script — we will get a non-working script. To have a working one, we must perform stronger semantic checks.

Probably, some other things are silly (from programming’s perspective) — don’t mention it :-)

And, again, we have intentionally built out Abstract Syntax Tree as above, missing some parts of our syntax. For demonstration’s sake.

You can find the code of this example and some other information at Resources page.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.