0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Common Lisp風のLISPを作ってみる(8.パーサー)

Posted at

my-lisp2ではパッケージの概念を意図的に無視しています。パーサーの作製難易度が跳ね上がるためです。my-lisp2のパーサーでは、単にトークンがシンボルであるか数値であるかを判別しています。
ソースコード

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.cchapter08/reader.cを比較してみてください。

test.c

chapter07/test.cと同じです。

動かしてみよう

基本的には7回目の時と動作は同じになります。ただし巨大な数を入力すると変な整数値に丸められます(仮数部の精度の限界)。

> 123
123
> 123.456000
123.456
> 1000000000000000000000000000000000000000000000000000000000000000000000000000
999999999999999926539781176481198923508803215199467887262646419780362305536
> 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 
オーバーフローしました
> abc   
ABC
> :from-end
パッケージは非対応です
> 1/2
有理数は非対応です
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?