File:  [Isaki's NoNo m68k/m88k emulator] / nono / util / iasm / scan.cpp
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:58 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// XM6i
// Copyright (C) 2013 Tetsuya Isaki
//
// 簡易アセンブラ: 字句解析
//

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "iasm.h"
#include "ascii_ctype.h"

/* コンストラクタ */
Token::Token()
{
	clear();
}

/* クリア */
void
Token::clear()
{
	type = T_END;
	word.erase();
	number = 0;
}

/* トークン種別 t の表示用文字列を返す (static 関数) */
const char *
Token::type_name(token_type_t t)
{
	if (t < 0 || t >= T_MAX) {
		return "?";
	}
	return type_str[t];
}

/* 現在のトークンの種別文字列を返す */
const char *
Token::type_name() const
{
	return type_name(type);
}

/* トークン種別の表示用文字列 */
/* token_type_t の順序と合わせること orz */
const char *Token::type_str[] = {
	"END",
	"SPACE",
	"CHAR",
	"STRING",
	"NUMBER",
	"LITERAL",
	"SIZE",
	"SHARP",
	"COLON",
	"COMMA",
	"LPAREN",
	"RPAREN",
	"PLUS",
	"MINUS",
	"MULTI",
	"DIVIDE",
	"LT",
	"GT",
};


/* 1文字の記号テーブル */
Scanner::mark_t Scanner::mark[] = {
	{ '#', T_SHARP, },
	{ ':', T_COLON, },
	{ ',', T_COMMA, },
	{ '(', T_LPAREN, },
	{ ')', T_RPAREN, },
	{ '+', T_PLUS, },
	{ '-', T_MINUS, },
	{ '*', T_MULTI, },
	{ '/', T_DIVIDE, },
	{ '<', T_LT, },
	{ '>', T_GT, },
};

/* コンストラクタ */
Scanner::Scanner(String& src)
{
	buf = src;
	ptr = 0;
}

/* デストラクタ */
Scanner::~Scanner()
{
}

/* リテラルの1文字目を判定 (ドットを含み数字は含まない) */
bool
Scanner::isLabel1(int c)
{
	return (isalpha(c) || c == '_' || c == '.');
}

/* リテラルの2文字目以降を判定 (数字は含みドットを含まない) */
bool
Scanner::isLabel2(int c)
{
	return (isalpha(c) || c == '_' || isdigit(c));
}

/*
 * サイズサフィックス文字を判定。
 * 正しいものならバイト数を返す。
 * bra.s (と fmove.s ?) のため .S なら 0 を返すのでそっちで判断しろ下さい。
 * 正しくなければ -1 を返す。
 */
int
Scanner::isSize(int c) const
{
	c = tolower(c);

	if (c == 's') {
		return 0;
	}
	if (c == 'b') {
		return 1;
	}
	if (c == 'w') {
		return 2;
	}
	if (c == 'l') {
		return 4;
	}

	return -1;
}

/* トークンを1つ切り出す */
bool
Scanner::get_token(Token& token)
{
	int c;

	/* 戻り値を初期化 */
	token.clear();
	errmsg.erase();

	c = getch();

	/* 終端 */
	if (c == 0) {
		token.type = T_END;
		return true;
	}

	/* 連続する空白は1つの空白 */
	if (is_ascii_space(c)) {
		for(; is_ascii_space((c = getch())); ptr++) {
			token.word += c;
		}
		token.type = T_SPACE;
		return true;
	}

	/* シングルクォートなら1文字 */
	if (c == '\'') {
		token.word = c;
		ptr++;
		/* 文字コンテキスト */
		bool escape = false;
		bool terminate = false;
		int charnum = 0;
		for (; (c = getch()) != 0; ptr++) {
			if (escape) {
				token.word += c;
				/* 制御文字のコードをここで解釈 */
				if (c == 't') {
					token.number = '\t';
				} else if (c == 'r') {
					token.number = '\r';
				} else if (c == 'n') {
					token.number = '\n';
				} else if (c == '0') {
					token.number = '\0';
				} else {
					token.number = c;
				}
				charnum++;
				escape = false;
				continue;
			}
			if (c == '\\') {
				token.word += c;
				escape = true;
				continue;
			}
			if (c == '\'') {
				token.word += c;
				terminate = true;
				ptr++;
				break;
			}
			token.word += c;
			token.number = c;
			charnum++;
		}
		if (!terminate) {
			errmsg = "single char not terminated";
			return false;
		}
		if (charnum != 1) {
			errmsg = "invalid char";
			return false;
		}
		token.type = T_CHAR;
		return true;
	}

	/* ダブルクォートなら文字列 */
	if (c == '\x22') {
		token.word = c;
		ptr++;
		/* 文字列コンテキスト */
		bool escape = false;
		bool terminate = false;
		for (; (c = getch()) != 0; ptr++) {
			if (escape) {
				token.word += c;
				escape = false;
				continue;
			}
			if (c == '\\') {
				token.word += c;
				escape = true;
				continue;
			}
			if (c == '\x22') {
				token.word += c;
				terminate = true;
				ptr++;
				break;
			}
			token.word += c;
		}
		if (!terminate) {
			errmsg = "string not terminated";
			return false;
		}
		token.type = T_STRING;
		return true;
	}

	/* 16進数 */
	if (c == '$') {
		token.word += c;
		ptr++;
		for (; isxdigit((c = getch())); ptr++) {
			token.word += c;
		}
		/* 8桁('$'含めて9文字)越えたらアウトっていう判定だけでいいはず */
		if (token.word.size() > 9) {
			errmsg = "too large number";
			return false;
		}
		/* 変換できるはず */
		token.number = (uint32_t)strtol(token.word.c_str() + 1, NULL, 16);
		token.type = T_NUMBER;
		return true;
	}

	/* 10進数 */
	if (isdigit(c)) {
		for (; isdigit((c = getch())); ptr++) {
			token.word += c;
		}
		errno = 0;
		long lval;
		lval = strtol(token.word.c_str(), NULL, 10);
		if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) {
			errmsg = "too large number";
			return false;
		}
		token.number = (uint32_t)lval;
		token.type = T_NUMBER;
		return true;
	}

	/* プレインなところでのセミコロンはコメント開始記号 */
	if (c == ';') {
		/*
		 * コメント以降を字句解析すると文法エラーになる可能性があるので
		 * ここで解析をやめる。
		 */
		token.word = c;
		token.type = T_END;
		return true;
	}

	/* 1文字の記号 */
	for (int i = 0; i < countof(mark); i++) {
		if (mark[i].chr == c) {
			token.word = c;
			ptr++;
			token.type = mark[i].type;
			return true;
		}
	}

	/* サイズサフィックス */
	if (c == '.') {
		int c1 = getch(1);
		int c2 = getch(2);
		int size;
		if ((size = isSize(c1)) != -1 && !isalpha(c2)) {
			token.word += c;
			token.word += c1;
			ptr += 2;
			token.type = T_SIZE;
			token.number = size;
			return true;
		}

		/* そうでなければリテラル(擬似命令)かも */
		/* PASSTHROUGH */
	}

	/* ラベル (1文字目と2文字目以降は判定条件が違う) */
	if (isLabel1(c)) {
		token.word = c;
		ptr++;
		for (; isLabel2((c = getch())); ptr++) {
			token.word += c;
		}
		token.type = T_LITERAL;
		return true;
	}

	errmsg = "syntax error";
	return false;
}

/* 現在位置の文字を返す。文字列の終端以降なら 0 を返す */
int
Scanner::getch(int offset) const
{
	int c;

	if (ptr + offset < buf.size()) {
		c = (int)buf[ptr + offset];
	} else {
		c = 0;
	}
	return c;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.