[Groonga-commit] groonga/groonga at c1d98ed [master] grn_ts: add grn_ts_expr_builder to simplify grn_ts_expr

Back to archive index

susumu.yata null+****@clear*****
Mon Nov 16 15:17:14 JST 2015


susumu.yata	2015-11-16 15:17:14 +0900 (Mon, 16 Nov 2015)

  New Revision: c1d98ed0ab0b3890911275c6f92a8da96154ac51
  https://github.com/groonga/groonga/commit/c1d98ed0ab0b3890911275c6f92a8da96154ac51

  Message:
    grn_ts: add grn_ts_expr_builder to simplify grn_ts_expr

  Added files:
    lib/ts/ts_expr_builder.c
    lib/ts/ts_expr_builder.h
  Modified files:
    lib/ts.c
    lib/ts/sources.am
    lib/ts/ts_buf.h
    lib/ts/ts_expr.c
    lib/ts/ts_expr.h
    lib/ts/ts_expr_node.c
    lib/ts/ts_expr_node.h
    lib/ts/ts_expr_parser.c
    lib/ts/ts_expr_parser.h
    lib/ts/ts_str.h
    lib/ts/ts_util.c

  Modified: lib/ts.c (+4 -3)
===================================================================
--- lib/ts.c    2015-11-16 14:59:23 +0900 (4a3c6f2)
+++ lib/ts.c    2015-11-16 15:17:14 +0900 (cb7ab1c)
@@ -429,7 +429,8 @@ grn_ts_writer_build(grn_ctx *ctx, grn_ts_writer *writer, grn_obj *table)
     const char *name_ptr;
     size_t name_size = grn_vector_get_element(ctx, &writer->name_buf, i,
                                               &name_ptr, NULL, NULL);
-    rc = grn_ts_expr_parse(ctx, table, name_ptr, name_size, &new_expr);
+    rc = grn_ts_expr_parse(ctx, table, (grn_ts_str){ name_ptr, name_size },
+                           &new_expr);
     if (rc != GRN_SUCCESS) {
       return rc;
     }
@@ -667,7 +668,7 @@ grn_ts_select_filter(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
     return (ctx->rc != GRN_SUCCESS) ? ctx->rc : GRN_UNKNOWN_ERROR;
   }
 
-  rc = grn_ts_expr_parse(ctx, table, str.ptr, str.size, &expr);
+  rc = grn_ts_expr_parse(ctx, table, str, &expr);
   if (rc == GRN_SUCCESS) {
     for ( ; ; ) {
       size_t i, batch_size;
@@ -776,7 +777,7 @@ grn_ts_select_scorer(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
       rest = str;
     }
   }
-  rc = grn_ts_expr_parse(ctx, table, rest.ptr, rest.size, &expr);
+  rc = grn_ts_expr_parse(ctx, table, rest, &expr);
   if (rc != GRN_SUCCESS) {
     return rc;
   }

  Modified: lib/ts/sources.am (+2 -0)
===================================================================
--- lib/ts/sources.am    2015-11-16 14:59:23 +0900 (17fcc62)
+++ lib/ts/sources.am    2015-11-16 15:17:14 +0900 (37732d6)
@@ -3,6 +3,8 @@ libgrnts_la_SOURCES =				\
 	ts_buf.h				\
 	ts_expr.c				\
 	ts_expr.h				\
+	ts_expr_builder.c			\
+	ts_expr_builder.h			\
 	ts_expr_node.c				\
 	ts_expr_node.h				\
 	ts_expr_parser.c			\

  Modified: lib/ts/ts_buf.h (+2 -0)
===================================================================
--- lib/ts/ts_buf.h    2015-11-16 14:59:23 +0900 (7a24b37)
+++ lib/ts/ts_buf.h    2015-11-16 15:17:14 +0900 (4c66a00)
@@ -19,6 +19,8 @@
 #ifndef GRN_TS_BUF_H
 #define GRN_TS_BUF_H
 
+#include "../grn.h"
+
 #include "ts_types.h"
 
 #ifdef __cplusplus

  Modified: lib/ts/ts_expr.c (+64 -918)
===================================================================
--- lib/ts/ts_expr.c    2015-11-16 14:59:23 +0900 (e987c40)
+++ lib/ts/ts_expr.c    2015-11-16 15:17:14 +0900 (7eadc66)
@@ -23,93 +23,74 @@
 #include <string.h>
 
 #include "../grn_ctx.h"
-#include "../grn_dat.h"
-#include "../grn_db.h"
-#include "../grn_geo.h"
-#include "../grn_hash.h"
-#include "../grn_pat.h"
-#include "../grn_store.h"
 
 #include "ts_log.h"
 #include "ts_str.h"
 #include "ts_util.h"
 #include "ts_expr_parser.h"
 
-/*-------------------------------------------------------------
- * grn_ts_expr_bridge.
- */
-
-/* grn_ts_expr_bridge_init() initializes a bridge. */
-static void
-grn_ts_expr_bridge_init(grn_ctx *ctx, grn_ts_expr_bridge *bridge)
-{
-  memset(bridge, 0, sizeof(*bridge));
-  bridge->src_table = NULL;
-  bridge->dest_table = NULL;
-}
-
-/* grn_ts_expr_bridge_fin() finalizes a bridge. */
-static void
-grn_ts_expr_bridge_fin(grn_ctx *ctx, grn_ts_expr_bridge *bridge)
-{
-  if (bridge->dest_table) {
-    grn_obj_unlink(ctx, bridge->dest_table);
-  }
-  /* Note: bridge->src_table does not increment a reference count. */
-}
-
-/*-------------------------------------------------------------
- * grn_ts_expr.
- */
-
 /* grn_ts_expr_init() initializes an expression. */
 static void
 grn_ts_expr_init(grn_ctx *ctx, grn_ts_expr *expr)
 {
   memset(expr, 0, sizeof(*expr));
   expr->table = NULL;
-  expr->curr_table = NULL;
   expr->root = NULL;
-  expr->stack = NULL;
-  expr->bridges = NULL;
 }
 
 /* grn_ts_expr_fin() finalizes an expression. */
 static void
 grn_ts_expr_fin(grn_ctx *ctx, grn_ts_expr *expr)
 {
-  size_t i;
-  if (expr->bridges) {
-    for (i = 0; i < expr->n_bridges; i++) {
-      grn_ts_expr_bridge_fin(ctx, &expr->bridges[i]);
-    }
-    GRN_FREE(expr->bridges);
-  }
-  if (expr->stack) {
-    for (i = 0; i < expr->stack_depth; i++) {
-      if (expr->stack[i]) {
-        grn_ts_expr_node_close(ctx, expr->stack[i]);
-      }
-    }
-    GRN_FREE(expr->stack);
+  if (expr->root) {
+    grn_ts_expr_node_close(ctx, expr->root);
   }
-  /* Note: expr->curr_table does not increment a reference count. */
   if (expr->table) {
     grn_obj_unlink(ctx, expr->table);
   }
 }
 
 grn_rc
-grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr **expr)
+grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr_node *root,
+                 grn_ts_expr **expr)
 {
   grn_rc rc;
   grn_ts_expr *new_expr;
+  grn_ts_expr_type type;
   if (!ctx) {
     return GRN_INVALID_ARGUMENT;
   }
-  if (!table || !grn_ts_obj_is_table(ctx, table) || !expr) {
+  if (!table || !grn_ts_obj_is_table(ctx, table) || !root || !expr) {
     GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
   }
+  switch (root->type) {
+    case GRN_TS_EXPR_ID_NODE: {
+      type = GRN_TS_EXPR_ID;
+      break;
+    }
+    case GRN_TS_EXPR_SCORE_NODE: {
+      type = GRN_TS_EXPR_SCORE;
+      break;
+    }
+    case GRN_TS_EXPR_KEY_NODE:
+    case GRN_TS_EXPR_VALUE_NODE: {
+      type = GRN_TS_EXPR_VARIABLE;
+      break;
+    }
+    case GRN_TS_EXPR_CONST_NODE: {
+      type = GRN_TS_EXPR_CONST;
+      break;
+    }
+    case GRN_TS_EXPR_COLUMN_NODE:
+    case GRN_TS_EXPR_OP_NODE:
+    case GRN_TS_EXPR_BRIDGE_NODE: {
+      type = GRN_TS_EXPR_VARIABLE;
+      break;
+    }
+    default: {
+      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+    }
+  }
   new_expr = GRN_MALLOCN(grn_ts_expr, 1);
   if (!new_expr) {
     GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
@@ -123,34 +104,35 @@ grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr **expr)
   }
   grn_ts_expr_init(ctx, new_expr);
   new_expr->table = table;
-  new_expr->curr_table = table;
+  new_expr->type = type;
+  new_expr->data_kind = root->data_kind;
+  new_expr->data_type = root->data_type;
+  new_expr->root = root;
   *expr = new_expr;
   return GRN_SUCCESS;
 }
 
 grn_rc
-grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table,
-                  const char *str_ptr, size_t str_size, grn_ts_expr **expr)
+grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
+                  grn_ts_expr **expr)
 {
   grn_rc rc;
   grn_ts_expr *new_expr;
+  grn_ts_expr_parser *parser;
   if (!ctx) {
     return GRN_INVALID_ARGUMENT;
   }
   if (!table || !grn_ts_obj_is_table(ctx, table) ||
-      (!str_ptr && str_size) || !expr) {
+      (!str.ptr && str.size) || !expr) {
     GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
   }
-  rc = grn_ts_expr_open(ctx, table, &new_expr);
+  rc = grn_ts_expr_parser_open(ctx, table, &parser);
   if (rc != GRN_SUCCESS) {
     return rc;
   }
-  rc = grn_ts_expr_push(ctx, new_expr, str_ptr, str_size);
-  if (rc == GRN_SUCCESS) {
-    rc = grn_ts_expr_complete(ctx, new_expr);
-  }
+  rc = grn_ts_expr_parser_parse(ctx, parser, str, &new_expr);
+  grn_ts_expr_parser_close(ctx, parser);
   if (rc != GRN_SUCCESS) {
-    grn_ts_expr_close(ctx, new_expr);
     return rc;
   }
   *expr = new_expr;
@@ -160,857 +142,14 @@ grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table,
 grn_rc
 grn_ts_expr_close(grn_ctx *ctx, grn_ts_expr *expr)
 {
-  if (!ctx || !expr) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  grn_ts_expr_fin(ctx, expr);
-  GRN_FREE(expr);
-  return GRN_SUCCESS;
-}
-
-grn_obj *
-grn_ts_expr_get_table(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  if (!ctx || !expr) {
-    return NULL;
-  }
-  /* The reference counting will never fail in practice. */
-  if (grn_ts_obj_increment_ref_count(ctx, expr->table) != GRN_SUCCESS) {
-    return NULL;
-  }
-  return expr->table;
-}
-
-grn_ts_expr_type
-grn_ts_expr_get_type(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  return (!ctx || !expr) ? GRN_TS_EXPR_BROKEN : expr->type;
-}
-
-grn_ts_data_kind
-grn_ts_expr_get_data_kind(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  return (!ctx || !expr) ? GRN_TS_VOID : expr->data_kind;
-}
-
-grn_ts_data_type
-grn_ts_expr_get_data_type(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  return (!ctx || !expr) ? GRN_DB_VOID : expr->data_type;
-}
-
-grn_ts_expr_node *
-grn_ts_expr_get_root(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  return (!ctx || !expr) ? NULL : expr->root;
-}
-
-/* grn_ts_expr_reserve_stack() extends a stack. */
-static grn_rc
-grn_ts_expr_reserve_stack(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  size_t i, n_bytes, new_size;
-  grn_ts_expr_node **new_stack;
-  if (expr->stack_depth < expr->stack_size) {
-    return GRN_SUCCESS;
-  }
-  new_size = expr->stack_size ? (expr->stack_size * 2) : 1;
-  n_bytes = sizeof(grn_ts_expr_node *) * new_size;
-  new_stack = GRN_REALLOC(expr->stack, n_bytes);
-  if (!new_stack) {
-    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
-                      "GRN_REALLOC failed: %" GRN_FMT_SIZE,
-                      n_bytes);
-  }
-  for (i = expr->stack_size; i < new_size; i++) {
-    new_stack[i] = NULL;
-  }
-  expr->stack = new_stack;
-  expr->stack_size = new_size;
-  return GRN_SUCCESS;
-}
-
-/* grn_ts_expr_deref() dereferences a node. */
-static grn_rc
-grn_ts_expr_deref(grn_ctx *ctx, grn_ts_expr *expr,
-                  grn_ts_expr_node **node_ptr)
-{
-  grn_ts_expr_node *node = *node_ptr;
-  while (node->data_kind == GRN_TS_REF) {
-    grn_rc rc;
-    grn_ts_expr_node *key_node, *bridge_node;
-    grn_id table_id = node->data_type;
-    grn_obj *table = grn_ctx_at(ctx, table_id);
-    if (!table) {
-      return GRN_OBJECT_CORRUPT;
-    }
-    if (!grn_ts_obj_is_table(ctx, table)) {
-      grn_obj_unlink(ctx, table);
-      return GRN_OBJECT_CORRUPT;
-    }
-    rc = grn_ts_expr_key_node_open(ctx, table, &key_node);
-    grn_obj_unlink(ctx, table);
-    if (rc != GRN_SUCCESS) {
-      return rc;
-    }
-    rc = grn_ts_expr_bridge_node_open(ctx, node, key_node, &bridge_node);
-    if (rc != GRN_SUCCESS) {
-      return rc;
-    }
-    node = bridge_node;
-  }
-  *node_ptr = node;
-  return GRN_SUCCESS;
-}
-
-grn_rc
-grn_ts_expr_push(grn_ctx *ctx, grn_ts_expr *expr,
-                 const char *str_ptr, size_t str_size)
-{
-  grn_rc rc;
-  grn_ts_expr_parser *parser;
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE) ||
-      (!str_ptr && str_size)) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  rc = grn_ts_expr_parser_open(ctx, expr, &parser);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  rc = grn_ts_expr_parser_parse(ctx, parser, str_ptr, str_size);
-  grn_ts_expr_parser_close(ctx, parser);
-  return rc;
-}
-
-grn_rc
-grn_ts_expr_push_name(grn_ctx *ctx, grn_ts_expr *expr,
-                     const char *name_ptr, size_t name_size)
-{
-  grn_obj *column;
-  grn_ts_str name = { name_ptr, name_size };
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE) ||
-      !grn_ts_str_is_name(name)) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  if (grn_ts_str_is_id_name(name)) {
-    return grn_ts_expr_push_id(ctx, expr);
-  }
-  if (grn_ts_str_is_score_name(name)) {
-    return grn_ts_expr_push_score(ctx, expr);
-  }
-  if (grn_ts_str_is_key_name(name)) {
-    return grn_ts_expr_push_key(ctx, expr);
-  }
-  if (grn_ts_str_is_value_name(name)) {
-    return grn_ts_expr_push_value(ctx, expr);
-  }
-  /* grn_obj_column() returns a column or accessor. */
-  column = grn_obj_column(ctx, expr->curr_table, name.ptr, name.size);
-  if (!column) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "object not found: \"%.*s\"",
-                      (int)name.size, name.ptr);
-  }
-  return grn_ts_expr_push_obj(ctx, expr, column);
-}
-
-#define GRN_TS_EXPR_PUSH_BULK_CASE(TYPE, kind)\
-  case GRN_DB_ ## TYPE: {\
-    return grn_ts_expr_push_ ## kind(ctx, expr, GRN_ ## TYPE ## _VALUE(obj));\
-  }
-/* grn_ts_expr_push_bulk() pushes a scalar. */
-static grn_rc
-grn_ts_expr_push_bulk(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj)
-{
-  switch (obj->header.domain) {
-    GRN_TS_EXPR_PUSH_BULK_CASE(BOOL, bool)
-    GRN_TS_EXPR_PUSH_BULK_CASE(INT8, int)
-    GRN_TS_EXPR_PUSH_BULK_CASE(INT16, int)
-    GRN_TS_EXPR_PUSH_BULK_CASE(INT32, int)
-    GRN_TS_EXPR_PUSH_BULK_CASE(INT64, int)
-    GRN_TS_EXPR_PUSH_BULK_CASE(UINT8, int)
-    GRN_TS_EXPR_PUSH_BULK_CASE(UINT16, int)
-    GRN_TS_EXPR_PUSH_BULK_CASE(UINT32, int)
-    /* The behavior is undefined if a value is greater than 2^63 - 1. */
-    GRN_TS_EXPR_PUSH_BULK_CASE(UINT64, int)
-    GRN_TS_EXPR_PUSH_BULK_CASE(FLOAT, float)
-    GRN_TS_EXPR_PUSH_BULK_CASE(TIME, time)
-    case GRN_DB_SHORT_TEXT:
-    case GRN_DB_TEXT:
-    case GRN_DB_LONG_TEXT: {
-      grn_ts_text value = { GRN_TEXT_VALUE(obj), GRN_TEXT_LEN(obj) };
-      return grn_ts_expr_push_text(ctx, expr, value);
-    }
-    case GRN_DB_TOKYO_GEO_POINT: {
-      grn_ts_geo value;
-      GRN_GEO_POINT_VALUE(obj, value.latitude, value.longitude);
-      return grn_ts_expr_push_tokyo_geo(ctx, expr, value);
-    }
-    case GRN_DB_WGS84_GEO_POINT: {
-      grn_ts_geo value;
-      GRN_GEO_POINT_VALUE(obj, value.latitude, value.longitude);
-      return grn_ts_expr_push_wgs84_geo(ctx, expr, value);
-    }
-    default: {
-      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "not bulk");
-    }
-  }
-}
-#undef GRN_TS_EXPR_PUSH_BULK_CASE
-
-#define GRN_TS_EXPR_PUSH_UVECTOR_CASE(TYPE, kind)\
-  case GRN_DB_ ## TYPE: {\
-    grn_ts_ ## kind ##_vector value = { (grn_ts_ ## kind *)GRN_BULK_HEAD(obj),\
-                                        grn_uvector_size(ctx, obj) };\
-    return grn_ts_expr_push_ ## kind ## _vector(ctx, expr, value);\
-  }
-#define GRN_TS_EXPR_PUSH_UVECTOR_CASE_WITH_TYPECAST(TYPE, kind)\
-  case GRN_DB_ ## TYPE: {\
-    size_t i;\
-    grn_rc rc;\
-    grn_ts_ ## kind *buf;\
-    grn_ts_ ## kind ## _vector value = { NULL, grn_uvector_size(ctx, obj) };\
-    if (!value.size) {\
-      return grn_ts_expr_push_ ## kind ## _vector(ctx, expr, value);\
-    }\
-    buf = GRN_MALLOCN(grn_ts_ ## kind, value.size);\
-    if (!buf) {\
-      GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,\
-                        "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",\
-                        sizeof(grn_ts_ ## kind));\
-    }\
-    for (i = 0; i < value.size; i++) {\
-      buf[i] = GRN_ ## TYPE ##_VALUE_AT(obj, i);\
-    }\
-    value.ptr = buf;\
-    rc = grn_ts_expr_push_ ## kind ## _vector(ctx, expr, value);\
-    GRN_FREE(buf);\
-    return rc;\
-  }
-/* grn_ts_expr_push_uvector() pushes an array of fixed-size values. */
-static grn_rc
-grn_ts_expr_push_uvector(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj)
-{
-  switch (obj->header.domain) {
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE(BOOL, bool)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE_WITH_TYPECAST(INT8, int)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE_WITH_TYPECAST(INT16, int)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE_WITH_TYPECAST(INT32, int)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE(INT64, int)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE_WITH_TYPECAST(UINT8, int)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE_WITH_TYPECAST(UINT16, int)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE_WITH_TYPECAST(UINT32, int)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE(UINT64, int)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE(TIME, time)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE(TOKYO_GEO_POINT, tokyo_geo)
-    GRN_TS_EXPR_PUSH_UVECTOR_CASE(WGS84_GEO_POINT, wgs84_geo)
-    default: {
-      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data type: %d",
-                        obj->header.domain);
-    }
-  }
-}
-#undef GRN_TS_EXPR_PUSH_UVECTOR_CASE_WITH_TYPECAST
-#undef GRN_TS_EXPR_PUSH_UVECTOR_CASE
-
-/* grn_ts_expr_push_vector() pushes an array of texts. */
-static grn_rc
-grn_ts_expr_push_vector(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj)
-{
-  switch (obj->header.domain) {
-    case GRN_DB_SHORT_TEXT:
-    case GRN_DB_TEXT:
-    case GRN_DB_LONG_TEXT: {
-      size_t i;
-      grn_rc rc;
-      grn_ts_text *buf;
-      grn_ts_text_vector value = { NULL, grn_vector_size(ctx, obj) };
-      if (!value.size) {
-        return grn_ts_expr_push_text_vector(ctx, expr, value);
-      }
-      buf = GRN_MALLOCN(grn_ts_text, value.size);
-      if (!buf) {
-        GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
-                          "GRN_MALLOCN failed: "
-                          "%" GRN_FMT_SIZE " x %" GRN_FMT_SIZE,
-                          sizeof(grn_ts_text), value.size);
-      }
-      for (i = 0; i < value.size; i++) {
-        buf[i].size = grn_vector_get_element(ctx, obj, i, &buf[i].ptr,
-                                             NULL, NULL);
-      }
-      value.ptr = buf;
-      rc = grn_ts_expr_push_text_vector(ctx, expr, value);
-      GRN_FREE(buf);
-      return rc;
-    }
-    default: {
-      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data type: %d",
-                        obj->header.domain);
-    }
-  }
-}
-
-static grn_rc
-grn_ts_expr_push_single_accessor(grn_ctx *ctx, grn_ts_expr *expr,
-                                 grn_accessor *accessor)
-{
-  switch (accessor->action) {
-    case GRN_ACCESSOR_GET_ID: {
-      return grn_ts_expr_push_id(ctx, expr);
-    }
-    case GRN_ACCESSOR_GET_SCORE: {
-      return grn_ts_expr_push_score(ctx, expr);
-    }
-    case GRN_ACCESSOR_GET_KEY: {
-      if (accessor->obj != expr->curr_table) {
-        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "table conflict");
-      }
-      return grn_ts_expr_push_key(ctx, expr);
-    }
-    case GRN_ACCESSOR_GET_VALUE: {
-      if (accessor->obj != expr->curr_table) {
-        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "table conflict");
-      }
-      return grn_ts_expr_push_value(ctx, expr);
-    }
-    case GRN_ACCESSOR_GET_COLUMN_VALUE: {
-      return grn_ts_expr_push_column(ctx, expr, accessor->obj);
-    }
-    default: {
-      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid accessor action: %d",
-                        accessor->action);
-    }
-  }
-}
-
-static grn_rc
-grn_ts_expr_push_accessor(grn_ctx *ctx, grn_ts_expr *expr,
-                          grn_accessor *accessor)
-{
-  grn_rc rc = grn_ts_expr_push_single_accessor(ctx, expr, accessor);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  for (accessor = accessor->next; accessor; accessor = accessor->next) {
-    rc = grn_ts_expr_begin_subexpr(ctx, expr);
-    if (rc != GRN_SUCCESS) {
-      return rc;
-    }
-    rc = grn_ts_expr_push_single_accessor(ctx, expr, accessor);
-    if (rc != GRN_SUCCESS) {
-      return rc;
-    }
-    rc = grn_ts_expr_end_subexpr(ctx, expr);
-    if (rc != GRN_SUCCESS) {
-      return rc;
-    }
-  }
-  return GRN_SUCCESS;
-}
-
-grn_rc
-grn_ts_expr_push_obj(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj)
-{
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE) || !obj) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  switch (obj->header.type) {
-    case GRN_BULK: {
-      return grn_ts_expr_push_bulk(ctx, expr, obj);
-    }
-    case GRN_UVECTOR: {
-      return grn_ts_expr_push_uvector(ctx, expr, obj);
-    }
-    case GRN_VECTOR: {
-      return grn_ts_expr_push_vector(ctx, expr, obj);
-    }
-    case GRN_ACCESSOR: {
-      return grn_ts_expr_push_accessor(ctx, expr, (grn_accessor *)obj);
-    }
-    case GRN_COLUMN_FIX_SIZE:
-    case GRN_COLUMN_VAR_SIZE: {
-      return grn_ts_expr_push_column(ctx, expr, obj);
-    }
-    default: {
-      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid object type: %d",
-                        obj->header.type);
-    }
-  }
-}
-
-grn_rc
-grn_ts_expr_push_id(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  grn_rc rc;
-  grn_ts_expr_node *node;
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE)) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  rc = grn_ts_expr_reserve_stack(ctx, expr);
-  if (rc == GRN_SUCCESS) {
-    rc = grn_ts_expr_id_node_open(ctx, &node);
-    if (rc == GRN_SUCCESS) {
-      expr->stack[expr->stack_depth++] = node;
-    }
-  }
-  return rc;
-}
-
-grn_rc
-grn_ts_expr_push_score(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  grn_rc rc;
-  grn_ts_expr_node *node;
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE)) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  rc = grn_ts_expr_reserve_stack(ctx, expr);
-  if (rc == GRN_SUCCESS) {
-    rc = grn_ts_expr_score_node_open(ctx, &node);
-    if (rc == GRN_SUCCESS) {
-      expr->stack[expr->stack_depth++] = node;
-    }
-  }
-  return rc;
-}
-
-grn_rc
-grn_ts_expr_push_key(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  grn_rc rc;
-  grn_ts_expr_node *node;
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE)) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  rc = grn_ts_expr_reserve_stack(ctx, expr);
-  if (rc == GRN_SUCCESS) {
-    rc = grn_ts_expr_key_node_open(ctx, expr->curr_table, &node);
-    if (rc == GRN_SUCCESS) {
-      expr->stack[expr->stack_depth++] = node;
-    }
-  }
-  return rc;
-}
-
-grn_rc
-grn_ts_expr_push_value(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  grn_rc rc;
-  grn_ts_expr_node *node;
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE)) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  rc = grn_ts_expr_reserve_stack(ctx, expr);
-  if (rc == GRN_SUCCESS) {
-    rc = grn_ts_expr_value_node_open(ctx, expr->curr_table, &node);
-    if (rc == GRN_SUCCESS) {
-      expr->stack[expr->stack_depth++] = node;
-    }
-  }
-  return rc;
-}
-
-grn_rc
-grn_ts_expr_push_const(grn_ctx *ctx, grn_ts_expr *expr,
-                       grn_ts_data_kind kind, const void *value)
-{
-  grn_rc rc;
-  grn_ts_expr_node *node;
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE) || !value) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  rc = grn_ts_expr_reserve_stack(ctx, expr);
-  if (rc == GRN_SUCCESS) {
-    rc = grn_ts_expr_const_node_open(ctx, kind, value, &node);
-    if (rc == GRN_SUCCESS) {
-      expr->stack[expr->stack_depth++] = node;
-    }
-  }
-  return rc;
-}
-
-grn_rc
-grn_ts_expr_push_column(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *column)
-{
-  grn_rc rc;
-  grn_ts_expr_node *node;
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE) ||
-      !column || !grn_ts_obj_is_column(ctx, column) ||
-      (DB_OBJ(expr->curr_table)->id != column->header.domain)) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  rc = grn_ts_expr_reserve_stack(ctx, expr);
-  if (rc == GRN_SUCCESS) {
-    rc = grn_ts_expr_column_node_open(ctx, column, &node);
-    if (rc == GRN_SUCCESS) {
-      expr->stack[expr->stack_depth++] = node;
-    }
-  }
-  return rc;
-}
-
-grn_rc
-grn_ts_expr_push_op(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_op_type op_type)
-{
-  grn_rc rc;
-  grn_ts_expr_node **args, *node;
-  size_t i, n_args, max_n_args;
-  if (!ctx) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE)) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-  n_args = grn_ts_op_get_n_args(op_type);
-  if (!n_args) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT,
-                      "invalid #arguments: %" GRN_FMT_SIZE,
-                      n_args);
-  }
-  max_n_args = expr->stack_depth;
-  if (expr->n_bridges) {
-    max_n_args -= expr->bridges[expr->n_bridges - 1].stack_depth;
-  }
-  if (n_args > max_n_args) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT,
-                      "invalid #arguments: %" GRN_FMT_SIZE ", %" GRN_FMT_SIZE,
-                      n_args, expr->stack_depth);
-  }
-  /* Arguments are the top n_args nodes in the stack. */
-  args = &expr->stack[expr->stack_depth - n_args];
-  for (i = 0; i < n_args; i++) {
-    /*
-     * FIXME: Operators "==" and "!=" should compare arguments as references
-     *        if possible.
-     */
-    rc = grn_ts_expr_deref(ctx, expr, &args[i]);
-    if (rc != GRN_SUCCESS) {
-      return rc;
-    }
-  }
-  expr->stack_depth -= n_args;
-  rc = grn_ts_expr_op_node_open(ctx, op_type, args, n_args, &node);
-  if (rc == GRN_SUCCESS) {
-    expr->stack[expr->stack_depth++] = node;
-  }
-  return rc;
-}
-
-grn_rc
-grn_ts_expr_push_bool(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_bool value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_BOOL, &value);
-}
-
-grn_rc
-grn_ts_expr_push_int(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_int value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_INT, &value);
-}
-
-grn_rc
-grn_ts_expr_push_float(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_float value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_FLOAT, &value);
-}
-
-grn_rc
-grn_ts_expr_push_time(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_time value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_TIME, &value);
-}
-
-grn_rc
-grn_ts_expr_push_text(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_text value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_TEXT, &value);
-}
-
-grn_rc
-grn_ts_expr_push_geo(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_geo value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_GEO, &value);
-}
-
-grn_rc
-grn_ts_expr_push_tokyo_geo(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_geo value)
-{
-  grn_rc rc = grn_ts_expr_push_const(ctx, expr, GRN_TS_GEO, &value);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  expr->stack[expr->stack_depth - 1]->data_type = GRN_DB_TOKYO_GEO_POINT;
-  return GRN_SUCCESS;
-}
-
-grn_rc
-grn_ts_expr_push_wgs84_geo(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_geo value)
-{
-  grn_rc rc = grn_ts_expr_push_const(ctx, expr, GRN_TS_GEO, &value);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  expr->stack[expr->stack_depth - 1]->data_type = GRN_DB_WGS84_GEO_POINT;
-  return GRN_SUCCESS;
-}
-
-grn_rc
-grn_ts_expr_push_bool_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                             grn_ts_bool_vector value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_BOOL_VECTOR, &value);
-}
-
-grn_rc
-grn_ts_expr_push_int_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                            grn_ts_int_vector value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_INT_VECTOR, &value);
-}
-
-grn_rc
-grn_ts_expr_push_float_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                              grn_ts_float_vector value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_FLOAT_VECTOR, &value);
-}
-
-grn_rc
-grn_ts_expr_push_time_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                             grn_ts_time_vector value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_TIME_VECTOR, &value);
-}
-
-grn_rc
-grn_ts_expr_push_text_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                             grn_ts_text_vector value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_TEXT_VECTOR, &value);
-}
-
-grn_rc
-grn_ts_expr_push_geo_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                            grn_ts_geo_vector value)
-{
-  return grn_ts_expr_push_const(ctx, expr, GRN_TS_GEO_VECTOR, &value);
-}
-
-grn_rc
-grn_ts_expr_push_tokyo_geo_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                  grn_ts_geo_vector value)
-{
-  grn_rc rc = grn_ts_expr_push_const(ctx, expr, GRN_TS_GEO_VECTOR, &value);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  expr->stack[expr->stack_depth - 1]->data_type = GRN_DB_TOKYO_GEO_POINT;
-  return GRN_SUCCESS;
-}
-
-grn_rc
-grn_ts_expr_push_wgs84_geo_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                  grn_ts_geo_vector value)
-{
-  grn_rc rc = grn_ts_expr_push_const(ctx, expr, GRN_TS_GEO_VECTOR, &value);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  expr->stack[expr->stack_depth - 1]->data_type = GRN_DB_TOKYO_GEO_POINT;
-  return GRN_SUCCESS;
-}
-
-/* grn_ts_expr_reserve_bridges() extends a bridge buffer for a new bridge. */
-static grn_rc
-grn_ts_expr_reserve_bridges(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  size_t n_bytes, new_max_n_bridges;
-  grn_ts_expr_bridge *new_bridges;
-  if (expr->n_bridges < expr->max_n_bridges) {
-    return GRN_SUCCESS;
-  }
-  new_max_n_bridges = expr->n_bridges * 2;
-  if (!new_max_n_bridges) {
-    new_max_n_bridges = 1;
-  }
-  n_bytes = sizeof(grn_ts_expr_bridge) * new_max_n_bridges;
-  new_bridges = (grn_ts_expr_bridge *)GRN_REALLOC(expr->bridges, n_bytes);
-  if (!new_bridges) {
-    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
-                      "GRN_REALLOC failed: %" GRN_FMT_SIZE,
-                      n_bytes);
-  }
-  expr->bridges = new_bridges;
-  expr->max_n_bridges = new_max_n_bridges;
-  return GRN_SUCCESS;
-}
-
-grn_rc
-grn_ts_expr_begin_subexpr(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  grn_rc rc;
-  size_t max_n_args;
-  grn_obj *obj;
-  grn_ts_expr_node *node;
-  grn_ts_expr_bridge *bridge;
   if (!ctx) {
     return GRN_INVALID_ARGUMENT;
   }
-  if (!expr || (expr->type != GRN_TS_EXPR_INCOMPLETE)) {
+  if (!expr) {
     GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
   }
-  max_n_args = expr->stack_depth;
-  if (expr->n_bridges) {
-    max_n_args -= expr->bridges[expr->n_bridges - 1].stack_depth;
-  }
-  if (!max_n_args) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
-  }
-
-  /* Check whehter or not the latest node refers to a table. */
-  node = expr->stack[expr->stack_depth - 1];
-  if ((node->data_kind & ~GRN_TS_VECTOR_FLAG) != GRN_TS_REF) {
-    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
-                      node->data_kind);
-  }
-  obj = grn_ctx_at(ctx, node->data_type);
-  if (!obj) {
-    GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_ctx_at failed: %d",
-                      node->data_type);
-  }
-  if (!grn_ts_obj_is_table(ctx, obj)) {
-    grn_obj_unlink(ctx, obj);
-    GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "not table: %d", node->data_type);
-  }
-
-  /* Creates a bridge to a subexpression. */
-  rc = grn_ts_expr_reserve_bridges(ctx, expr);
-  if (rc != GRN_SUCCESS) {
-    grn_obj_unlink(ctx, obj);
-    return rc;
-  }
-  bridge = &expr->bridges[expr->n_bridges++];
-  grn_ts_expr_bridge_init(ctx, bridge);
-  bridge->src_table = expr->curr_table;
-  bridge->dest_table = obj;
-  bridge->stack_depth = expr->stack_depth;
-  expr->curr_table = bridge->dest_table;
-  return GRN_SUCCESS;
-}
-
-grn_rc
-grn_ts_expr_end_subexpr(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  grn_rc rc;
-  grn_ts_expr_node **args, *node;
-  grn_ts_expr_bridge *bridge;
-  if (!ctx || !expr || (expr->type != GRN_TS_EXPR_INCOMPLETE) ||
-      (expr->stack_depth < 2) || !expr->n_bridges) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  /* Check whehter or not the subexpression is complete.*/
-  bridge = &expr->bridges[expr->n_bridges - 1];
-  if (expr->stack_depth != (bridge->stack_depth + 1)) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  /* Creates a bridge node. */
-  args = &expr->stack[expr->stack_depth - 2];
-  rc = grn_ts_expr_bridge_node_open(ctx, args[0], args[1], &node);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  /* Note: grn_ts_expr_reserve_stack() is not required. */
-  expr->stack_depth -= 2;
-  expr->stack[expr->stack_depth++] = node;
-  expr->curr_table = bridge->src_table;
-  grn_ts_expr_bridge_fin(ctx, bridge);
-  expr->n_bridges--;
-  return GRN_SUCCESS;
-}
-
-grn_rc
-grn_ts_expr_complete(grn_ctx *ctx, grn_ts_expr *expr)
-{
-  grn_rc rc;
-  grn_ts_expr_node *root;
-  if (!ctx || !expr || (expr->type != GRN_TS_EXPR_INCOMPLETE)) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  if (expr->stack_depth != 1) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  rc = grn_ts_expr_deref(ctx, expr, &expr->stack[0]);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  root = expr->stack[0];
-  switch (root->data_kind) {
-    case GRN_TS_REF:
-    case GRN_TS_REF_VECTOR: {
-      return GRN_INVALID_ARGUMENT;
-    }
-    default: {
-      break;
-    }
-  }
-  switch (root->type) {
-    case GRN_TS_EXPR_ID_NODE: {
-      expr->type = GRN_TS_EXPR_ID;
-      break;
-    }
-    case GRN_TS_EXPR_SCORE_NODE: {
-      expr->type = GRN_TS_EXPR_SCORE;
-      break;
-    }
-    case GRN_TS_EXPR_KEY_NODE:
-    case GRN_TS_EXPR_VALUE_NODE: {
-      expr->type = GRN_TS_EXPR_VARIABLE;
-      break;
-    }
-    case GRN_TS_EXPR_CONST_NODE: {
-      expr->type = GRN_TS_EXPR_CONST;
-      break;
-    }
-    case GRN_TS_EXPR_COLUMN_NODE:
-    case GRN_TS_EXPR_OP_NODE:
-    case GRN_TS_EXPR_BRIDGE_NODE: {
-      expr->type = GRN_TS_EXPR_VARIABLE;
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  expr->data_type = root->data_type;
-  expr->data_kind = root->data_kind;
-  expr->root = root;
+  grn_ts_expr_fin(ctx, expr);
+  GRN_FREE(expr);
   return GRN_SUCCESS;
 }
 
@@ -1019,10 +158,12 @@ grn_ts_expr_evaluate_to_buf(grn_ctx *ctx, grn_ts_expr *expr,
                             const grn_ts_record *in, size_t n_in,
                             grn_ts_buf *out)
 {
-  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_INCOMPLETE) ||
-      (expr->type == GRN_TS_EXPR_BROKEN) || (!in && n_in) || !out) {
+  if (!ctx) {
     return GRN_INVALID_ARGUMENT;
   }
+  if (!expr || (!in && n_in) || !out) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
   if (!n_in) {
     return GRN_SUCCESS;
   }
@@ -1033,10 +174,12 @@ grn_rc
 grn_ts_expr_evaluate(grn_ctx *ctx, grn_ts_expr *expr,
                      const grn_ts_record *in, size_t n_in, void *out)
 {
-  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_INCOMPLETE) ||
-      (expr->type == GRN_TS_EXPR_BROKEN) || (!in && n_in) || (n_in && !out)) {
+  if (!ctx) {
     return GRN_INVALID_ARGUMENT;
   }
+  if (!expr || (!in && n_in) || (n_in && !out)) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
   if (!n_in) {
     return GRN_SUCCESS;
   }
@@ -1048,11 +191,12 @@ grn_ts_expr_filter(grn_ctx *ctx, grn_ts_expr *expr,
                    grn_ts_record *in, size_t n_in,
                    grn_ts_record *out, size_t *n_out)
 {
-  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_INCOMPLETE) ||
-      (expr->type == GRN_TS_EXPR_BROKEN) || (!in && n_in) ||
-      !out || !n_out) {
+  if (!ctx) {
     return GRN_INVALID_ARGUMENT;
   }
+  if (!expr || (!in && n_in) || !out || !n_out) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
   if (!n_in) {
     *n_out = 0;
     return GRN_SUCCESS;
@@ -1064,10 +208,12 @@ grn_rc
 grn_ts_expr_adjust(grn_ctx *ctx, grn_ts_expr *expr,
                    grn_ts_record *io, size_t n_io)
 {
-  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_INCOMPLETE) ||
-      (expr->type == GRN_TS_EXPR_BROKEN) || (!io && n_io)) {
+  if (!ctx) {
     return GRN_INVALID_ARGUMENT;
   }
+  if (!expr || (!io && n_io)) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
   if (!n_io) {
     return GRN_SUCCESS;
   }

  Modified: lib/ts/ts_expr.h (+18 -161)
===================================================================
--- lib/ts/ts_expr.h    2015-11-16 14:59:23 +0900 (8dfcaca)
+++ lib/ts/ts_expr.h    2015-11-16 15:17:14 +0900 (d32ce9f)
@@ -19,9 +19,11 @@
 #ifndef GRN_TS_EXPR_H
 #define GRN_TS_EXPR_H
 
+#include "../grn.h"
+
 #include "ts_buf.h"
 #include "ts_expr_node.h"
-#include "ts_op.h"
+#include "ts_str.h"
 #include "ts_types.h"
 
 #ifdef __cplusplus
@@ -33,13 +35,10 @@ extern "C" {
  */
 
 typedef enum {
-  GRN_TS_EXPR_INCOMPLETE, /* An incomplete expression. */
-  GRN_TS_EXPR_BROKEN,     /* A broken expression. */
-                          /* Any operation fails for a broken expression. */
-  GRN_TS_EXPR_ID,         /* An expression associated with _id. */
-  GRN_TS_EXPR_SCORE,      /* An expression associated with _score. */
-  GRN_TS_EXPR_CONST,      /* A const. */
-  GRN_TS_EXPR_VARIABLE    /* An expression that contains a variable. */
+  GRN_TS_EXPR_ID,      /* IDs (_id). */
+  GRN_TS_EXPR_SCORE,   /* Scores (_score). */
+  GRN_TS_EXPR_CONST,   /* A const. */
+  GRN_TS_EXPR_VARIABLE /* An expression that contains a variable. */
 } grn_ts_expr_type;
 
 /*-------------------------------------------------------------
@@ -47,170 +46,28 @@ typedef enum {
  */
 
 typedef struct {
-  grn_obj *src_table;  /* The source table of a bridge (no ref. count). */
-  grn_obj *dest_table; /* The destination table of a bridge. */
-  size_t stack_depth;  /* The stack depth (position) of a bridge. */
-} grn_ts_expr_bridge;
-
-typedef struct {
-  grn_obj *table;              /* Associated table. */
-  grn_obj *curr_table;         /* Current table (no ref. count). */
-  grn_ts_expr_type type;       /* Expression type. */
-  grn_ts_data_kind data_kind;  /* Abstract data type. */
-  grn_ts_data_type data_type;  /* Detailed data type. */
-  grn_ts_expr_node *root;      /* Root node. */
-  grn_ts_expr_node **stack;    /* Node stack. */
-  size_t stack_depth;          /* Node stack's current depth. */
-  size_t stack_size;           /* Node stack's size (capacity). */
-  grn_ts_expr_bridge *bridges; /* Bridges to subexpressions. */
-  size_t n_bridges;            /* Number of bridges (subexpression depth). */
-  size_t max_n_bridges;        /* Max. number (capacity) of bridges. */
+  grn_obj *table;             /* Associated table. */
+  grn_ts_expr_type type;      /* Expression type. */
+  grn_ts_data_kind data_kind; /* Abstract data type. */
+  grn_ts_data_type data_type; /* Detailed data type. */
+  grn_ts_expr_node *root;     /* Root node. */
 } grn_ts_expr;
 
-/* grn_ts_expr_open() creates an empty expression. */
-grn_rc grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr **expr);
+/* grn_ts_expr_open() creates an expression. */
+grn_rc grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr_node *root,
+                        grn_ts_expr **expr);
 
-/* grn_ts_expr_parse() creates an expression from a string. */
-grn_rc grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table,
-                         const char *str_ptr, size_t str_size,
+/* grn_ts_expr_parse() parses a string and creates an expression. */
+grn_rc grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
                          grn_ts_expr **expr);
 
 /* grn_ts_expr_close() destroys an expression. */
 grn_rc grn_ts_expr_close(grn_ctx *ctx, grn_ts_expr *expr);
 
-/*
- * grn_ts_expr_get_table() returns the associated table.
- * If arguments are invalid, the return value is NULL.
- */
-grn_obj *grn_ts_expr_get_table(grn_ctx *ctx, grn_ts_expr *expr);
-
-/*
- * grn_ts_expr_get_type() returns the expression type.
- * If arguments are invalid, the return value is GRN_EXPR_BROKEN.
- */
-grn_ts_expr_type grn_ts_expr_get_type(grn_ctx *ctx, grn_ts_expr *expr);
-
-/*
- * grn_ts_expr_get_data_kind() returns the data kind.
- * If arguments are invalid, the return value is GRN_TS_VOID.
- */
-grn_ts_data_kind grn_ts_expr_get_data_kind(grn_ctx *ctx, grn_ts_expr *expr);
-
-/*
- * grn_ts_expr_get_data_type() returns the data type.
- * If arguments are invalid, the return value is GRN_DB_VOID.
- */
-
-grn_ts_data_type grn_ts_expr_get_data_type(grn_ctx *ctx, grn_ts_expr *expr);
-
-/*
- * grn_ts_expr_get_root() returns the root node.
- * If arguments are invalid, the return value is NULL.
- */
-grn_ts_expr_node *grn_ts_expr_get_root(grn_ctx *ctx, grn_ts_expr *expr);
-
-/* grn_ts_expr_push() parses a string and pushes the result. */
-grn_rc grn_ts_expr_push(grn_ctx *ctx, grn_ts_expr *expr,
-                        const char *str_ptr, size_t str_size);
-
-/* grn_ts_expr_push_name() pushes a named object. */
-grn_rc grn_ts_expr_push_name(grn_ctx *ctx, grn_ts_expr *expr,
-                             const char *name_ptr, size_t name_size);
-
-/*
- * grn_ts_expr_push_obj() pushes an object.
- *
- * Acceptable objects are as follows:
- * - Consts
- *  - GRN_BULK: GRN_DB_*.
- *  - GRN_UVECTOR: GRN_DB_* except GRN_DB_[SHORT/LONG_]TEXT.
- *  - GRN_VECTOR: GRN_DB_[SHORT/LONG_]TEXT.
- * - Columns
- *  - GRN_ACCESSOR: _id, _score, _key, and _value.
- *  - GRN_COLUMN_FIX_SIZE: GRN_DB_* except GRN_DB_[SHORT/LONG_]TEXT.
- *  - GRN_COLUMN_VAR_SIZE: GRN_DB_[SHORT/LONG_]TEXT.
- */
-grn_rc grn_ts_expr_push_obj(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj);
-
-/* grn_ts_expr_push_id() pushes "_id". */
-grn_rc grn_ts_expr_push_id(grn_ctx *ctx, grn_ts_expr *expr);
-/* grn_ts_expr_push_score() pushes "_score". */
-grn_rc grn_ts_expr_push_score(grn_ctx *ctx, grn_ts_expr *expr);
-/* grn_ts_expr_push_key() pushes "_key". */
-grn_rc grn_ts_expr_push_key(grn_ctx *ctx, grn_ts_expr *expr);
-/* grn_ts_expr_push_key() pushes "_value". */
-grn_rc grn_ts_expr_push_value(grn_ctx *ctx, grn_ts_expr *expr);
-/* grn_ts_expr_push_const() pushes a const. */
-grn_rc grn_ts_expr_push_const(grn_ctx *ctx, grn_ts_expr *expr,
-                              grn_ts_data_kind kind, const void *value);
-/* grn_ts_expr_push_column() pushes a column. */
-grn_rc grn_ts_expr_push_column(grn_ctx *ctx, grn_ts_expr *expr,
-                               grn_obj *column);
-/* grn_ts_expr_push_op() pushes an operator. */
-grn_rc grn_ts_expr_push_op(grn_ctx *ctx, grn_ts_expr *expr,
-                           grn_ts_op_type op_type);
-
-/* grn_ts_expr_push_bool() pushes a Bool const. */
-grn_rc grn_ts_expr_push_bool(grn_ctx *ctx, grn_ts_expr *expr,
-                             grn_ts_bool value);
-/* grn_ts_expr_push_int() pushes an Int64 const. */
-grn_rc grn_ts_expr_push_int(grn_ctx *ctx, grn_ts_expr *expr,
-                            grn_ts_int value);
-/* grn_ts_expr_push_float() pushes a Float const. */
-grn_rc grn_ts_expr_push_float(grn_ctx *ctx, grn_ts_expr *expr,
-                              grn_ts_float value);
-/* grn_ts_expr_push_time() pushes a Time const. */
-grn_rc grn_ts_expr_push_time(grn_ctx *ctx, grn_ts_expr *expr,
-                             grn_ts_time value);
-/* grn_ts_expr_push_text() pushes a Text const. */
-grn_rc grn_ts_expr_push_text(grn_ctx *ctx, grn_ts_expr *expr,
-                             grn_ts_text value);
-/* grn_ts_expr_push_geo() pushes a GeoPoint const. */
-grn_rc grn_ts_expr_push_geo(grn_ctx *ctx, grn_ts_expr *expr,
-                            grn_ts_geo value);
-/* grn_ts_expr_push_tokyo_geo() pushes a TokyoGeoPoint const. */
-grn_rc grn_ts_expr_push_tokyo_geo(grn_ctx *ctx, grn_ts_expr *expr,
-                                  grn_ts_geo value);
-/* grn_ts_expr_push_wgs84_geo() pushes a WGS84GeoPoint const. */
-grn_rc grn_ts_expr_push_wgs84_geo(grn_ctx *ctx, grn_ts_expr *expr,
-                                  grn_ts_geo value);
-/* grn_ts_expr_push_bool_vector() pushes a Bool vector const. */
-grn_rc grn_ts_expr_push_bool_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                    grn_ts_bool_vector value);
-/* grn_ts_expr_push_int_vector() pushes an Int64 vector const. */
-grn_rc grn_ts_expr_push_int_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                   grn_ts_int_vector value);
-/* grn_ts_expr_push_float_vector() pushes a Float vector const. */
-grn_rc grn_ts_expr_push_float_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                     grn_ts_float_vector value);
-/* grn_ts_expr_push_time_vector() pushes a Time vector const. */
-grn_rc grn_ts_expr_push_time_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                    grn_ts_time_vector value);
-/* grn_ts_expr_push_text_vector() pushes a Text vector const. */
-grn_rc grn_ts_expr_push_text_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                    grn_ts_text_vector value);
-/* grn_ts_expr_push_geo_vector() pushes a GeoPoint vector const. */
-grn_rc grn_ts_expr_push_geo_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                   grn_ts_geo_vector value);
-/* grn_ts_expr_push_tokyo_geo_vector() pushes a TokyoGeoPoint vector const. */
-grn_rc grn_ts_expr_push_tokyo_geo_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                         grn_ts_geo_vector value);
-/* grn_ts_expr_push_wgs84_geo_vector() pushes a WGS84GeoPoint vector const. */
-grn_rc grn_ts_expr_push_wgs84_geo_vector(grn_ctx *ctx, grn_ts_expr *expr,
-                                         grn_ts_geo_vector value);
-
-/* grn_ts_expr_begin_subexpr() begins a subexpression. */
-grn_rc grn_ts_expr_begin_subexpr(grn_ctx *ctx, grn_ts_expr *expr);
-
-/* grn_ts_expr_end_subexpr() ends a subexpression. */
-grn_rc grn_ts_expr_end_subexpr(grn_ctx *ctx, grn_ts_expr *expr);
-
-/* grn_rc grn_ts_expr_complete() completes an expression. */
-grn_rc grn_ts_expr_complete(grn_ctx *ctx, grn_ts_expr *expr);
-
 /* grn_ts_expr_evaluate() evaluates an expression. */
 grn_rc grn_ts_expr_evaluate(grn_ctx *ctx, grn_ts_expr *expr,
                             const grn_ts_record *in, size_t n_in, void *out);
+
 /* grn_ts_expr_evaluate_to_buf() evaluates an expression. */
 grn_rc grn_ts_expr_evaluate_to_buf(grn_ctx *ctx, grn_ts_expr *expr,
                                    const grn_ts_record *in, size_t n_in,

  Added: lib/ts/ts_expr_builder.c (+759 -0) 100644
===================================================================
--- /dev/null
+++ lib/ts/ts_expr_builder.c    2015-11-16 15:17:14 +0900 (a7d69c4)
@@ -0,0 +1,759 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+  Copyright(C) 2015 Brazil
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License version 2.1 as published by the Free Software Foundation.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "ts_expr_builder.h"
+
+#include <string.h>
+
+#include "../grn_ctx.h"
+#include "../grn_db.h"
+
+#include "ts_log.h"
+#include "ts_util.h"
+
+/*-------------------------------------------------------------
+ * grn_ts_expr_bridge.
+ */
+
+/* grn_ts_expr_bridge_init() initializes a bridge. */
+static void
+grn_ts_expr_bridge_init(grn_ctx *ctx, grn_ts_expr_bridge *bridge)
+{
+  memset(bridge, 0, sizeof(*bridge));
+  bridge->src_table = NULL;
+  bridge->dest_table = NULL;
+}
+
+/* grn_ts_expr_bridge_fin() finalizes a bridge. */
+static void
+grn_ts_expr_bridge_fin(grn_ctx *ctx, grn_ts_expr_bridge *bridge)
+{
+  if (bridge->dest_table) {
+    grn_obj_unlink(ctx, bridge->dest_table);
+  }
+  /* Note: bridge->src_table does not increment a reference count. */
+}
+
+/*-------------------------------------------------------------
+ * grn_ts_expr_builder.
+ */
+
+/* grn_ts_expr_builder_init() initializes an expression builder. */
+static void
+grn_ts_expr_builder_init(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  memset(builder, 0, sizeof(*builder));
+  builder->table = NULL;
+  builder->curr_table = NULL;
+  builder->nodes = NULL;
+  builder->bridges = NULL;
+}
+
+/* grn_ts_expr_builder_fin() finalizes an expression builder. */
+static void
+grn_ts_expr_builder_fin(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  size_t i;
+  if (builder->bridges) {
+    for (i = 0; i < builder->n_bridges; i++) {
+      grn_ts_expr_bridge_fin(ctx, &builder->bridges[i]);
+    }
+    GRN_FREE(builder->bridges);
+  }
+  if (builder->nodes) {
+    for (i = 0; i < builder->n_nodes; i++) {
+      if (builder->nodes[i]) {
+        grn_ts_expr_node_close(ctx, builder->nodes[i]);
+      }
+    }
+    GRN_FREE(builder->nodes);
+  }
+  /* Note: builder->curr_table does not increment a reference count. */
+  if (builder->table) {
+    grn_obj_unlink(ctx, builder->table);
+  }
+}
+
+grn_rc
+grn_ts_expr_builder_open(grn_ctx *ctx, grn_obj *table,
+                         grn_ts_expr_builder **builder)
+{
+  grn_rc rc;
+  grn_ts_expr_builder *new_builder;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!table || !grn_ts_obj_is_table(ctx, table) || !builder) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  new_builder = GRN_MALLOCN(grn_ts_expr_builder, 1);
+  if (!new_builder) {
+    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
+                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE,
+                      sizeof(grn_ts_expr_builder));
+  }
+  rc = grn_ts_obj_increment_ref_count(ctx, table);
+  if (rc != GRN_SUCCESS) {
+    GRN_FREE(new_builder);
+    return rc;
+  }
+  grn_ts_expr_builder_init(ctx, new_builder);
+  new_builder->table = table;
+  new_builder->curr_table = table;
+  *builder = new_builder;
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_builder_close(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  grn_ts_expr_builder_fin(ctx, builder);
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_deref() dereferences a node. */
+static grn_rc
+grn_ts_expr_builder_deref(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                          grn_ts_expr_node **node_ptr)
+{
+  grn_ts_expr_node *node = *node_ptr;
+  while (node->data_kind == GRN_TS_REF) {
+    grn_rc rc;
+    grn_ts_expr_node *key_node, *bridge_node;
+    grn_id table_id = node->data_type;
+    grn_obj *table = grn_ctx_at(ctx, table_id);
+    if (!table) {
+      return GRN_OBJECT_CORRUPT;
+    }
+    if (!grn_ts_obj_is_table(ctx, table)) {
+      grn_obj_unlink(ctx, table);
+      return GRN_OBJECT_CORRUPT;
+    }
+    rc = grn_ts_expr_key_node_open(ctx, table, &key_node);
+    grn_obj_unlink(ctx, table);
+    if (rc != GRN_SUCCESS) {
+      return rc;
+    }
+    rc = grn_ts_expr_bridge_node_open(ctx, node, key_node, &bridge_node);
+    if (rc != GRN_SUCCESS) {
+      return rc;
+    }
+    node = bridge_node;
+  }
+  *node_ptr = node;
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_builder_complete(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                             grn_ts_expr **expr)
+{
+  grn_rc rc;
+  grn_ts_expr *new_expr;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder || (builder->n_nodes != 1) || builder->n_bridges || !expr) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  rc = grn_ts_expr_builder_deref(ctx, builder, &builder->nodes[0]);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_open(ctx, builder->table, builder->nodes[0], &new_expr);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  builder->n_nodes = 0;
+  *expr = new_expr;
+  return GRN_SUCCESS;
+}
+
+/*
+ * grn_ts_expr_builder_push_node() pushes a node.
+ * The given node will be closed on failure.
+ */
+static grn_rc
+grn_ts_expr_builder_push_node(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                              grn_ts_expr_node *node)
+{
+  if (builder->n_nodes == builder->max_n_nodes) {
+    size_t n_bytes, new_max_n_nodes;
+    grn_ts_expr_node **new_nodes;
+    new_max_n_nodes = builder->n_nodes ? (builder->n_nodes * 2) : 1;
+    n_bytes = sizeof(grn_ts_expr_node *) * new_max_n_nodes;
+    new_nodes = (grn_ts_expr_node **)GRN_REALLOC(builder->nodes, n_bytes);
+    if (!new_nodes) {
+      grn_ts_expr_node_close(ctx, node);
+      GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
+                        "GRN_REALLOC failed: %" GRN_FMT_SIZE, n_bytes);
+    }
+    builder->nodes = new_nodes;
+    builder->max_n_nodes = new_max_n_nodes;
+  }
+  builder->nodes[builder->n_nodes++] = node;
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_builder_push_name(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                              grn_ts_str name)
+{
+  grn_obj *column;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder || !grn_ts_str_is_name(name)) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  if (grn_ts_str_is_id_name(name)) {
+    return grn_ts_expr_builder_push_id(ctx, builder);
+  }
+  if (grn_ts_str_is_score_name(name)) {
+    return grn_ts_expr_builder_push_score(ctx, builder);
+  }
+  if (grn_ts_str_is_key_name(name)) {
+    return grn_ts_expr_builder_push_key(ctx, builder);
+  }
+  if (grn_ts_str_is_value_name(name)) {
+    return grn_ts_expr_builder_push_value(ctx, builder);
+  }
+  /* grn_obj_column() returns a column or accessor. */
+  column = grn_obj_column(ctx, builder->curr_table, name.ptr, name.size);
+  if (!column) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "object not found: \"%.*s\"",
+                      (int)name.size, name.ptr);
+  }
+  return grn_ts_expr_builder_push_obj(ctx, builder, column);
+}
+
+#define GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(TYPE, KIND, kind)\
+  case GRN_DB_ ## TYPE: {\
+    grn_ts_ ## kind value = (grn_ts_ ## kind)GRN_ ## TYPE ## _VALUE(obj);\
+    return grn_ts_expr_builder_push_const(ctx, builder,\
+                                          GRN_TS_ ## KIND, obj->header.domain,\
+                                          &value);\
+  }
+/* grn_ts_expr_push_builder_bulk() pushes a scalar const. */
+static grn_rc
+grn_ts_expr_builder_push_bulk(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                              grn_obj *obj)
+{
+  switch (obj->header.domain) {
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(BOOL, BOOL, bool)
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(INT8, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(INT16, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(INT32, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(INT64, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(UINT8, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(UINT16, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(UINT32, INT, int)
+    /* The behavior is undefined if a value is greater than 2^63 - 1. */
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(UINT64, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(FLOAT, FLOAT, float)
+    GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(TIME, TIME, time)
+    case GRN_DB_SHORT_TEXT:
+    case GRN_DB_TEXT:
+    case GRN_DB_LONG_TEXT: {
+      grn_ts_text value = { GRN_TEXT_VALUE(obj), GRN_TEXT_LEN(obj) };
+      return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_TEXT,
+                                            obj->header.domain, &value);
+    }
+    case GRN_DB_TOKYO_GEO_POINT:
+    case GRN_DB_WGS84_GEO_POINT: {
+      grn_ts_geo value;
+      GRN_GEO_POINT_VALUE(obj, value.latitude, value.longitude);
+      return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_GEO,
+                                            obj->header.domain, &value);
+    }
+    default: {
+      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "not bulk");
+    }
+  }
+}
+#undef GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE
+
+#define GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(TYPE, KIND, kind)\
+  case GRN_DB_ ## TYPE: {\
+    grn_ts_ ## kind ##_vector value = { (grn_ts_ ## kind *)GRN_BULK_HEAD(obj),\
+                                        grn_uvector_size(ctx, obj) };\
+    return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_ ## KIND,\
+                                          obj->header.domain, &value);\
+  }
+#define GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(TYPE, KIND, kind)\
+  case GRN_DB_ ## TYPE: {\
+    size_t i;\
+    grn_rc rc;\
+    grn_ts_ ## kind *buf;\
+    grn_ts_ ## kind ## _vector value = { NULL, grn_uvector_size(ctx, obj) };\
+    if (!value.size) {\
+      return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_ ## KIND,\
+                                            obj->header.domain, &value);\
+    }\
+    buf = GRN_MALLOCN(grn_ts_ ## kind, value.size);\
+    if (!buf) {\
+      GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,\
+                        "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",\
+                        sizeof(grn_ts_ ## kind));\
+    }\
+    for (i = 0; i < value.size; i++) {\
+      buf[i] = GRN_ ## TYPE ##_VALUE_AT(obj, i);\
+    }\
+    value.ptr = buf;\
+    rc = grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_ ## KIND,\
+                                        obj->header.domain, &value);\
+    GRN_FREE(buf);\
+    return rc;\
+  }
+/* grn_ts_expr_builder_push_uvector() pushes an array of fixed-size values. */
+static grn_rc
+grn_ts_expr_builder_push_uvector(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                                 grn_obj *obj)
+{
+  switch (obj->header.domain) {
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(BOOL, BOOL, bool)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(INT8, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(INT16, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(INT32, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(INT64, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(UINT8, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(UINT16, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(UINT32, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(UINT64, INT, int)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(TIME, TIME, time)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(TOKYO_GEO_POINT, GEO, geo)
+    GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(WGS84_GEO_POINT, GEO, geo)
+    default: {
+      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data type: %d",
+                        obj->header.domain);
+    }
+  }
+}
+#undef GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST
+#undef GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE
+
+/* grn_ts_expr_builder_push_vector() pushes a Text vector. */
+static grn_rc
+grn_ts_expr_builder_push_vector(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                                grn_obj *obj)
+{
+  switch (obj->header.domain) {
+    case GRN_DB_SHORT_TEXT:
+    case GRN_DB_TEXT:
+    case GRN_DB_LONG_TEXT: {
+      size_t i;
+      grn_rc rc;
+      grn_ts_text *buf;
+      grn_ts_text_vector value = { NULL, grn_vector_size(ctx, obj) };
+      if (!value.size) {
+        return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_TEXT_VECTOR,
+                                              obj->header.domain, &value);
+      }
+      buf = GRN_MALLOCN(grn_ts_text, value.size);
+      if (!buf) {
+        GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
+                          "GRN_MALLOCN failed: "
+                          "%" GRN_FMT_SIZE " x %" GRN_FMT_SIZE,
+                          sizeof(grn_ts_text), value.size);
+      }
+      for (i = 0; i < value.size; i++) {
+        buf[i].size = grn_vector_get_element(ctx, obj, i, &buf[i].ptr,
+                                             NULL, NULL);
+      }
+      value.ptr = buf;
+      rc = grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_TEXT_VECTOR,
+                                          obj->header.domain, &value);
+      GRN_FREE(buf);
+      return rc;
+    }
+    default: {
+      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data type: %d",
+                        obj->header.domain);
+    }
+  }
+}
+
+static grn_rc
+grn_ts_expr_builder_push_single_accessor(grn_ctx *ctx,
+                                         grn_ts_expr_builder *builder,
+                                         grn_accessor *accessor)
+{
+  switch (accessor->action) {
+    case GRN_ACCESSOR_GET_ID: {
+      return grn_ts_expr_builder_push_id(ctx, builder);
+    }
+    case GRN_ACCESSOR_GET_SCORE: {
+      return grn_ts_expr_builder_push_score(ctx, builder);
+    }
+    case GRN_ACCESSOR_GET_KEY: {
+      if (accessor->obj != builder->curr_table) {
+        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "table conflict");
+      }
+      return grn_ts_expr_builder_push_key(ctx, builder);
+    }
+    case GRN_ACCESSOR_GET_VALUE: {
+      if (accessor->obj != builder->curr_table) {
+        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "table conflict");
+      }
+      return grn_ts_expr_builder_push_value(ctx, builder);
+    }
+    case GRN_ACCESSOR_GET_COLUMN_VALUE: {
+      return grn_ts_expr_builder_push_column(ctx, builder, accessor->obj);
+    }
+    default: {
+      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid accessor action: %d",
+                        accessor->action);
+    }
+  }
+}
+
+static grn_rc
+grn_ts_expr_builder_push_accessor(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                                  grn_accessor *accessor)
+{
+  grn_rc rc = grn_ts_expr_builder_push_single_accessor(ctx, builder, accessor);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  for (accessor = accessor->next; accessor; accessor = accessor->next) {
+    rc = grn_ts_expr_builder_begin_subexpr(ctx, builder);
+    if (rc != GRN_SUCCESS) {
+      return rc;
+    }
+    rc = grn_ts_expr_builder_push_single_accessor(ctx, builder, accessor);
+    if (rc != GRN_SUCCESS) {
+      return rc;
+    }
+    rc = grn_ts_expr_builder_end_subexpr(ctx, builder);
+    if (rc != GRN_SUCCESS) {
+      return rc;
+    }
+  }
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_builder_push_obj(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                             grn_obj *obj)
+{
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder || !obj) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  switch (obj->header.type) {
+    case GRN_BULK: {
+      return grn_ts_expr_builder_push_bulk(ctx, builder, obj);
+    }
+    case GRN_UVECTOR: {
+      return grn_ts_expr_builder_push_uvector(ctx, builder, obj);
+    }
+    case GRN_VECTOR: {
+      return grn_ts_expr_builder_push_vector(ctx, builder, obj);
+    }
+    case GRN_ACCESSOR: {
+      return grn_ts_expr_builder_push_accessor(ctx, builder,
+                                               (grn_accessor *)obj);
+    }
+    case GRN_COLUMN_FIX_SIZE:
+    case GRN_COLUMN_VAR_SIZE: {
+      return grn_ts_expr_builder_push_column(ctx, builder, obj);
+    }
+    default: {
+      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid object type: %d",
+                        obj->header.type);
+    }
+  }
+}
+
+grn_rc
+grn_ts_expr_builder_push_id(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  rc = grn_ts_expr_id_node_open(ctx, &node);
+  if (rc == GRN_SUCCESS) {
+    rc = grn_ts_expr_builder_push_node(ctx, builder, node);
+  }
+  return rc;
+}
+
+grn_rc
+grn_ts_expr_builder_push_score(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  rc = grn_ts_expr_score_node_open(ctx, &node);
+  if (rc == GRN_SUCCESS) {
+    rc = grn_ts_expr_builder_push_node(ctx, builder, node);
+  }
+  return rc;
+}
+
+grn_rc
+grn_ts_expr_builder_push_key(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  rc = grn_ts_expr_key_node_open(ctx, builder->curr_table, &node);
+  if (rc == GRN_SUCCESS) {
+    rc = grn_ts_expr_builder_push_node(ctx, builder, node);
+  }
+  return rc;
+}
+
+grn_rc
+grn_ts_expr_builder_push_value(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  rc = grn_ts_expr_value_node_open(ctx, builder->curr_table, &node);
+  if (rc == GRN_SUCCESS) {
+    rc = grn_ts_expr_builder_push_node(ctx, builder, node);
+  }
+  return rc;
+}
+
+grn_rc
+grn_ts_expr_builder_push_const(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                               grn_ts_data_kind kind, grn_ts_data_type type,
+                               const void *value)
+{
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  rc = grn_ts_expr_const_node_open(ctx, kind, type, value, &node);
+  if (rc == GRN_SUCCESS) {
+    rc = grn_ts_expr_builder_push_node(ctx, builder, node);
+  }
+  return rc;
+}
+
+grn_rc
+grn_ts_expr_builder_push_column(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                                grn_obj *column)
+{
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder || !column || !grn_ts_obj_is_column(ctx, column) ||
+      (DB_OBJ(builder->curr_table)->id != column->header.domain)) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  rc = grn_ts_expr_column_node_open(ctx, column, &node);
+  if (rc == GRN_SUCCESS) {
+    rc = grn_ts_expr_builder_push_node(ctx, builder, node);
+  }
+  return rc;
+}
+
+grn_rc
+grn_ts_expr_builder_push_op(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                            grn_ts_op_type op_type)
+{
+  grn_rc rc;
+  grn_ts_expr_node **args, *node;
+  size_t i, n_args, max_n_args;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  n_args = grn_ts_op_get_n_args(op_type);
+  if (!n_args) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT,
+                      "invalid #arguments: %" GRN_FMT_SIZE,
+                      n_args);
+  }
+  max_n_args = builder->n_nodes;
+  if (builder->n_bridges) {
+    max_n_args -= builder->bridges[builder->n_bridges - 1].stack_depth;
+  }
+  if (n_args > max_n_args) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT,
+                      "invalid #arguments: %" GRN_FMT_SIZE ", %" GRN_FMT_SIZE,
+                      n_args, builder->n_nodes);
+  }
+  /* Arguments are the top n_args nodes in the stack. */
+  args = &builder->nodes[builder->n_nodes - n_args];
+  for (i = 0; i < n_args; i++) {
+    /*
+     * FIXME: Operators "==" and "!=" should compare arguments as references
+     *        if possible.
+     */
+    rc = grn_ts_expr_builder_deref(ctx, builder, &args[i]);
+    if (rc != GRN_SUCCESS) {
+      return rc;
+    }
+  }
+  builder->n_nodes -= n_args;
+  rc = grn_ts_expr_op_node_open(ctx, op_type, args, n_args, &node);
+  if (rc == GRN_SUCCESS) {
+    builder->nodes[builder->n_nodes++] = node;
+  }
+  return rc;
+}
+
+/* grn_ts_expr_builder_push_bridge() pushes a bridge. */
+static grn_rc
+grn_ts_expr_builder_push_bridge(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                                grn_ts_expr_bridge *bridge)
+{
+  if (builder->n_bridges == builder->max_n_bridges) {
+    size_t n_bytes, new_max_n_bridges;
+    grn_ts_expr_bridge *new_bridges;
+    new_max_n_bridges = builder->n_bridges ? (builder->n_bridges * 2) : 1;
+    n_bytes = sizeof(grn_ts_expr_bridge) * new_max_n_bridges;
+    new_bridges = (grn_ts_expr_bridge *)GRN_REALLOC(builder->bridges, n_bytes);
+    if (!new_bridges) {
+      grn_ts_expr_bridge_fin(ctx, bridge);
+      GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
+                        "GRN_REALLOC failed: %" GRN_FMT_SIZE, n_bytes);
+    }
+    builder->bridges = new_bridges;
+    builder->max_n_bridges = new_max_n_bridges;
+  }
+  builder->bridges[builder->n_bridges++] = *bridge;
+  builder->curr_table = bridge->dest_table;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_builder_pop_bridge() pops a bridge. */
+static void
+grn_ts_expr_builder_pop_bridge(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  grn_ts_expr_bridge *bridge = &builder->bridges[builder->n_bridges - 1];
+  builder->curr_table = bridge->src_table;
+  grn_ts_expr_bridge_fin(ctx, bridge);
+  builder->n_bridges--;
+}
+
+grn_rc
+grn_ts_expr_builder_begin_subexpr(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  grn_rc rc;
+  size_t max_n_args;
+  grn_obj *obj;
+  grn_ts_expr_node *node;
+  grn_ts_expr_bridge bridge;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!builder) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  max_n_args = builder->n_nodes;
+  if (builder->n_bridges) {
+    max_n_args -= builder->bridges[builder->n_bridges - 1].stack_depth;
+  }
+  if (!max_n_args) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  /* Check whehter or not the latest node refers to a table. */
+  node = builder->nodes[builder->n_nodes - 1];
+  if ((node->data_kind & ~GRN_TS_VECTOR_FLAG) != GRN_TS_REF) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
+                      node->data_kind);
+  }
+  obj = grn_ctx_at(ctx, node->data_type);
+  if (!obj) {
+    GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_ctx_at failed: %d",
+                      node->data_type);
+  }
+  if (!grn_ts_obj_is_table(ctx, obj)) {
+    grn_obj_unlink(ctx, obj);
+    GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "not table: %d", node->data_type);
+  }
+  /* Creates a bridge to a subexpression. */
+  grn_ts_expr_bridge_init(ctx, &bridge);
+  bridge.src_table = builder->curr_table;
+  bridge.dest_table = obj;
+  bridge.stack_depth = builder->n_nodes;
+  rc = grn_ts_expr_builder_push_bridge(ctx, builder, &bridge);
+  if (rc != GRN_SUCCESS) {
+    grn_obj_unlink(ctx, obj);
+    return rc;
+  }
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_builder_end_subexpr(grn_ctx *ctx, grn_ts_expr_builder *builder)
+{
+  grn_rc rc;
+  grn_ts_expr_node **args, *node;
+  grn_ts_expr_bridge *bridge;
+  if (!ctx || !builder || (builder->n_nodes < 2) || !builder->n_bridges) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  /* Check whehter or not the subexpression is complete.*/
+  bridge = &builder->bridges[builder->n_bridges - 1];
+  if (builder->n_nodes != (bridge->stack_depth + 1)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  /* Creates a bridge node. */
+  args = &builder->nodes[builder->n_nodes - 2];
+  rc = grn_ts_expr_bridge_node_open(ctx, args[0], args[1], &node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  /* Note: The following grn_ts_expr_push_node() must not fail. */
+  builder->n_nodes -= 2;
+  grn_ts_expr_builder_push_node(ctx, builder, node);
+  grn_ts_expr_builder_pop_bridge(ctx, builder);
+  return GRN_SUCCESS;
+}

  Added: lib/ts/ts_expr_builder.h (+129 -0) 100644
===================================================================
--- /dev/null
+++ lib/ts/ts_expr_builder.h    2015-11-16 15:17:14 +0900 (a886459)
@@ -0,0 +1,129 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+  Copyright(C) 2015 Brazil
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License version 2.1 as published by the Free Software Foundation.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef GRN_TS_EXPR_BUILDER_H
+#define GRN_TS_EXPR_BUILDER_H
+
+#include "../grn.h"
+
+#include "ts_buf.h"
+#include "ts_expr.h"
+#include "ts_expr_node.h"
+#include "ts_op.h"
+#include "ts_str.h"
+#include "ts_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+  grn_obj *src_table;  /* The source table of a bridge (no ref. count). */
+  grn_obj *dest_table; /* The destination table of a bridge. */
+  size_t stack_depth;  /* The stack depth (position) of a bridge. */
+} grn_ts_expr_bridge;
+
+typedef struct {
+  grn_obj *table;              /* Associated table. */
+  grn_obj *curr_table;         /* Current table (no ref. count). */
+  grn_ts_expr_node **nodes;    /* Node stack. */
+  size_t n_nodes;              /* Number of nodes (stack depth). */
+  size_t max_n_nodes;          /* Maximum number of nodes (stack capacity). */
+  grn_ts_expr_bridge *bridges; /* Bridges to subexpressions. */
+  size_t n_bridges;            /* Number of bridges (subexpression depth). */
+  size_t max_n_bridges;        /* Max. number (capacity) of bridges. */
+} grn_ts_expr_builder;
+
+/* grn_ts_expr_builder_open() creates an expression builder. */
+grn_rc grn_ts_expr_builder_open(grn_ctx *ctx, grn_obj *table,
+                                grn_ts_expr_builder **builder);
+
+/* grn_ts_expr_builder_close() destroys an expression builder. */
+grn_rc grn_ts_expr_builder_close(grn_ctx *ctx, grn_ts_expr_builder *builder);
+
+/* grn_ts_expr_builder_complete() completes an expression. */
+grn_rc grn_ts_expr_builder_complete(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                                    grn_ts_expr **expr);
+
+/* grn_ts_expr_builder_push_name() pushes a named object. */
+grn_rc grn_ts_expr_builder_push_name(grn_ctx *ctx,
+                                     grn_ts_expr_builder *builder,
+                                     grn_ts_str name);
+
+/*
+ * TODO: Update the comment.
+ *
+ * grn_ts_expr_builder_push_obj() pushes an object.
+ *
+ * Acceptable objects are as follows:
+ * - Consts
+ *  - GRN_BULK: GRN_DB_*.
+ *  - GRN_UVECTOR: GRN_DB_* except GRN_DB_[SHORT/LONG_]TEXT.
+ *  - GRN_VECTOR: GRN_DB_[SHORT/LONG_]TEXT.
+ * - Columns
+ *  - GRN_ACCESSOR: _id, _score, _key, and _value.
+ *  - GRN_COLUMN_FIX_SIZE: GRN_DB_* except GRN_DB_[SHORT/LONG_]TEXT.
+ *  - GRN_COLUMN_VAR_SIZE: GRN_DB_[SHORT/LONG_]TEXT.
+ */
+grn_rc grn_ts_expr_builder_push_obj(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                                    grn_obj *obj);
+
+/* grn_ts_expr_builder_push_id() pushes "_id". */
+grn_rc grn_ts_expr_builder_push_id(grn_ctx *ctx, grn_ts_expr_builder *builder);
+
+/* grn_ts_expr_builder_push_score() pushes "_score". */
+grn_rc grn_ts_expr_builder_push_score(grn_ctx *ctx,
+                                      grn_ts_expr_builder *builder);
+
+/* grn_ts_expr_builder_push_key() pushes "_key". */
+grn_rc grn_ts_expr_builder_push_key(grn_ctx *ctx,
+                                    grn_ts_expr_builder *builder);
+
+/* grn_ts_expr_builder_push_value() pushes "_value". */
+grn_rc grn_ts_expr_builder_push_value(grn_ctx *ctx,
+                                      grn_ts_expr_builder *builder);
+
+/* grn_ts_expr_builder_push_const() pushes a const. */
+grn_rc grn_ts_expr_builder_push_const(grn_ctx *ctx,
+                                      grn_ts_expr_builder *builder,
+                                      grn_ts_data_kind kind,
+                                      grn_ts_data_type type,
+                                      const void *value);
+
+/* grn_ts_expr_builder_push_column() pushes a column. */
+grn_rc grn_ts_expr_builder_push_column(grn_ctx *ctx,
+                                       grn_ts_expr_builder *builder,
+                                       grn_obj *column);
+
+/* grn_ts_expr_builder_push_op() pushes an operator. */
+grn_rc grn_ts_expr_builder_push_op(grn_ctx *ctx, grn_ts_expr_builder *builder,
+                                   grn_ts_op_type op_type);
+
+/* grn_ts_expr_builder_begin_subexpr() begins a subexpression. */
+grn_rc grn_ts_expr_builder_begin_subexpr(grn_ctx *ctx,
+                                         grn_ts_expr_builder *builder);
+
+/* grn_ts_expr_builder_end_subexpr() ends a subexpression. */
+grn_rc grn_ts_expr_builder_end_subexpr(grn_ctx *ctx,
+                                       grn_ts_expr_builder *builder);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_TS_EXPR_BUILDER_H */

  Modified: lib/ts/ts_expr_node.c (+10 -5)
===================================================================
--- lib/ts/ts_expr_node.c    2015-11-16 14:59:23 +0900 (3dfd33f)
+++ lib/ts/ts_expr_node.c    2015-11-16 15:17:14 +0900 (5539f77)
@@ -2175,10 +2175,11 @@ grn_ts_expr_const_node_check_value(grn_ctx *ctx, grn_ts_data_kind kind,
 #undef GRN_TS_EXPR_CONST_NODE_CHECK_VALUE
 
 grn_rc
-grn_ts_expr_const_node_open(grn_ctx *ctx, grn_ts_data_kind kind,
+grn_ts_expr_const_node_open(grn_ctx *ctx, grn_ts_data_kind data_kind,
+                            grn_ts_data_type data_type,
                             const void *value, grn_ts_expr_node **node)
 {
-  grn_rc rc = grn_ts_expr_const_node_check_value(ctx, kind, value);
+  grn_rc rc = grn_ts_expr_const_node_check_value(ctx, data_kind, value);
   if (rc != GRN_SUCCESS) {
     return rc;
   }
@@ -2189,9 +2190,13 @@ grn_ts_expr_const_node_open(grn_ctx *ctx, grn_ts_data_kind kind,
                       sizeof(grn_ts_expr_const_node));
   }
   grn_ts_expr_const_node_init(ctx, new_node);
-  new_node->data_kind = kind;
-  new_node->data_type = grn_ts_data_kind_to_type(kind);
-  if (kind & GRN_TS_VECTOR_FLAG) {
+  new_node->data_kind = data_kind;
+  if (data_type != GRN_DB_VOID) {
+    new_node->data_type = data_type;
+  } else {
+    new_node->data_type = grn_ts_data_kind_to_type(data_kind);
+  }
+  if (data_kind & GRN_TS_VECTOR_FLAG) {
     rc = grn_ts_expr_const_node_set_vector(ctx, new_node, value);
   } else {
     rc = grn_ts_expr_const_node_set_scalar(ctx, new_node, value);

  Modified: lib/ts/ts_expr_node.h (+4 -1)
===================================================================
--- lib/ts/ts_expr_node.h    2015-11-16 14:59:23 +0900 (cab0cae)
+++ lib/ts/ts_expr_node.h    2015-11-16 15:17:14 +0900 (8ef6e42)
@@ -19,6 +19,8 @@
 #ifndef GRN_TS_EXPR_NODE_H
 #define GRN_TS_EXPR_NODE_H
 
+#include "../grn.h"
+
 #include "ts_buf.h"
 #include "ts_op.h"
 #include "ts_types.h"
@@ -68,7 +70,8 @@ grn_rc grn_ts_expr_value_node_open(grn_ctx *ctx, grn_obj *table,
                                    grn_ts_expr_node **node);
 
 /* grn_ts_expr_const_node_open() creates a node associated with a const. */
-grn_rc grn_ts_expr_const_node_open(grn_ctx *ctx, grn_ts_data_kind kind,
+grn_rc grn_ts_expr_const_node_open(grn_ctx *ctx, grn_ts_data_kind data_kind,
+                                   grn_ts_data_type data_type,
                                    const void *value, grn_ts_expr_node **node);
 
 /* grn_ts_expr_column_node_open() creates a node associated with a column. */

  Modified: lib/ts/ts_expr_parser.c (+59 -25)
===================================================================
--- lib/ts/ts_expr_parser.c    2015-11-16 14:59:23 +0900 (5e83335)
+++ lib/ts/ts_expr_parser.c    2015-11-16 15:17:14 +0900 (45cd097)
@@ -24,6 +24,7 @@
 
 #include "ts_log.h"
 #include "ts_str.h"
+#include "ts_util.h"
 
 /*-------------------------------------------------------------
  * grn_ts_expr_parser.
@@ -76,7 +77,7 @@ typedef grn_ts_expr_token grn_ts_expr_bridge_token;
 typedef grn_ts_expr_token grn_ts_expr_bracket_token;
 
 struct grn_ts_expr_parser {
-  grn_ts_expr *expr;                     /* Associated expression. */
+  grn_ts_expr_builder *builder;          /* Builder. */
   grn_ts_buf str_buf;                    /* Buffer for a source string. */
   grn_ts_expr_token **tokens;            /* Tokens. */
   size_t n_tokens;                       /* Number of tokens. */
@@ -326,11 +327,10 @@ grn_ts_expr_token_close(grn_ctx *ctx, grn_ts_expr_token *token)
 
 /* grn_ts_expr_parser_init() initializes a parser. */
 static void
-grn_ts_expr_parser_init(grn_ctx *ctx, grn_ts_expr *expr,
-                        grn_ts_expr_parser *parser)
+grn_ts_expr_parser_init(grn_ctx *ctx, grn_ts_expr_parser *parser)
 {
   memset(parser, 0, sizeof(*parser));
-  parser->expr = expr;
+  parser->builder = NULL;
   grn_ts_buf_init(ctx, &parser->str_buf);
   parser->tokens = NULL;
   parser->dummy_tokens = NULL;
@@ -359,28 +359,52 @@ grn_ts_expr_parser_fin(grn_ctx *ctx, grn_ts_expr_parser *parser)
     GRN_FREE(parser->tokens);
   }
   grn_ts_buf_fin(ctx, &parser->str_buf);
+  if (parser->builder) {
+    grn_ts_expr_builder_close(ctx, parser->builder);
+  }
 }
 
 grn_rc
-grn_ts_expr_parser_open(grn_ctx *ctx, grn_ts_expr *expr,
+grn_ts_expr_parser_open(grn_ctx *ctx, grn_obj *table,
                         grn_ts_expr_parser **parser)
 {
-  grn_ts_expr_parser *new_parser = GRN_MALLOCN(grn_ts_expr_parser, 1);
+  grn_rc rc;
+  grn_ts_expr_parser *new_parser;
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!table || !grn_ts_obj_is_table(ctx, table) || !parser) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  new_parser = GRN_MALLOCN(grn_ts_expr_parser, 1);
   if (!new_parser) {
     GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                       "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                       sizeof(grn_ts_expr_parser));
   }
-  grn_ts_expr_parser_init(ctx, expr, new_parser);
+  grn_ts_expr_parser_init(ctx, new_parser);
+  rc = grn_ts_expr_builder_open(ctx, table, &new_parser->builder);
+  if (rc != GRN_SUCCESS) {
+    grn_ts_expr_parser_fin(ctx, new_parser);
+    GRN_FREE(new_parser);
+    return  rc;
+  }
   *parser = new_parser;
   return GRN_SUCCESS;
 }
 
-void
+grn_rc
 grn_ts_expr_parser_close(grn_ctx *ctx, grn_ts_expr_parser *parser)
 {
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!parser) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
   grn_ts_expr_parser_fin(ctx, parser);
   GRN_FREE(parser);
+  return GRN_SUCCESS;
 }
 
 /* grn_ts_expr_parser_tokenize_start() creates the start token. */
@@ -879,17 +903,24 @@ grn_ts_expr_parser_push_const(grn_ctx *ctx, grn_ts_expr_parser *parser,
 {
   switch (token->data_kind) {
     case GRN_TS_BOOL: {
-      return grn_ts_expr_push_bool(ctx, parser->expr, token->content.as_bool);
+      return grn_ts_expr_builder_push_const(ctx, parser->builder,
+                                            GRN_TS_BOOL, GRN_DB_VOID,
+                                            &token->content.as_bool);
     }
     case GRN_TS_INT: {
-      return grn_ts_expr_push_int(ctx, parser->expr, token->content.as_int);
+      return grn_ts_expr_builder_push_const(ctx, parser->builder,
+                                            GRN_TS_INT, GRN_DB_VOID,
+                                            &token->content.as_int);
     }
     case GRN_TS_FLOAT: {
-      return grn_ts_expr_push_float(ctx, parser->expr,
-                                    token->content.as_float);
+      return grn_ts_expr_builder_push_const(ctx, parser->builder,
+                                            GRN_TS_FLOAT, GRN_DB_VOID,
+                                            &token->content.as_float);
     }
     case GRN_TS_TEXT: {
-      return grn_ts_expr_push_text(ctx, parser->expr, token->content.as_text);
+      return grn_ts_expr_builder_push_const(ctx, parser->builder,
+                                            GRN_TS_TEXT, GRN_DB_VOID,
+                                            &token->content.as_text);
     }
     default: {
       GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
@@ -903,8 +934,7 @@ static grn_rc
 grn_ts_expr_parser_push_name(grn_ctx *ctx, grn_ts_expr_parser *parser,
                              grn_ts_expr_name_token *token)
 {
-  return grn_ts_expr_push_name(ctx, parser->expr,
-                               token->src.ptr, token->src.size);
+  return grn_ts_expr_builder_push_name(ctx, parser->builder, token->src);
 }
 
 /* grn_ts_expr_parser_push_op() pushes a token to an expression. */
@@ -912,7 +942,7 @@ static grn_rc
 grn_ts_expr_parser_push_op(grn_ctx *ctx, grn_ts_expr_parser *parser,
                            grn_ts_expr_op_token *token)
 {
-  return grn_ts_expr_push_op(ctx, parser->expr, token->op_type);
+  return grn_ts_expr_builder_push_op(ctx, parser->builder, token->op_type);
 }
 
 /*
@@ -939,7 +969,7 @@ grn_ts_expr_parser_apply_one(grn_ctx *ctx, grn_ts_expr_parser *parser,
   /* Check the number of arguments. */
   switch (stack[depth - 2]->type) {
     case GRN_TS_EXPR_BRIDGE_TOKEN: {
-      rc = grn_ts_expr_end_subexpr(ctx, parser->expr);
+      rc = grn_ts_expr_builder_end_subexpr(ctx, parser->builder);
       if (rc != GRN_SUCCESS) {
         return rc;
       }
@@ -1040,7 +1070,7 @@ static grn_rc
 grn_ts_expr_parser_analyze_bridge(grn_ctx *ctx, grn_ts_expr_parser *parser,
                                   grn_ts_expr_bridge_token *token)
 {
-  grn_rc rc = grn_ts_expr_begin_subexpr(ctx, parser->expr);
+  grn_rc rc = grn_ts_expr_builder_begin_subexpr(ctx, parser->builder);
   if (rc != GRN_SUCCESS) {
     return rc;
   }
@@ -1212,18 +1242,22 @@ grn_ts_expr_parser_analyze(grn_ctx *ctx, grn_ts_expr_parser *parser)
 
 grn_rc
 grn_ts_expr_parser_parse(grn_ctx *ctx, grn_ts_expr_parser *parser,
-                         const char *str_ptr, size_t str_size)
+                         grn_ts_str str, grn_ts_expr **expr)
 {
   grn_rc rc;
-  grn_ts_str str;
-  rc = grn_ts_buf_reserve(ctx, &parser->str_buf, str_size + 1);
+  if (!ctx) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!parser || (!str.ptr && str.size)) {
+    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
+  }
+  rc = grn_ts_buf_reserve(ctx, &parser->str_buf, str.size + 1);
   if (rc != GRN_SUCCESS) {
     return rc;
   }
-  grn_memcpy(parser->str_buf.ptr, str_ptr, str_size);
-  ((char *)parser->str_buf.ptr)[str_size] = '\0';
+  grn_memcpy(parser->str_buf.ptr, str.ptr, str.size);
+  ((char *)parser->str_buf.ptr)[str.size] = '\0';
   str.ptr = (const char *)parser->str_buf.ptr;
-  str.size = str_size;
   rc = grn_ts_expr_parser_tokenize(ctx, parser, str);
   if (rc != GRN_SUCCESS) {
     return rc;
@@ -1232,5 +1266,5 @@ grn_ts_expr_parser_parse(grn_ctx *ctx, grn_ts_expr_parser *parser,
   if (rc != GRN_SUCCESS) {
     return rc;
   }
-  return GRN_SUCCESS;
+  return grn_ts_expr_builder_complete(ctx, parser->builder, expr);
 }

  Modified: lib/ts/ts_expr_parser.h (+6 -8)
===================================================================
--- lib/ts/ts_expr_parser.h    2015-11-16 14:59:23 +0900 (64f0d71)
+++ lib/ts/ts_expr_parser.h    2015-11-16 15:17:14 +0900 (1164561)
@@ -20,7 +20,8 @@
 #define GRN_TS_EXPR_PARSER_H
 
 #include "ts_expr.h"
-#include "ts_op.h"
+#include "ts_expr_builder.h"
+#include "ts_str.h"
 #include "ts_types.h"
 
 #ifdef __cplusplus
@@ -30,18 +31,15 @@ extern "C" {
 typedef struct grn_ts_expr_parser grn_ts_expr_parser;
 
 /* grn_ts_expr_parser_open() creates a parser. */
-grn_rc grn_ts_expr_parser_open(grn_ctx *ctx, grn_ts_expr *expr,
+grn_rc grn_ts_expr_parser_open(grn_ctx *ctx, grn_obj *table,
                                grn_ts_expr_parser **parser);
 
 /* grn_ts_expr_parser_close() destroys a parser. */
-void grn_ts_expr_parser_close(grn_ctx *ctx, grn_ts_expr_parser *parser);
+grn_rc grn_ts_expr_parser_close(grn_ctx *ctx, grn_ts_expr_parser *parser);
 
-/*
- * grn_ts_expr_parser_parse() parses a string and pushes nodes into an
- * expression.
- */
+/* grn_ts_expr_parser_parse() parses a string and creates an expression. */
 grn_rc grn_ts_expr_parser_parse(grn_ctx *ctx, grn_ts_expr_parser *parser,
-                                const char *str_ptr, size_t str_size);
+                                grn_ts_str str, grn_ts_expr **expr);
 
 #ifdef __cplusplus
 }

  Modified: lib/ts/ts_str.h (+2 -0)
===================================================================
--- lib/ts/ts_str.h    2015-11-16 14:59:23 +0900 (4a26aca)
+++ lib/ts/ts_str.h    2015-11-16 15:17:14 +0900 (5def7d6)
@@ -19,6 +19,8 @@
 #ifndef GRN_TS_STR_H
 #define GRN_TS_STR_H
 
+#include "../grn.h"
+
 #include "ts_types.h"
 
 #ifdef __cplusplus

  Modified: lib/ts/ts_util.c (+0 -1)
===================================================================
--- lib/ts/ts_util.c    2015-11-16 14:59:23 +0900 (3d6b47c)
+++ lib/ts/ts_util.c    2015-11-16 15:17:14 +0900 (0fed481)
@@ -23,7 +23,6 @@
 #include "../grn_pat.h"
 
 #include "ts_log.h"
-#include "ts_types.h"
 
 grn_rc
 grn_ts_obj_increment_ref_count(grn_ctx *ctx, grn_obj *obj)




More information about the Groonga-commit mailing list
Back to archive index