Common Lisp風のLISPを作ってみる（８．パーサー）

my-lisp2ではパッケージの概念を意図的に無視しています。パーサーの作製難易度が跳ね上がるためです。my-lisp2のパーサーでは、単にトークンがシンボルであるか数値であるかを判別しています。
[ソースコード](https://github.com/haruo-wakakusa/my-lisp2/tree/main/chapter08)
# parser.h
```parser.h
/*
 * parser.h
 */

#ifndef PARSER_H_
#define PARSER_H_

void *parser_parse(BUFFER buf);

#endif
```
# parser.c
まずは、インクルード文です。
```parser.c
/*
 * parser.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include "../chapter07/helper.h"
#include "../chapter03/buffer.h"
#include "../chapter05/state.h"
```
`has_colon`関数は文字列内にコロンがあるかを判別します。
```parser.c
static int has_colon(char *str) {
    while (*str) {
        if (*str == ':') return 1;
        str++;
    }
    return 0;
}
```
`integerp`関数は文字列が整数のフォーマットであるかを判別します。
```parser.c
static int integerp(char *str) {
    if (*str == '+' || *str == '-') str++;
    if (*str == '\0') return 0;
    while (*str) {
        if (!isdigit(*str)) return 0;
        str++;
    }
    return 1;
}
```
`ratiop`関数は文字列が有理数のフォーマットであるかを判別します。
```parser.c
static int ratiop(char *str) {
    if (!strchr(str, '/')) return 0;
    if (*str == '+' || *str == '-') str++;
    if (*str == '/') return 0;
    while (*str != '/') {
        if (!isdigit(*str)) return 0;
        str++;
    }
    str++;
    if (*str == '\0') return 0;
    while (*str) {
        if (!isdigit(*str)) return 0;
        str++;
    }
    return 1;
}
```
`floatp`関数は文字列が浮動小数点数のフォーマットであるかを判別します。
```parser.c
static int floatp(char *str) {
    int no_numerators;
    if (!strchr(str, '.')) return 0;
    if (*str == '+' || *str == '-') str++;
    no_numerators = (*str == '.');
    while (*str != '.') {
        if (!isdigit(*str)) return 0;
        str++;
    }
    str++;
    if (no_numerators && *str == '\0') return 0;
    while (*str) {
        if (!isdigit(*str)) return 0;
        str++;
    }
    return 1;
}
```
`parser_parse`関数は文字列バッファの内容を読んで、シンボル型オブジェクトか数値型オブジェクトを返します。文字列バッファの中にコロンがあったり有理数のフォーマットであった場合はエラーを返します。
```parser.c
void *parser_parse(BUFFER buf) {
    size_t len = buffer_get_size(buf) + 1;
    char *str = (char *)malloc(len);
    buffer_copy(buf, str);
    str[len - 1] = '\0';
    void *retval = 0;
    if (has_colon(str)) {
        fprintf(stderr, "パッケージは非対応です\n");
        state = STATE_ERROR;
        retval = 0;
    } else if (ratiop(str)) {
        fprintf(stderr, "有理数は非対応です\n");
        state = STATE_ERROR;
        retval = 0;
    } else if (integerp(str) || floatp(str)) {
        double d;
        if (sscanf(str, "%lf", &d) != 1 || !isfinite(d)) {
            fprintf(stderr, "オーバーフローしました\n");
            state = STATE_ERROR;
            retval = 0;
        } else {
            NUMBER *num = (NUMBER *)malloc(sizeof(NUMBER));
            num->h.type = TYPE_NUMBER;
            num->num = d;
            retval = num;
        }
    } else {
        retval = make_symbol(str);
    }
    free(str);
    return retval;
}
```
# reader.c
`reader.c`に記述されている`make_symbol_from_buffer`関数の呼び出しを全て`parser_parse`関数に置き換えます。ただし`parser_parse`関数はエラーを出す場合があるので、リソースを解放して早期リターンするコードも記述する必要があります。詳細については、リポジトリのソースコードで`chapter06/reader.c`と`chapter08/reader.c`を比較してみてください。
# test.c
`chapter07/test.c`と同じです。
# 動かしてみよう
基本的には7回目の時と動作は同じになります。ただし巨大な数を入力すると変な整数値に丸められます（仮数部の精度の限界）。
```
> 123
123
> 123.456000
123.456
> 1000000000000000000000000000000000000000000000000000000000000000000000000000
999999999999999926539781176481198923508803215199467887262646419780362305536
> 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 
オーバーフローしました
> abc   
ABC
> :from-end
パッケージは非対応です
> 1/2
有理数は非対応です
```