diff --git a/.gitignore b/.gitignore index 5d62fb9..f4fd33f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,2 @@ -/MANIFEST -/README.html -/*.tar -/*.bak -/*.tar.gz - -luacov.* +*.dot diff --git a/CHANGES b/CHANGES deleted file mode 100644 index 86f9fce..0000000 --- a/CHANGES +++ /dev/null @@ -1,14 +0,0 @@ -Revision history for lua-CodeGen - -0.2.1 Fri Nov 26 10:30:00 2010 - - fix compatibility with Lua 5.2.0 alpha - -0.2.0 Thu Sep 9 15:30:00 2010 - - separator handles decimal escape sequence - - graph without ellipse shape - - fix render : don't alter the original table - - modify the syntax of template application - (avoid confusion with the method call in plain Lua) - -0.1.0 Mon Aug 30 10:30:00 2010 - First release diff --git a/COPYRIGHT b/COPYRIGHT deleted file mode 100644 index df7cc8a..0000000 --- a/COPYRIGHT +++ /dev/null @@ -1,30 +0,0 @@ -lua-CodeGen License -------------------- - -lua-CodeGen is licensed under the terms of the MIT/X11 license reproduced below. - -=============================================================================== - -Copyright (C) 2010 Francois Perrad. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -=============================================================================== - -(end of COPYRIGHT) diff --git a/Makefile b/Makefile index f765ca7..c9887c2 100644 --- a/Makefile +++ b/Makefile @@ -1,106 +1,12 @@ -LUA := lua -VERSION := $(shell cd src && $(LUA) -e "m = require [[CodeGen]]; print(m._VERSION)") -TARBALL := lua-codegen-$(VERSION).tar.gz -ifndef REV - REV := 1 -endif +check: + xmllint --noout --valid index.html + xmllint --noout --valid codegen.html + xmllint --noout --valid graph.html -ifndef DESTDIR - DESTDIR := /usr/local -endif -LIBDIR := $(DESTDIR)/share/lua/5.1 - -install: - mkdir -p $(LIBDIR)/CodeGen - cp src/CodeGen.lua $(LIBDIR) - cp src/CodeGen/Graph.lua $(LIBDIR)/CodeGen - -uninstall: - rm -f $(LIBDIR)/CodeGen.lua - rm -f $(LIBDIR)/CodeGen/Graph.lua - -manifest_pl := \ -use strict; \ -use warnings; \ -my @files = qw{MANIFEST}; \ -while (<>) { \ - chomp; \ - next if m{^\.}; \ - next if m{^doc/\.}; \ - next if m{^doc/google}; \ - next if m{^rockspec/}; \ - push @files, $$_; \ -} \ -print join qq{\n}, sort @files; - -rockspec_pl := \ -use strict; \ -use warnings; \ -use Digest::MD5; \ -open my $$FH, q{<}, q{$(TARBALL)} \ - or die qq{Cannot open $(TARBALL) ($$!)}; \ -binmode $$FH; \ -my %config = ( \ - version => q{$(VERSION)}, \ - rev => q{$(REV)}, \ - md5 => Digest::MD5->new->addfile($$FH)->hexdigest(), \ -); \ -close $$FH; \ -while (<>) { \ - s{@(\w+)@}{$$config{$$1}}g; \ - print; \ -} - -version: - @echo $(VERSION) - -CHANGES: - perl -i.bak -pe "s{^$(VERSION).*}{q{$(VERSION) }.localtime()}e" CHANGES - -tag: - git tag -a -m 'tag release $(VERSION)' $(VERSION) - -doc: - git read-tree --prefix=doc/ -u remotes/origin/gh-pages - -MANIFEST: doc - git ls-files | perl -e '$(manifest_pl)' > MANIFEST - -$(TARBALL): MANIFEST - [ -d lua-CodeGen-$(VERSION) ] || ln -s . lua-CodeGen-$(VERSION) - perl -ne 'print qq{lua-CodeGen-$(VERSION)/$$_};' MANIFEST | \ - tar -zc -T - -f $(TARBALL) - rm lua-CodeGen-$(VERSION) - rm -rf doc - git rm doc/* - -dist: $(TARBALL) - -rockspec: $(TARBALL) - perl -e '$(rockspec_pl)' rockspec.in > rockspec/lua-codegen-$(VERSION)-$(REV).rockspec - -install-rock: clean dist rockspec - perl -pe 's{http://cloud.github.com/downloads/fperrad/lua-CodeGen/}{};' \ - rockspec/lua-codegen-$(VERSION)-$(REV).rockspec > lua-codegen-$(VERSION)-$(REV).rockspec - luarocks install lua-codegen-$(VERSION)-$(REV).rockspec - -check: test - -test: - cd src && prove --exec=$(LUA) ../test/*.t - -coverage: - rm -f src/luacov.stats.out src/luacov.report.out - cd src && prove --exec="$(LUA) -lluacov" ../test/*.t - cd src && luacov - -README.html: README.md - Markdown.pl README.md > README.html +graph: + lua -l CodeGen.Graph -e "print(CodeGen.Graph.to_dot(CodeGen.Graph.template))" > graph.dot + dot -T png -o graph.png graph.dot clean: - rm -rf doc - rm -f MANIFEST *.bak src/luacov.*.out *.rockspec README.html - -.PHONY: test rockspec CHANGES - + rm -f *.dot *.png diff --git a/README.md b/README.md deleted file mode 100644 index b5dd90c..0000000 --- a/README.md +++ /dev/null @@ -1,48 +0,0 @@ - -lua-CodeGen : a template engine -=============================== - -Introduction ------------- - -lua-CodeGen is a "safe" template engine. - -lua-CodeGen enforces a strict Model-View separation. -Only 4 primitives are supplied : - -- attribute reference, -- template include, -- conditional include -- and template application (i.e., _map_ operation). - -lua-CodeGen allows to split template in small chunk, -and encourages the reuse of them by inheritance. - -Each chunk of template is like a rule of a grammar -for an _unparser generator_. - -lua-CodeGen is not dedicated to HTML, -it could generate any kind of textual code. - - -References ----------- - -the Terence Parr's articles : - -+ [Enforcing Strict Model-View Separation in Template Engines](http://www.cs.usfca.edu/~parrt/papers/mvc.templates.pdf) -+ [A Functional Language For Generating Structured Text](http://www.cs.usfca.edu/~parrt/papers/ST.pdf) - -Links ------ - -The homepage is at [http://fperrad.github.com/lua-CodeGen](http://fperrad.github.com/lua-CodeGen), -and the sources are hosted at [http://github.com/fperrad/lua-CodeGen](http://github.com/fperrad/lua-CodeGen). - -Copyright and License ---------------------- - -Copyright (c) 2010 Francois Perrad - -This library is licensed under the terms of the MIT/X11 license, like Lua itself. - diff --git a/codegen.html b/codegen.html new file mode 100644 index 0000000..540df40 --- /dev/null +++ b/codegen.html @@ -0,0 +1,313 @@ + + + + Module CodeGen + + + + + + + + + + +
+ +
+ +
lua-CodeGen
+
a template engine
+
+ +
+ + + +
+ +

Manual

+ +

For the impatient

+ +
+local CodeGen = require 'CodeGen'
+
+tmpl = CodeGen {    -- instanciation
+    tarball = "${name}-${version}.tar.gz",
+    name = 'lua',
+}
+tmpl.version = 5.1
+output = tmpl 'tarball'     -- interpolation
+print(output) --> lua-5.1.tar.gz
+
+ +

The instanciation

+ +

The instanciation of a template is done by the call of CodeGen +with optional parameters. This first parameter is a table. This table uses +only string as key and could contains 3 kinds of value : +

+
    +
  • chunk of template which is a string and could contains primitives i.e. ${...}
  • +
  • data which gives access to the data model
  • +
  • formatter which is a function which accepts a string as parameter and + returns it after a transformation. + The typical usage is for escape sequence.
  • +
+

+The other parameters allow inheritance (ie. access to field) from other templates or simple tables. +

+ +

A common pattern is to put this step in an external file. +

+ +
+-- file: tarball.tmpl
+return CodeGen {
+    tarball = "${name}-${version}.tar.gz",
+}
+
+ +
+tmpl = dofile 'tarball.tmpl'
+
+ +

Setting data and other alteration

+ +

After the instanciation and before the interpolation, all member of the template +are accessible and modifiable like in a table. +

+ +

Typically, data from the model are added after the instanciation. +

+ +

The interpolation

+ +

The interpolation is done by calling the template with one string parameter +which is the keyname of the entry point template. +

+ +

The interpolation returns a string as result and an optional string which +contains some error messages. +

+ +

The 4 primitives in template

+ +

The data could be in the form of foo.bar.baz. +

+ +

1. Attribute reference

+ +

The syntax is ${data[; separator='sep'][; format=name]}. +

+ +

An undefined data produces an empty string. +

+ +

The option format allows to specify a formatter function. +The default is the standard Lua function tostring. +

+ +

When data is a table, the option separator +is used as parameter of table.concat(data, sep). +This parameter could be simple quoted or double quoted, +and it handles escape sequence like Lua. +The characters { and } are forbidden, +there must be represented by a decimal escape sequence \ddd. +

+ +
+local CodeGen = require 'CodeGen'
+
+tmpl = CodeGen {
+    call = "${name}(${parameters; separator=', '});",
+}
+tmpl.name = 'print'
+tmpl.parameters = { 1, 2, 3 }
+output = tmpl 'call'
+print(output) --> print(1, 2, 3);
+
+ +

2. Template include

+ +

The syntax is ${name()} where name +is the keyname of a chunk template. +

+ +

If name is not the keyname of a valid chunk, +there are no substitution and an error is reported. +

+ +

3. Conditional include

+ +

The if syntax is ${data?name1()} +and the if/else syntax is ${data?name1()!name2()} +where name1 and name2 are the keyname of a chunk template +and data is evaluated as a boolean. +

+ +

4. Template application

+ +

The syntax is ${data/name()[; separator='sep']} +where data must be a table. +The template name is called for each item of the array data, +and the result is concatened with an optional separator. +

+

The template has a direct access in the item, +and inherits access from the caller. +If the item is not a table, it is accessible via the key it. +

+ +

Examples

+ +

A generic template for rockspec.

+ +
+-- file: rockspec.tmpl
+return CodeGen {
+    rockspec = [[
+package = '${name}'
+version = '${version}-${revision}'
+${_source()}
+${_description()}
+${_dependencies()}
+]],
+    _source = [[
+source = {
+    url = ${_url()},
+    md5 = '${md5}',
+    dir = '${name}-${version}',
+},
+]],
+    _description = [[
+description = {
+    ${desc.summary?_summary()}
+    ${desc.homepage?_homepage()}
+    ${desc.maintainer?_maintainer()}
+    ${desc.license?_license()}
+},
+]],
+    _summary = 'summary = "${desc.summary}",',
+    _homepage = 'homepage = "${desc.homepage}",',
+    _maintainer = 'maintainer = "${desc.maintainer}",',
+    _license = 'license = "${desc.license}",',
+    _dependencies = [[
+dependencies = {
+${dependencies/_depend()}
+}
+]],
+    _depend = [[
+    '${name} >= ${version}',
+]],
+}
+
+ +

A specialization for all my projects.

+ +
+-- file: my_rockspec.tmpl
+local parent = dofile 'rockspec.tmpl'
+
+return CodeGen({
+    lower = string.lower,
+    _tarball = "${name; format=lower}-${version}.tar.gz",
+    _url = "'http://cloud.github.com/downloads/fperrad/${name}/${_tarball()}'",
+    _homepage = 'homepage = "http://fperrad.github.com/${name}",',
+    desc = {
+        homepage = true,
+        maintainer = "Francois Perrad",
+        license = "MIT/X11",
+    },
+}, parent)
+
+ +

And finally, an use for this project.

+ +
+CodeGen = require 'CodeGen'
+
+local rs = dofile 'my_rockspec.tmpl'
+rs.name = 'lua-CodeGen'
+rs.version = '0.1.0'
+rs.revision = 1
+rs.md5 = 'XxX'
+rs.desc.summary = "a template engine"
+rs.dependencies = {
+    { name = 'lua', version = 5.1 },
+    { name = 'lua-testmore', version = '0.2.1' },
+}
+print(rs 'rockspec')
+
+ +

The output is :

+ +
+package = 'lua-CodeGen'
+version = '0.1.0-1'
+source = {
+    url = 'http://cloud.github.com/downloads/fperrad/lua-CodeGen/lua-codegen-0.1.0.tar.gz',
+    md5 = 'XxX',
+    dir = 'lua-CodeGen-0.1.0',
+},
+description = {
+    summary = "a template engine",
+    homepage = "http://fperrad.github.com/lua-CodeGen",
+    maintainer = "Francois Perrad",
+    license = "MIT/X11",
+},
+dependencies = {
+    'lua >= 5.1',
+    'lua-testmore >= 0.2.1',
+}
+
+ +
+ +
+ +
+
+ +
+ + + diff --git a/googlee25fe224e5cc09a6.html b/googlee25fe224e5cc09a6.html new file mode 100644 index 0000000..0b39cba --- /dev/null +++ b/googlee25fe224e5cc09a6.html @@ -0,0 +1 @@ +google-site-verification: googlee25fe224e5cc09a6.html \ No newline at end of file diff --git a/graph.html b/graph.html new file mode 100644 index 0000000..a1c8e55 --- /dev/null +++ b/graph.html @@ -0,0 +1,112 @@ + + + + Module CodeGen.Graph + + + + + + + + + + +
+ +
+ +
lua-CodeGen
+
a template engine
+
+ +
+ + + +
+ +

Reference

+ +

This module produces the call tree between chunks of a template.

+ +

It is useful to find orphan chunk.

+ +

Functions

+ +

to_dot( tmpl )

+ +

Returns a string in DOT format.

+ +

Examples

+ +
+$ lua -e "print(require 'CodeGen.Graph'.to_dot(require 'CodeGen.Graph'.template))" > graph.dot
+$ dot -T png -o graph.png graph.dot
+
+ +
+$ cat graph.dot
+digraph {
+    node [ shape = none ];
+
+    _node;
+    TOP;
+    _edge;
+
+    TOP -> _node;
+    TOP -> _edge;
+}
+
+ +

graph.pnggraph.png

+ +
+ +
+ +
+
+ +
+ + + diff --git a/graph.png b/graph.png new file mode 100644 index 0000000..ed23975 Binary files /dev/null and b/graph.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..9ac3f62 --- /dev/null +++ b/index.html @@ -0,0 +1,172 @@ + + + + lua-CodeGen - Homepage + + + + + + + + + + +
+ +
+ +
lua-CodeGen
+
a template engine
+
+ +
+ + + +
+ +

+ Perfection is achieved, not when there is nothing more to add,
+ but when there is nothing left to take away.
+ — Antoine de Saint-Exupéry +

+ +

Overview

+ +

lua-CodeGen is a "safe" template engine.

+ +

lua-CodeGen enforces a strict Model-View separation. +Only 4 primitives are supplied : +

+
    +
  • attribute reference,
  • +
  • template include,
  • +
  • conditional include,
  • +
  • and template application (i.e., map operation).
  • +
+ +

lua-CodeGen allows to split template in small chunk, +and encourages the reuse of them by inheritance. +

+ +

Each chunk of template is like a rule of a grammar +for an unparser generator. +

+ +

lua-CodeGen is not dedicated to HTML, +it could generate any kind of textual code. +

+ +

References

+ +

the Terence Parr's articles :

+ + +

Status

+ +

lua-CodeGen is in beta stage.

+ +

It's developed for Lua 5.1.

+ +

Download

+ +

lua-CodeGen source can be downloaded from +GitHub or +Lua Forge.

+ +

Installation

+ +

lua-CodeGen is available via LuaRocks:

+ +
+luarocks install lua-codegen
+
+ +

or manually, with:

+ +
+make install
+
+ +

Test

+ +

The test suite requires the module +lua-TestMore.

+ +
+make test
+
+ +

Copyright and License

+ +

Copyright © 2010 François Perrad + + ohloh profile for François Perrad + + + View François Perrad's profile on LinkedIn + +

+ +

This library is licensed under the terms of the MIT/X11 license, +like Lua itself.

+ +
+ +
+ +
+
+ +
+ + + diff --git a/rockspec.in b/rockspec.in deleted file mode 100644 index d4f4ae4..0000000 --- a/rockspec.in +++ /dev/null @@ -1,36 +0,0 @@ -package = 'lua-CodeGen' -version = '@version@-@rev@' -source = { - url = 'http://cloud.github.com/downloads/fperrad/lua-CodeGen/lua-codegen-@version@.tar.gz', - md5 = '@md5@', - dir = 'lua-CodeGen-@version@', -} -description = { - summary = "a template engine", - detailed = [[ - lua-CodeGen is a "safe" template engine. - - lua-CodeGen enforces a strict Model-View separation. - - lua-CodeGen allows to split template in small chunk, - and encourages the reuse of them by inheritance. - - lua-CodeGen is not dedicated to HTML, - it could generate any kind of textual code. - ]], - homepage = 'http://fperrad.github.com/lua-CodeGen', - maintainer = 'Francois Perrad', - license = 'MIT/X11' -} -dependencies = { - 'lua >= 5.1', - 'lua-testmore >= 0.2.3', -} -build = { - type = 'none', - modules = { - ['CodeGen'] = 'src/CodeGen.lua', - ['CodeGen.Graph'] = 'src/CodeGen/Graph.lua', - }, - copy_directories = { 'doc', 'test' }, -} diff --git a/rockspec/lua-codegen-0.1.0-1.rockspec b/rockspec/lua-codegen-0.1.0-1.rockspec deleted file mode 100644 index 5f20d08..0000000 --- a/rockspec/lua-codegen-0.1.0-1.rockspec +++ /dev/null @@ -1,36 +0,0 @@ -package = 'lua-CodeGen' -version = '0.1.0-1' -source = { - url = 'http://cloud.github.com/downloads/fperrad/lua-CodeGen/lua-codegen-0.1.0.tar.gz', - md5 = '07e59fcbcb4acf07bdaecb5c0d7302ae', - dir = 'lua-CodeGen-0.1.0', -} -description = { - summary = "a template engine", - detailed = [[ - lua-CodeGen is a "safe" template engine. - - lua-CodeGen enforces a strict Model-View separation. - - lua-CodeGen allows to split template in small chunk, - and encourages the reuse of them by inheritance. - - lua-CodeGen is not dedicated to HTML, - it could generate any kind of textual code. - ]], - homepage = 'http://fperrad.github.com/lua-CodeGen', - maintainer = 'Francois Perrad', - license = 'MIT/X11' -} -dependencies = { - 'lua >= 5.1', - 'lua-testmore >= 0.2.1', -} -build = { - type = 'builtin', - modules = { - ['CodeGen'] = 'src/CodeGen.lua', - ['CodeGen.Graph'] = 'src/CodeGen/Graph.lua', - }, - copy_directories = { 'doc', 'test' }, -} diff --git a/rockspec/lua-codegen-0.2.0-1.rockspec b/rockspec/lua-codegen-0.2.0-1.rockspec deleted file mode 100644 index bc56fdd..0000000 --- a/rockspec/lua-codegen-0.2.0-1.rockspec +++ /dev/null @@ -1,36 +0,0 @@ -package = 'lua-CodeGen' -version = '0.2.0-1' -source = { - url = 'http://cloud.github.com/downloads/fperrad/lua-CodeGen/lua-codegen-0.2.0.tar.gz', - md5 = '6a18d61e48f8e4707661947f39476f0f', - dir = 'lua-CodeGen-0.2.0', -} -description = { - summary = "a template engine", - detailed = [[ - lua-CodeGen is a "safe" template engine. - - lua-CodeGen enforces a strict Model-View separation. - - lua-CodeGen allows to split template in small chunk, - and encourages the reuse of them by inheritance. - - lua-CodeGen is not dedicated to HTML, - it could generate any kind of textual code. - ]], - homepage = 'http://fperrad.github.com/lua-CodeGen', - maintainer = 'Francois Perrad', - license = 'MIT/X11' -} -dependencies = { - 'lua >= 5.1', - 'lua-testmore >= 0.2.1', -} -build = { - type = 'builtin', - modules = { - ['CodeGen'] = 'src/CodeGen.lua', - ['CodeGen.Graph'] = 'src/CodeGen/Graph.lua', - }, - copy_directories = { 'doc', 'test' }, -} diff --git a/rockspec/lua-codegen-0.2.1-1.rockspec b/rockspec/lua-codegen-0.2.1-1.rockspec deleted file mode 100644 index e62a459..0000000 --- a/rockspec/lua-codegen-0.2.1-1.rockspec +++ /dev/null @@ -1,36 +0,0 @@ -package = 'lua-CodeGen' -version = '0.2.1-1' -source = { - url = 'http://cloud.github.com/downloads/fperrad/lua-CodeGen/lua-codegen-0.2.1.tar.gz', - md5 = 'f8acf7841dcbaeae3f9589d1b9b88472', - dir = 'lua-CodeGen-0.2.1', -} -description = { - summary = "a template engine", - detailed = [[ - lua-CodeGen is a "safe" template engine. - - lua-CodeGen enforces a strict Model-View separation. - - lua-CodeGen allows to split template in small chunk, - and encourages the reuse of them by inheritance. - - lua-CodeGen is not dedicated to HTML, - it could generate any kind of textual code. - ]], - homepage = 'http://fperrad.github.com/lua-CodeGen', - maintainer = 'Francois Perrad', - license = 'MIT/X11' -} -dependencies = { - 'lua >= 5.1', - 'lua-testmore >= 0.2.3', -} -build = { - type = 'none', - modules = { - ['CodeGen'] = 'src/CodeGen.lua', - ['CodeGen.Graph'] = 'src/CodeGen/Graph.lua', - }, - copy_directories = { 'doc', 'test' }, -} diff --git a/rockspec/lua-codegen-0.2.1-2.rockspec b/rockspec/lua-codegen-0.2.1-2.rockspec deleted file mode 100644 index 79ed488..0000000 --- a/rockspec/lua-codegen-0.2.1-2.rockspec +++ /dev/null @@ -1,36 +0,0 @@ -package = 'lua-CodeGen' -version = '0.2.1-2' -source = { - url = 'http://cloud.github.com/downloads/fperrad/lua-CodeGen/lua-codegen-0.2.1.tar.gz', - md5 = 'f8acf7841dcbaeae3f9589d1b9b88472', - dir = 'lua-CodeGen-0.2.1', -} -description = { - summary = "a template engine", - detailed = [[ - lua-CodeGen is a "safe" template engine. - - lua-CodeGen enforces a strict Model-View separation. - - lua-CodeGen allows to split template in small chunk, - and encourages the reuse of them by inheritance. - - lua-CodeGen is not dedicated to HTML, - it could generate any kind of textual code. - ]], - homepage = 'http://fperrad.github.com/lua-CodeGen', - maintainer = 'Francois Perrad', - license = 'MIT/X11' -} -dependencies = { - 'lua >= 5.1', - 'lua-testmore >= 0.2.3', -} -build = { - type = 'builtin', - modules = { - ['CodeGen'] = 'src/CodeGen.lua', - ['CodeGen.Graph'] = 'src/CodeGen/Graph.lua', - }, - copy_directories = { 'doc', 'test' }, -} diff --git a/src/CodeGen.lua b/src/CodeGen.lua deleted file mode 100644 index d6690e5..0000000 --- a/src/CodeGen.lua +++ /dev/null @@ -1,253 +0,0 @@ - --- --- lua-CodeGen : --- - -local setmetatable = setmetatable -local tonumber = tonumber -local tostring = tostring -local type = type -local tconcat = require 'table'.concat -local _G = _G -local string = require 'string' - -_ENV = nil -local m = {} - -local function render (val, sep, formatter) - formatter = formatter or tostring - if val == nil then - return '' - end - if type(val) == 'table' then - local t = {} - for i = 1, #val do - t[i] = formatter(val[i]) - end - return tconcat(t, sep) - else - return formatter(val) - end -end - -local special = { - ['a'] = "\a", - ['b'] = "\b", - ['f'] = "\f", - ['n'] = "\n", - ['r'] = "\r", - ['t'] = "\t", - ['v'] = "\v", - ['\\'] = '\\', - ['"'] = '"', - ["'"] = "'", -} - -local function unescape(str) - str = str:gsub([[\(%d+)]], function (s) - local n = tonumber(s:sub(1, 3)) - return string.char(n % 256) .. s:sub(4) - end - ) - return str:gsub([[\([abfnrtv\"'])]], special) -end - -local new -local function eval (self, name) - local cyclic = {} - local msg = {} - - local function interpolate (self, template, tname) - if type(template) ~= 'string' then - return nil - end - local lineno = 1 - - local function add_message (...) - msg[#msg+1] = tname .. ':' .. lineno .. ': ' .. tconcat{...} - end -- add_message - - local function get_value (vname) - local i = 1 - local t = self - for w in vname:gmatch "(%w+)%." do - i = i + w:len() + 1 - t = t[w] - if type(t) ~= 'table' then - add_message(vname, " is invalid") - return nil - end - end - return t[vname:sub(i)] - end -- get_value - - local function interpolate_line (line) - local function get_repl (capt) - local function apply (self, tmpl) - if cyclic[tmpl] then - add_message("cyclic call of ", tmpl) - return capt - end - cyclic[tmpl] = true - local result = interpolate(self, self[tmpl], tmpl) - cyclic[tmpl] = nil - if result == nil then - add_message(tmpl, " is not a template") - return capt - end - return result - end -- apply - - local capt1, pos = capt:match("^%${([%a_][%w%._]*)()", 1) - if not capt1 then - add_message(capt, " does not match") - return capt - end - local sep, pos_sep = capt:match("^;%s+separator%s*=%s*'([^']+)'%s*()", pos) - if not sep then - sep, pos_sep = capt:match("^;%s+separator%s*=%s*\"([^\"]+)\"%s*()", pos) - end - if sep then - sep = unescape(sep) - end - local fmt, pos_fmt = capt:match("^;%s+format%s*=%s*([%a_][%w_]*)%s*()", pos_sep or pos) - if capt:match("^}", pos_fmt or pos_sep or pos) then - if fmt then - local formatter = self[fmt] - if type(formatter) ~= 'function' then - add_message(fmt, " is not a formatter") - return capt - end - return render(get_value(capt1), sep, formatter) - else - return render(get_value(capt1), sep) - end - end - if capt:match("^%(%)}", pos) then - return apply(self, capt1) - end - local capt2 = capt:match("^?([%a_][%w_]*)%(%)}", pos) - if capt2 then - if get_value(capt1) then - return apply(self, capt2) - else - return '' - end - end - local capt2, capt3 = capt:match("^?([%a_][%w_]*)%(%)!([%a_][%w_]*)%(%)}", pos) - if capt2 and capt3 then - if get_value(capt1) then - return apply(self, capt2) - else - return apply(self, capt3) - end - end - local capt2, pos = capt:match("^/([%a_][%w_]*)%(%)()", pos) - if capt2 then - local sep, pos_sep = capt:match("^;%s+separator%s*=%s*'([^']+)'%s*()", pos) - if not sep then - sep, pos_sep = capt:match("^;%s+separator%s*=%s*\"([^\"]+)\"%s*()", pos) - end - if sep then - sep = unescape(sep) - end - if capt:match("^}", pos_sep or pos) then - local array = get_value(capt1) - if array == nil then - return '' - end - if type(array) ~= 'table' then - add_message(capt1, " is not a table") - return capt - end - local results = {} - for i = 1, #array do - local item = array[i] - if type(item) ~= 'table' then - item = { it = item } - end - local result = apply(new(item, self), capt2) - results[#results+1] = result - if result == capt then - break - end - end - return tconcat(results, sep) - end - end - add_message(capt, " does not match") - return capt - end -- get_repl - - local indent = line:match "^(%s*)%$%b{}$" - local result = line:gsub("(%$%b{})", get_repl) - if indent == '' then - result = result:gsub("\n$", '') - elseif indent then - result = result:gsub("\n", "\n" .. indent) - result = result:gsub("^" .. indent .. "\n", "\n") - repeat - local nb - result, nb = result:gsub("\n" .. indent .. "\n", "\n\n") - until nb == 0 - result = result:gsub("\n" .. indent .. "$", '') - end - return result - end -- interpolate_line - - if template:find "\n" then - local results = {} - for line in template:gmatch "([^\n]*)\n" do - local result = interpolate_line(line) - if result == line or not result:match'^%s*$' then - results[#results+1] = result - end - lineno = lineno + 1 - end - results[#results+1] = '' - return tconcat(results, "\n") - else - return interpolate_line(template) - end - end -- interpolate - - local val = self[name] - if type(val) == 'string' then - return interpolate(self, val, name), - (#msg > 0 and tconcat(msg, "\n")) or nil - else - return render(val) - end -end - -function new (env, ...) - local obj = { env or {}, ... } - setmetatable(obj, { - __tostring = function () return 'CodeGen' end, - __call = function (...) return eval(...) end, - __index = function (t, k) - for i = 1, #t do - local v = t[i][k] - if v ~= nil then - return v - end - end - end, - }) - return obj -end -m.new = new - -setmetatable(m, { - __call = function (func, ...) return new(...) end -}) -_G.CodeGen = m - -m._VERSION = "0.2.1" -m._DESCRIPTION = "lua-CodeGen : a template engine" -m._COPYRIGHT = "Copyright (c) 2010 Francois Perrad" -return m --- --- This library is licensed under the terms of the MIT/X11 license, --- like Lua itself. --- diff --git a/src/CodeGen/Graph.lua b/src/CodeGen/Graph.lua deleted file mode 100644 index ee7a4b3..0000000 --- a/src/CodeGen/Graph.lua +++ /dev/null @@ -1,88 +0,0 @@ - --- --- lua-CodeGen : --- - -local pairs = pairs -local type = type -local _G = _G -local table = require 'table' -local CodeGen = require 'CodeGen' - -_ENV = nil -local m = {} - -local template = CodeGen { - TOP = [[ -digraph { - node [ shape = none ]; - - ${nodes/_node()} - - ${edges/_edge()} -} -]], - _node = [[ -${name}; -]], - _edge = [[ -${caller} -> ${callee}; -]], -} -m.template = template - -function m:to_dot () - local done = {} - local nodes = {} - local edges = {} - - local function parse (key) - if not done[key] then - done[key] = true - local tmpl = self[key] - if type(tmpl) == 'string' then - table.insert(nodes, { name = key }) - for capt in tmpl:gmatch "(%$%b{})" do - local capt1, pos = capt:match("^%${([%a_][%w%._]*)()", 1) - if capt1 then - if capt:match("^%(%)", pos) then - table.insert(edges, { caller = key, callee = capt1 }) - parse(capt1) - else - local capt2, capt3 = capt:match("^?([%a_][%w_]*)%(%)!([%a_][%w_]*)%(%)", pos) - if capt2 and capt3 then - table.insert(edges, { caller = key, callee = capt2 }) - table.insert(edges, { caller = key, callee = capt3 }) - parse(capt2) - parse(capt3) - else - local capt2 = capt:match("^[?/]([%a_][%w_]*)%(%)", pos) - if capt2 then - table.insert(edges, { caller = key, callee = capt2 }) - parse(capt2) - end - end - end - end - end - end - end - end -- parse - - for k in pairs(self[1]) do - parse(k) - end - template.nodes = nodes - template.edges = edges - local dot = template 'TOP' - return dot -end - -_G.CodeGen.Graph = m -return m --- --- Copyright (c) 2010 Francois Perrad --- --- This library is licensed under the terms of the MIT/X11 license, --- like Lua itself. --- diff --git a/test/00-require.t b/test/00-require.t deleted file mode 100755 index 4ad434a..0000000 --- a/test/00-require.t +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env lua - -require 'Test.More' - -plan(8) - -if not require_ok 'CodeGen' then - BAIL_OUT "no lib" -end - -local m = require 'CodeGen' -type_ok( m, 'table' ) -is( m, CodeGen ) -is( m, package.loaded.CodeGen ) - -like( m._COPYRIGHT, 'Perrad', "_COPYRIGHT" ) -like( m._DESCRIPTION, 'template engine', "_DESCRIPTION" ) -type_ok( m._VERSION, 'string', "_VERSION" ) -like( m._VERSION, '^%d%.%d%.%d$' ) - diff --git a/test/01-basic.t b/test/01-basic.t deleted file mode 100755 index ef3058a..0000000 --- a/test/01-basic.t +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env lua - -local CodeGen = require 'CodeGen' - -require 'Test.More' - -plan(8) - -tmpl = CodeGen() -type_ok( tmpl, 'table', "new CodeGen" ) -tmpl.a = 'some text' -is( tmpl 'a', 'some text', "eval 'a'" ) - -tmpl = CodeGen{ - pi = 3.14159, - str = 'some text', -} -type_ok( tmpl, 'table', "new CodeGen" ) -tmpl.pi = 3.14 -is( tmpl 'str', 'some text', "eval 'str'" ) -is( tmpl 'pi', '3.14', "eval 'pi'" ) -isnt( tmpl 'pi', 3.14 ) -is( tmpl 'unk', '', "unknown gives an empty string" ) - -is( tostring(tmpl), 'CodeGen', "__tostring" ) diff --git a/test/02-attr.t b/test/02-attr.t deleted file mode 100755 index 8c4c182..0000000 --- a/test/02-attr.t +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env lua - -local CodeGen = require 'CodeGen' - -require 'Test.More' - -plan(21) - -tmpl = CodeGen{ - code = [[print("${hello}, ${_guy1; format=upper}");]], - upper = string.upper, - hello = "Hello", - _guy1 = "you", -} -is( tmpl 'code', [[print("Hello, YOU");]], "scalar attributes" ) -tmpl.hello = "Hi" -res, msg = tmpl 'code' -is( res, [[print("Hi, YOU");]] ) -is( msg, nil, "no error" ) - -tmpl = CodeGen() -tmpl.a = { 'abc', 'def', 'hij' } -tmpl.upper1 = string.upper -tmpl.upper2 = function (str) return string.upper(str) end -tmpl.upper3 = function (str) return str:upper() end -tmpl.code = [[print(${a})]] -is( tmpl 'code', [[print(abcdefhij)]], "array" ) -tmpl.code = [[print(${a; separator=', '})]] -is( tmpl 'code', [[print(abc, def, hij)]], "array with sep" ) -tmpl.code = [[print(${a; separator = "\44\32" })]] -is( tmpl 'code', [[print(abc, def, hij)]], "array with sep" ) -tmpl.code = [[print(${a; format=upper1 })]] -is( tmpl 'code', [[print(ABCDEFHIJ)]], "array" ) -tmpl.code = [[print(${a; separator='\044\032'; format=upper2})]] -is( tmpl 'code', [[print(ABC, DEF, HIJ)]], "array with sep & format" ) -eq_array( tmpl.a, { 'abc', 'def', 'hij' }, "don't alter the original table" ) -tmpl.code = [[print(${a; separator = ", " ; format = upper3 })]] -is( tmpl 'code', [[print(ABC, DEF, HIJ)]], "array with sep & format" ) -eq_array( tmpl.a, { 'abc', 'def', 'hij' }, "don't alter the original table" ) - -tmpl = CodeGen{ - code = [[print("${data.hello}, ${data.people.guy}");]], - data = { - hello = "Hello", - people = { - guy = "you", - }, - }, -} -is( tmpl 'code', [[print("Hello, you");]], "complex attr" ) -tmpl.data.hello = "Hi" -is( tmpl 'code', [[print("Hi, you");]] ) - -tmpl.code = [[print("${hello}, ${people.guy}");]] -res, msg = tmpl 'code' -is( res, [[print(", ");]], "missing attr" ) -is( msg, "code:1: people.guy is invalid" ) - -tmpl.code = [[print("${hello-people}");]] -res, msg = tmpl 'code' -is( res, [[print("${hello-people}");]], "no match" ) -is( msg, "code:1: ${hello-people} does not match" ) - -tmpl.code = [[print("${ hello }");]] -res, msg = tmpl 'code' -is( res, [[print("${ hello }");]], "no match" ) -is( msg, "code:1: ${ hello } does not match" ) - -tmpl.code = [[print("${hello; format=lower }");]] -res, msg = tmpl 'code' -is( res, [[print("${hello; format=lower }");]], "no formatter" ) -is( msg, "code:1: lower is not a formatter" ) - diff --git a/test/03-include.t b/test/03-include.t deleted file mode 100755 index e4e3778..0000000 --- a/test/03-include.t +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env lua - -local CodeGen = require 'CodeGen' - -require 'Test.More' - -plan(5) - -tmpl = CodeGen{ - outer = [[ -begin - ${inner()} -end -]], - inner = [[print("${hello}");]], - hello = "Hello, world!", -} -is( tmpl 'outer', [[ -begin - print("Hello, world!"); -end -]] , "" ) - -tmpl.inner = 3.14 -res, msg = tmpl 'outer' -is( res, [[ -begin - ${inner()} -end -]] , "not a template" ) -is( msg, "outer:2: inner is not a template" ) - -tmpl = CodeGen{ - top = [[ -${outer()} -]], - outer = [[ -begin - ${inner()} -end -]], - inner = [[print("${outer()}");]], -} -res, msg = tmpl 'top' -is( res, [[ -begin - print("${outer()}"); -end -]], "cyclic call" ) -is( msg, "inner:1: cyclic call of outer" ) diff --git a/test/04-apply.t b/test/04-apply.t deleted file mode 100755 index 3ad49fb..0000000 --- a/test/04-apply.t +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env lua - -local CodeGen = require 'CodeGen' - -require 'Test.More' - -plan(10) - -tmpl = CodeGen{ - outer = [[ -begin -${data/inner()} -end -]], - inner = [[ - print("${name()} = ${value}"); -]], -} -is( tmpl 'outer', [[ -begin -end -]] , "empty" ) - -tmpl = CodeGen{ - outer = [[ -begin - ${data/inner()} -end -]], - inner = [[ - print("${name()} = ${value}"); -]], -} -tmpl.data = { - { name = 'key1', value = 1 }, - { name = 'key2', value = 2 }, - { name = 'key3', value = 3 }, -} -is( tmpl 'outer', [[ -begin - print("key1 = 1"); - print("key2 = 2"); - print("key3 = 3"); -end -]] , "with array" ) - -tmpl.inner = 3.14 -res, msg = tmpl 'outer' -is( res, [[ -begin - ${data/inner()} -end -]] , "not a template" ) -is( msg, "outer:2: inner is not a template" ) - -tmpl.data = 3.14 -res, msg = tmpl 'outer' -is( res, [[ -begin - ${data/inner()} -end -]] , "not a table" ) -is( msg, "outer:2: data is not a table" ) - -tmpl = CodeGen{ - outer = [[ -begin -${data/inner()} -end -]], - inner = [[ - print(${it}); -]], -} -tmpl.data = { 1, 2, 3 } -is( tmpl 'outer', [[ -begin - print(1); - print(2); - print(3); -end -]] , "it" ) - -tmpl.data = {} -is( tmpl 'outer', [[ -begin -end -]] , "it" ) - -tmpl = CodeGen{ - outer = [[ -begin - call(${data/inner(); separator=', '}); -end -]], - inner = "${it}", -} -tmpl.data = { 1, 2, 3 } -is( tmpl 'outer', [[ -begin - call(1, 2, 3); -end -]] , "with sep" ) - -tmpl = CodeGen{ - outer = [[ -begin - list( - ${data/inner(); separator=",\n"} - ); -end -]], - inner = "${it}", -} -tmpl.data = { 1, 2, 3 } -is( tmpl 'outer', [[ -begin - list( - 1, - 2, - 3 - ); -end -]] , "sep with escape seq" ) diff --git a/test/05-cond.t b/test/05-cond.t deleted file mode 100755 index 6dcec0f..0000000 --- a/test/05-cond.t +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env lua - -local CodeGen = require 'CodeGen' - -require 'Test.More' - -plan(4) - -tmpl = CodeGen{ - outer = [[ -begin - ${data.locale?inner_fr()!inner_en()} -end -]], - inner_en = [[print("Hello, ${data.guy}");]], - inner_fr = [[print("Bonjour, ${data.guy}");]], -} - -tmpl.data = {} -tmpl.data.locale = true -tmpl.data.guy = 'toi' -is( tmpl 'outer', [[ -begin - print("Bonjour, toi"); -end -]] , "" ) - -tmpl.data.locale = false -tmpl.data.guy = 'you' -is( tmpl 'outer', [[ -begin - print("Hello, you"); -end -]] , "" ) - -tmpl = CodeGen{ - outer = [[ -begin -${data.guy?inner()} -end -]], - inner = [[print("Hello, ${data.guy}");]], -} -tmpl.data = {} -tmpl.data.guy = 'you' -is( tmpl 'outer', [[ -begin -print("Hello, you"); -end -]] , "" ) - -tmpl.data.guy = nil -is( tmpl 'outer', [[ -begin -end -]] , "" ) diff --git a/test/06-external.t b/test/06-external.t deleted file mode 100755 index 090f236..0000000 --- a/test/06-external.t +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env lua - -CodeGen = require 'CodeGen' - -require 'Test.More' - -plan(1) - -tmpl = dofile '../test/tmpl.lua' -tmpl.data = { - { name = 'key1', value = 1 }, - { name = 'key2', value = 2 }, - { name = 'key3', value = 3 }, -} -is( tmpl 'top', [[ -begin - print("key1 = 1"); - print("key2 = 2"); - print("key3 = 3"); -end -]] , "external" ) diff --git a/test/07-inheritance.t b/test/07-inheritance.t deleted file mode 100755 index 59d1bab..0000000 --- a/test/07-inheritance.t +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env lua - -local CodeGen = require 'CodeGen' - -require 'Test.More' - -plan(2) - -tmpl1 = CodeGen { - _a = [[ ${a} ${_b()} ]], - _b = [[ (${b}) ]], - a = 'print', - b = 1, -} - -is( tmpl1 '_a', ' print (1) ' ) - -tmpl2 = CodeGen({ - _b = [[ [${c}] ]], - a = 'call', -}, tmpl1, { c = 2 }) - -is( tmpl2 '_a', ' call [2] ' ) - diff --git a/test/09-examples.t b/test/09-examples.t deleted file mode 100755 index f310db5..0000000 --- a/test/09-examples.t +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env lua - -local CodeGen = require 'CodeGen' - -require 'Test.More' - -plan(2) - -tmpl = CodeGen { -- instanciation - tarball = "${name}-${version}.tar.gz", - name = 'lua', -} -tmpl.version = 5.1 -output = tmpl 'tarball' -- interpolation -is( output, 'lua-5.1.tar.gz', "for the impatient" ) - - -tmpl = CodeGen { - call = "${name}(${parameters; separator=', '});", -} -tmpl.name = 'print' -tmpl.parameters = { 1, 2, 3 } -output = tmpl 'call' -is( output, 'print(1, 2, 3);', "attribute reference" ) - - diff --git a/test/tmpl.lua b/test/tmpl.lua deleted file mode 100644 index 08a8e20..0000000 --- a/test/tmpl.lua +++ /dev/null @@ -1,10 +0,0 @@ -return CodeGen{ - top = [[ -begin - ${data/inner()} -end -]], - inner = [[ - print("${name()} = ${value}"); -]], -}