Chain Language
Chain is serial data description language.
Use
- cui
- argument parser
- inter-application communication
- serialize for non nest data
Use for cui and argument parser
import 'package:bpriver_chain/bpriver_chain.dart';
// This library is result type library.
// Chain depends on this library.
import 'package:bpriver_origin/logger_result.dart';
// This library is Option library.
// Chain depends on this library.
import 'package:bpriver_box/bpriver_box.dart';
// Define several exception class.
sealed class ExampleCommandException
implements
Exception
{
String get message;
}
final class ExampleCommandExceptionA
extends
ExampleCommandException
{
String get message => 'Command name is unknown.';
}
final class ExampleCommandExceptionB
extends
ExampleCommandException
{
String get message => 'Invalid Command name.';
}
final class ExampleCommandExceptionC
extends
ExampleCommandException
{
String get message => 'Others.';
}
// Define several sealed classes for commands.
// Sealed class allows for safe branching.
sealed class ExampleCommand
{
// Danger is the same as a general Result.
static Danger<ExampleCommand, ExampleCommandException> fromChain(Chain chain) {
late final Danger<ExampleCommand, ChainException> result;
// chain's head is the same as command name.
// Check if command name is entered.
// Some is a type that indicates that there was input.
final head = chain.head;
if (head is! Some<Head>) return Failure(ExampleCommandExceptionA());
// Safely retrieve command name from head.
switch (head.wrapped.name) {
// Write a class corresponding to each command name.
case 'version': result = Version.fromChain(chain);
case 'hello': result = Hello.fromChain(chain);
case 'goodbye': result = Goodbye.fromChain(chain);
case _: return Failure(ExampleCommandExceptionB());
}
if (result is! Success<ExampleCommand, ChainException>) return Failure(ExampleCommandExceptionC());
return Success(result);
}
}
// For example, the version command.
final class Version
extends
ExampleCommand
{
// Use it for ExampleCommand.fromChain()
// Other names besides fromChain() are also acceptable.
static Danger<Version, ChainException> fromChain(Chain chain) {
final result = VersionRequest();
return Success(result);
}
}
// For example, the hello xxx command.
final class Hello
extends
ExampleCommand
{
// String, bool, num, int, double and those lists are available.
final String name;
Hello(this.name);
// Use it for ExampleCommand.fromChain()
// Danger is the same as a general Result.
// Other names besides fromChain() are also acceptable.
static Danger<Hello, ChainException> fromChain(Chain chain) {
// Define the parameters required for this command.
// This is the specification for Hello command.
// Here too, we use the result type to safely retrieve the data.
final nameResult = chain.getVarietyAsString('name');
if (nameResult is! Success<String, ChainException>) return Failure(nameResult.asException);
final result = Hello(
nameResult.wrapped,
);
return Success(result);
}
}
// For example, the goodbye xxx command.
final class Goodbye
extends
ExampleCommand
{
final String name;
final bool yesOrNo;
final List<String> remainingTaskList;
Goodbye(this.name, this.yesOrNo, this.remainingTaskList);
static Danger<Hello, ChainException> fromChain(Chain chain) {
// You can set defaults.
// For example, Michael.
final nameResult = chain.getVarietyAsStringWithDefault('name', 'Michael');
if (nameResult is! Success<String, ChainException>) return Failure(nameResult.asException, log);
// Safety means it will not fail.
final yesOrNoResult = chain.isFlag('yewOrNo');
final remainingTaskListResult = chain.getVarietyEnumerationAsStringWithDefault('remainingTaskList', ['Saving the world from crisis', 'Max\'s walk']);
final result = Hello(
nameResult.wrapped,
yesOrNoResult.wrapped,
remainingTaskListResult.wrapped,
);
return Success(result, log);
}
}
// The process can be simplified by consolidating the processing appropriately into each class.
void main(List<String> arguments) {
// You can safely retrieve the data using the Result Type.
final chainResult = Chain.fromArguments(arguments);
if (chainResult is! Success<Chain, ChainException>) {
// Some kind of error handling.
return;
}
// The above process allowed us to safely retrieve chain instance.
final chain = chainResult.wrapped;
final commandResult = ExampleCommand.fromChain(chain);
if (commandResult is! Success<ExampleCommand, ExampleCommandException>) {
// Some kind of error handling.
print(commandResult.asException.message);
return;
}
final command = commandResult.wrapped;
// Write the corresponding code for each command.
switch (command) {
case Version():
print('1.0.0');
case Hello():
print('hello ${command.name}!');
case Goodbye():
switch (command.yesOrNo) {
case true:
print('goodbye ${command.name}!');
case false:
print('${command.name}! You still have work to do.');
for (final i in command.remainingTaskListResult) {
print(i);
}
}
}
}
For example, goodbye --name A -yesOrNo ---remainingTaskList A B C.
Command name at the beginning.
bool is SingleHyphen.
This type of specification is also possible.
-yesOrNo true, -yesOrNo false
String is DoubleHyphen.
List is TripleHyphen.
The above is just one example, and with some ingenuity, it could be made even more convenient.
データ型
Flag(二値型)
true or false の 二値 を扱う型.
Variety(多値型)
true or false を含む 1つの 多値 を扱う型.
VarietyEnumeration(多値列挙型)
複数の 多値型 を扱う型.
構文
Example - xxx ---a A AA AAA --b B -c true
xxx, ---a 'A' 'AA' 'AAA', --b 'B', -c true はそれぞれ Chain Element という.
chain 文の開始にある hyphen で始まらない Chain Element は Head といい ここでは xxx である.
---a 'A' 'AA' 'AAA', --b 'B', -c true はそれぞれ Body という.
---a 'A' 'AA' 'AAA' という 3つの hyphen で始まる Body を VarietyEnumeration という.
--b 'B' という 2つの hyphen で始まる Body を Variety という.
-c true という 1つの hyphen で始まる Body を Flag という.
---a の a, --b の b, -c の c, はそれぞれ名前を表す.
Head は必ず名前のみを持ち, 値を持たない.
Body は必ず名前を持ち, そのすぐ後ろに値を記述し 紐づけることができる.
Flag は 二値型 を, Variety は 多値型を, VarietyEnumeration は多値列挙型を, それぞれ紐づけることができる.
xxx, a, b, c, のように 1つの chain 文に存在する Chain Element の名前はすべて一意でなければならない.
区切り文字(Delimiter)は 1つ以上の連続する space である.
そのほかの white space である 改行 や tab は区切り文字として無効である.
Example - ---a A AA AAA
Head は省略可能である.
Example - ---a A 'A A' '' "" --b "A B C"
多値型を記述する際 space が値の中に含まれる場合 single quotation もしくは double quotation で囲むことができる.
また何も囲まないことで空文字の表現ができる.
a は A, A A, 空文字, 空文字, の計4つの値が紐づけられている.
Example - ---a 'A\'A' "B\"B"
, でエスケープが可能.
A'A, B"B, の値が紐づけられた a という名前の VarietyEnumeration.
Example - ---a 'A"A' "A'A"
異なる quotation にすれば エスケープ処理も不要.
A"A, A'A, の値が紐づけられた a という名前の VarietyEnumeration.
Example - '' ---a A AA AAA
この chain 文は無効.
空文字を Head にすることはできない.
Example - --b B -c true ---a A AA AAA -d false ---e E --f F
VarietyEnumeration, Variety, Flag, の配置順序に決まりはない.
また Head とは異なり それぞれ定義できる個数に制限はない.
ここでは下記の Chain Element が存在する.
A, AA, AAA, の値が紐づけられた a という名前の VarietyEnumeration.
B, の値が紐づけられた b という名前の Variety.
true, の値が紐づけられた c という名前の Flag.
false, の値が紐づけられた d という名前の Flag.
E, の値が紐づけられた e という名前の VarietyEnumeration.
F, の値が紐づけられた f という名前の Variety.
Example - ---a --b -c
VarietyEnumeration, Variety, Flag, は同様に紐づける値を省略することができる.
VarietyEnumeration の場合 空のリスト が紐づけられたものとして扱われる.
Variety, Flag, においては何も値が紐づけられていないものとして扱われる.
Example - ---a \-A --b \-\-\-\-AAAA
hyphen で開始する値を紐づけたい場合 \ で - をエスケープすることで値として扱われる.
-A, の値が紐づけられた a という名前の VarietyEnumeration.
----AAAA, の値が紐づけられた b という名前の Variety.
Example - ---a A AA
区切り文字の space は複数連続しても有効.
A, AA, の値が紐づけられた a という名前の VarietyEnumeration.