INASOFT 管理人のひとこと


フリーソフトダウンロードサイト「INASOFT」の管理人 矢吹拓也 が日々の「ひとこと」を語るページです。
2021年1月1日より、旧ブログ(blog.inasoft.org)からお引越ししました。
・INASOFT Webサイト: https://www.inasoft.org/
・管理人のふたこと(長文記事/寄稿文): https://www.inasoft.org/talk/
2022年7月下旬より再び本業多忙化してきているため、更新頻度は落ちます。 [2022/7/24 19:32]

目次 | ←前へ / 2026-01-27 00:39

■生成AIは大学入試できるみたいだけど、大学卒業もできるかな? R-Scrpitインタプリタを生成AIに作らせてみたら

2026年 1月27日(火) 0:39:25



RSSRSS配信中
https://www.inasoft.org/



さて、昨日、R-Scriptを軽量化して現代的に再実装するということを書いたわけですが、現代的な再実装の方法がよく分からなくて、ChatGPTに相談したら、Boost.Spiritを紹介されました。

というわけで、色々とChatGPTに教えを乞う形でお勉強をしていました。
ChatGPTに教えを乞う場合、けっこう、気を利かせて「実はあなたはこれを知りたいんじゃないですか?」と聞いたこと以外の事を教えてくれることも多く、それがドンピシャに当たってたりして、過去に「言われたことは(間違っていても)忠実に実行するが、言われたことしかしない」なんて言われていたコンピュータは、ずいぶんと発展を遂げたなぁとビックリしてました。

コンピュータが空気読むようになってしまったら、いよいよ人間の存在意義がなくなってきてしまう。

まぁ、そういうことはさておき…。
で、言語仕様をまとめて、Visual Studio 2010とBoost.Spirit.Qiで作れるようなプログラムを生成AIに依頼して書いてもらうことにします。
ただ、R-Scriptをそのまま実装すると、ファイルを読み書きできたり、レジストリを読み書きできたり、負数を扱えなかったりするものができることになります。今回は、マウスふるふる(mousefr)においてスクリプトでマウスの動きを記述できることを目指すことにして、それに合わせてR-Scriptの仕様を軽量化しつつ改良します。せっかくなら、frという文字を使いたいですね。名前はFR-Scriptでいかがでしょうか。

Fは軽量を意味させることにして、Featherってことにします。軽量版R-Scriptってことで。
まずは、言語仕様と指示をプロンプトで表現します。こんな感じでどうでしょうか。
(文末をセミコロンにしなかったのは失敗だったのですが、今回はあえてそれを踏襲することにします。文字列型変数に$を付けるルールは外します)


下記の仕様を持つ独自のスクリプト言語「FR-Script」を処理するインタプリタを作ってください。
なお、開発環境はVisual Studio 2010を用い、開発言語はC++言語とし、boost.spirit.qiを用いる。また、文字列の文字コードはUTF-16LEとする。

■インタプリタ

インタプリタは、ワイド文字のスクリプト文字列、コールバック関数へのポインタを引数にして、C++言語から呼び出せる関数の形をとる。関数名はfr_scriptとする。コールバック関数については後述する。

■式

式は、数値または文字列の戻り値を持つ。この言語での数値は32bit符号付整数とする。
式は、数値の場合、一般的な優先度で加減乗除と括弧を処理する。また、以下の演算子を使用可能とする。
式1%式2 :式1を式2で割ったときの余を返す。
式1|式2 :式1と式2をビットORした結果を返す。
式1&式2 :式1と式2をビットANDした結果を返す。
式1^式2 :式1と式2をビットXORした結果を返す。
~式1 :式1の全ビットを反転した結果を返す。
式1>>式2 :式1を式2の数だけ右シフトした結果を返す。
式1<<式2 :式1を式2の数だけ左シフトした結果を返す。

式は、文字列の場合、加算(文字列の追加)が可能である。文字列+文字列は文字列。文字列+数値は文字列(数値は文字列に変換される)。数値+文字列は数値(文字列は先頭から数値とみなせる部分までを数値に変換する)となる。
数値は、そのままの場合は10進数、0xで始まる場合は16進数、0bで始まる場合は2進数として扱う。
文字列リテラルは、ダブルコーテーションまたはシングルコーテーションで囲まれる。
文字列リテラルの中に含まれる\は、エスケープシーケンス記号として扱う。
 \n は改行。
 \t は水平タブ。
 \r は復帰。
 \\ は \ そのものを表す。
 \" は " そのものを表す。
 \' は ' そのものを表す。
 \v は垂直タブを表す。
 \a はビープを表す。
 \b はバックスペースを表す。
 \f はフォームフィードを表す。
ただし、文字列リテラルが @"文字列" または @'文字列' の形式で書かれた場合、文字列中の\はエスケープシーケンス記号として扱われず、通常の文字列の扱いとなる。
トークンは、スペース・タブ・改行で区切られる。スペース・タブ・改行はホワイトスペースとなる。改行は文の区切りもあらわす。
ただし、改行の直前が=,+,-,*,/,(で終わる場合は、次の行に文が継続しているとみなす。
シンボル名は、_またはアルファベットで始まり、その後に_またはアルファベットまたは数値が続く。シンボル名の大文字・小文字は区別しない。
変数はシンボル名で表す。
変数は無宣言で使用可能とする。変数の型は数値または文字列とし、無宣言の場合は、数値なら0、文字列なら""(空文字列)となる。

■文

文の終端は改行である。
変数=式で代入文を表す。代入文は式ではなく文である。
変数+=式で変数=変数+式を表す。数値の場合は数値の加算を、文字列の場合は文字列の追記を行う。
変数-=式で変数=変数-式を表す。
++変数または変数++で変数=変数+1を表す。
--変数または変数--で変数=変数-1を表す。

■条件式

条件式として、次の演算子が使用可能。
式1==式2 :式1と式2が等しければ真、等しくなければ偽。式の左右で文字と数値の型が異なる場合は、文字列変換して比較する。
式1!=式2 :式1と式2が等しければ偽、等しくなければ真。式の左右で文字と数値の型が異なる場合は、文字列変換して比較する。
式1===式2:式1と式2が等しければ真、等しくなければ偽。式の左右で文字と数値の型が異なる場合は偽。
式1!==式2:式1と式2が等しければ偽、等しくなければ真。式の左右で文字と数値の型が異なる場合は真。
式1<式2 :式1よりも式2の方が大きければ真、そうでなければ偽。式が数値の場合は単純な大小関係、式が文字列の場合は辞書順での比較とする。式の左右で文字と数値の型が異なる場合は偽。
式1>式2 :式1よりも式2の方が小さければ真、そうでなければ偽。式が数値の場合は単純な大小関係、式が文字列の場合は辞書順での比較とする。式の左右で文字と数値の型が異なる場合は偽。
式1<=式2 :式1よりも式2の方が大きいまたは等しければ真、そうでなければ偽。式が数値の場合は単純な大小関係、式が文字列の場合は辞書順での比較とする。式の左右で文字と数値の型が異なる場合は偽。
式1>=式2 :式1よりも式2の方が小さいまたは等しければ真、そうでなければ偽。式が数値の場合は単純な大小関係、式が文字列の場合は辞書順での比較とする。式の左右で文字と数値の型が異なる場合は偽。
TRUE :常に真を意味する。大文字・小文字は区別しない。
FALSE :常に偽を意味する。大文字・小文字は区別しない。

条件式は以下の演算子で組合せ可能とする。
条件式1&&条件式2 :2つの条件式のAND条件とする
条件式1||条件式2 :2つの条件式のOR条件とする
!条件式1 :条件式の真偽を反転する

文を1つ以上連ねたものをブロックとすることができる。ブロックは { と } で囲まれた1つ以上の文とする。

■制御文

フロー制御文は以下とする。

・IF,ELSE IF, ELSE

IF 条件式1 {
 ブロック1
}
ELSE IF 条件式2 {
 ブロック2
}
  :
  :
ELSE {
 ブロックn
}

条件式1を満たす場合はブロック1が実行され、条件式1を満たさず条件式2を満たす場合はブロック2が実行され、…、すべての条件式を満たさない場合にブロックnが実行される。
なお、IF、ELSEのキーワードは、大文字・小文字を区別しない。

・WHILE, LOOP

WHILE 条件式1 {
 ブロック1
}

条件式1を満たす限り、ブロック1を実行し続ける。
ブロック1の中にBREAKが現れたら、WHILEから抜ける。CONTINUEが現れたら、条件式1の評価に戻る。
WHILE(TRUE)の特殊な書き方として、以下の書き方ができるようにする。

LOOP {
 ブロック1
}

なお、WHILE、BREAK、CONTINUE、LOOPのキーワードは、大文字・小文字を区別しない。

・EXIT

EXIT または EXIT 数値式
プログラムを終了する。EXITの場合はインタプリタの呼び出し元に0を返し、EXIT 数値式の場合は、呼び出し元に数値式の結果の数値を返す。
なお、EXITが無くても、スクリプトの終端に達すれば、戻り値0でインタプリタを終了する。

■関数

関数は、インタプリタが事前に準備した関数を呼び出すものと、C言語で定義した関数をコールバックして呼び出すものの2種類がある。
関数の呼び出し文は、 シンボル(引数) の形とする。
引数は、0個以上の式をカンマで区切ったものとする。


・インタプリタが事前に準備する関数。なお、関数名の大文字・小文字は区別しない。
sleep(数値) :数値で指定したミリ秒待機する。常に1を返す。
rand() :疑似乱数を生成して返す。
srand(数値) :乱数生成のシード値を設定する。常に1を返す。
getenv(文字列) :文字列で指定した環境変数を返す。
setenv(文字列1,文字列2) :文字列1で指定した環境変数に文字列2を設定する。設定に成功したい場合は1を返し、失敗した場合は0を返す。
GetCurrentYear() :現在のローカル日時のうち、年(西暦)を返す。ローカル日時は、Windows APIのGetLocalTime()を用いて取得する。
GetCurrentMonth() :現在のローカル日時のうち、月を返す。ローカル日時は、Windows APIのGetLocalTime()を用いて取得する。
GetCurrentDay() :現在のローカル日時のうち、日を返す。ローカル日時は、Windows APIのGetLocalTime()を用いて取得する。
GetCurrentDayOfWeek() :現在のローカル日時のうち、曜日を返す。ローカル日時は、Windows APIのGetLocalTime()を用いて取得する。
GetCurrentHour() :現在のローカル日時のうち、時間を返す。ローカル日時は、Windows APIのGetLocalTime()を用いて取得する。
GetCurrentMinute() :現在のローカル日時のうち、分を返す。ローカル日時は、Windows APIのGetLocalTime()を用いて取得する。
GetCurrentSecond() :現在のローカル日時のうち、秒を返す。ローカル日時は、Windows APIのGetLocalTime()を用いて取得する。
GetCurrentMilliseconds() … 現在のローカル日時のうち、ミリ秒を返す。ローカル日時は、Windows APIのGetLocalTime()を用いて取得する。
strlen(文字列) :文字列の文字数を数値で返す。
strstr(文字列1,文字列2) :文字列1の中から文字列2を探し、何文字目に存在していたかを返す。存在しない場合は-1を返す。大文字・小文字を区別する。
strstri(文字列1,文字列2) :文字列1の中から文字列2を探し、何文字目に存在していたかを返す。存在しない場合は-1を返す。大文字・小文字を区別しない。
toInt(文字列) :文字列を数値に変換したものを返す。文字列の先頭から解析し、数値として解釈可能な部分までを数値として返す。
toStr(数値) :数値を10進数とみなして、文字列に変換したものを返す。
toStr2(数値) :数値を2進数とみなして、文字列に変換したものを返す。
toStr16(数値) :数値を16進数とみなして、文字列に変換したものを返す。
strupr(文字列) :文字列のうち、アルファベット小文字を大文字に変換した文字列を返す
strlwr(文字列) :文字列のうち、アルファベット大文字を小文字に変換した文字列を返す
left(文字列,数値) :文字列のうち、先頭から数値文字抜き出した文字列を返す。文字列の文字数が足りない場合は、元の文字列を返す。
right(文字列,数値) :文字列のうち、末尾から数値文字抜き出した文字列を返す。文字列の文字数が足りない場合は、元の文字列を返す。
mid(文字列,数値1,数値2) :文字列のうち、先頭の数値1文字目から数値2も自分を抜き出した文字列を返す。文字列の文字数が足りない場合は、元の文字列を返す。
midの別名として、substr, substring も使えるようにする。
textout(文字列1,文字列2) :Windows APIのMessageBox APIを利用し、タイトルバーに文字列2、ダイアログ内に文字列1を表示する。メッセージボックスのスタイルとして、MB_OKを指定する。常に0を返す。
textoutの別名として、message, messageboxも使えるようにする。
Warning(文字列1,文字列2) :Windows APIのMessageBox APIを利用し、タイトルバーに文字列2、ダイアログ内に文字列1を表示する。メッセージボックスのスタイルとして、MB_OK|MB_ICONWARNINGを指定する。常に0を返す。
Information(文字列1,文字列2) :Windows APIのMessageBox APIを利用し、タイトルバーに文字列2、ダイアログ内に文字列1を表示する。メッセージボックスのスタイルとして、MB_OK|MB_ICONINFORMATIONを指定する。常に0を返す。
Error(文字列1,文字列2) :Windows APIのMessageBox APIを利用し、タイトルバーに文字列2、ダイアログ内に文字列1を表示する。メッセージボックスのスタイルとして、MB_OK|MB_ICONSTOPを指定する。常に0を返す。
Question(文字列1,文字列2) :Windows APIのMessageBox APIを利用し、タイトルバーに文字列2、ダイアログ内に文字列1を表示する。メッセージボックスのスタイルとして、MB_YESNO|MB_ICONQUESTIONを指定する。はいが選択されたら1を返し、いいえが選択されたら0を返す。
Question_OkCancel(文字列1,文字列2) :Windows APIのMessageBox APIを利用し、タイトルバーに文字列2、ダイアログ内に文字列1を表示する。メッセージボックスのスタイルとして、MB_OKCANCEL|MB_ICONQUESTIONを指定する。OKが選択されたら1を返し、キャンセルが選択されたら0を返す。
Question_YesNoCancel(文字列1,文字列2) :Windows APIのMessageBox APIを利用し、タイトルバーに文字列2、ダイアログ内に文字列1を表示する。メッセージボックスのスタイルとして、MB_YESNOCANCEL|MB_ICONQUESTIONを指定する。はいが選択されたら1を返し、いいえが選択されたら0を返し、キャンセルが選択されたら-1を返す。

・C言語定義した関数を登録してコールバックする方法
インタプリタを呼び出す際の第2引数に、コールバック関数へのポインタを与える。ここがNULLの場合は、コールバック関数は存在しないものとみなす。
コールバック関数呼び出しは、次の形式の文とする。
CB.シンボル(引数)
CBの部分は大文字・小文字を区別しないが、その後に続くシンボルは大文字・小文字を区別する。
コールバック関数の引数は、シンボルの文字列と、引数の型・引数の内容を渡すための構造体とする。構造体の内容については任せるので、使い方を教えてください。

■コメント
#で始まる行、;で始まる行、//で始まる行はコメントとして扱い、処理を行わない。



これを文字に起こすのには40分ほどかかりました。
ちなみに、R-Scriptの作成はflex, kmyaccとVisual C++(当時は.NET 2002)を使って約1年かかりまして、これを学部卒の卒業研究としたのです。

生成AIが大学入試のテストで高得点みたいなニュースを聞く昨今ですが、生成AIは大学卒業もできるんでしょうか?

上記のプロンプトをChatGPTに与えたところ「正直に言って、これを1つのファイルで出力することは現実的でない」とか、フェルマーみたいなことを言い出しまして、概念説明と簡単なプログラム断片の出力だけで終わりました。ChatGPT、不合格ね。ってか、AIが語る「正直」ってなんなんだ?

仕方ないので、Gemini(読み方はジェミニらしい)の思考モードにお願いしたところ、3分くらい長考したのち、凄まじい勢いでC++のプログラムを出力しだしまして、なんかそれっぽいのが出てきました。
さっそく、Visual C++に流し込んで試してみたところ、コンパイルエラー。あれ?

Gemini自身に原因を聞いてみると、Visual C++ 2010のコンパイラの型推論エンジンが古すぎてダメなんですって。
代替案を出力してもらったものの、そちらもダメ。

自力で調査したところ、型推論エンジンがヘッダファイル内で型推論に失敗してしまっており、さすがにヘッダファイル内を書き換えるわけにもいかず、お手上げ状態。
ちなみに失敗を起こしている箇所は、ソースコードを解析してインタプリタのAST(抽象構文木)を作っている部分。

例えば 3+4 という文字列を3と+と4に分けて、3という数字、4という数字、+というオペレータに分解してASTに入れるのですが、+と3と4を見てASTに入れようとして、型推論に失敗している感じ。

…となると、AST木を作るんじゃなくて、セマンティックアクションとスタック(stack)を駆使してpush 3, push 4, add みたいなバイトコードを吐き出すようにしたほうがよかったりするかな?
(3+4を 3 4 +の順に並べ替えるのは、まさに「逆ポーランド記法」ですね。計算機プログラムをコンパイラにかけると、逆ポーランド記法を出力するという有名な話。実はこれ、ちょっとやってみたかった)
(add(+)とは、2つの値をstackからpopして足し合わせて、結果の値をstackにpushすることを意味する)

ASTを作れないとなると、ifとwhile(制御構文)のバイトコード化に苦労しそうですが、そこらへんはがんばってみるか…。
目標が、インタプリタ作成からコンパイラ作成に変わりそうです。

ちなみに僕は、Visual C++ 2010とBoost.Spirit.Qiの使用にこだわったので失敗しましたが、もし最新のVisual Studio(無料で手に入るよね?)とかBoost.Spirit.X3(これも無料で手に入るよね?)を使うぞって方は、上のプロンプトを投げ込んでみて、どうなるか試してください。一発でうまくいくでしょうかね?生成AIは大学を卒業できるでしょうかね?

目次 | ←前へ / 2026-01-27 00:39


目次の表示:


ブログではないので、コメント機能とトラックバック機能は提供していません。ご質問・ご意見等はメールフィードバックまたはTwitter等からお願いします。いただいたご質問・ご意見などは、この「管理人のひとこと」の記事に追加、あるいは新規の記事にする形で一部または全文をそのまま、あるいは加工させていただいた上で、ご紹介させていただく場合があります。
当サイトでは掲載内容による不具合等に関する責任を持ちません。また、内容の正確性についての保証もありませんので、情報をご利用の際は、利用者の自己責任で確認をお願いします。本ページは公開から1年半後の任意のタイミングで削除される予定です。


- 最近の更新 -



3433886 (+0141)[+0664]

Copyright© 2010-2026 INASOFT