From: Martin Mares Date: Tue, 9 Jun 2015 10:01:41 +0000 (+0200) Subject: Initial support for LUA bindings in stylesheets X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=c4f70c847416f1d0a2249beaaed3c7e7fd78b3b6;p=leo.git Initial support for LUA bindings in stylesheets So far, only querying for tags of the current OSM object works, but the whole thing looks very promising. --- diff --git a/Makefile b/Makefile index b1c6eee..8cff907 100644 --- a/Makefile +++ b/Makefile @@ -14,13 +14,13 @@ BUILDSYS=$(s)/build include $(BUILDSYS)/Maketop PROGS+=$(o)/leo -CFLAGS+=$(LIBUCW_CFLAGS) +CFLAGS+=$(LIBUCW_CFLAGS) $(LUA_CFLAGS) -LEO_MODULES=leo xml osm svg svg-icon css-parse css-lex style css dict sym sym-point sym-line sym-scale sym-text map shp fixed +LEO_MODULES=leo xml osm svg svg-icon css-parse css-lex style css dict sym sym-point sym-line sym-scale sym-text map shp fixed leo_wrap LEO_OBJECTS=$(addprefix $(o)/, $(addsuffix .o, $(LEO_MODULES))) $(o)/leo: $(LEO_OBJECTS) -$(o)/leo: LIBS+=$(LIBUCW_LIBS) $(LIBUCW_CHARSET_LIBS) $(LIBUCW_XML_LIBS) $(PANGOFT2_LIBS) $(FREETYPE_LIBS) -lproj -lm +$(o)/leo: LIBS+=$(LIBUCW_LIBS) $(LIBUCW_CHARSET_LIBS) $(LIBUCW_XML_LIBS) $(PANGOFT2_LIBS) $(FREETYPE_LIBS) $(LUA_LIBS) -lproj -lm $(o)/sym-text.o: CFLAGS+=$(FREETYPE_CFLAGS) $(PANGOFT2_CFLAGS) $(o)/svg-icon.o: CFLAGS+=$(LIBUCW_XML_CFLAGS) @@ -34,6 +34,12 @@ $(o)/css-parse.c: css-parse.y $(o)/dict-%.h: dict-%.t $(BUILDSYS)/gen-dict build/gen-dict <$< >$@ +$(o)/leo_wrap.o: $(o)/leo_wrap.c + +$(o)/leo_wrap.c: $(s)/expr.i + $(M)SWIG $< + $(Q)swig -I$(s) -lua -outdir $(o) -o $@ $< + ifndef CONFIG_LOCAL install: all $(INSTALL_TARGETS) else diff --git a/configure b/configure index 4b2d61e..2eb7b9e 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #!/usr/bin/perl # Configure Script for Hic Est Leo -# (c) 2014 Martin Mares +# (c) 2014--2015 Martin Mares use warnings; use strict; @@ -35,5 +35,26 @@ UCW::Configure::Pkg::PkgConfig("libucw-xml") or Fail("libucw-xml is required"); UCW::Configure::Pkg::PkgConfig("pangoft2") or Fail("pangoft2 is required"); UCW::Configure::Pkg::TrivConfig("freetype", script => "freetype-config") or Fail("freetype2 is required"); +UCW::Configure::Pkg::PkgConfig("lua5.2") or Fail("liblua5.2 is required"); +Set("LUA_CFLAGS", Get("LUA5_2_CFLAGS")); +Set("LUA_LIBS", Get("LUA5_2_LIBS")); + +Log "Checking for SWIG ... "; +my $swig = UCW::Configure::Pkg::TryCmd("swig -version"); +if (!defined $swig) { + Log "NO\n"; + Fail("SWIG is required"); +} else { + my ($ver) = ($swig =~ /^SWIG Version (.*)$/m); + if (!defined $ver) { + Log "UNKNOWN VERSION\n"; + } else { + Log "YES: version $ver\n"; + if (UCW::Configure::Pkg::ver_norm($ver) lt UCW::Configure::Pkg::ver_norm("2.0")) { + Fail "SWIG is too old, need version 2.0 or newer."; + } + } +} + Finish(); Log "\nConfigured, run `make' to build everything.\n"; diff --git a/css-lex.c b/css-lex.c index 1a287f2..129b1be 100644 --- a/css-lex.c +++ b/css-lex.c @@ -1,5 +1,5 @@ /* - * Experimental Map Renderer -- MapCSS Lexer + * Hic Est Leo -- MapCSS Lexer * * (c) 2014--2015 Martin Mares */ @@ -19,7 +19,7 @@ static struct fastbuf *fb; static struct fastbuf fbbuf; static int lino; -void css_error(char *err, ...) +void css_error(const char *err, ...) { va_list args; va_start(args, err); @@ -49,7 +49,7 @@ int css_lex(void) { struct mempool *mp = css_this->pool; char *p; - int c, next, len, tok; + int c, next, len, tok, nesting; if (tok = css_this->pushed_token) { @@ -198,6 +198,32 @@ int css_lex(void) css_lval.s = p; return RGB; + case '@': + if (next != '{') + return '@'; + // Lua block + p = mp_start(mp, 1); + // FIXME p = mp_append_string(mp, p, "function () "); + nesting = 1; + bgetc(fb); + for (;;) + { + c = bgetc(fb); + if (c < 0) + css_error("Unterminated Lua block"); + if (c == '{') + nesting++; + else if (c == '}') + { + nesting--; + if (!nesting) + break; + } + p = mp_append_char(mp, p, c); + } + css_lval.s = mp_end_string(mp, p); + return LUA; + // One-character operators case '{': case '}': diff --git a/css-parse.y b/css-parse.y index 3329321..b9937bb 100644 --- a/css-parse.y +++ b/css-parse.y @@ -1,7 +1,7 @@ /* * Hic Est Leo -- MapCSS Parser * - * (c) 2014 Martin Mares + * (c) 2014--2015 Martin Mares */ %{ @@ -13,6 +13,7 @@ #include "leo.h" #include "css.h" +#include "expr.h" static void *css_alloc(size_t n) { @@ -49,7 +50,7 @@ static void css_add_to_val_list(struct style_prop *list, struct style_prop *elt) %token LE GE NE CC %token SINGLE_PROP -%token NUMBER IDENT QUOTED RGB +%token NUMBER IDENT QUOTED RGB LUA %type ident_or_quoted %type rule rule_start rule_selectors rule_start_actions rule_actions @@ -237,9 +238,16 @@ action: ident_or_quoted ':' prop_value { $$ = css_alloc(sizeof(struct css_action)); + $$->type = CSS_ACTION_SET; $$->prop = $3; $$->prop.key = style_prop_encode($1); } + | LUA + { + $$ = css_alloc(sizeof(struct css_action)); + $$->type = CSS_ACTION_EXPR; + expr_compile($$, $1); + } ; prop_value: diff --git a/css.c b/css.c index 38464aa..c05fae7 100644 --- a/css.c +++ b/css.c @@ -1,12 +1,13 @@ /* * Hic Est Leo -- MapCSS Stylesheets * - * (c) 2014 Martin Mares + * (c) 2014--2015 Martin Mares */ #include "leo.h" #include "style.h" #include "css.h" +#include "expr.h" #include @@ -44,7 +45,16 @@ void css_dump_selector(struct css_selector *s) void css_dump_action(struct css_action *a) { printf("\tAction: "); - style_dump_prop(&a->prop); + switch (a->type) + { + case CSS_ACTION_SET: + style_dump_prop(&a->prop); + break; + case CSS_ACTION_EXPR: + printf("\n"); + default: + ASSERT(0); + } } static bool css_match_condition(struct css_condition *c, struct osm_object *o) @@ -150,7 +160,17 @@ static bool css_match_selector(struct css_selector *s, struct style_results *r) static void css_apply_action(struct css_action *sa, struct style_results *r, layer_t layer) { - style_set_by_layer(r, layer, &sa->prop); + switch (sa->type) + { + case CSS_ACTION_SET: + style_set_by_layer(r, layer, &sa->prop); + break; + case CSS_ACTION_EXPR: + expr_execute(sa, r, layer); + break; + default: + ASSERT(0); + } } void css_apply(struct css_sheet *ss, struct style_results *r) diff --git a/css.h b/css.h index 8ec1896..2ae99fb 100644 --- a/css.h +++ b/css.h @@ -73,9 +73,15 @@ struct css_condition { osm_val_t val; }; +enum css_action_type { + CSS_ACTION_SET, + CSS_ACTION_EXPR, +}; + struct css_action { cnode n; - struct style_prop prop; + enum css_action_type type; + struct style_prop prop; // for CSS_ACTION_SET }; /* css-parse.y */ @@ -87,7 +93,7 @@ struct style_prop *css_parse_prop(struct mempool *mp, char *objname, const char /* css-lex.c */ -void css_error(char *err, ...); +void css_error(const char *err, ...); int css_lex(void); void css_lex_open(void); void css_lex_close(void); diff --git a/expr.h b/expr.h new file mode 100644 index 0000000..e3d0770 --- /dev/null +++ b/expr.h @@ -0,0 +1,18 @@ +/* + * Hic Est Leo -- Expression Evaluation via Lua + * + * (c) 2015 Martin Mares + */ + +#ifndef _LEO_EXPR_H +#define _LEO_EXPR_H + +#include + +int luaopen_leo(lua_State *L); + +void expr_init(void); +void expr_compile(void *key, char *source); +void expr_execute(void *key, struct style_results *r, layer_t layer); + +#endif diff --git a/expr.i b/expr.i new file mode 100644 index 0000000..14baf32 --- /dev/null +++ b/expr.i @@ -0,0 +1,183 @@ +/* + * Hic Est Leo -- Bindings for Lua + * + * (c) 2015 Martin Mares + */ + +%module leo +%nodefaultctor; + +%{ +#include "leo.h" +#include "osm.h" +#include "css.h" +#include "expr.h" + +#include +#include +%} + +/*** Proxy for OSM object attributes ***/ + +%{ + +struct proxy_tag { + struct osm_object *o; +}; + +static void push_proxy_tag(lua_State *L, struct proxy_tag *t) +{ + SWIG_NewPointerObj(L, t, SWIGTYPE_p_proxy_tag, 0); +} + +static const char *proxy_tag_index(struct proxy_tag *t, const char *key) +{ + osm_key_t k = osm_key_encode(key); + osm_val_t v = osm_obj_find_tag(t->o, k); + return osm_val_decode(v); +} + +%} + +struct proxy_tag { }; + +%extend proxy_tag { + const char *index(const char *key); +}; + +%init %{ + // This overrides default index function generated by SWIG. + // The tag proxy therefore cannot have any methods. + SWIG_Lua_get_class_metatable(L, "proxy_tag"); + SWIG_Lua_add_function(L, "__index", _wrap_proxy_tag_index); + lua_pop(L, 1); +%} + +/*** Proxy for style properties ***/ + +%{ + +struct proxy_style { + struct style_results *sr; + layer_t layer; +}; + +static void push_proxy_style(lua_State *L, struct proxy_style *s) +{ + SWIG_NewPointerObj(L, s, SWIGTYPE_p_proxy_style, 0); +} + +static const char *proxy_style_index(struct proxy_style *s, const char *key) +{ + return NULL; +} + +%} + +struct proxy_style { }; + +%extend proxy_style { + const char *index(const char *key); +}; + +%init %{ + // This overrides default index function generated by SWIG. + SWIG_Lua_get_class_metatable(L, "proxy_style"); + SWIG_Lua_add_function(L, "__index", _wrap_proxy_style_index); + lua_pop(L, 1); +%} + +/*** Compilation and running of expressions ***/ + +%{ + +static lua_State *lua; + +void expr_init(void) +{ + lua = luaL_newstate(); + lua_State *L = lua; + + luaL_openlibs(L); + luaopen_leo(L); + + // SWIG init code leaves data in Lua stack. Get rid of it. + lua_pop(L, lua_gettop(L)); + + lua_checkstack(L, 30); + + lua_pushliteral(L, "Leo"); + lua_newtable(L); + lua_settable(L, LUA_REGISTRYINDEX); +} + +#if 0 +// Not used at the moment (FIXME) + +static char *current_src; + +static const char *expr_reader(lua_State *L UNUSED, void *data UNUSED, size_t *sizep) +{ + if (current_src) + { + char *res = current_src; + *sizep = strlen(res); + current_src = NULL; + return res; + } + else + { + *sizep = 0; + return NULL; + } +} + +#endif + +void expr_compile(void *key, char *source) +{ + lua_State *L = lua; + + lua_pushliteral(L, "Leo"); // stack: "Leo" + lua_gettable(L, LUA_REGISTRYINDEX); // stack: leo-table + ASSERT(lua_istable(L, -1)); + lua_pushlightuserdata(L, key); // stack: leo-table, key + +#if 0 + current_src = source; + int err = lua_load(L, expr_reader, NULL, "@(expr)", NULL); +#else + int err = luaL_loadbuffer(L, source, strlen(source), "@expression"); +#endif + if (err != LUA_OK) + css_error(lua_tostring(L, -1)); + // stack: leo-table, key, function + + lua_settable(L, -3); // stack: leo-table + lua_pop(L, 1); // stack: -- +} + +void expr_execute(void *key, struct style_results *r, layer_t layer) +{ + lua_State *L = lua; + + struct proxy_tag pt = { .o = r->obj }; + push_proxy_tag(L, &pt); // stack: proxy + lua_setglobal(L, "t"); // stack: -- + + struct proxy_style ps = { .sr = r, .layer = layer }; + push_proxy_style(L, &ps); // stack: proxy + lua_setglobal(L, "s"); // stack: -- + + lua_pushliteral(L, "Leo"); // stack: "Leo" + lua_gettable(L, LUA_REGISTRYINDEX); // stack: leo-table + ASSERT(lua_istable(L, -1)); + lua_pushlightuserdata(L, key); // stack: leo-table, key + lua_gettable(L, -2); // stack: leo-table, function + lua_remove(L, -2); // stack: function + ASSERT(lua_isfunction(L, -1)); + + lua_call(L, 0, 0); // stack: -- +} + +%} diff --git a/leo.c b/leo.c index a221a94..3508cbd 100644 --- a/leo.c +++ b/leo.c @@ -11,6 +11,7 @@ #include "css.h" #include "sym.h" #include "map.h" +#include "expr.h" #include #include @@ -52,6 +53,7 @@ int main(int argc UNUSED, char **argv) osm_init(); styles_init(); + expr_init(); map_load_styles(); map_load_sources(); map_set_scale(); diff --git a/poskole.css b/poskole.css index 30631f5..49cc444 100644 --- a/poskole.css +++ b/poskole.css @@ -705,3 +705,9 @@ node[hack=raisetext], way[hack=raisetext] { text-offset: 3; } + +/*** Experiments ***/ + +node { + @{print(t.name)} ; +}