git.haldean.org wallbot / 4f12d5a lang / meka.c
4f12d5a

Tree @4f12d5a (Download .tar.gz)

meka.c @4f12d5araw · history · blame

/*
 * meka.c: meka runtime
 * Copyright (C) 2018, Haldean Brown
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "meka.h"
#include "meka_internal.h"

#include <stdio.h>
#include <string.h>

#define _is_whitespace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r')

static meka_word p[4096];
static uint16_t pn = 0;

meka_register reg[] = {
        { .name = {'c', 'a', 'l',  0 }, .typ = meka_tag_bool },
        { .name = {'g', 'o',  0,   0 }, .typ = meka_tag_bool },
        { .name = {'j', 'o', 'g', 'q'}, .typ = meka_tag_qvec },
        { .name = {'j', 'o', 'g', 'r'}, .typ = meka_tag_rvec },
        { .name = {'m', 'l', 'd',  0 },  .typ = meka_tag_chan },
        { .name = {'m', 'l', 'e',  0 },  .typ = meka_tag_chan },
        { .name = {'m', 'l', 's',  0 },  .typ = meka_tag_chan },
        { .name = {'m', 'r', 'd',  0 },  .typ = meka_tag_chan },
        { .name = {'m', 'r', 'e',  0 },  .typ = meka_tag_chan },
        { .name = {'m', 'r', 's',  0 },  .typ = meka_tag_chan },
        { .name = {'p', 'e', 'n',  0 },  .typ = meka_tag_bool },
        { .name = {'p', 'i', 'v', 'l'}, .typ = meka_tag_rvec },
        { .name = {'p', 'i', 'v', 'r'}, .typ = meka_tag_rvec },
        { .name = {'r', 'a', 'd', 'l'}, .typ = meka_tag_int },
        { .name = {'r', 'a', 'd', 'r'}, .typ = meka_tag_int },
        { .name = {'s', 't', 'e', 'p'}, .typ = meka_tag_int },
};
const size_t regn = sizeof(reg) / sizeof(*reg);

void
meka_skip_to_token(meka_source *src)
{
        char c;
        while (src->next != src->end)
        {
                c = *src->next;
                if (_is_whitespace(c))
                        continue;
                if (c == '#')
                {
                        for (; src->next != src->end && *src->next != '\n';
                             src->next++);
                        continue;
                }
                return;
        }
}

int
meka_next_token(meka_token *res, meka_source *src)
{
        char c;

        meka_skip_to_token(src);
        if (src->next == src->end)
                return 0;

        res->marker = 0xFF & meka_tag_none;
        res->data = src->next;
        for (res->len = 0; src->next != src->end; res->len++)
        {
                c = *src->next++;
                if (_is_whitespace(c) || c == '#')
                        break;
        }
        return res->len > 0;
}

meka_parse_result
meka_parse_number(meka_token *t, meka_value *v)
{
        int32_t res;
        int8_t s;
        int8_t i;
        char c;

        res = 0;
        i = 0;
        s = 1;

        c = *t->data;
        if (c == '-')
        {
                s = -1;
                i++;
        }
        else if (c == '+')
        {
                i++;
        }

        /* Strip leading zeroes */
        while (t->data[i] == '0')
                i++;

        /* The largest 28-bit integer is 268435456, which is 9 digits long. Any
         * number larger than 9 digits will overflow our 28-bit type, so I
         * early-reject those and then don't have to check for overflow when
         * building the 32-bit intermediate result. */
        if (t->len - i > 9)
        {
                /* distinguish between not-a-number and number-but-overflow */
                for (; i < t->len; i++)
                        if ('0' > t->data[i] || t->data[i] > '9')
                                return PARSE_FAIL;
                return PARSE_INVALID_VALUE;
        }

        for (; i < t->len; i++)
        {
                c = t->data[i];
                if ('0' <= c && c <= '9')
                        res = res * 10 + (c - '0');
                else
                        return PARSE_FAIL;
        }

        if (res > 0xFFFFFFF)
                return PARSE_INVALID_VALUE;
        *v = meka_pack(int, s * res);
        return PARSE_OK;
}

meka_parse_result
meka_parse_register(meka_token *t, meka_value *v)
{
        size_t i;
        meka_register test = {0};

        if (t->len <= 0 || t->len > 5 || t->data[0] != '%')
                return PARSE_FAIL;

        for (i = 1; i < t->len; i++)
                test.name[i - 1] = t->data[i];

        for (i = 0; i < regn; i++)
        {
                if (test.namebits == reg[i].namebits)
                {
                        *v = meka_pack(reg, i);
                        return PARSE_OK;
                }
        }
        return PARSE_INVALID_VALUE;
}

int
meka_load(meka_source *src)
{
        meka_token tok = {0};
        meka_parse_result rc;

        pn = 0;
        while (meka_next_token(&tok, src))
        {
                rc = meka_parse_number(&tok, &p[pn++].v);
                if (rc == PARSE_OK)
                        continue;
                if (rc == PARSE_INVALID_VALUE)
                {
                        printf("number '");
                        fwrite(tok.data, 1, tok.len, stdout);
                        printf("' overflows a 28-bit signed value\n");
                        goto fail;
                }

                rc = meka_parse_register(&tok, &p[pn++].v);
                if (rc == PARSE_OK)
                        continue;
                if (rc == PARSE_INVALID_VALUE)
                {
                        printf("register ");
                        fwrite(tok.data, 1, tok.len, stdout);
                        printf(" doesn't exist\n");
                        goto fail;
                }

                p[pn++].t = tok;
        }

        return pn;

fail:
        pn = 0;
        memset(p, 0, sizeof(p));
        return pn;
}