はじめに
2023年の1月下旬よりQiita様へ参加させていただき、自作言語 Re:Mind(リマインド)の企画趣旨をQiita内の記事でご説明させていただいております。仕様面でも、いったん2022 Lv1としてとりまとめにいたっておりますが、まだ実装がなにもないので、そろそろ試作に着手しようかと考えております。(と書いたのが2023年の4月下旬であっと言っている間に1ヶ月以上たってしまいました。完成にはほど遠いのでいったん内部設計の冒頭部分を投稿します。)
この記事内容の作業目的
自作言語Re:Mindの構文仕様策定の節目として、昨年(2022年末)までに考案した内容プラスアルファを2022 Lv1としてまとめまていますが、その一部仕様を試作してみます。試作にあたって内部構想を、日本語ロジック仕様記述言語 Re:Mindで記述しておきます。本試作範囲は策定仕様のごくごく一部です。ターゲット言語は一部のみ対応、言語の対応ソース構文も超限定的です。
この記事内容の保証
※この記事には仕様的な情報が含まれます。自作言語はまだ設計開発中のため、発案者による実装は存在しません。本記事に開示された仕様は次バージョンの仕様においては予告なく変更される場合があります。また、このバージョンの仕様内で補足や追加がなされる場合があります。
補足
日本語ロジック仕様記述言語 Re:Mind(リマインド)はオープンな設計言語仕様で、どなたでもこの記法を使いロジックを記述することができます。
制御構文の開始シンボルとして、◇、〇、□、・などの全角記号を用い、箇条書きされた日本文としての体裁を日本語トランスコンパイラ言語 Re:Mindの構文と共有しています。◇は分岐構文、〇はループ構文の開始と終了を表し、ここは日本語キーワード表記がなくてもフロー図の表現に慣れている方が直感的に認識できることを考慮しています。設計言語の場合、実装には直結しないので、いくぶん曖昧な表現が可能です。
日本語トランスコンパイラ言語 Re:Mind(リマインド)はオープンな実装言語仕様で、どなたでもコンパイラ・トランスコンパイラを実装することができます。
試作概要・試作目的
本試作では日本語トランスコンパイラ言語 Re:Mind(リマインド)の原型を検証し、いろいろな問題点を洗い出します。ターゲット言語のソースコード入力は実装言語のソースコードのインライン記述にとどまります。試作範囲は段階的に拡張してまいります。
本試作の設計言語
・ロジック仕様記述言語 Re:Mind
本試作の実装言語
・C#
コンソールアプリケーションとして実装します。
本試作のターゲット言語
・C#
・Java
・Mind
・想定稼働OS Windows
本試作のターゲット言語の対応仕様範囲
"Hello World!"がコンソール出力されるソースコードを入力して(インライン・リテラルコーディング)、ターゲット言語のソースコードを(双方だらだらと)コンソール出力する。(どちらを出力するかは起動パラメータにする。)
お題のターゲット言語出力ソースコード
C#
using System;
namespace HelloWorld
{
/// <summary>プログラム型</summary>
public class Program
{
/// <summary>メイン</summary>
/// <param name="args">引数</param>
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Java
package HelloWorld
import java.lang.System;
/**
* プログラム型
*/
public class Program
{
/**
* メイン
* @param args 引数
*/
public static void main(String[] args)
{
out.println("Hello World!");
}
}
Mind
メインとは
「Hello World!」を 一行表示する。
お題のトランスコンパイラ言語 Re:Mind入力ソースコード(ターゲット言語依存)
C#
既存ライブラリを引用する記述は毎回書く必要をなくす想定ですが、まだライブラリのサンプルコードは皆無のため、本試作ではライブラリ引用ソースコードも明確に書いてもらう想定です。Systemは既定のUsing文として明示不要であっても既定でない今後のUsing文のため、出力するとしています。
▽名前空間 HelloWorld
/**
* Program
*/
▽public クラス プログラム型
/**
* Main
* @param 引数 args
*/
▽static void メイン(string[] 引数)
□コンソール.一行表示する("Hello World!")
△
△
△
■インポートする System
▼public static class コンソール Console
▼public static 一行表示する(string? value)
WriteLine (string? value)
▲
▲
2023/07/29 訂正
メインの戻り値はvoidを明示に訂正しています。
■usingは■インポートするに訂正しています。
Java
Javaの場合、コンソールライブラリがSystemクラスのメンバクラスのPrintStreamのインスタンスを介して呼び出すというところが、ちょっとトランスコンパイラ的にめんどうなところです。
▽パッケージ HelloWorld
/**
* Program
*/
▽public クラス プログラム型
/**
* main
* @param 引数 args
*/
▽public static void メイン(string[] 引数)
□コンソール.一行表示する("Hello World!")
△
△
△
■import java.lang.System
▼public class コンソール PrintStream System.out
▼public static 一行表示する(string value)
println (String value)
▲
▲
2023/07/29 訂正
メインの戻り値はvoidを明示に訂正しています。
■importは■インポートするに訂正しています。
PrintStreamのインスタンス名をoutではなくSystem.outに訂正しています。
Mind
Mindの場合もユーザー拡張単語を引用する場合は同様の記述が必要となる想定ですが、今回は「一行表示」は予約語想定で、ユーザー拡張単語の引用記述はオミットします。
▽メイン
□コンソールへ一行表示する("Hello World!")
△
▽コンソールへ一行表示する(string value)
□一行表示する(string value)
△
本試作の内部設計の妥当性
試作のため本実装には採用されない可能性があります。また、試作品の実装段階でフィードバック訂正される場合があります。トークナイザとコードジェネレータはありますが、パーサの設計(や他もすべて)は現時点で適当です。本試作では実装していませんが、ソースコードを生成したらターゲット言語のコンパイラにいったん投げて、文法チェックはターゲット言語コンパイラにお任せを想定しています。
クラス構成
本試作では下記のクラス構成で実装します。
・プログラム型 Program C#コンソールアプリケーションのMain関数の既定クラス
・ターゲットC#型 TargetCs ターゲット言語C#のトークナイザとコードジェネレータを保持します。
・ターゲットJava型 TargetJava ターゲット言語Javaのトークナイザとコードジェネレータを保持します。
・ターゲットMind型 TargetMind ターゲット言語Mindのトークナイザとコードジェネレータを保持します。
クラス内のメソッドは当面はstaticで実装して、インスタンス化しないで実行できるようにしておきます。
試作実装言語C#のソースファイル分割
上記のクラス構成に対応して下記のソースファイル分割を行います。ソースコードが多くなった場合はさらに分割しますが、今回の試作は下記の状態とします。
・main.cs: main関数
・TargetCs.cs: TargetCsクラスを実装
・TargetJava.cs: TargetJavaクラスを実装
・TargetMind.cs: TargetMindクラスを実装
クラス・メンバ構成概要
クラス・メンバ構成概要の概要です。▽△は実際の定義(を割愛していること)を意味します。実際の定義は後述します。
クラスProgram
▽名前空間 ReMind
▽class プログラム型
・定数文字列 入力ソースサンプルC#
・定数文字列 入力ソースサンプルJava
・定数文字列 入力ソースサンプルMind
▽列挙体 ターゲット言語
△
▽int メイン(string[] 引数)
△
△
△
クラスTargetCs
ターゲット言語C#のトークナイザとコードジェネレータを保持するクラス。
▽名前空間 ReMind
▽クラス ターゲットC#型
▽クラス ソース行型
△
▽クラス トークン型
△
▽列挙体 ノード種類列挙体
△
▽連想配列 トークン辞書
△
// ソース行のノード種類
▽クラス ソース行のノード種類
△
// ターゲットソース行のノード種類
▽クラス ターゲットソース行のノード種類
△
// サブメイン
▽static int サブメイン(string 引数)
△
// ソース入力文字列を改行で分割してノードカテゴライズされたソース行リストを参照で返す
▽static int ノードカテゴライズする(string 入力文字列,リスト型<ソース行型> ソース行リスト )
△
// ノードカテゴライズされたソース行リストをトランスコンパイルしてターゲットソース行リストを参照で返す
▽static int トランスコンパイルする(ソース行型 ソース行,リスト型<ソース行型> ターゲットソース行リスト)
△
// ターゲットソース行をコンソール出力する
▽static int コード生成する(リスト型<ターゲットソース行型> ターゲットソース行リスト)
△
△
△
クラスTargetJava
ターゲット言語Javaのトークナイザとコードジェネレータを保持するクラス。
▽名前空間 ReMind
▽クラス ターゲットJava型
▽クラス ソース行型
△
▽クラス トークン型
△
▽列挙体 ノード種類列挙体
△
▽連想配列 トークン辞書
△
// ソース行のノード種類
▽クラス ソース行のノード種類
△
// ターゲットソース行のノード種類
▽クラス ターゲットソース行のノード種類
△
// サブメイン
▽static int サブメイン(string 引数)
△
// ソース入力文字列を改行で分割してノードカテゴライズされたソース行リストを参照で返す
▽static int ノードカテゴライズする(string 入力文字列,リスト型<ソース行型> ソース行リスト )
△
// ノードカテゴライズされたソース行リストをトランスコンパイルしてターゲットソース行リストを参照で返す
▽static int トランスコンパイルする(ソース行型 ソース行,リスト型<ソース行型> ターゲットソース行リスト)
△
// ターゲットソース行をコンソール出力する
▽static int コード生成する(リスト型<ターゲットソース行型> ターゲットソース行リスト)
△
△
△
クラスTargetMind
ターゲット言語Mindのトークナイザとコードジェネレータを保持するクラス。
ターゲット言語別に同じクラス名の内部クラスを保持しますが、いまのところ共通クラスの継承などは想定していません。
▽名前空間 ReMind
▽クラス ターゲットMind型
▽クラス ソース行型
△
▽クラス トークン型
△
▽列挙体 トークン種類
△
▽連想配列 トークン辞書
△
// ソース行のノード種類
▽クラス ソース行のノード種類
△
// ターゲットソース行のノード種類
▽クラス ターゲットソース行のノード種類
△
// サブメイン
▽static int サブメイン(string 引数)
△
// ソース入力文字列を改行で分割してノードカテゴライズされたソース行リストを参照で返す
▽static int ノードカテゴライズする(string 入力文字列,リスト型<ソース行型> ソース行リスト )
△
// ノードカテゴライズされたソース行リストをトランスコンパイルしてターゲットソース行リストを参照で返す
▽static int トランスコンパイルする(ソース行型 ソース行,リスト型<ソース行型> ターゲットソース行リスト)
△
// ターゲットソース行をコンソール出力する
▽static コード生成する(リスト型<ターゲットソース行型> ターゲットソース行リスト)
△
△
△
クラス・メンバの構成
プログラミング言語っぽいロジック仕様記述言語で書いておりますと、実装と内部詳細設計の境があいまいですが、プログラミング言語の記述ほど厳密ではありません。
クラスProgram
列挙体、クラス(変数のみ)の宣言
対応するターゲット言語を記述します。
▽クラス ターゲット言語
・定数文字列 C# = "cs" // C#
・定数文字列 Java = "java" // Java
・定数文字列 Mind = "mind" // Mind
△
Main
ターゲット言語が追加された場合は、対応クラスとサブメインのcaseを追加していく構造としていますが、実装言語をターゲット言語に寄せる場合はまた構造が異なっていく想定です。
// メイン
▽int メイン(string[] 引数)
・int 戻り値 = 1;
◇(引数.Length != 1) の場合
□コンソール出力( "引数の個数が正しくありません\n");
□戻り値 を返す
◇ここまで
◇(引数) で分岐する
◇ターゲット言語.C#
□戻り値=ターゲットC#型.サブメイン(入力ソースサンプルC#)
◇ターゲット言語.Java
□戻り値=ターゲットJava型.サブメイン(入力ソースサンプルJava)
◇ターゲット言語.Mind
□戻り値=ターゲットMind型.サブメイン(入力ソースサンプルMind)
◇既定
□コンソール出力( "引数が正しくありません\n");
□戻り値 を返す
◇ここまで
□戻り値 を返す
△
設計言語としてはswitch文がフォークスルーするかどうかはあまり気にしていないためブレーク文行を明示していませんが、同じ処理を通過させるためフォークスルーさせたい場合はケース文行が連続したらフォークスルーするとして、ケース文行が連続しない場合はフォークスルー無の想定です。(上記の表記の問題で、ターゲットの仕様とは直接関係ありません。)
クラスTargetCs
ターゲット言語C#のトークナイザとコードジェネレータを保持するクラスの実装です。
列挙体、クラス(変数のみ)の宣言
// ソース行の格納情報
▽クラス ソース行型
・string 行文字列
・string ノードタイプ
・int ソース行番号
・リスト型<int> ターゲット行番号リスト
・リスト型<トークン型> ソース行トークン構成
△
▽クラス トークン型
・トークン種類 種類
・int 次索引 // Next token
・int 値 // 種類が整数リテラルの場合、その数値
・string? 文字列 // 種類が識別子・文字列リテラルの場合、その値
・int 長さ // Token length
△
▽列挙体 トークン種類
先端 // 入力の開始を表すトークン、すなわち、ソース行のノード
予約語, // 記号
識別子, // 識別子
整数リテラル, // 整数リテラル
文字列リテラル, // 文字列リテラル
△
▽連想配列 トークン辞書
・key string ソース識別子名
・value string ターゲット識別子名
△
▽クラス ターゲットソース行型
・string ターゲット行文字列
・int ターゲットソース行番号
・int ソースコード行番号
△
▽列挙体 ノード種類
定義開始, // ▽下三角
定義終了, // △上三角
引数開始, // かっこ
引数終了, // かっこ
宣言開始, // ・中点
実行開始, // □白四角
引用開始, // ■黒四角
引用定義開始, // ▼黒下三角
引用定義終了, // ▲黒三角
単行備考開始, // //ダブルスラッシュ
JavaDoc備考開始, // /**
JavaDoc備考途中, // *
JavaDoc備考変数, // @param
JavaDoc備考終了 // **/
△
// ソース行のノード種類
▽クラス ソース行のノード種類
・定数文字列 定義開始記号 = "▽" // ▽下三角
・定数文字列 定義終了記号 = "△" // △上三角
・定数文字列 引数開始記号 = "(" // かっこ
・定数文字列 引数終了記号 = ")" // かっこ
・定数文字列 宣言開始記号 = "・" // ・中点
・定数文字列 実行開始記号 = "□" // □白四角
・定数文字列 引用開始記号 = "■" // ■黒四角
・定数文字列 引用定義開始記号 = "▼" // ▼黒下三角
・定数文字列 引用定義終了記号 = "▲" // ▲黒三角
・定数文字列 単行備考開始記号 = "//" // //ダブルスラッシュ
・定数文字列 JavaDoc備考開始記号 = "/**" // /**
・定数文字列 JavaDoc備考途中記号 = "*" // *
・定数文字列 JavaDoc備考変数記号 = "@param" // @param
・定数文字列 JavaDoc備考終了記号 = "**/" // **/
△
// ターゲットソース行のノード種類
▽クラス ターゲットソース行のノード種類
・定数文字列 定義開始記号 = "{" // {中かっこ
・定数文字列 定義終了記号 = "}" // }中かっこ
・定数文字列 引数開始記号 = "(" // かっこ
・定数文字列 引数終了記号 = ")" // かっこ
・定数文字列 宣言開始記号 = "" // 変換対象無は空列
・定数文字列 実行開始記号 = "" // 変換対象無は空列
・定数文字列 単行備考開始記号 = "//" // //ダブルスラッシュ
・定数文字列 CsDoc備考開始記号 = "///" // ///トリプルスラッシュ
・定数文字列 CsDoc備考途中記号 = "///" // ///トリプルスラッシュ
・定数文字列 CsDoc備考変数記号 = "$<param name=""{en}"">{jp}</param>" // @param
・定数文字列 CsDoc備考終了記号 = "///" // ///トリプルスラッシュ
△
この記述は近い将来修正される予定です。
SubMain
お手数ですがRe:Mindで内部設計する(ターゲット言語C#)を参照してください。
クラスTargetJava
お手数ですがRe:Mindで内部設計する(ターゲット言語Java)を参照してください。
クラスTargetMind
お手数ですがRe:Mindで内部設計する(ターゲット言語Mind)を参照してください。
ソースコード
GitHub準備中
備考
2023/07/29 試作品の再実行に伴い一部設計・仕様を訂正しています。
参考リンク