言語としての Vnano

ここでは、Vnano のスクリプトエンジン上で動作する簡易スクリプト言語の、主な文法・仕様を、 短いサンプルコードを上げながら列挙的に解説します。なお、このスクリプト言語の名前も「 Vnano 」です。

※ このページの内容は、他のプログラミング言語をある程度扱われている方を想定したものになっています。 プログラミングに触れるのがはじめての方は、代わりに スタートアップガイド をご参照ください。
- 目次 -

Vnano とは

Vnano (VCSSL nano) は、C言語系の文法を持つ、シンプルなプログラミング言語です。 特に、アプリケーション組み込み用途に焦点を絞った言語であるため、 一般的なプログラミング言語と比べると、用途的に必要性の低い機能は大幅に削られています。 これはスクリプトエンジンの実装規模をコンパクトに抑える事で、 機能性よりもカスタマイズ性や保守性、セキュリティ、および移植性などを優先的に高めるためです。

このページ内のサンプルコードの実行方法

このページ内のサンプルコードを実行するには、まずコード内容を適当なテキストエディタに貼り付けて、拡張子「 .vnano 」を付けて保存してください。 するとVnanoのスクリプトファイルになります。それを実行するには、下記の通り複数の方法があります。用途に併せて選んでください。

VCSSLランタイムで実行する

最も簡単なのは、VCSSLランタイムをダウンロードして起動 し、保存したVnanoのスクリプトファイルを選んで実行する事です。 Vnano の文法/機能は VCSSL のサブセットなので、VCSSLランタイム上で実行可能で、その場合は標準プラグインの機能も全て利用できます。 コマンドラインから vcssl コマンドで実行する事もできます。

RINPnで実行する

他にも、プログラム関数電卓ソフト RINPn (りんぷん) のユーザーの方は、RINPn 上で簡単に実行できます。 具体的には、RINPn のフォルダ内に Vnano のスクリプトを置いて、RINPn でそのファイル名を入力するだけです。 別の場所にあるスクリプトも、ファイルパスを入力して実行できます。標準プラグインの全機能も利用できます。

Windows 10 では、Shiftキーを押しながらファイルを右クリックすると「パスのコピー」メニューが出現して便利です。Windows 11 では通常の右クリックメニュー内にあります。

Vnano Engine で直接実行する

最後の方法は、Vnano のスクリプトエンジンである Vnano Engine の コマンドラインモード で実行する事です。 これは、各種の開発用オプションが利用できるため、Vnano を用いるアプリの開発者の方に向いた方法です。 半面、基礎的な機能群( print 関数なども )を提供する標準プラグインがデフォルトでは同梱されていないため、あらかじめ導入する必要があります。 方法は コマンドラインモードの説明欄 に記載されています。

コードの実行方法については以上です。 それでは、実際にサンプルコードを挙げながら、Vnano の文法・仕様の説明に入りましょう。

データ型

Vnano は、データ型として int (=long)、float (=double)、bool、および string 型のみをサポートしています。

データ型名 説明
int (or long) 64ビット精度符号付き整数型
float (or double) 64ビット精度浮動小数点数型
bool 論理型(真偽型)
string 文字列型

上記以外の基本データ型や、ポインタ、構造体、およびクラスなどはサポートされません。 一方で、上記の表にあるデータ型の配列型はサポートされており、C言語系の記法で使用できます。

ただし、Vnano(および VCSSL)における配列は、ポインタや参照型ではなく、値型として振舞う事に注意してください。 配列の代入演算(=)も、参照の代入ではなく、全要素値のコピー代入になります。 文字列についても同様で、Vnanoで文字列を扱う string 型は、参照型ではなく値型として振舞います。

つまるところ、Vnano に参照型は存在せず、全てのデータ型は値型になっています。 これにより、Vnanoのスクリプトエンジンではガベージコレクション(GC)を省略しています。

なお、配列に、要素数の異なる配列が代入される場合には、過不足なく全要素のコピーを行うために、 コピー先(代入演算子「=」の左辺)の配列のメモリー領域が自動で再確保され、コピー元(右辺)と同じ要素数になるように調整されます。

変数宣言文

以下のように、C言語系の表記で変数宣言文を記述できます。

スカラ変数の宣言

以下は、スカラ変数(配列ではない普通の変数)を宣言する例のコードです:

lang/DeclVarScalar.vnano

このスクリプトコードを実行すると、実行結果は:

1
2.3
true
Hello, World !

一方でVnanoでは、以下のように一つの文の中で複数の変数を宣言する事はできません:

lang/DeclVarScalarInvalid.vnano

配列の宣言

配列は以下のように宣言して使用できます:

lang/DeclVarArray.vnano

このコードを実行すると、実行結果は:

123

一方でVnanoでは、以下のような配列初期化子は使用できません:

lang/DeclVarArrayInvalid.vnano

制御文

C言語系の制御文の中で、Vnano では if / else / for / while / break / continue 文がサポートされています。

if 文と else 文

以下は if 文と else 文の使用例です:

lang/ControlIfElse.vnano

実行結果は:

x is 1.

ところでVnanoでは、原則として、if / else / for / while 文の直後にはブロック文 {...} が続かなければいけません。 従って、以下のように if 文の後に、波括弧 { } で囲まれていない単文を記述する事はできません:

lang/ControlIfInvalid.vnano

ただし、else 文の直後に if 文が続く( いわゆる else if の )場合のみ、特例的に else 文の直後にブロック文が無くても大丈夫です。

for 文

以下は for 文の使用例です:

lang/ControlFor.vnano

ここでも波括弧 { } は省略できない事に注意してください。実行結果は:

i=1
i=2
i=3
i=4
i=5

while 文

以下は while 文の使用例です:

lang/ControlWhile.vnano

ここでも波括弧 { } は省略できない事に注意してください。実行結果は:

a=500
a=377
a=254
a=131
a=8

break 文

以下は break 文の使用例です:

lang/ControlBreak.vnano

実行結果は:

i=1
i=2
i=3

continue 文

以下は continue 文の使用例です:

lang/ControlContinue.vnano

実行結果は:

i=1
i=2
i=4
i=5
i=7
i=8
i=10

式の構文要素

式は、演算処理などを記述するための、演算子とオペランドおよび括弧 ( ) で構成される一連のトークン(字句)列です。 単独でも「;」を付ければ「式文」として文となり得ますし、加えて if 文の条件式など、他の種類の文の構成要素にもなります。

ここで演算子とは、要するに演算を行う記号( + や - など)の事です。オペランドとは、 演算子にとっての演算対象の事で、典型例はリテラル(1や2.3など)や識別子(変数名など)です。 ただしオペランドには、さらに演算子を含む部分式が入り得ます。

このような文法的な説明だとややこしいですが、具体例としては:

lang/ExprExample.vnano

のようなものが式です。

上の式において、 + と * は演算子、x と 2 と 3 はオペランド、そして ( ) は括弧です。 ただし演算子「 * 」にとっては、部分式 (x + 2) もまとめて1つのオペランドと見なせるため、 この呼び方は場面によっては少しあいまいです。 そこで x と 2 と 3 のように、部分式ではない末端層のオペランドの事を、特にリーフオペランドとも呼びます。

なお、VnanoではC言語と同様、代入の記号「 = 」も演算子なので、以下の内容も式になります:

lang/ExprExampleAssign.vnano

演算子

Vnano でサポートされている演算子は、以下の一覧の通りです。なお、優先度の値は、小さい方が高い優先度になります。

演算子 優先度 構文 結合性 オペランドの型 演算結果の値の型
関数コールの
( ... , ... , ... )
1000 多項
(multiary)
関数による 関数による
配列参照の
[ ... ][ ... ][ ... ]
1000 多項 int 配列による
++(後置インクリメント) 1000 後置 int int
--(後置デクリメント) 1000 後置 int int
++(前置インクリメント) 2000 前置 int int
--(前置デクリメント) 2000 前置 int int
+(単項プラス) 2000 前置 int int
-(単項マイナス) 2000 前置 int int
! 2000 前置 bool bool
キャストの
(...)
2000 前置 任意 記述による
* 3000 二項 int, float int, float ※次表参照
/ 3000 二項 int, float int, float
% 3000 二項 int, float int, float
+ 3100 二項 int, float, string int, float, string
- 3100 二項 int, float int, float
< 4000 二項 int, float bool
<= 4000 二項 int, float bool
> 4000 二項 int, float bool
>= 4000 二項 int, float bool
== 4100 二項 全て bool
!= 4100 二項 全て bool
&& 5000 二項 bool bool
|| 5100 二項 bool bool
= 6000 二項 任意 左辺の型
*= 6000 二項 int, float int, float
/= 6000 二項 int, float int, float
%= 6000 二項 int, float int, float
+= 6000 二項 int, float, string int, float, string
-= 6000 二項 int, float int, float

ここで二項の算術演算子(*, /, %, +, -)および算術複合代入演算子(*=, /=, %=, +=, -=)における値の型(演算された値のデータ型)は、以下の表の通りに決定されます:

オペランドAの型 オペランドBの型 算術系の演算結果の値の型
int int int
int float float
int string string ( +, += のみ演算可能 )
float int float
float float float
float string string ( +, += のみ演算可能 )
string int string ( +, += のみ演算可能 )
string float string ( +, += のみ演算可能 )
string string string ( +, += のみ演算可能 )

上の表において、右と左のどちらのオペランドをオペランドA(またはB)に選んでも構いません。

関数

Vnanoのスクリプトコード内で、C言語系の記法で関数を宣言し、呼び出す事ができます。 ただし、Vnanoのスクリプトエンジンでは、ローカル変数が非常に単純な仕組みで実装されているため、関数の再帰呼び出しには対応していません。

なお、Vnano では、プラグインによってスクリプトエンジン外部から提供される関数を「 外部関数 」と呼ぶため、 それに対する呼び方として、ここで扱うようにスクリプト内で宣言された関数の事を「 内部関数 」とも呼びます。

スカラを引数や戻り値とする関数

以下は、スカラ変数(配列ではない普通の変数)を引数や戻り値とする関数のコード例です:

lang/FunctionScalar.vnano

このコードをで実行すると、実行結果は:

3

配列を引数や戻り値とする関数

配列を引数や戻り値にしたい場合の例は、以下の通りです:

lang/FunctionArray.vnano

実行結果は:

z[0]=3
z[1]=5
z[2]=7

なお、データ型の項目でも触れた通り、 Vnano(および VCSSL)における配列は、ポインタや参照型ではなく、値型として振舞う事に注意してください。

この事により、配列の引数/戻り値の受け渡しは、参照の代入ではなく、全要素値のコピー代入によって行われます。 その際、要素数の異なる配列がコピーされる場合には、過不足なく全要素のコピーを行うために、コピー先(受け取り側) の配列のメモリー領域が自動で再確保され、 コピー元と同じ要素数になるように調整されます。

従って上記のコードでは、いくつかの場所で、配列宣言時に要素数を指定するのを省略しています ( "int a[]"、 "int b[]"、 および "int z[] = fun(x, y, 3)" の箇所 )。

仮引数と実引数

以下の例の「 a 」のように、関数側で宣言されている引数の事を「仮引数」と呼びます。 それに対して、以下の例の「 x 」のように、呼び出し元から関数に渡している引数の事を「実引数」と呼びます。

lang/FunctionParamsAndArgs.vnano

引数の値渡し

デフォルトでは、関数内における仮引数の値の変更は、呼び出し元の実引数の値には反映されません。例えば:

lang/FunctionCallByValue.vnano

実行結果は:

x = 0
y[0] = 0
y[1] = 0
y[2] = 0

上の例の通り、関数「 fun 」内で仮引数「 a 」と「 b 」の値を変更していますが、 呼び出し元の実引数「 x 」と「 y 」の値は変化していない事がわかります。

これは、デフォルトでの関数の引数の受け渡しが、単純に「 実引数の値を仮引数にコピーする 」事によって行われるからです。 このような引数の渡し方を「値渡し」と呼びます。

引数の参照渡し

関数内での仮引数の値の変更を、呼び出し元の実引数の値に反映させたい場合は、仮引数の宣言において、 名前の前に「 & 」記号を付加してください。例えば:

lang/FunctionCallByRef.vnano

実行結果は:

x = 2
y[0] = 10
y[1] = 11
y[2] = 12

上の例の通り、関数「 fun 」内で仮引数「 a 」と「 b 」の値を変更した結果、 呼び出し元の実引数「 x 」と「 y 」も、同じ値に変化した事がわかります。

これは、「 & 」を付けて宣言された仮引数のデータへのメモリ参照が、実引数のデータへのメモリ参照と共有されるためです。 このような引数の渡し方を「参照渡し」と呼びます。

import / include 宣言

Vnano の親言語(フルセット版)である VCSSL では、 スクリプト内で import 宣言や include 宣言を記述する事により、ライブラリを読み込む事ができます。例えば:

lang/Import.vnano

などと記述すると、import 宣言の箇所で ExampleLibrary ライブラリが自動で読み込まれ、 それに属する exampleFunction 関数がコード内で使えるようになる、といった具合です。

一方で、アプリケーション組み込み用に機能が絞られた Vnano では、スクリプト側からのライブラリの読み込み指示や、 プラグインの接続指示などはできません。 使用する全てのライブラリやプラグインは、アプリケーション側や設定ファイル等で導入/指定しておく必要があります( Vnano Engine の主な機能と用例 参照 )。 これは、組み込み用途でのセキュリティ等を考慮した仕様です。

しかしながら、import / include 宣言自体は、Vnano のスクリプト内でも記述できます。 Vnano では、import / include 宣言があると、「 指定されているライブラリ(または同名の名前空間を提供するプラグイン)が読み込み済みかどうか 」を確認し、無ければその旨のエラーメッセージを表示してくれます。

例えば、上のサンプルコードを Vnano スクリプトとして実行すると、 ExampleLibrary という名前のライブラリ(または「 ExampleLibrary 」名前空間を提供するプラグイン)が未読み込みなら:

このスクリプトは「 ExampleLibrary 」の機能群を使用しますが、それを提供するライブラリ/プラグインが読み込まれていません。 ライブラリ/プラグインの読み込み設定を確認してください。

というエラーが表示されます。もし「 import ExampleLibrary; 」の行が無ければ:

存在しない関数「 exampleFunction(float) 」を呼び出しています。

というエラーが表示されます。 前者のエラーメッセージの方が、より根本的な原因を伝えていて、どうすればいいかも分かりやすい内容です。

import / include 宣言で指定したライブラリやプラグインが、読み込み済みで利用可能な場合には、何も起こりません。 従って、Vnano では書いても書かなくても構わないのですが、書いておくと、一応は上記のような利点があります。