my-lisp2ではパッケージの概念を意図的に無視しています。パーサーの作製難易度が跳ね上がるためです。my-lisp2のパーサーでは、単にトークンがシンボルであるか数値であるかを判別しています。
ソースコード
parser.h
/*
* parser.h
*/
#ifndef PARSER_H_
#define PARSER_H_
void *parser_parse(BUFFER buf);
#endif
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
関数は文字列内にコロンがあるかを判別します。
static int has_colon(char *str) {
while (*str) {
if (*str == ':') return 1;
str++;
}
return 0;
}
integerp
関数は文字列が整数のフォーマットであるかを判別します。
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
関数は文字列が有理数のフォーマットであるかを判別します。
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
関数は文字列が浮動小数点数のフォーマットであるかを判別します。
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
関数は文字列バッファの内容を読んで、シンボル型オブジェクトか数値型オブジェクトを返します。文字列バッファの中にコロンがあったり有理数のフォーマットであった場合はエラーを返します。
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
有理数は非対応です