概要
みなさん、c言語は好きですか?
以下のデータから分かるように、大体の人は嫌いだと思います.
そこで、私はc言語に足りない機能を追加するツール(笑)を制作しました!
単純に書くと 独自の構文 を c言語に置き換えて出力するツールです.
インストール方法
brew tap satooru65536/cmm
brew install cmm
使い方
使い方は SatooRu65536/cmm-compiler を見てください
実装内容
c言語で変数を文字列内で展開するには以下のようになります.
int num = 65536;
printf("2^16 = %d", num);
ちょっと分かりずらいですよね. 例えば以下の場合では
printf("%s: %d / %d = %f", quesId, n1, n2, n3);
これでは目の反復横跳びで疲れてしまいます.
しかも何書いているかわからない!!
そこで私は Python のように f文字列 を使えるようにしたいと思いました.
以下はPythonでのf文字列の例です
num = 65536
print(f"2^16 = {num}") # 2^16 = 65536
最終的には以下のような構文を実装します
int num = 65536;
printf(f"num = {num:d}");
実装
はじめに入力, 出力ファイルを読み込みます.
FILE *inputFile;
FILE *outputFile;
inputFile = fopen("in.c", "r");
outputFile = fopen("out.c", "w");
続いて入力ファイルを1行ずつ読み込んで parseLine関数
を呼び出します.
char line[256];
while (fgets(line, sizeof(line), inputFile) != NULL) {
parseLine(line, outputFile);
}
そして単語ごとに切り出します!
1文字ずつ見ていき、
"
があれば次の "
までを文字列とし、word
に追加、
(
)
{
}
(空白)
,
;
であれば evalWord関数
を呼び出し、その文字を出力、
\n
\0
であれば evalWord関数
を呼び出す.
といった感じです.
説明を読むよりもコードを見た方が早いですね!
void parseLine(char *line, FILE *outputFile) {
int i = 0;
char word[256] = {'\0'};
while (1) {
char c = *line;
switch (c) {
case '"':
// 次の " までを文字列として扱う
word[i] = c;
i++;
line++;
while (1) {
c = *line;
word[i] = c;
i++;
if (c == '"') {
break;
}
line++;
}
break;
case '(':
case ')':
case '{':
case '}':
case ',':
case ';':
case ' ':
word[i] = '\0';
evalWord(word, outputFile);
fprintf(outputFile, "%c", c);
i = 0;
break;
case '\0':
word[i] = '\0';
evalWord(word, outputFile);
return;
case '\n':
word[i] = '\0';
evalWord(word, outputFile);
fprintf(outputFile, "%c", '\n');
return;
default:
word[i] = c;
i++;
break;
}
line++;
}
}
これで一単語ずつ呼び出されるようになりました!!
単語が f"
から始まっていた場合は f文字列しかないので
いい感じに処理をするようにします
void evalWord(char *word, FILE *outputFile) {
// f文字列の場合
if (word[0] == 'f' && word[1] == '"') {
processFString(word, outputFile);
return;
}
// それ以外の場合はそのまま書き込む
fprintf(outputFile, "%s", word);
}
// f文字列を処理する
void processFString(char *word, FILE *outputFile) {
char str[256] = {'\0'};
replaceString(word, str);
fprintf(outputFile, "%s", str);
char vars[256] = {'\0'};
pickupVariable(word, vars);
fprintf(outputFile, "%s", vars);
}
文字列の変数をフォーマット演算子に置き換えます.
f"{hoge:d} {fuga:lf}" -> "%d %d"
説明は...プログラムから理解しましょう!!!
(面倒で GitHub Copilot に書かせた...)
void replaceString(char *word, char *str) {
int i = 1;
int j = 0;
while (1) {
char c = word[i];
if (c == '\0') break;
if (c != '{') {
str[j] = c;
j++;
} else {
i++;
while (1) {
c = word[i];
i++;
if (c == ':') break;
}
char f = 0;
char format[5] = {'\0'};
while (1) {
c = word[i];
format[f] = c;
if (c == '}') break;
f++;
i++;
}
str[j] = '%';
for (int k = 0; k < f; k++) str[j + k + 1] = format[k];
j += f + 1;
}
i++;
}
}
同様に文字列から変数名を取り出します.
f"{hoge:d} {fuga:lf}" -> , hoge, fuga
void pickupVariable(char *word, char *vars) {
int i = 0;
int j = 0;
while (1) {
char c = word[i];
if (c == '\0') {
break;
}
if (c == '{') {
i++;
vars[j] = ',';
vars[j + 1] = ' ';
j += 2;
while (1) {
c = word[i];
if (c == ':') {
while (c != '}') {
c = word[i];
i++;
}
break;
}
vars[j] = c;
i++;
j++;
}
}
i++;
}
}
以上で完成です!!
以下のコードが書かれた in.c
を作成して実行すると、変換してくれると思います
printf(f"num = {num:d}");
printf("num = %d", num);
最後に
今回は単語ごとに分割して置き換えるというところまでしました.
さらに 抽象構文木 に変換して、云々すればプログラミング言語の完成ですね!
ぜひやってみてください!!
最終的なコードを貼り付けておきます
#include <stdio.h>
#include <string.h>
void pickupVariable(char *word, char *vars) {
int i = 0;
int j = 0;
while (1) {
char c = word[i];
if (c == '\0') {
break;
}
if (c == '{') {
i++;
vars[j] = ',';
vars[j + 1] = ' ';
j += 2;
while (1) {
c = word[i];
if (c == ':') {
while (c != '}') {
c = word[i];
i++;
}
break;
}
vars[j] = c;
i++;
j++;
}
}
i++;
}
}
void replaceString(char *word, char *str) {
int i = 1;
int j = 0;
while (1) {
char c = word[i];
if (c == '\0') break;
if (c != '{') {
str[j] = c;
j++;
} else {
i++;
while (1) {
c = word[i];
i++;
if (c == ':') break;
}
char f = 0;
char format[5] = {'\0'};
while (1) {
c = word[i];
format[f] = c;
if (c == '}') break;
f++;
i++;
}
str[j] = '%';
for (int k = 0; k < f; k++) str[j + k + 1] = format[k];
j += f + 1;
}
i++;
}
}
void processFString(char *word, FILE *outputFile) {
char str[256] = {'\0'};
replaceString(word, str);
fprintf(outputFile, "%s", str);
char vars[256] = {'\0'};
pickupVariable(word, vars);
fprintf(outputFile, "%s", vars);
}
void evalWord(char *word, FILE *outputFile) {
// f文字列の場合
if (word[0] == 'f' && word[1] == '"') {
processFString(word, outputFile);
return;
}
// それ以外の場合
fprintf(outputFile, "%s", word);
}
void parseLine(char *line, FILE *outputFile) {
int i = 0;
char word[256] = {'\0'};
while (1) {
char c = *line;
switch (c) {
case '"':
// 次の " までを文字列として扱う
word[i] = c;
i++;
line++;
while (1) {
c = *line;
word[i] = c;
i++;
if (c == '"') {
break;
}
line++;
}
break;
case '(':
case ')':
case '{':
case '}':
case ',':
case ';':
case ' ':
word[i] = '\0';
evalWord(word, outputFile);
fprintf(outputFile, "%c", c);
i = 0;
break;
case '\0':
word[i] = '\0';
evalWord(word, outputFile);
return;
case '\n':
word[i] = '\0';
evalWord(word, outputFile);
fprintf(outputFile, "%c", '\n');
return;
default:
word[i] = c;
i++;
break;
}
line++;
}
}
int main(void) {
FILE *inputFile;
FILE *outputFile;
inputFile = fopen("in.c", "r");
outputFile = fopen("out.c", "w");
char line[256];
while (fgets(line, sizeof(line), inputFile) != NULL) {
parseLine(line, outputFile);
}
return 0;
}