/** * @file toy_interpreter.c * @brief 簡易インタプリタ サンプル. * @author tenk* * @note * if,else,while文, print文, {複文} * // コメントあり * a-zの26個の1文字変数アリ. * 演算順位 * 1 数値 単項- 単項+ 単項! (式) * 2 * / % * 3 + - * 4 > >= < <= * 5 == != * - Public Domain Software */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <errno.h> // =========================================================================== // どうも pcc(100306) では BSSに配置される変数の扱いがバグってる?ようなので、 // 強制的に初期値を設定しておく. static const char *pCh_ = 0; ///< 解析中の文字列へのポインタ. static unsigned tok_ = 0; ///< トークンの種類. static int tokVal_ = 0; ///< トークンが数値だった時の値. static int tokStrFlg_ = 0; ///< 文字列か否か. static char tokName_[260] = {0}; ///< 名前だったときの名前文字列. static char tokStr_[16*1024] = {0}; ///< "文字列"だったときの文字列. static int variNo_ = -1; ///< 変数だったときの番号. static int vari_[26] = {0}; ///< 変数テーブル. // --------------------------------------------------------------------- #if !(defined __cplusplus) && !(defined inline) && defined _WIN32 #define inline __inline #endif #ifdef COINS // tolowerを使った箇所で')'がらみのエラーがでる..ので回避. #define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)+'a'-'A' : (c)) #endif /// 2つの半角キャラを、上位バイト下位バイトに持つ値を返す. #define CC(a,b) (((a)<<8) | (b)) static int expr(void); /// 簡易インタプリタ言語の初期化. void Interpreter_init() { int n; pCh_ = 0; tok_ = 0; tokVal_ = 0; tokStrFlg_ = 0; variNo_ = -1; tokName_[0] = 0; tokStr_[0] = 0; for (n = 0; n < 26; ++n) vari_[n] = 0; } /// エラーメッセージを表示して、exit. static void exitputs(const char *msg) { printf("%s", msg); exit(1); } /// 1文字取得. static inline int getCh(void) { int ch = *pCh_; if (ch) ++pCh_; // EOF対策で ch == 0 のときはポインタを進めない. return ch; } /** 1語取得. * tok_は '+' '-' '/' '*' '(' ')' '!' '!=' '=' '==' '>' '>=' '<' '<=' * '0' 'V' 'i' 'e' 'w' 'p' '"' のいずれか. */ static void getTok(void) { int i = 0; int ch; do { // 空白はスキップして1文字取得. ch = getCh(); if (ch == '/' && *pCh_ == '/') { // "//"があればコメント扱い. do { ch = getCh(); } while (ch != '\n' && ch != '\0'); } } while (0 < ch && ch <= 0x20); if (isdigit(ch)) { // 数値. 10進数, 0xの16進数, 0で始まる8進数. tokVal_ = strtoul(pCh_-1, (char**)&pCh_, 0); tok_ = '0'; // 数値を表す値として'0'を用いる. } else if (isalpha(ch)) { // アルファベットのとき. do { if (i < (int)sizeof(tokName_)-1) tokName_[i++] = tolower( ch ); ch = getCh(); } while (isalnum(ch)); --pCh_; tokName_[i] = 0; variNo_ = -1; if (i == 1) {variNo_=*tokName_-'a'; tok_ = 'V'; } // 英1字は変数. else if (strcmp(tokName_, "if" ) == 0) tok_ = 'i'; // if else if (strcmp(tokName_, "else" ) == 0) tok_ = 'e'; // else else if (strcmp(tokName_, "while") == 0) tok_ = 'w'; // while else if (strcmp(tokName_, "print") == 0) tok_ = 'p'; // print else printf("unkown name `%s'\n", tokName_); } else if (ch == '"') { // "文字列" while ((ch = getCh()) != '"') { if (ch == 0) exitputs("bad EOF\n"); if (i < (int)(sizeof tokStr_)-1) tokStr_[i++] = ch; } tokStr_[i] = 0; tok_ = '"'; } else { // 記号 tok_ = ch; switch(ch) { case '/': case '*': case '%': case '+': case '-': case '(': case ')': case '{': case '}': case ';': case ',': case '\0': break; case '=': if (*pCh_ == '=') { ++pCh_; tok_ = CC('=','='); } break; case '!': if (*pCh_ == '=') { ++pCh_; tok_ = CC('!','='); } break; case '>': if (*pCh_ == '=') { ++pCh_; tok_ = CC('>','='); } break; case '<': if (*pCh_ == '=') { ++pCh_; tok_ = CC('<','='); } break; default: printf("想定していない文字'%c'があらわれた\n", ch); } } } /// 数値, -式, +式, (式) 単項の処理. static int exprUnary(void) { int l = 0; tokStrFlg_ = 0; getTok(); if (tok_ == '0') { // 数値の処理. l = tokVal_; getTok(); } else if (tok_ == 'V') { // 変数の処理. l = vari_[variNo_]; getTok(); } else if (tok_ == '"') { // 文字列の処理. tokStrFlg_ = 1; getTok(); } else if (tok_ == '-') { // -負号. l = -exprUnary(); } else if (tok_ == '+') { // + l = exprUnary(); } else if (tok_ == '!') { // 否定. l = !exprUnary(); } else if (tok_ == '(') { // ( 式 ) の処理. l = expr(); if (tok_ == ')') getTok(); else exitputs("There is no ')'.\n"); } else { exitputs("The expression is illegal.\n"); } return l; } /// 掛け算、割り算、余り算の処理. static int exprMulDivMod(void) { int l = exprUnary(); for (; ;) { if (tok_ == '*') { l = l * exprUnary(); } else if (tok_ == '/') { int n = exprUnary(); if (n == 0) exitputs("It divided by 0.\n"); else l = l / n; } else if (tok_ == '%') { int n = exprUnary(); if (n == 0) exitputs("It divided by 0.\n"); else l = l % n; } else { break; } } return l; } /// 足し算, 引き算の処理. static int exprAddSub(void) { int l = exprMulDivMod(); for (; ;) { if (tok_ == '+') { l = l + exprMulDivMod(); } else if (tok_ == '-') { l = l - exprMulDivMod(); } else { break; } } return l; } /// < <= > >= の大小比較演算の処理. static int exprCmpLtGt(void) { int l = exprAddSub(); for (; ;) { if (tok_ == '<') { l = l < exprAddSub(); } else if (tok_ == CC('<','=')) { l = l <= exprAddSub(); } else if (tok_ == '>') { l = l > exprAddSub(); } else if (tok_ == CC('>','=')) { l = l >= exprAddSub(); } else { break; } } return l; } /// == != の等値比較演算の処理. static int expr(void) { int l = exprCmpLtGt(); for (; ;) { if (tok_ == CC('=','=')) { l = l == exprCmpLtGt(); } else if (tok_ == CC('!','=')) { l = l != exprCmpLtGt(); } else { break; } } return l; } /// if,else,while,print,{複文} 等の処理. eflg=1なら実行,0ならスキップ. void statement(int eflg) { int n; if (tok_ == ';') { // 空行. getTok(); } else if (tok_ == 'i') { // if の処理. n = (expr() != 0); statement(eflg & n); if (tok_ == 'e') // else の処理. statement(eflg & !n); } else if (tok_ == 'w') { // while の処理. const char *wp = pCh_; do { pCh_ = wp; n = (expr() != 0); statement(eflg & n); } while (eflg & n); } else if (tok_ == '{') { // { 複文 } の処理. getTok(); do { statement(eflg); } while (tok_ != '}'); getTok(); } else if (tok_ == 'V') { // 変数への代入処理. int e; n = variNo_; getTok(); if (tok_ != '=') exitputs("Syntax Error.\n"); e = expr(); if (eflg) vari_[(int)n] = e; if (tok_ == ';') getTok(); } else if (tok_ == 'p') { // print文. do { n = expr(); if (eflg) { if (tokStrFlg_) printf("%s", tokStr_); else printf("%d", n); } } while (tok_ == ','); if (eflg) printf("\n"); if (tok_ == ';') getTok(); } else if (tok_ == 0) { // EOF のとき. ; } else { exitputs("Syntax Error\n"); } } /// 引数の文字列を計算した結果を返す. int Interpreter_run(const char *p) { pCh_ = p; getTok(); while (tok_ != 0) { // eof が現れるまで実行. statement(1); } return 1; } // =========================================================================== // 時間計測関係 #ifndef PERFCNT_TICK_T #ifdef _WIN32 #include <windows.h> typedef unsigned __int64 PERFCNT_TICK_T; inline PERFCNT_TICK_T PERFCNT_getTick() { PERFCNT_TICK_T c; QueryPerformanceCounter((LARGE_INTEGER*)&c); return c; } inline PERFCNT_TICK_T PERFCNT_tickPerSec() { static PERFCNT_TICK_T s = 0; if (!s) QueryPerformanceFrequency((LARGE_INTEGER*)&s); return s; } #elif defined LINUX #include <sys/resource.h> typedef unsigned long long PERFCNT_TICK_T; inline PERFCNT_TICK_T PERFCNT_getTick() { struct rusage t; getrusage(RUSAGE_SELF, &t); return t.ru_utime.tv_sec * 1000000ULL + t.ru_utime.tv_usec; } #define PERFCNT_tickPerSec() 1000000ULL #else #include <time.h> typedef clock_t PERFCNT_TICK_T; #define PERFCNT_getTick() clock() #define PERFCNT_tickPerSec() CLOCKS_PER_SEC #endif #endif // =========================================================================== /// テキストファイル読みこみ. char *textfile_load(const char *name, char *buf, unsigned bufSz) { int l; FILE *fp = name ? fopen(name,"rb") : stdin; // ファイルオープン. if (fp == 0) return 0; l = fread(buf, 1, bufSz-1, fp); // ファイル読み込み. if (ferror(fp) || l < 0) { printf("%s\n", strerror( errno)); return 0; } buf[l] = 0; if (name) fclose(fp); return buf; } /// プログラムのエントリー. int main(int argc, char *argv[]) { // pccでの不具合回避で static化&サイズを16KBに下げる. static char buf[0x4000]; PERFCNT_TICK_T tim; if (textfile_load((argc >= 2) ? argv[1] : 0, buf, sizeof buf) == 0) { if (argv[1]) printf("File '%s' not loaded.\n", argv[1]); return 1; } tim = PERFCNT_getTick(); Interpreter_init(); Interpreter_run(buf); tim = PERFCNT_getTick() - tim; printf("\n%8.3g sec.\n", (double)tim / PERFCNT_tickPerSec() ); return 0; }