From 6f820084c32a135ef21eed570fda2eec14df556c Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 30 Nov 2012 19:26:04 +0200 Subject: [PATCH] Re: [PATCH 1/3] Adding an S-expression structured output printer. --- 68/9d6081eaa100e74167dc9197d344ed83d3e9b6 | 367 ++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 68/9d6081eaa100e74167dc9197d344ed83d3e9b6 diff --git a/68/9d6081eaa100e74167dc9197d344ed83d3e9b6 b/68/9d6081eaa100e74167dc9197d344ed83d3e9b6 new file mode 100644 index 000000000..8368772e7 --- /dev/null +++ b/68/9d6081eaa100e74167dc9197d344ed83d3e9b6 @@ -0,0 +1,367 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id CFD96431FAF + for ; Fri, 30 Nov 2012 09:26:16 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: -0.7 +X-Spam-Level: +X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5 + tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled +Received: from olra.theworths.org ([127.0.0.1]) + by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id tFelwKdreYMo for ; + Fri, 30 Nov 2012 09:26:12 -0800 (PST) +Received: from mail-la0-f53.google.com (mail-la0-f53.google.com + [209.85.215.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits)) + (No client certificate requested) + by olra.theworths.org (Postfix) with ESMTPS id 66E6E431FAE + for ; Fri, 30 Nov 2012 09:26:12 -0800 (PST) +Received: by mail-la0-f53.google.com with SMTP id w12so590012lag.26 + for ; Fri, 30 Nov 2012 09:26:09 -0800 (PST) +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=google.com; s=20120113; + h=from:to:subject:in-reply-to:references:user-agent:date:message-id + :mime-version:content-type:x-gm-message-state; + bh=HPMQqgOfEDcMzhu8+WSPqDPq7KmQnfSQ0fywEx4OWkk=; + b=fikZmAVJMXLfJYBB5hGZ/l4LOydrGh+z1a7zTgS1sQkF9+bx6+HYq1Q6Dk/YOL0NHm + Df7j3E0qVK8OhN+C6dEvzem9a/14lCpJbBL6ukGbyMo2wIhm7n2rXxf+++Tw1+YPprZR + kUKxqadNnmHEY/xg6KOQpTm43qAPIH6yboACAuWZdufKCNLQ3vARfuO6xeeuDVKL1mG3 + LfMCt6vKtXBdmiuXDzjOloDlLzsxEHiz4CI0k33CYF+MFknboREkZM5pNiXHWmN+DnU9 + WkPBAkqhtDDDLcR+1d4Yr0/Nlzc+DX7BUwKUAn2g4UqdWcszjDdWt5mundYbo7SQvaTT + 4FIg== +Received: by 10.112.30.70 with SMTP id q6mr1145589lbh.51.1354296369343; + Fri, 30 Nov 2012 09:26:09 -0800 (PST) +Received: from localhost (dsl-hkibrasgw4-fe51df00-27.dhcp.inet.fi. + [80.223.81.27]) + by mx.google.com with ESMTPS id q2sm2320546lbd.14.2012.11.30.09.26.05 + (version=SSLv3 cipher=OTHER); Fri, 30 Nov 2012 09:26:06 -0800 (PST) +From: Jani Nikula +To: Peter Feigl , notmuch@notmuchmail.org +Subject: Re: [PATCH 1/3] Adding an S-expression structured output printer. +In-Reply-To: <1354264143-30173-1-git-send-email-craven@gmx.net> +References: <1354264143-30173-1-git-send-email-craven@gmx.net> +User-Agent: Notmuch/0.14+124~g3b17402 (http://notmuchmail.org) Emacs/23.4.1 + (i686-pc-linux-gnu) +Date: Fri, 30 Nov 2012 19:26:04 +0200 +Message-ID: <87a9tzj93n.fsf@nikula.org> +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +X-Gm-Message-State: + ALoCoQk0AEL6uGu+djMyN5G1XsAjOMnlJHHDtQqzZA+tRUY2Wbn1GeSML5dts5nYF3VdnrMGaE1q +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.13 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Fri, 30 Nov 2012 17:26:16 -0000 + + +Hi Peter, looks good save for a few nitpicks (see comments +inline). Someone more experienced in lisp should still have a look. + +BR, +Jani. + + +On Fri, 30 Nov 2012, Peter Feigl wrote: +> This commit adds an sprinter for Lisp S-Expressions. Later commits will +> use this printer. +> +> The structure is the same as json, but: +> - arrays are written as lists: ("foo" "bar" "baaz" 1 2 3) +> - maps are written as a-lists: ((key "value") (other-key "other-value")) +> - true is written as t +> - false is written as nil +> - null is written as nil +> --- +> Makefile.local | 1 + +> sprinter-sexp.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +> 2 files changed, 236 insertions(+) +> create mode 100644 sprinter-sexp.c +> +> diff --git a/Makefile.local b/Makefile.local +> index 2b91946..0db1713 100644 +> --- a/Makefile.local +> +++ b/Makefile.local +> @@ -270,6 +270,7 @@ notmuch_client_srcs = \ +> notmuch-tag.c \ +> notmuch-time.c \ +> sprinter-json.c \ +> + sprinter-sexp.c \ +> sprinter-text.c \ +> query-string.c \ +> mime-node.c \ +> diff --git a/sprinter-sexp.c b/sprinter-sexp.c +> new file mode 100644 +> index 0000000..8401c52 +> --- /dev/null +> +++ b/sprinter-sexp.c +> @@ -0,0 +1,235 @@ + +Copyright comment is missing. (Seems to be missing from sprinter-json.c +too, but that's no excuse! ;) + +> +#include +> +#include +> +#include +> +#include "sprinter.h" +> + +> +struct sprinter_sexp { +> + struct sprinter vtable; +> + FILE *stream; +> + /* Top of the state stack, or NULL if the printer is not currently +> + * inside any aggregate types. */ +> + struct sexp_state *state; +> + +> + /* A flag to signify that a separator should be inserted in the +> + * output as soon as possible. +> + */ +> + notmuch_bool_t insert_separator; +> +}; +> + +> +struct sexp_state { +> + struct sexp_state *parent; +> + +> + /* True if nothing has been printed in this aggregate yet. +> + * Suppresses the space before a value. */ +> + notmuch_bool_t first; +> + +> + /* True if the state is a map state. +> + Used to add a space between key/value pairs. */ + +The multi-line comments should use the same style here and elsewhere. + +> + notmuch_bool_t in_map; +> + +> + /* The character that closes the current aggregate. */ +> + char close; +> +}; +> + +> +/* Helper function to set up the stream to print a value. If this +> + * value follows another value, prints a space. */ +> +static struct sprinter_sexp * +> +sexp_begin_value (struct sprinter *sp) +> +{ +> + struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; +> + +> + if (sps->state) { +> + if (! sps->state->first) { +> + if (sps->insert_separator) { +> + fputc ('\n', sps->stream); +> + sps->insert_separator = FALSE; +> + } else { +> + if( ! sps->state->in_map) + +Spacing should be "if (! " ... + +> + fputc (' ', sps->stream); +> + } +> + } else { +> + sps->state->first = FALSE; +> + } +> + } +> + return sps; +> +} +> + +> +/* Helper function to begin an aggregate type. Prints the open +> + * character and pushes a new state frame. */ +> +static void +> +sexp_begin_aggregate (struct sprinter *sp, char open, char close) +> +{ +> + struct sprinter_sexp *sps = sexp_begin_value (sp); +> + struct sexp_state *state = talloc (sps, struct sexp_state); +> + fputc (open, sps->stream); +> + state->parent = sps->state; +> + state->first = TRUE; +> + state->in_map = FALSE; +> + state->close = close; +> + sps->state = state; +> +} +> + +> +static void +> +sexp_begin_map (struct sprinter *sp) +> +{ +> + struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; +> + sexp_begin_aggregate (sp, '(', ')'); +> + sps->state->in_map = TRUE; +> +} +> + +> +static void +> +sexp_begin_list (struct sprinter *sp) +> +{ +> + sexp_begin_aggregate (sp, '(', ')'); +> +} +> + +> +static void +> +sexp_end (struct sprinter *sp) +> +{ +> + struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; +> + struct sexp_state *state = sps->state; +> + +> + if (sps->state->in_map) +> + fputc (')', sps->stream); +> + fputc (sps->state->close, sps->stream); +> + sps->state = state->parent; +> + talloc_free (state); +> + if (sps->state == NULL) +> + fputc ('\n', sps->stream); +> +} +> + +> +/* This implementation supports embedded NULs as allowed by the JSON +> + * specification and Unicode. Support for *parsing* embedded NULs +> + * varies, but is generally not a problem outside of C-based parsers +> + * (Python's json module and Emacs' json.el take embedded NULs in +> + * stride). */ + +References to JSON seem out of place here. + +> +static void +> +sexp_string_len_internal (struct sprinter *sp, const char *val, size_t len, notmuch_bool_t quote) +> +{ +> + static const char *const escapes[] = { +> + ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b", +> + ['\f'] = "\\f", ['\n'] = "\\n", ['\t'] = "\\t" +> + }; +> + struct sprinter_sexp *sps = sexp_begin_value (sp); +> + +> + if(quote) +> + fputc ('"', sps->stream); +> + for (; len; ++val, --len) { +> + unsigned char ch = *val; +> + if (ch < ARRAY_SIZE (escapes) && escapes[ch]) +> + fputs (escapes[ch], sps->stream); +> + else if (ch >= 32) +> + fputc (ch, sps->stream); +> + else +> + fprintf (sps->stream, "\\u%04x", ch); +> + } +> + if(quote) +> + fputc ('"', sps->stream); +> +} +> + +> +static void +> +sexp_string_len (struct sprinter *sp, const char *val, size_t len) +> +{ +> + sexp_string_len_internal (sp, val, len, TRUE); /* print quoted */ +> +} +> + +> +static void +> +sexp_symbol_len (struct sprinter *sp, const char *val, size_t len) +> +{ +> + sexp_string_len_internal (sp, val, len, FALSE); /* print unquoted */ +> +} +> + +> +static void +> +sexp_string (struct sprinter *sp, const char *val) +> +{ +> + if (val == NULL) +> + val = ""; +> + sexp_string_len (sp, val, strlen (val)); +> +} +> + +> +static void +> +sexp_symbol (struct sprinter *sp, const char *val) +> +{ +> + if (val == NULL) +> + val = ""; +> + sexp_symbol_len (sp, val, strlen (val)); +> +} +> + +> +static void +> +sexp_integer (struct sprinter *sp, int val) +> +{ +> + struct sprinter_sexp *sps = sexp_begin_value (sp); +> + +> + fprintf (sps->stream, "%d", val); +> +} +> + +> +static void +> +sexp_boolean (struct sprinter *sp, notmuch_bool_t val) +> +{ +> + struct sprinter_sexp *sps = sexp_begin_value (sp); +> + +> + fputs (val ? "t" : "nil", sps->stream); +> +} +> + +> +static void +> +sexp_null (struct sprinter *sp) +> +{ +> + struct sprinter_sexp *sps = sexp_begin_value (sp); +> + +> + fputs ("nil", sps->stream); +> +} +> + +> +static void +> +sexp_map_key (struct sprinter *sp, const char *key) +> +{ +> + struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; +> + +> + if( sps->state->in_map && ! sps->state->first) + +Spacing should be "if (sps" ... + +> + fputs (") ", sps->stream); +> + fputc ('(', sps->stream); +> + sexp_symbol (sp, key); +> + fputc (' ', sps->stream); +> +} +> + +> +static void +> +sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name)) +> +{ +> +} +> + +> +static void +> +sexp_separator (struct sprinter *sp) +> +{ +> + struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; +> + +> + sps->insert_separator = TRUE; +> +} +> + +> +struct sprinter * +> +sprinter_sexp_create (const void *ctx, FILE *stream) +> +{ +> + static const struct sprinter_sexp template = { +> + .vtable = { +> + .begin_map = sexp_begin_map, +> + .begin_list = sexp_begin_list, +> + .end = sexp_end, +> + .string = sexp_string, +> + .string_len = sexp_string_len, +> + .integer = sexp_integer, +> + .boolean = sexp_boolean, +> + .null = sexp_null, +> + .map_key = sexp_map_key, +> + .separator = sexp_separator, +> + .set_prefix = sexp_set_prefix, +> + .is_text_printer = FALSE, +> + } +> + }; +> + struct sprinter_sexp *res; +> + +> + res = talloc (ctx, struct sprinter_sexp); +> + if (! res) +> + return NULL; +> + +> + *res = template; +> + res->stream = stream; +> + return &res->vtable; +> +} +> -- +> 1.8.0 +> +> _______________________________________________ +> notmuch mailing list +> notmuch@notmuchmail.org +> http://notmuchmail.org/mailman/listinfo/notmuch -- 2.26.2