はじめに
2023年の1月下旬よりQiita様へ参加させていただき、自作言語 Re:Mind(リマインド)の企画趣旨をQiita内の記事でご説明させていただいております。まだ実装がなにもないので、そろそろ試作に着手しようかと考えております。本記事はその試作品の内部設計情報で、ターゲット言語をMindとしています。
この記事内容の作業目的
試作にあたって内部構想を、日本語ロジック仕様記述言語 Re:Mindで記述しておきます。本試作範囲は策定仕様のごくごく一部です。ターゲット言語は一部のみ対応、言語の対応ソース構文も超限定的です。
この記事内容の保証
※この記事には仕様的な情報が含まれます。自作言語はまだ設計開発中のため、発案者による実装は存在しません。本記事に開示された仕様は次バージョンの仕様においては予告なく変更される場合があります。また、このバージョンの仕様内で補足や追加がなされる場合があります。
補足
日本語ロジック仕様記述言語 Re:Mind(リマインド)はオープンな設計言語仕様で、どなたでもこの記法を使いロジックを記述することができます。
日本語トランスコンパイラ言語 Re:Mind(リマインド)はオープンな実装言語仕様で、どなたでもコンパイラ・トランスコンパイラを実装することができます。
試作概要・試作目的
本試作では日本語トランスコンパイラ言語 Re:Mind(リマインド)の原型を検証し、いろいろな問題点を洗い出します。ターゲット言語のソースコード入力は実装言語のソースコードのインライン記述にとどまります。試作範囲は段階的に拡張してまいります。
本試作の設計言語
・ロジック仕様記述言語 Re:Mind
本試作の実装言語
・C#
コンソールアプリケーションとして実装します。
本試作のターゲット言語
本試作のターゲット言語としては下記の3言語を想定しております、本記事はそのうちのMindに該当する内容です。
・C#
・Java
・Mind ←本記事(仕様)の対象言語
・想定稼働OS Windows
本試作のターゲット言語の対応仕様範囲
"Hello World!"がコンソール出力されるソースコードを入力して(インライン・リテラルコーディング)、ターゲット言語のソースコードを(双方だらだらと)コンソール出力する。(どちらを出力するかは起動パラメータにする。)
お題のターゲット言語出力ソースコード
Mind
メインとは
「Hello World!」を 一行表示する。
お題のトランスコンパイラ言語 Re:Mind入力ソースコード(ターゲット言語依存)
Mind
当初、下記のようなソースコードを想定しておりましたが、(インラインソース機能風)
▽メイン
□コンソールへ一行表示する("Hello World!")
△
▽コンソールへ一行表示する(string value)
□一行表示する(string value)
△
下記のように引用定義としてMind予約語の「一行表示する」を定義するに訂正しました。Mind予約語の再定義記法としてはあまりにもC言語風の記法に寄ってしまっていますが、現時点ではこちらで暫定対応とします。
▽メイン
□コンソールへ一行表示する("Hello World!")
△
▼コンソールへ一行表示する(string value)
■一行表示する(string value)
▲
本試作の内部設計の妥当性
試作のため本実装には採用されない可能性があります。また、試作品の実装段階でフィードバック訂正される場合があります。
クラス構成
お手数ですが内部設計(基本構成)をご参照ください。
クラス・メンバの構成
お手数ですが内部設計(基本構成)をご参照ください。
クラスTargetMind
ターゲット言語Mindのトークナイザとコードジェネレータを保持するクラスの実装です。
列挙体、クラス(変数のみ)の宣言
お手数ですが内部設計(基本構成)をご参照ください。
SubMain
クラスProgramのメイン関数から、コンソールアプリケーションの起動パラメータで"mind"が指定されたときに実行されるサブメイン関数です。(クラスTargetMindの実質メイン関数)
クラスTargetCsのサブメイン関数との差異は、リスト型 ユージングリストがないことです。
/**
* SubMain
* @param ソース soruce
*/
▽static サブメイン(string ソース)
・int 戻り値 = 0
・リスト型<ソース行型> ソース行リスト =リスト型<ソース行型>で初期化
・リスト型<ターゲットソース行型> ターゲットソース行リスト =リスト型<ターゲットソース行型>で初期化
・辞書型 名称辞書 = 辞書型<string, string>で初期化
□名称辞書を初期化する(名称辞書)
□戻り値= ノードカテゴライズする(ソース,ソース行リスト)
◇戻り値!=0 の場合
□コンソール.一行表示( "ノードカテゴライズに失敗しました。")
□戻り値を 返す
◇ここまで
□戻り値= 名称辞書を収集する(ソース行リスト,名称辞書)
◇戻り値!=0 の場合
□コンソール.一行表示( "名称辞書の収集に失敗しました。")
□戻り値を 返す
◇ここまで
□戻り値= トランスコンパイルする(ソース行リスト,ターゲットソース行リスト,名称辞書)
◇戻り値!=0 の場合
□コンソール.一行表示( "トランスコンパイルに失敗しました。")
□戻り値を 返す
◇ここまで
□コード出力する(ターゲットソース行リスト,ソース行リスト)
□戻り値を 返す
△
SubMainから直接呼ばれる主要関数
名称辞書を初期化する
この処理は今回の試作では不要ですが、将来的にMindの予約語とRe:Mindの予約語を対応づけるために用意しておきます。
/**
* initNameDictionary
* @param 名称辞書 nameDictionary
*/
▽static 名称辞書を初期化する(辞書型<string, string> 名称辞書)
△
ノードカテゴライズする
第1引数の「ソース」はRe:Mind(リマインド)のソースコード文字列全体です。「ソース」を改行で分割して、各ソース行の先頭文字「ノード」でソース行をカテゴライズした第2引数「ソース行リスト」に格納します。
/**
* nodeCategorized
* @param ソース source
* @param ソース行リスト sourcesLineList
*/
▽static int ノードカテゴライズする(string ソース,リスト型<ソース行型> ソース行リスト)
・int 戻り値 = 1
□ライン文字列配列 = ソース.Split("\r\n")
〇i=0,i<ライン文字列配列.Length,i++ 繰り返す
・ソース行 =new ソース行型
・ソース行文字列=ライン文字列配列[i]
□ソース行.ソース行番号=i+1
□ソース行.オリジナルソース文字列=ソース行文字列
□ソース行文字列=ソース行文字列.Trim()
□ソース行.オリジナルインデント=ソース行.オリジナルソース文字列.Substring(0,ソース行.オリジナルソース文字列.Length-ソース行文字列.Length)
□ソース行.ノード種類=ソース行ノードを判定する(ref ソース行文字列)//ノード種類を判定する
◇ソース行.ノード種類==ノード種類.例外 の場合
コンソール.一行表示する(ソース行.オリジナルソース文字列)
□戻り値を 返す
◇ここまで
□ソース行.行文字列=ソース行文字列
□ソース行リスト.追加(ソース行)
〇ここまで
□ret=0//ここまで進行したら成功
□戻り値を 返す
△
トランスコンパイルする
第1引数の「ソース行リスト」はノードカテゴライズするでカテゴライズされたソース行型のリストで、これをもう一度分析して、第3引数の名称辞書を使ってターゲット言語Mindの単語に置換してから、第2引数のターゲットソース行型のリストを返します。
コード出力する
ターゲットソース行リストを出力しますが、ソース行リストから元のインデントを復元しています。ユージングリストの事前出力はいまのところありません。
/**
* codeOutput
* @param ターゲットソース行リスト targetSourcesLineList
* @param ソース行リスト sourcesLineList
*/
▽static コード出力するする(リスト型<ターゲットソース行型> ターゲットソース行リスト,リスト型<ソース行型> ソース行リスト)
〇i=0,i<ターゲットソース行リスト.Count,i++ 繰り返す
・ターゲットソース =ターゲットソース行リスト.ElementAt(i)
□コンソール.表示する(オリジナルのインデントを取得する(ソース行リスト,ターゲットソース.ソー0ス行番号))
□コンソール.一行表示する(ターゲットソース.ターゲット行文字列)
〇ここまで
△
支援関数
ノードカテゴライズする から呼ばれる支援関数
ソース行ノードを判定する
ソース行のノードを判定して、ノード種類列挙体の特定値を返します。
この関数のC#をターゲットとしたクラスTargetCsとの差異はありません。詳細はお手数ですが内部設計(ターゲット言語C#)をご参照ください。
トランスコンパイルする から呼ばれる支援関数
ターゲットソース行を生成する
/**
* generateTargetSouceLine
* @param ソース行 sourceLines
* @param ターゲットソース行リスト targetSourcesLineList
* @param ノード種類 nodeKind
* @param 名称辞書 nameDictionary
* @param ターゲットソース行番号 targetSouceLineNumber
*/
▽static int ターゲットソース行を生成する(ソース行型 ソース行,リスト型<ターゲットソース行型> ターゲットソース行リスト,ノード種類列挙体 ノード種類,辞書型<string, string> 名称辞書,ref int ターゲットソース行番号)
・int 戻り値 = 1
・ターゲットソース行 =new ターゲットソース行型
・ソース行文字列=ソース行.行文字列 ?? ""
◇ノード種類 で分岐する
◇ノード種類列挙体.定義開始//定義開始
□ターゲットソース行番号+=1
□ターゲットソース行.ソース行番号=ソース行.ソース行番号
□ターゲットソース行.ターゲット行文字列=名称辞書でターゲットソース行を置換する(名称辞書,ソース行文字列)+"とは"//名称辞書で置換する+"とは"
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行リスト.追加(ターゲットソース行)
□脱出する
◇ノード種類列挙体.定義終了//定義終了
□脱出する
◇ノード種類列挙体.引用定義終了//引用定義終了
□脱出する
◇ノード種類列挙体.宣言開始//宣言開始
◇ノード種類列挙体.実行開始//実行開始
□ターゲットソース行番号+=1
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行.ターゲット行文字列=名称辞書でターゲットソース行を置換する(名称辞書,ソース行文字列)+"。"//名称辞書で置換し実行文終端+"。"を追加
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行リスト.追加(ターゲットソース行)
□脱出する
◇ノード種類列挙体.引用定義開始//引用定義開始
□脱出する
◇ノード種類列挙体.単行備考開始//単行備考開始
□ターゲットソース行番号+=1
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行.ターゲット行文字列=ソース行.行文字列.置換(ソース行のノード種類.単行備考開始,ターゲットソース行のノード種類.単行備考開始)
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行リスト.追加(ターゲットソース行)
□脱出する
◇ノード種類列挙体.JavaDoc備考開始//JavaDoc備考開始
□ターゲットソース行番号+=1
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行.ターゲット行文字列=ソース行.行文字列.置換(ソース行のノード種類.JavaDoc備考開始,ターゲットソース行のノード種類.JavaDoc備考開始)
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行リスト.追加(ターゲットソース行)
□脱出する
◇ノード種類列挙体.JavaDoc備考途中//JavaDoc備考途中
□ターゲットソース行番号+=1
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行.ターゲット行文字列= ソース行.行文字列.置換(ソース行のノード種類.JavaDoc備考途中,ターゲットソース行のノード種類.JavaDoc備考途中)
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行リスト.追加(ターゲットソース行)
□脱出する
◇ノード種類列挙体.JavaDoc備考変数//JavaDoc備考変数
□ターゲットソース行番号+=1
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行.ターゲット行文字列= ソース行.行文字列.置換(ソース行のノード種類.JavaDoc備考変数,ターゲットソース行のノード種類.JavaDoc備考変数)
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行リスト.追加(ターゲットソース行)
□脱出する
◇ノード種類列挙体.JavaDoc備考終了//JavaDoc備考終了
□ターゲットソース行番号+=1
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行.ターゲット行文字列=ソース行.行文字列.置換(ソース行のノード種類.JavaDoc備考終了,ターゲットソース行のノード種類.JavaDoc備考終了)
□ターゲットソース行.ターゲットソース行番号=ターゲットソース行番号
□ターゲットソース行リスト.追加(ターゲットソース行)
□脱出する
//行レベルカテゴライズ済のため例外は生じない
◇ここまで
□ret=0//ここまで進行したら成功
□戻り値を 返す
△
名称辞書でターゲットソース行を置換する
支援関数のC#をターゲットとしたクラスTargetCsとの差異はありません。詳細はお手数ですが内部設計(ターゲット言語C#)をご参照ください。
引数ブロックを置換する
Mindの引数の語順を置換する処理です。
/**
* nameDictionaryReplase
* @param 名称辞書 nameDictionary
* @param ソース行文字列 sourceLineString
*/
▽static string 引数ブロックを置換する(string ソース行文字列)
・temp引数ブロック=""
・tempソース=""
・pos=ソース行文字列.IndexOf(ソース行のノード種類.引数開始)
◇pos>0 の場合//かっこが出現した場合
tempソース=ソース行文字列.置換(ソース行文字列.Substring(pos),"").Trim()//かっこ開始から右側は除去
temp引数ブロック=ソース行文字列.Substring(pos);
temp引数ブロック=temp引数ブロック.置換(ソース行のノード種類.引数開始,"").置換(ソース行のノード種類.引数終了,"")
temp引数ブロック="「"+temp引数ブロック.置換("\"","")+"」を"
ソース行文字列=temp引数ブロック+" "+tempソース
◇ここまで
□ソース行文字列を 返す
△
参考リンク