Important: the script on this page is now obsolete. A much more capable script is in scripts.leo on Leo's cvs site. However, this page still has historical interest: it shows the simple beginnings of what turned into a sophisticated project.

Here is a cute Leo script. It makes two passes over a Leo outline and puts its results in the body text of node whose headline is @scan_g.es_results. Call this the root node.

The first pass discovers all variables used in g.es statements. At the end of pass 1, the script creates dummy bindings for all these variables at the start of the root node. The second pass adds the g.es statements themselves at the end of the root node.

Thus, after this script, you can execute the body text of the root node. In particular, assuming that g.translateString converts all translated text to upper case, it is easy to tell which strings have been properly (or improperly) translated, and which have been properly (or improperly) not translated.

Here is the "flattened" version of the script. The actual script is in scripts.leo:

# @button print g.es stats
@first # -*- coding: utf-8 -*-

class controllerClass:

    def __init__ (self,c,p):
        self.c = c
        self.p = p
        self.d = {}
        self.results = [] # The future contents of the results node.
        self.root = g.findNodeAnywhere(c,'@scan_g.es_results')
        self.traceFlag = False

    def error (self,s):
        if self.thePass == 2:
            self.out('***error: %s' % s)

    def out (self,s):
        self.results.append(s)

    def trace(self,s):
        if self.traceFlag and self.thePass == 2:
            self.out(s)

    def outDict(self):
        self.out('# Bindings...')
        for z in self.d.keys():
            self.out('%s = "%s"' % (z,z))

    def scan(self,thePass):
        self.thePass = thePass
        for p in self.p.self_and_subtree_iter():
            self.scanNode(p)

    def scanIds(self,s):
        i = 0
        while i < len(s):
            progress = i ; ch = s[i]
            if ch in ("'",'"',):
                i = g.skip_python_string(s,i)
            elif g.is_c_id(ch):
                j = i ; k = g.skip_id(s,i)
                theId = s[j:k]
                self.trace('id: %s' % (theId))
                self.d[theId] = theId
                i = j
            i = max(i,progress+1)

    def scanMatch(self,s):
        if self.thePass == 2:
            self.out(g.toEncodedString(s,'ascii'))
        i = s.find('(')
        assert(i > -1)
        assert s[-1] == ')'
        # Find all args.
        s = s[i+1:-1]
        # self.trace('args: %s' % (s))
        i = 0 ; n = 1
        keywords = ('color','newline') # Keyword args to g.es
        while i < len(s):
            progress = i ; ch = s[i] ; arg = None
            if ch in ("'",'"',):
                # Skip a string, and possible %
                start = i
                i = g.skip_python_string(s,i)
                if i > start:
                    j = g.skip_ws(s,i)
                    if g.match(s,j,'%'):
                        i = j + 1
                        i = g.skip_ws(s,i)
                        if g.match(s,i,'('):
                            j = g.skip_matching_python_parens(s,i)
                            if j == -1:
                                self.error('**incomplete: %s' % (s[start:i+1]))
                            else:
                                arg = s[start:j+1]
                                self.scanIds(s[i+1:j])
                                i = j
                        elif i < len(s) and g.is_c_id(s[i]):
                            j = i ; k = g.skip_id(s,i)
                            theId = s[j:k]
                            self.trace('id: %s' % (theId))
                            self.d[theId] = theId
                            arg = s[start:k]
                            i = k
                    else:
                        arg = s[start:i]
            elif g.is_c_id(ch):
                start = i ; i = g.skip_id(s,i)
                theId = s[start:i]
                if theId in keywords:
                    i = len(s)
                else:
                    self.trace('id: %s' % (theId))
                    arg = s[start:i]
                    self.d[arg] = theId

            if arg and arg.strip():
                # self.trace('arg %s %s' % (n,arg.strip()))
                n += 1
            i = max(i,progress+1)

    def scanNode(self,p):
        s,h = p.bodyString(), p.headString()
        if h.startswith('@thin') and self.thePass == 2:
            self.out('# -- %s' % (h))
        tags = ('g.es','g.es_print',)
        i = 0 ; print_h = True
        while i < len(s):
            progress = i
            for tag in tags:
                if g.match_word(s,i,tag):
                    start = i ; i += len(tag)
                    i = g.skip_ws(s,i)
                    if g.match(s,i,'('):
                        j = g.skip_matching_python_parens(s,i)
                        if j == -1:
                            self.error('incomplete %s' % s[start:i])
                        else:
                            if print_h and self.thePass == 2:
                                if not h.startswith('@thin'): self.out('# %s' % (h))
                                print_h = False
                            self.scanMatch(s[start:j+1])
                            i = j
                    break
            i = max(i,progress+1)

    def setResults (self):
        results = '\n'.join(self.results)
        if self.root:
            c.setBodyString(self.root,results)
        else:
            print '@results node not found'
            print results


x = controllerClass(c,p)
x.scan(thePass=1)
x.outDict()
x.scan(thePass=2)
x.setResults()
g.es('done')