/*
 * syntax.h: Data types to represent language syntax
 *
 * Copyright (C) 2007-2016 David Lutterkort
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Author: David Lutterkort <dlutter@redhat.com>
 */

#ifndef SYNTAX_H_
#define SYNTAX_H_

#include <limits.h>
#include "internal.h"
#include "lens.h"
#include "ref.h"
#include "fa.h"
#include "regexp.h"
#include "info.h"

void syntax_error(struct info *info, const char *format, ...)
    ATTRIBUTE_FORMAT(printf, 2, 3);

void fatal_error(struct info *info, const char *format, ...)
    ATTRIBUTE_FORMAT(printf, 2, 3);

enum term_tag {
    A_MODULE,
    A_BIND,              /* Module scope binding of a name */
    A_LET,               /* local LET .. IN binding */
    A_COMPOSE,
    A_UNION,
    A_MINUS,
    A_CONCAT,
    A_APP,
    A_VALUE,
    A_IDENT,
    A_BRACKET,
    A_FUNC,
    A_REP,
    A_TEST
};

enum test_result_tag {
    TR_CHECK,
    TR_PRINT,
    TR_EXN
};

enum quant_tag {
    Q_STAR,
    Q_PLUS,
    Q_MAYBE
};

struct term {
    struct term *next;
    unsigned int ref;
    struct info *info;
    struct type *type;                /* Filled in by the typechecker */
    enum term_tag tag;
    union {
        struct {                       /* A_MODULE */
            char          *mname;
            char          *autoload;
            struct term   *decls;
        };
        struct {                       /* A_BIND */
            char          *bname;
            struct term   *exp;
        };
        struct {              /* A_COMPOSE, A_UNION, A_CONCAT, A_APP, A_LET */
            struct term *left;
            struct term *right;
        };
        struct value    *value;         /* A_VALUE */
        struct term     *brexp;         /* A_BRACKET */
        struct string   *ident;         /* A_IDENT */
        struct {                        /* A_REP */
            enum quant_tag quant;
            struct term  *rexp;
        };
        struct {                       /* A_FUNC */
            struct param *param;
            struct term   *body;
        };
        struct {                       /* A_TEST */
            enum test_result_tag tr_tag;
            struct term         *test;
            struct term         *result;
        };
    };
};

struct param {
    struct info   *info;
    unsigned int   ref;
    struct string *name;
    struct type   *type;
};

/* The protoype for the implementation of a native/builtin function in the
 * interpreter.
 *
 * The arguments are passed as a NULL-terminated array of values.
 */
typedef struct value *(*func_impl)(struct info *, struct value *argv[]);

struct native {
    unsigned int argc;
    struct type *type;
    func_impl    impl;
};

/* An exception in the interpreter. Some exceptions are reported directly
 * into the central struct error; an exception for those is only generated
 * to follow the control flow for exceptions. Such exceptions have both
 * seen and error set to 1. They are the only exceptions with error == 1.
 * When error == 1, none of the other fields in the exn will be usable.
 */
struct exn {
    struct info *info;
    unsigned int seen : 1;      /* Whether the user has seen this EXN */
    unsigned int error : 1;
    char        *message;
    size_t       nlines;
    char       **lines;
};

/*
 * Values in the interpreter
 */
enum value_tag {
    V_STRING,
    V_REGEXP,
    V_LENS,
    V_TREE,
    V_FILTER,
    V_TRANSFORM,
    V_NATIVE,
    V_EXN,
    V_CLOS,
    V_UNIT
};

#define EXN(v) ((v)->tag == V_EXN)

struct value {
    unsigned int   ref;
    struct info   *info;
    enum value_tag tag;
    /* Nothing in this union for V_UNIT */
    union {
        struct string  *string;  /* V_STRING */
        struct regexp  *regexp;  /* V_REGEXP */
        struct lens    *lens;    /* V_LENS */
        struct native  *native;  /* V_NATIVE */
        struct tree    *origin;  /* V_TREE */
        struct filter  *filter;  /* V_FILTER */
        struct transform *transform; /* V_TRANSFORM */
        struct exn     *exn;     /* V_EXN */
        struct {                 /* V_CLOS */
            struct term     *func;
            struct binding  *bindings;
        };
    };
};

/* All types except for T_ARROW (functions) are simple. Subtype relations
 * for the simple types:
 *   T_STRING <: T_REGEXP
 * and the usual subtype relation for functions.
 */
enum type_tag {
    T_STRING,
    T_REGEXP,
    T_LENS,
    T_TREE,
    T_FILTER,
    T_TRANSFORM,
    T_ARROW,
    T_UNIT
};

struct type {
    unsigned int   ref;
    enum type_tag  tag;
    struct type   *dom;    /* T_ARROW */
    struct type   *img;    /* T_ARROW */
};

struct binding {
    unsigned int    ref;
    struct binding *next;
    struct string  *ident;
    struct type    *type;
    struct value   *value;
};

/* A module maps names to TYPE * VALUE. */
struct module {
    unsigned int       ref;
    struct module     *next;     /* Only used for the global list of modules */
    struct transform  *autoload;
    char              *name;
    struct binding    *bindings;
};

struct type *make_arrow_type(struct type *dom, struct type *img);
struct type *make_base_type(enum type_tag tag);
/* Do not call this directly. Use unref(t, type) instead */
void free_type(struct type *type);

/* Constructors for some terms in syntax.c Constructor assumes ownership of
 * arguments without incrementing. Caller owns returned objects.
 */
struct term *make_term(enum term_tag tag, struct info *info);
void free_term(struct term *term);
struct term *make_param(char *name, struct type *type, struct info *info);
struct value *make_value(enum value_tag tag, struct info *info);
struct value *make_unit(struct info *info);
struct term *make_app_term(struct term *func, struct term *arg,
                           struct info *info);
struct term *make_app_ident(char *id, struct term *func, struct info *info);

/* Print a tree in the braces style used in modules */
void print_tree_braces(FILE *out, int indent, struct tree *tree);

/* Make an EXN value
 * Receive ownership of INFO
 *
 * FORMAT and following arguments are printed to a new string. Caller must
 * clean those up.
 */
struct value *make_exn_value(struct info *info, const char *format, ...)
    ATTRIBUTE_FORMAT(printf, 2, 3);

/* Add NLINES lines (passed as const char *) to EXN, which must be a
 * value with tag V_EXN, created by MAKE_EXN_VALUE.
 *
 * The strings belong to EXN * after the call.
 */
void exn_add_lines(struct value *exn, int nlines, ...);

void exn_printf_line(struct value *exn, const char *format, ...)
    ATTRIBUTE_FORMAT(printf, 2, 3);

/* Do not call these directly, use UNREF instead */
void free_value(struct value *v);
void free_module(struct module *module);

/* Turn a list of PARAMS (represented as terms tagged as A_FUNC with the
 * param in PARAM) into nested A_FUNC terms
 */
struct term *build_func(struct term *params, struct term *exp);

struct module *module_create(const char *name);

#define define_native(error, module, name, argc, impl, types ...)       \
    define_native_intl(__FILE__, __LINE__, error, module, name,         \
                       argc, impl, ## types)

ATTRIBUTE_RETURN_CHECK
int define_native_intl(const char *fname, int line,
                       struct error *error,
                       struct module *module, const char *name,
                       int argc, func_impl impl, ...);

struct module *builtin_init(struct error *);

int load_module_file(struct augeas *aug, const char *filename, const char *name);

/* The name of the builtin function that checks recursive lenses */
#define LNS_CHECK_REC_NAME "lns_check_rec"

int interpreter_init(struct augeas *aug);

struct lens *lens_lookup(struct augeas *aug, const char *qname);
#endif


/*
 * Local variables:
 *  indent-tabs-mode: nil
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 * End:
 */
