• R/O
  • HTTP
  • SSH
  • HTTPS

标签
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Baremetal Lisp interpreter and compiler for low-resource devices


File Info

Rev. c57088232b60ef6f3c8c88a9d246d2987e01cd35
大小 21,787 字节
时间 2020-09-21 09:51:46
作者 AlaskanEmily
Log Message

Add some very basic typechecking for SL_I_Execute

Content

/* Copyright (c) 2020 AlaskanEmily
 *
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the authors be held liable for any damages arising from
 * the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *   claim that you wrote the original software. If you use this software in a
 *   product, an acknowledgment in the product documentation would be
 *   appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *   misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */

#include "sl_x.h"

/*****************************************************************************/

const struct SL_S_Atom sl_x_integers[SL_X_N_INTEGERS] = {
    SL_S_STATIC_ATOM("0"),
    SL_S_STATIC_ATOM("1"),
    SL_S_STATIC_ATOM("2"),
    SL_S_STATIC_ATOM("3"),
    SL_S_STATIC_ATOM("4"),
    SL_S_STATIC_ATOM("5"),
    SL_S_STATIC_ATOM("6"),
    SL_S_STATIC_ATOM("7"),
    SL_S_STATIC_ATOM("8"),
    SL_S_STATIC_ATOM("9"),
    SL_S_STATIC_ATOM("10"),
    SL_S_STATIC_ATOM("11"),
    SL_S_STATIC_ATOM("12"),
    SL_S_STATIC_ATOM("13"),
    SL_S_STATIC_ATOM("14"),
    SL_S_STATIC_ATOM("15"),
};

/*****************************************************************************/

#define SL_X_ATOM(X, TXT) const struct SL_S_Atom X = SL_S_STATIC_ATOM(TXT)
#define SL_X_HINT(X) const struct SL_S_Atom sl_x_ ## X ## _hint = \
    SL_S_STATIC_ATOM("^" #X)

SL_X_ATOM(sl_x_nil,     "nil");
SL_X_ATOM(sl_x_true,    "true");
SL_X_ATOM(sl_x_false,   "false");
SL_X_HINT(list);
SL_X_HINT(atom);

#define SL_X_HINT_END(X) SL_X_HINT(X);
SL_X_INTEGRAL_TYPES(SL_X_HINT_END)
#undef SL_X_HINT_END

#ifdef SL_S_ENABLE_POINTERS
# define SL_X_PTR_HINT_END(X) \
    const struct SL_S_Atom sl_x_ptr_ ## X ## _hint = \
    SL_S_STATIC_ATOM("^ptr-" #X);
SL_X_INTEGRAL_TYPES(SL_X_PTR_HINT_END)
# undef SL_X_PTR_HINT_END
#endif

SL_X_ATOM(sl_x_defun,   "defun");
SL_X_ATOM(sl_x_def,     "def");
SL_X_ATOM(sl_x_defrec,  "defrec");
SL_X_ATOM(sl_x_defproto,"defproto");
SL_X_ATOM(sl_x_if,      "if");
SL_X_ATOM(sl_x_let,     "let");
SL_X_ATOM(sl_x_plus,    "+");
SL_X_ATOM(sl_x_minus,   "-");
SL_X_ATOM(sl_x_times,   "*");
SL_X_ATOM(sl_x_divide,  "/");
SL_X_ATOM(sl_x_mod,     "%");
SL_X_ATOM(sl_x_comment, "comment");
SL_X_ATOM(sl_x_dot,     ".");
SL_X_ATOM(sl_x_tick,    "'");
SL_X_ATOM(sl_x_shift_left,"<<");
SL_X_ATOM(sl_x_shift_right,">>");
SL_X_ATOM(sl_x_bit_or,  "|");
SL_X_ATOM(sl_x_bit_and, "&");
SL_X_ATOM(sl_x_bit_xor, "^");
SL_X_ATOM(sl_x_bit_not, "~");
SL_X_ATOM(sl_x_is_nil,  "nil?");
SL_X_ATOM(sl_x_is_atom, "atom?");
SL_X_ATOM(sl_x_is_list, "list?");
SL_X_ATOM(sl_x_is_int,  "int?");
SL_X_ATOM(sl_x_concat,  "++");
SL_X_ATOM(sl_x_cons,    "cons");
SL_X_ATOM(sl_x_head,    "head");
SL_X_ATOM(sl_x_tail,    "tail");
SL_X_ATOM(sl_x_index,   "index");
SL_X_ATOM(sl_x_not,     "!");
SL_X_ATOM(sl_x_eq,      "=");
SL_X_ATOM(sl_x_ne,      "!=");
SL_X_ATOM(sl_x_gt,      ">");
SL_X_ATOM(sl_x_ge,      ">=");
SL_X_ATOM(sl_x_lt,      "<");
SL_X_ATOM(sl_x_le,      "<=");

/*****************************************************************************/

const struct SL_S_Atom *const sl_x_all_hints[] = {
    &sl_x_list_hint,
    &sl_x_atom_hint,

#define SL_X_HINT_REF(X) &(sl_x_ ## X ## _hint),
SL_X_INTEGRAL_TYPES(SL_X_HINT_REF)
#undef SL_X_HINT_REF

#ifdef SL_S_ENABLE_POINTERS
# define SL_X_PTR_HINT_REF(X) &(sl_x_ptr_ ## X ## _hint),
SL_X_INTEGRAL_TYPES(SL_X_PTR_HINT_REF)
# undef SL_X_PTR_HINT_REF
#endif
    SL_S_NIL
};

/*****************************************************************************/

static int sl_x_is_builtin(const struct SL_S_Atom *arg){
    return (SL_S_IS_NIL(arg) || arg->len > 5) ? 1 : !(
        SL_S_COMPARE_ATOMS(&sl_x_if, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_let, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_plus, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_minus, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_times, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_divide, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_mod, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_shift_left, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_shift_right, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_bit_or, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_bit_and, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_bit_xor, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_bit_not, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_is_nil, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_is_atom, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_is_list, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_is_int, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_concat, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_cons, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_head, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_tail, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_index, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_not, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_eq, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_ne, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_gt, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_ge, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_lt, arg) ||
        SL_S_COMPARE_ATOMS(&sl_x_le, arg));
}

/*****************************************************************************/

static int sl_x_parse_int(const struct SL_S_Atom *arg, int *out){
    register sl_s_len_t i;
    register char c;
    int r;
    unsigned short is_neg;
    
    if(SL_S_IS_NIL(arg) || arg->len < 1)
        return 1;
    
    r = 0;
    c = arg->text[0];
    if(c == '-'){
        if(arg->len < 2)
            return 1;
        c = arg->text[1];
        is_neg = 1;
        i = 1;
    }
    else if(c == '+'){
        if(arg->len < 2)
            return 1;
        c = arg->text[1];
        is_neg = 0;
        i = 1;
    }
    else{
        is_neg = 0;
        i = 0;
    }
    
    if(c == '0'){
        if(arg->len > i + 1 &&
            (arg->text[i + 1] == 'x' || arg->text[i + 1] == 'X')){
            for(i += 2; i < arg->len; i++){
                c = arg->text[i];
                r <<= 4;
                if(c >= 'a' && c <= 'f')
                    c = c - 'a' + 10;
                else if(c >= 'A' && c <= 'F')
                    c = c - 'A' + 10;
                else if(c >= '0' && c <= '9')
                    c = c - '0';
                else
                    return 1;
                r += c;
            }
        }
        else if(arg->len > 2 &&
            (arg->text[i + 1] == 'b' || arg->text[i + 1] == 'B')){
            
            for(i += 2; i < arg->len; i++){
                c = arg->text[i];
                r <<= 1;
                if(c == '1')
                    r |= 1;
                if(c != '0')
                    return 1;
            }
        }
        else{
            for(i += 1; i < arg->len; i++){
                /* Octal. */
                c = arg->text[i];
                r <<= 3;
                if(c >= '0' && c <= '7')
                    r += c - '0';
                else
                    return 1;
            }
        }
    }
    else{
        for(; i < arg->len; i++){
                c = arg->text[i];
                r *= 10;
                if(c >= '0' && c <= '9')
                    r += c - '0';
                else
                    return 1;
        }
    }
    if(is_neg)
        r = -r;
    *out = r;
    return 0;
}

/*****************************************************************************/

SL_S_PURE_FUNC(int) SL_X_IsRuntimeConstant(const void *value){
    int tag;
    void *data;
    struct SL_S_Atom *head;
    struct SL_S_List *list;
recurse:
    SL_S_PTR_TAG_DATA(value, data, tag);
    if(SL_S_IS_NIL(data))
        return 0;
    
    switch(tag){
        case SL_S_ATOM_TAG:
            if(SL_S_COMPARE_ATOMS(&sl_x_nil, (struct SL_S_Atom*)data))
                return 0;
            return sl_x_parse_int(data, &tag);
        case SL_S_LIST_TAG:
            list = data;
            if(SL_S_IS_NIL(list->head))
                goto check_tail;
            
            if(SL_S_IS_LIST(list->head)){
                if(SL_X_IsRuntimeConstant(list->head) != 0)
                    return 1;
                goto check_tail;
            }
            
            head = SL_S_PTR_FROM_TAG(list->head);
            if(SL_S_COMPARE_ATOMS(&sl_x_comment, head))
                return 0;
            
            if(SL_S_COMPARE_ATOMS(&sl_x_dot, head)){
                value = list->head;
                goto recurse;
            }
            
            if(SL_S_COMPARE_ATOMS(&sl_x_tick, head)
                || sl_x_is_builtin(head) == 0){
                
                goto check_tail;
            }
            
            return 1;
        default:
#if (defined __GNUC__)
            __builtin_unreachable();
#elif (defined _MSC_VER)
            __assume(0);
#endif
    }
    
check_tail:
    while((list = list->tail) != SL_S_NIL){
        if(list->tail == SL_S_NIL){
            value = list->head;
            goto recurse;
        }
        
        if(SL_X_IsRuntimeConstant(list->head) != 0)
            return 1;
    }
    return 0;
}

/*****************************************************************************/

SL_S_PURE_FUNC(int) SL_X_IsInt(const struct SL_S_Atom *arg){
    int unused;
    return sl_x_parse_int(arg, &unused);
}

/*****************************************************************************/

SL_S_FUNC(void) SL_X_IsIntCB(const void *value, unsigned char *out_flag){
    if(*out_flag == 0)
        return;
    *out_flag = SL_S_IS_ATOM(value) &&
        SL_X_IsInt(SL_S_PTR_FROM_TAG(value)) == 0;
}

/*****************************************************************************/

SL_S_PURE_FUNC(int) SL_X_ParseInt(const struct SL_S_Atom *arg){
    int r;
    r = 0;
    sl_x_parse_int(arg, &r);
    return r;
}

/*****************************************************************************/

SL_S_PURE_FUNC(int) SL_X_TryParseInt(const struct SL_S_Atom *arg, int *out){
    return sl_x_parse_int(arg, out);
}

/*****************************************************************************/

SL_S_PURE_FUNC(int) SL_X_IsDefun(const struct SL_S_List *code){
    if(!SL_S_IS_NIL(code) &&
        SL_S_IS_ATOM(code->head) &&
        !SL_S_IS_NIL(code->tail) &&
        SL_S_IS_ATOM(code->tail->head) &&
        !SL_S_IS_NIL(code->tail->tail) &&
        SL_S_IS_LIST(code->tail->tail->head) &&
        SL_S_COMPARE_ATOMS(&sl_x_defun,
            (struct SL_S_Atom*)SL_S_PTR_FROM_TAG(code->head))){
        
        return 0;
    }
    return -1;
}

/*****************************************************************************/
#define SL_X_DEF_ARITY1 3
static const unsigned char sl_x_def_flags1[SL_X_DEF_ARITY1] = {
    SL_S_IN_ATOM,
    SL_S_OUT_ATOM,
    SL_S_OUT_ANY
};

#define SL_X_DEF_ARITY2 4
static const unsigned char sl_x_def_flags2[SL_X_DEF_ARITY2] = {
    SL_S_IN_ATOM,
    SL_S_OUT_ATOM,
    SL_S_OUT_ATOM,
    SL_S_OUT_ANY
};

#define SL_X_DEF_MAX_ARITY 4

SL_S_PURE_FUNC(int) SL_X_IsDef(const struct SL_S_List *code){
    const void *args[SL_X_DEF_MAX_ARITY];
    args[0] = SL_S_MK_ATOM(&sl_x_def);
    
    /* Check for form 1. */
    if(SL_S_Match(code,
            args,
            sl_x_def_flags1,
            SL_X_DEF_ARITY1,
            SL_X_DEF_ARITY1) == SL_X_DEF_ARITY1 ||
        SL_S_Match(code,
            args,
            sl_x_def_flags2,
            SL_X_DEF_ARITY2,
            SL_X_DEF_ARITY2) == SL_X_DEF_ARITY2){
        
        return 0;
    }
    else{
        return 1;
    }
}

/*****************************************************************************/
#define SL_X_DEFREC_MIN_ARITY 2
static const unsigned char sl_x_defrec_flags[SL_X_DEFREC_MIN_ARITY] = {
    SL_S_IN_ATOM,
    SL_S_OUT_ATOM
};

SL_S_PURE_FUNC(int) SL_X_IsDefrec(const struct SL_S_List *code){
    const void *args[SL_X_DEFREC_MIN_ARITY];
    int arity;
    
    args[0] = SL_S_MK_ATOM(&sl_x_defrec);
    
    arity = SL_S_Match(code,
        args,
        sl_x_defrec_flags,
        SL_X_DEFREC_MIN_ARITY,
        SL_X_DEFREC_MIN_ARITY);
    
    if(arity == SL_X_DEFREC_MIN_ARITY){
        return 0;
    }
    else if(arity < 0 && (arity = -arity) > SL_X_DEFREC_MIN_ARITY){
        /* Test for the correct form in the fields. */
        code = code->tail->tail;
        do{
            if(!SL_S_IS_ATOM(code->head))
                return 1;
        }while((code = code->tail) != SL_S_NIL);
        return 0;
    }
    else{
        return 1;
    }
}

/*****************************************************************************/

SL_S_PURE_FUNC(int) SL_X_IsDefproto(const struct SL_S_List *code){
    if(!SL_S_IS_NIL(code) &&
        SL_S_IS_ATOM(code->head) &&
        !SL_S_IS_NIL(code->tail) &&
        SL_S_IS_ATOM(code->tail->head) &&
        SL_S_COMPARE_ATOMS(&sl_x_defproto,
            (struct SL_S_Atom*)SL_S_PTR_FROM_TAG(code->head))){
        
        return 0;
    }
    return -1;
}

/*****************************************************************************/

SL_S_FUNC(int) SL_X_ParseDefun(const struct SL_S_List *code,
    const struct SL_S_Atom **out_name,
    const struct SL_S_List **out_args,
    const struct SL_S_List **out_body){
    
    if(SL_X_IsDefun(code) != 0)
        return -1;
    
    *out_name = SL_S_PTR_FROM_TAG(code->tail->head);
    *out_args = SL_S_PTR_FROM_TAG(code->tail->tail->head);
    *out_body = SL_S_PTR_FROM_TAG(code->tail->tail->tail);
    return 0;
}

/*****************************************************************************/

SL_S_FUNC(int) SL_X_ParseDef(const struct SL_S_List *code,
    const struct SL_S_Atom **out_hint,
    const struct SL_S_Atom **out_name,
    const void **out_value){
    
    const void *args[SL_X_DEF_MAX_ARITY];
    args[0] = SL_S_MK_ATOM(&sl_x_def);
    
    if(SL_S_Match(code,
        args,
        sl_x_def_flags1,
        SL_X_DEF_ARITY1,
        SL_X_DEF_ARITY1) == SL_X_DEF_ARITY1){
        
        /* No hint */
        *out_hint = SL_S_NIL;
        *out_name = SL_S_PTR_FROM_TAG(code->tail->head);
        *out_value = code->tail->tail->head;
        return 0;
    }
    else if(SL_S_Match(code,
        args,
        sl_x_def_flags2,
        SL_X_DEF_ARITY2,
        SL_X_DEF_ARITY2) == SL_X_DEF_ARITY2){
        
        /* With hint */
        *out_hint = SL_S_PTR_FROM_TAG(code->tail->head);
        /* Check if the hint is valid */
        if((*out_hint)->len <= 1 || (*out_hint)->text[0] != '^'){
            return 1;
        }
        
        *out_name = SL_S_PTR_FROM_TAG(code->tail->tail->head);
        *out_value = code->tail->tail->tail->head;
        return 0;
    }
    else{
        return 1;
    }
}

/*****************************************************************************/

static struct SL_S_List *sl_x_add_rec_field(
    const struct SL_S_Atom *name,
    const struct SL_S_Atom *hint){
    
    struct SL_S_List *arg_pair, *to;
    
    arg_pair = SL_S_Malloc(sizeof(struct SL_S_List));
    arg_pair->ref = 1;
    arg_pair->tail = SL_S_Malloc(sizeof(struct SL_S_List));
    arg_pair->tail->ref = 1;
    arg_pair->tail->tail = SL_S_NIL;
    
    arg_pair->head = SL_S_MK_ATOM(hint);
    arg_pair->tail->head = SL_S_MK_ATOM(name);
    
    to = SL_S_Malloc(sizeof(struct SL_S_List));
    to->ref = 1;
    to->head = SL_S_MK_LIST(arg_pair);
    return to;
}

SL_S_FUNC(int) SL_X_ParseDefrec(const struct SL_S_List *code,
    const struct SL_S_Atom **out_name,
    const struct SL_S_List **out_fields){
    
    const struct SL_S_Atom *name, *hint;
    struct SL_S_List *ret, **dest;
    register const struct SL_S_Atom *atom;
    register const struct SL_S_Atom **target;
    
    *out_name = SL_S_PTR_FROM_TAG(code->tail->head);
    code = code->tail->tail;
    if(code == SL_S_NIL){
        *out_fields = SL_S_NIL;
        return 0;
    }
    
    dest = &ret;
    do{
        atom = SL_S_PTR_FROM_TAG(code->head);
        SL_S_INCREF(atom);
        if(atom->len > 1 && atom->text[0] == '^')
            target = &hint;
        else
            target = &name;
        if(*target != SL_S_NIL){
            *dest = sl_x_add_rec_field(hint, name);
            dest = &((*dest)->tail);
        }
        *target = atom;
    }while((code = code->tail) != SL_S_NIL);
    
    if(hint != SL_S_NIL || name != SL_S_NIL){
        *dest = sl_x_add_rec_field(hint, name);
        (*dest)->tail = SL_S_NIL;
    }
    else{
        *dest = SL_S_NIL;
    }
    *out_fields = ret;
    return 0;
}

/*****************************************************************************/
/* Returns 0 on success.
 * This DOES incref the methods, unlike most other parsers.
 * This does no incref the name.
 */
SL_S_FUNC(int) SL_X_ParseDefproto(const struct SL_S_List *code,
    const struct SL_S_Atom **out_name,
    const struct SL_S_List **out_methods){
    
    if(SL_X_IsDefproto(code) != 0)
        return -1;
    
    *out_name = SL_S_PTR_FROM_TAG(code->tail->head);
    *out_methods = SL_S_PTR_FROM_TAG(code->tail->tail);
    return 0;
    
}

/*****************************************************************************/

static struct SL_S_List *sl_x_create_args(const struct SL_S_Atom *hint,
    const struct SL_S_Atom *name){
    
    struct SL_S_List *list;
    
    list = SL_S_Malloc(sizeof(struct SL_S_List));
    list->ref = 1;
    list->head = (hint == SL_S_NIL) ? SL_S_NIL : SL_S_MK_ATOM(hint);
    list->tail = SL_S_Malloc(sizeof(struct SL_S_List));
    list->tail->ref = 1;
    list->tail->head = (name == SL_S_NIL) ? SL_S_NIL : SL_S_MK_ATOM(name);
    list->tail->tail = SL_S_NIL;
    SL_S_INCREF(hint);
    SL_S_INCREF(name);
    return list;
}

/*****************************************************************************/

SL_S_FUNC(struct SL_S_List) *SL_X_ParseArgs(const struct SL_S_List *args){
    struct SL_S_Atom *hint, *name;
    register struct SL_S_Atom *value, **target;
    struct SL_S_List *ret, **dest;
    if(SL_S_IS_NIL(args))
        return SL_S_NIL;
    
    hint = SL_S_NIL;
    name = SL_S_NIL;
    dest = &ret;
    do{
        if(!SL_S_IS_ATOM(args->head))
            goto fail;
        value = SL_S_PTR_FROM_TAG(args->head);
        if(value->len > 0 && value->text[0] == '^')
            target = &hint;
        else
            target = &name;
        
        if(*target != SL_S_NIL){
            *dest = SL_S_Malloc(sizeof(struct SL_S_List));
            (*dest)->head = sl_x_create_args(hint, name);
            dest = &((*dest)->tail);
            hint = SL_S_NIL;
            name = SL_S_NIL;
        }
        *target = value;
    }while((args = args->tail));
    
    if(hint != SL_S_NIL || name != SL_S_NIL){
        *dest = SL_S_Malloc(sizeof(struct SL_S_List));
        (*dest)->head = sl_x_create_args(hint, name);
        dest = &((*dest)->tail);
    }
    *dest = SL_S_NIL;
    return ret;
fail:
    *dest = SL_S_NIL;
    SL_S_DECREF(ret); /* This should recurse on down. */
    return SL_S_NIL;
}

/*****************************************************************************/

#define SL_I_BUFFER_INIT 256
#define SL_I_BUFFER_DOUBLE 4096

/*****************************************************************************/

SL_S_FUNC(struct SL_S_Atom) *SL_X_BufferFile(const struct SL_X_FileOps *ops,
    const struct SL_S_Atom *name){
    
    void *file;
    struct SL_S_Atom *to;
    char *old;
    sl_s_len_t cap;
    
    /* TODO: Also try the system include directories */
    file = ops->open_read(name->text);
    
    if(!file)
        return SL_S_NIL;
    
    cap = 0;
    
    to = SL_S_Malloc(sizeof(struct SL_S_Atom));
    to->ref = 1;
    to->text = (void*)0;
    to->len = 0;
    do{
        old = to->text;
        if(cap == 0){
            cap = SL_I_BUFFER_INIT;
        }
        else if(cap < SL_I_BUFFER_DOUBLE){
            cap <<= 1;
        }
        else{
            if(SL_S_MAX_LEN - SL_I_BUFFER_DOUBLE < cap){
                /* over-length. Just stop reading the file. Sorry. */
                if(SL_S_MAX_LEN == cap)
                    break;
                else
                    cap = SL_S_MAX_LEN;
            }
            else{
                cap += SL_I_BUFFER_DOUBLE;
            }
        }
        
        to->text = SL_S_Malloc(cap);
        if(!to->text){
            /* OOM */
            if(old)
                SL_S_Free(old);
            return SL_S_NIL;
        }
        if(old){
            SL_S_MemCopy(to->text, old, to->len);
            SL_S_Free(old);
        }
        to->len += ops->read(file, to->text + to->len, cap - to->len);
    }while(!ops->is_eof(file));
    
    ops->close(file);
    
    return to;
}

/*****************************************************************************/

SL_S_FUNC(void) SL_X_MeasureAtomsCB(const void *value, void *arg){
    struct SL_X_MeasureResult *result;
    sl_s_len_t len;
    result = arg;
    
    if(result->flag)
        return;
    
    if(!SL_S_IS_ATOM(value)){
        result->flag = 1;
    }
    else{
        len = ((struct SL_S_Atom*)SL_S_PTR_FROM_TAG(value))->len;
        /* Check for overflow. */
        if(SL_S_MAX_LEN - len < result->len)
            result->flag = 1;
        else
            result->len += len;
    }
}

/*****************************************************************************/

SL_S_FUNC(void) SL_X_ConcatAtomsCB(const void *value, void *arg){
    struct SL_S_Atom *atom, *to;
    register sl_s_len_t len;
    atom = SL_S_PTR_FROM_TAG(value);
    to = arg;
    len = atom->len;
    
    SL_S_MemCopy(to->text + to->len, atom->text, len);
    to->len += len;
}

/*****************************************************************************/