Lexical Scanning in Go
Intro
The content and motivation of this talk is based on a talk by Rob Pike I stumbled upon on youtube. I also found the slides. I also found a blog post by Eli Bendersky taking the aforementioned talk as a basis for a solution in Python.
My shot at this
So I watched the video and that it was a kinda cool idea, also as a chance to highlight Go’s features nicely. But I didn’t want to dig further for the source code nor was I particularily interested in the exact syntax and semantics of the template engine mentioned in the talk. What I was going for instead was a very simple toy implementation of a potential template engine very loosly influenced from what I saw on the slides, just to get a feel for the idea and Go itself.
The template engine
So here is a description of the toy template eninge:
Variable content, as in the talk, are kept between {{ and }}
An “action” inside these meta characters can have only one of these forms
- .ident
- .ident!mod
- .guard? .ident
- .guard? .ident!mod
ident stands for identifier, mod for modifier and guard for a guard expression
The identifier is a lookup string for a map object called the context. It is used to find the text that has to be substituted for the identifier
The guard is a string that, if it reads “true” causes the meta action to be evaluated, otherwise it is ignored
The modifier can be used to modifiy the string that is specified by ident, in this simple implementation the only supported modifiers are lower and upper
The renderer of the template engine will accept a file path specifying the template, another file path specifying the output file and at last a context (Go map) holding all the variables to look up
Everything else is interpreted as text and copied as is to the rendered document
The implementation
So without much more talking here comes the code. As already mentioned above a lot of it is based on Rob Pike’s work. I just filled in some gaps here and there to make the the lexer fully working. In the end follows a little script that tests the library.
The output
./template_test /path/to/template.txt path/to/out.txt && cat path/to/out.txt
Starting to lex
path/to/template.txt
(Text,Hello )
(LeftMeta,{{)
(Operator,.)
(Identifier,print)
(Operator,?)
(Operator,.)
(Identifier,who)
(Operator,!)
(Identifier,upper)
(RightMeta,}})
(Text,, from )
(LeftMeta,{{)
(Operator,.)
(Identifier,print)
(Operator,?)
(Operator,.)
(Identifier,name)
(Operator,!)
(Identifier,lower)
(RightMeta,}})
(Text,!
)
Finished lexing...
Rendering template now to
path/to/out.txt
Hello WORLD, from lexer!