bpriver_chain 0.16.2
bpriver_chain: ^0.16.2 copied to clipboard
Implementation of chain language. Available as cui, argument parser or inter-appliation communication.
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);
// Don't forget to change the type, bool.
final yesOrNoResult = chain.getVarietyAsBool('yewOrNo');
if (yesOrNoResult is! Success<bool, ChainException>) return Failure(yesOrNoResult.asException, log);
// Safety means it will not fail.
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.
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 true ---remainingTaskList A B C.
Command name at the beginning.
bool is SingleHyphen.
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.