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 0A6AA431FBC for ; Sat, 1 Dec 2012 08:14:54 -0800 (PST) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -1.098 X-Spam-Level: X-Spam-Status: No, score=-1.098 tagged_above=-999 required=5 tests=[DKIM_ADSP_CUSTOM_MED=0.001, FREEMAIL_FROM=0.001, NML_ADSP_CUSTOM_MED=1.2, RCVD_IN_DNSWL_MED=-2.3] 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 r9MEK1+-76UV for ; Sat, 1 Dec 2012 08:14:50 -0800 (PST) Received: from mail2.qmul.ac.uk (mail2.qmul.ac.uk [138.37.6.6]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by olra.theworths.org (Postfix) with ESMTPS id C13BA431FAF for ; Sat, 1 Dec 2012 08:14:49 -0800 (PST) Received: from smtp.qmul.ac.uk ([138.37.6.40]) by mail2.qmul.ac.uk with esmtp (Exim 4.71) (envelope-from ) id 1TepiI-0003yf-19; Sat, 01 Dec 2012 16:14:48 +0000 Received: from 93-97-24-31.zone5.bethere.co.uk ([93.97.24.31] helo=localhost) by smtp.qmul.ac.uk with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69) (envelope-from ) id 1TepiH-00049c-EH; Sat, 01 Dec 2012 16:14:45 +0000 From: Mark Walters 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+81~g9730584 (http://notmuchmail.org) Emacs/23.4.1 (x86_64-pc-linux-gnu) Date: Sat, 01 Dec 2012 16:14:44 +0000 Message-ID: <87sj7p3g23.fsf@qmul.ac.uk> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Sender-Host-Address: 93.97.24.31 X-QM-SPAM-Info: Sender has good ham record. :) X-QM-Body-MD5: 4787153bdec301a1bb606d636b5488a5 (of first 20000 bytes) X-SpamAssassin-Score: -1.7 X-SpamAssassin-SpamBar: - X-SpamAssassin-Report: The QM spam filters have analysed this message to determine if it is spam. We require at least 5.0 points to mark a message as spam. This message scored -1.7 points. Summary of the scoring: * -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, * medium trust * [138.37.6.40 listed in list.dnswl.org] * 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider * (markwalters1009[at]gmail.com) * 0.6 AWL AWL: From: address is in the auto white-list X-QM-Scan-Virus: ClamAV says the message is clean 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: Sat, 01 Dec 2012 16:14:54 -0000 One small niggle with this patch: it seems to have lots of spaces rather than tabs (unless I am misreading it) Best wishes Mark 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 @@ > +#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. */ > + 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) > + 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). */ > +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) > + 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