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)
$(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
#!/usr/bin/perl
# Configure Script for Hic Est Leo
-# (c) 2014 Martin Mares <mj@ucw.cz>
+# (c) 2014--2015 Martin Mares <mj@ucw.cz>
use warnings;
use strict;
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";
/*
- * Experimental Map Renderer -- MapCSS Lexer
+ * Hic Est Leo -- MapCSS Lexer
*
* (c) 2014--2015 Martin Mares <mj@ucw.cz>
*/
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);
{
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)
{
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 '}':
/*
* Hic Est Leo -- MapCSS Parser
*
- * (c) 2014 Martin Mares <mj@ucw.cz>
+ * (c) 2014--2015 Martin Mares <mj@ucw.cz>
*/
%{
#include "leo.h"
#include "css.h"
+#include "expr.h"
static void *css_alloc(size_t n)
{
%token LE GE NE CC
%token SINGLE_PROP
-%token <s> NUMBER IDENT QUOTED RGB
+%token <s> NUMBER IDENT QUOTED RGB LUA
%type <s> ident_or_quoted
%type <rule> rule rule_start rule_selectors rule_start_actions rule_actions
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:
/*
* Hic Est Leo -- MapCSS Stylesheets
*
- * (c) 2014 Martin Mares <mj@ucw.cz>
+ * (c) 2014--2015 Martin Mares <mj@ucw.cz>
*/
#include "leo.h"
#include "style.h"
#include "css.h"
+#include "expr.h"
#include <ucw/mempool.h>
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("<expr>\n");
+ default:
+ ASSERT(0);
+ }
}
static bool css_match_condition(struct css_condition *c, struct osm_object *o)
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)
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 */
/* 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);
--- /dev/null
+/*
+ * Hic Est Leo -- Expression Evaluation via Lua
+ *
+ * (c) 2015 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _LEO_EXPR_H
+#define _LEO_EXPR_H
+
+#include <lua.h>
+
+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
--- /dev/null
+/*
+ * Hic Est Leo -- Bindings for Lua
+ *
+ * (c) 2015 Martin Mares <mj@ucw.cz>
+ */
+
+%module leo
+%nodefaultctor;
+
+%{
+#include "leo.h"
+#include "osm.h"
+#include "css.h"
+#include "expr.h"
+
+#include <lualib.h>
+#include <lauxlib.h>
+%}
+
+/*** 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: --
+}
+
+%}
#include "css.h"
#include "sym.h"
#include "map.h"
+#include "expr.h"
#include <ucw/conf.h>
#include <ucw/opt.h>
osm_init();
styles_init();
+ expr_init();
map_load_styles();
map_load_sources();
map_set_scale();
way[hack=raisetext] {
text-offset: 3;
}
+
+/*** Experiments ***/
+
+node {
+ @{print(t.name)} ;
+}