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 という.

---aa, --bb, -cc, はそれぞれ名前を表す.
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.

Libraries

bpriver_chain