资源说明:iMatix GSL code generator
# GSL/4.1 - a Universal Code Generator ## Contents **[Overview](#overview)** * [Contributing](#contributing) * [Scope and Goals](#scope-and-goals) * [Ownership and License](#ownership-and-license) [Ownership and License of generated sources](#ownership-and-license-of-generated-sources) * [Building and installing](#building-and-installing) [Building on FreeBSD 10](#building-on-freebsd-10) [Building on Cygwin](#building-on-cygwin) * [This Document](#this-document) **[Starting with GSL](#starting-with-gsl)** * [Hello World](#hello-world) * [Templates and Scripts](#templates-and-scripts) * [Modeling a Web Site](#modeling-a-web-site) * [First Draft](#first-draft) * [Inserting Variables](#inserting-variables) * [Looping through Trees](#looping-through-trees) * [Building the Output](#building-the-output) * [Putting it All Together](#putting-it-all-together) * [Exercise for the Reader](#exercise-for-the-reader) * [Extending the Model](#extending-the-model) **[Model-Oriented Programming](#model-oriented-programming)** * [Becoming a Very Good Programmer](#becoming-a-very-good-programmer) * [Tools that Write Software](#tools-that-write-software) * [Abstractions and Modeling Languages](#abstractions-and-modeling-languages) * [Leverage to Move Mountains](#leverage-to-move-mountains) * [Case Study - OpenAMQ](#case-study---openamq) * [Other Model-Driven Architectures](#other-model-driven-architectures) * [Why use MOP?](#why-use-mop) * [A Short History of Code Generation](#a-short-history-of-code-generation) * [Myths about Code Generation](#myths-about-code-generation) * [The Correctness of Generated Code](#the-correctness-of-generated-code) **[GSL/4.1 Reference Manual](#gsl41-reference-manual)** * [Command-line Syntax](#command-line-syntax) * [Concepts](#concepts) [Scalar Data Types](#scalar-data-types) [Structured Data Types](#structured-data-types) [Constants](#constants) [Scopes](#scopes) [Data Specifiers](#data-specifiers) [Expressions](#expressions) * [Internals](#internals) [Internal Variables](#internal-variables) [Template and Script Modes](#template-and-script-modes) [Template Lines](#template-lines) [Script Lines](#script-lines) [Comments](#comments) [Ignorecase](#ignorecase) [Shuffle](#shuffle) [COBOL](#cobol) [Line Terminators](#line-terminators) [Escape Symbol](#escape-symbol) [Substitute Symbol](#substitute-symbol) [Arguments](#arguments) [Predefined Identifiers](#predefined-identifiers) * [Built-In Functions](#built-in-functions) [Global Functions](#global-functions) [conv](#conv) [diag](#diag) [environment](#environment) [fileio](#fileio) [Directory Iteration](#directory-iteration) [gsl control](#gsl-control) [math](#math) [regexp](#regexp) [process management](#process-management) [script](#script) [socket](#socket) [string](#string) [symb](#symb) [thread](#thread) [time](#time) [XML](#xml) * [Script Commands](#script-commands) [Output File Manipulation](#output-file-manipulation) [Control Structures](#control-structures) [Scope Manipulation](#scope-manipulation) [Symbol Definition](#symbol-definition) [Structured Data Manipulation](#structured-data-manipulation) [Script Manipulation](#script-manipulation) [Macros and Functions](#macros-and-functions) [Miscellaneous](#miscellaneous) [Examples](#examples) ## Overview ### Contributing We use the C4.1 process, see: https://rfc.zeromq.org/spec:22. ### Scope and Goals GSL/4.1 is a code construction tool. It will generate code in all languages and for all purposes. If this sounds too good to be true, welcome to 1996, when we invented these techniques. Magic is simply technology that is twenty years ahead of its time. In addition to code construction, GSL has been used to generate database schema definitions, user interfaces, reports, system administration tools and much more. This is the fourth major version of GSL, now considered a stable product, repackaged together with its dependencies for easy building from git. ### Ownership and License GSL was actively developed by [iMatix Corporation](http://www.imatix.com) from 1995-2005 and is copyright © 1991-2010 iMatix Corporation. Version 4 was developed as part of the technical infrastructure for the [OpenAMQ](http://www.openamq.org) messaging product. The authors grant you free use of this software under the terms of the GNU General Public License version 3 or, at your choice, any later version. (GPLv3+). For details see the files `COPYING` in this directory. #### Ownership and License of generated sources The copyright of the output of GSL is by default the property of the user or whomever writes the template(s). ### Building and installing Dependencies: * pcre package (e.g. libpcre3-dev) To build from git on a UNIX-like box, and install into `/usr/local/bin`: git clone git://github.com/imatix/gsl cd gsl/src make sudo make install To install it to another location, change the last command to: sudo make install DESTDIR=/my/custom/prefix To show command-line help: ./gsl #### Building on FreeBSD 10 Install GNU Make and GNU Compiler. For example, with `pkg`, `pkg install gmake gcc`. Then edit `src/Makefile` and add "-lm" to `src/Makefile` where you see CCLIBS configured. It may look similar to: export CCLIBS = -lpcre You want to add the math library: export CCLIBS = -lpcre -lm Cd to `src` and run: CCNAME=gcc47 gmake gmake install #### Building on Cygwin Install apt-cyg, a cygwin package manager: lynx -source rawgit.com/transcode-open/apt-cyg/master/apt-cyg > apt-cyg install apt-cyg /bin Install git: apt-cyg install git Install gcc's dependencies: apt-cyg install wget gcc-g++ make diffutils libmpfr-devel libgmp-devel libmpc-devel libpcre-devel libcrypt-devel Download, Build and Install gcc: wget http://ftpmirror.gnu.org/gcc/gcc-4.9.2/gcc-4.9.2.tar.gz tar xf gcc-4.9.2.tar.gz mkdir build-gcc && cd build-gcc ../gcc-4.9.2/configure --program-suffix=-4.9.2 --enable-languages=c,c++ --disable-bootstrap --disable-shared make -j4 make install Finally build gsl: git clone git://github.com/imatix/gsl cd gsl/src make make install ### This Document This document was written by Pieter Hintjens in October 2010 based on two 2005 articles on 'model oriented programming', and the GSL reference manual. This text is originally at README.txt and is built using [gitdown](http://github.com/imatix/gitdown). The text was updated by Gyepi Sam in January 2013 to port documentation from earlier versions and to include more examples. ## Starting with GSL GSL is an acronym for Generator Scripting Language. And that is what it does. You write scripts in gsl, feed it some data from some XML files and it generates nicely formatted text files for you. These files can be source code, a web site, a recipe book or whatever you like. Read on to get you started with code generation!### Hello World Our first step is to make a "hello world" program in GSL. It's quite simple. Make a file called `hello.gsl` that contains one line: echo "hello world" To run this, use the following command: gsl hello GSL is a simple language and you'll not have any difficulty understanding its syntax, except in a few places where it does specialised work. It will take you a little longer to understand what you can do with GSL, but that is the real point of these articles. GSL is not as rich as other scripting languages. It is a code generator scripting language, not a programming tool. It lacks some control structures, and it runs a little slowly. Initially, GSL looks like any other scripting language. I can write little scripts like this: amount = 1000 year = 2006 while year < 2026 amount = amount * 1.05 year = year + 1 endwhile echo amount Which calculates the value of my savings account if I were to leave it untouched for twenty years, and the interest rate were steady at five percent. Note these syntax aspects: * `variable = expression` - Assign a value to a variable * `while condition... endwhile` - Repeat a block while the condition is true To run the above program, assuming it was saved in a file called `interest.gsl`, I type this command: gsl interest This executes the script and tells me that if I am really patient, I'll be rich one day. Now I'm going to change this little program to make the same kind of calculation for different amounts, rates, and years. Where do I put these different terms and rates? The answer is, in an XML file. The file is called `deposits.xml`: We change our script to give the result below. .template 0 for deposit year = 1 accumulated = amount while year < years accumulated = accumulated * (rate / 100 + 1) year = year + 1 endwhile echo "Original amount:" + amount + " becomes: " + accumulated endfor .endtemplate Note these syntax aspects: * `.template 0` - Start script (non-template) block * `for ` - Repeat block for all instances of child item called `childname` We will run the new interest calculation script using this command: gsl deposits.xml Note the change of command syntax. We first ran the GSL script. Now we're running the XML file. This is one of GSL's features - you can run XML files as if they were scripts. It's the `script =` setting that does the trick, working much like the hash-bang `#!` command in Linux. Any GSL script, no matter how simple, works with an XML document loaded into GSL's memory as a data tree. In our first `interest.gsl` script, the data tree contains just this: GSL automatically creates this data tree when we ask it to execute a GSL script. If, on the other hand, we ask GSL to execute an XML file, it loads this XML file into its data tree. Assuming we also asked for it, it will then execute a GSL script against that XML tree. Technically speaking, GSL searches the root item - which can have any name - for an attribute called "script". We can put attributes into the root item in several ways. One is to simply add them to the XML file, as we did. The other is to place them on the command line, like this: gsl -script:interest deposits.xml All variables that we define and use are stored in the data tree, somewhere. This is the only data structure that GSL scripts work with, and it can get very complex. For many people, understanding this complexity is the most difficult thing about using GSL - hierarchies of data are one of those things most human brains do not handle very well. We use abstractions like XNF to make this simpler, but that is something I'll discuss later. ### Templates and Scripts GSL uses the term "template" to describe text that is output as generated code. GSL works in two modes - script mode, and template mode. When you execute a GSL script directly, as we did in the first example, GSL starts in script mode. When you execute a GSL script indirectly, through an XML file, as we did in the second example, GSL starts in template mode. Try removing the `.template 0` and `.endtemplate` lines and you'll see what I mean. The script just gets copied to the output stream, the console, by default. In template mode, GSL commands start with a dot in the first column. In script mode, all lines are assumed to be GSL commands unless they start with `>` (output) in the first column, in which case they are handled as template lines. Script mode is useful when you are doing a lot of GSL scripting work. Often you need to prepare data, check the XML tree, and so on, before you can start to generate code. Template mode is useful when you want to output a lot of data, or actually want to generate code. You can mix GSL commands and template code by putting a dot at the start of lines with GSL commands. Like this: .while year < years . accumulated = accumulated * (rate / 100 + 1) . year = year + 1 .endwhile I'm now going to generate a little HTML report of the different calculations. The listing below shows the third version of `interest.gsl`: .output "deposits.html" So You Want To Be A Millionaire? So You Want To Be A Millionaire?
Note these syntax aspects: * `output
.for deposit . year = 1 . accumulated = amount . while year < years . accumulated = accumulated * (rate / 100 + 1) . year = year + 1 . endwhile Original amount Interest rate Term, years Final amount .endfor $(amount) $(rate)% $(years) $(accumulated) ` - Start sending output to the filename specified * $(name) - Insert value of attribute in output text To produce the HTML report run the same command as before: gsl deposits.xml And then load deposits.html into your browser to see what it looks like. If you're a web developer with any experience, you will see right away what's happening. We're generating a web page dynamically, just like a hundred other web tools. But there are significant differences: Unlike a dynamic web page, here we explicitly specify the output file ourselves, using the "output" command. We can output zero, one, or a hundred different files if we want to. We're working off a data tree that can be as complex as we want. Each "for" loop opens a new scope, acting on a set of child entities. A dynamic web page works off some flat data, coming from the browser or a database. You can make web pages that work on a hierarchical data set, but it's extra work. GSL lets you load and navigate XML data so easily that you don't even realize you're busy. The combination of an explicit script language like GSL plus a hierarchical XML data tree works well. ### Modeling a Web Site I'm going to propose a simple abstract model for a web site, as an example. When you understand this example, you'll have a much better idea of how we design new models, so that you can design your own. To start with, I'll explain how I design a new model, and then I'll take you through the steps of building a code generator that brings it to life. Our model lets us build simple web sites. A web site is a mixture of different types of document, for instance: * HTML pages for the content. * JavaScript for menus. * CSS style sheets for look and feel. * Images for icons and for cosmetics. And so on. When we make a new model, it's worth asking the question, "how would I make a thousand of these?" I.E., a thousand web sites. Well, we'd have lots of content, which would be different for each web site, possibly with some common parts. The content could definitely be based on standard templates - it's unlikely we'd make each of a thousand sites entirely from scratch. If we used JavaScript menus, we'd presumably use the same code in each site, changing only the menu content to match the structure of the site. Most likely we'd use a unique CSS stylesheet for each site, to give each site a unique look and feel, but they could also be based on a standard template. Finally, the images and icons would be a mixture of standard graphics and customised graphics, depending on how pretty we want each site to look. Our model is going to be the basis for code generation, that is, the mass production of as much of the above as is reasonable. To do this, we need to make a compact and efficient statement of exactly what is needed to produce each web site. It's like constructing a thousand houses. It's expensive to design and build each house as a unique thing. It's much cheaper to make a single common plan, and then for each house, state the differences. So one house might have a different roof shape, while another has larger windows, but all houses share the same materials, wall and floor construction, and so on. When we mass produce something, we're clearly aiming for low cost and consistent , and hopefully high, quality. It's the same with code generation. So, let's get to our web site model. What information do we actually need to specify? * First, we need to know all the pages in the web site, so that we can build menus. * Second, we need basic information for each page. Typically, I like to define a title and subtitle, an image (for pretty marketing purposes), and a block of content (which can be raw HTML). * Third, we some information for all pages - for example, a logo and a copyright statement. The next step is to sketch a model that can hold this information in a useful way. Remember that we use XML as a modeling language. So, we invent an XML syntax for our model. For each page, I'd like to write something like this: When I design new XML languages like the above, I use entity attributes to hold single-line properties, and child entities to hold multi-line properties or properties that can occur more than once. It just seems more elegant than putting properties in child entities, since this implies those properties can occur many times. It does not make sense for a page to have more than one name, title, subtitle, or image in our model, so we define these as attributes of the page entity. The iMatix MOP tools use this style very heavily. Once we've defined a set of pages, how do we tie these together into a web site? Let's use a second model for the overall web site: Content HTML goes here I've defined a ` ... ... ` tag that breaks the pages into groups. Now let's jump right in and make ourselves a web site. There's no better way to test a model than to try using it. As an example, I'll make a new web site for my local grocer, who has decided, finally, to go on-line. ### First Draft We'll make the web site as several XML files. This is a design choice. We could also make the site as a single large XML file. It's a trade-off between ease of use (a single file is easier in smaller cases) and scalability (it's not practical to edit a large site with hundreds of pages as a single file). To start with, we'll define the overall site like this: Note the first line, which defines the file as XML, and the `script` tag, which tells GSL what script to run to process the data. We've defined three pages. Let's write very a simple version of each of these: Next, we will write three more short XML files as shown below. First the index page: Next, the fruit page: Close to you
We're just around the corner, if you live near by.
Always open
And if we're closed, just come back tomorrow.
Cheap and convenient
Much cheaper and easier than growing your own vegetables and fruit.
and last the vegetable page: Always fresh
Just like it was plucked from the tree last month.
Special deal
Any five pieces of fruit, for the price of ten!
Money back if not satisfied
We'll give you your money back if we're not satisfied with it!
Finally, here is the first draft of the web generation script. It does not produce anything, it simply loads the web site data into an XML tree and then saves this (in a file called `root.xml`) that we can look at to see what live data the script is actually working with: .### Since we run the script off the XML file, it starts in .### template mode. .template 0 for section for page ### Load XML 100% organic vegetables
All vegetables made from cardon, oxygen, and hydrogen molecules with trace elements.
Country fresh style
We don't know what that means, but it sounded nice!
Unique take-away concept
Now you can consume your vegetables in the comfort of your own home.
data xml to section from "$(page.name).xml" ### Delete old tag delete page endfor endfor save root .endtemplate Let's look at what this script does. First, it switches off template mode so we can write ordinary GSL without starting each line with a dot. GSL starts scripts in template mode if they are launched from the XML file. It's useful in many cases but not here. So, we wrap the whole script in `.template 0` and `.endtemplate`. Second, the script works through each section and page, and loads the XML data for that page. It does this using two commands, `xml` and `delete`. The first loads XML data from a file into the specified scope (` `, in this case), and the second deletes the current page (since the loaded data also contains a ` ` tag). Finally, the script saves the whole XML tree to a file. If you want to try the next steps you must have installed GSL, as I described in the last article. Run the script like this: gsl site GSL looks for the file called `site.xml`. When the script has run, take a look at `root.xml`. This shows you what we're going to work with to generate the real HTML. ### Inserting Variables When we generate output, we insert variable values into the generated text. This is very much like using shell variables. GSL does automatic case conversion on output variable. This is very useful when we generate programming languages. For example, the $(name) form outputs a variable in lower case: output "$(filename).c" The $(NAME) form outputs the same value in uppercase: #if defined ($(FILENAME)_INCLUDED) And the $(Name) form outputs the variable in title case, i.e. the first letter is capitalised: ################### $(Filename) ################# One side-effect of automatic case conversion is that we'll often get variables converted to lower case simply because we used the $(name) form. If we don't want a variable to be automatically case converted, we use this form: $(name:). This is also called the 'empty modifier'. A second side-effect of automatic case conversion is that variable names are not case sensitive. By default GSL ignores the case of variable names so that $(me) and $(ME) refer to the same variable. But putting empty modifiers in every variable expansion gets tiresome, and GSL lets us switch off automatic case conversion, using this instruction: ignorecase = 0 This tells GSL, "variable names are case sensitive, and do not convert variable values on output". ### Looping through Trees In our first draft we loaded each page into the XML tree and deleted the original page definition. That was this text: for section for page xml to section from "$(page.name).xml" delete page endfor endfor To generate output for each page, we're going to iterate through the sections one more time. Since we're deleting old ` ` entities and loading new ones from the XML definitions, we need to iterate through the sections and pages over again. This is the code that generates the output for each page: for section for page include "template.gsl" endfor endfor The include command executes GSL code in another file. We're going to do all the hard work in a separate file, which I've called `template.gsl`, so that it's easy to change the HTML generation independently from the top-level GSL code. This is good practice for several reasons: It's nice, in larger projects, that each big code generation task sits in its own file where it can be owned by a single person. We can add more templates - to produce other types of output - for the same model very easily and safely. And you'll see in later examples that we tend to write a single GSL file for each output we want to produce. In XNF - the tool we use for larger-scale code generation projects - these scripts are called "targets". ### Building the Output The HTML template looks like this: .template 1 .echo "Generating $(page.name) page..." .output "$(page.name).html" ... .endtemplate Most of it is fairly straight-forward, though you do need to understand how XHTML and CSS work (and I'm not going to explain that here). * The echo command tells the user what's going on. It's polite to do this, although in realistic cases we'll also let the user suppress such reports using a 'quiet' option. * The output command creates the HTML page. * The text `` to `