Metadata

We create an IDL called Protoconf to describe Excel's structure(metadata), based on Protobuf (proto3).

Notation

The syntax is specified using Extended Backus-Naur Form (EBNF).

Workbook -> Protoconf

Basic

workbook: (AliasTest)DemoTest, worksheet: (AliasActivity)DemoActivity

  • protoconf file name is alias_test.proto. If with no (), name will be demo_test.proto
  • configuration message name is AliasActivity. If with no (), name will be DemoActivity
  • list: [ELEM-TYPE]COLUMN-TYPE, COLUMN-TYPE is column type, ELEM-TYPE is message name and list prefix (must not conflict with the protobuf keyword).
  • map: map<KEY-TYPE,VALUE-TYPE>, KEY-TYPE must be scalar types, and VALUE-TYPE is message name and map prefix (must not conflict with build-in scalar type).
  • import message types: .TYPE, e.g.: .Item represents the message Item already defined in the same protobuf package, and should not redefine it.
  • well-known types
    • Timestamp: google.protobuf.Timestamp
    • Duration: google.protobuf.Duration
ActivityIDActivityNameActivityBeginTimeActivityDurationChapterIDChapterNameSectionIDSectionNameSectionItem1IdSectionItem1NumSectionItem2IdSectionItem2Num
map<uint32,Activity>stringtimestampdurationmap<uint32,Chapter>string[Section]uint32int32[.Item]int32int32int32int32
1activity12020-01-01 05:00:0072h1chapter11section11001110022
1activity12020-01-01 05:00:0072h1chapter12section21001110022
1activity12020-01-01 05:00:0072h2chapter21section11001110022
2activity22020-01-01 05:00:0072h3m0.5s1chapter11section11001110022
// common.proto
message Item {
  int32 id = 1 [(tableau.field).name = "Id"];
  int32 num= 2 [(tableau.field).name = "Num"];
}

Output without prefix

// demo_test.proto
import "common.proto"

message DemoActivity{
  map<uint32, Activity> activity_map = 1 [(key) = "ActivityID"];
  message Activity {
    uint32 id= 1 [(tableau.field).name = "ActivityID"];
    string name = 2 [(tableau.field).name = "ActivityName"];
    map<uint32, Chapter> chapter_map = 3 [(tableau.field).key = "ChapterID"];
  }
  message Chapter {
    uint32 id= 1 [(tableau.field).name = "ChapterID"];
    string name = 2 [(tableau.field).name = "ChapterName"];
    repeated Section section_list = 3 [(tableau.field).layout = LAYOUT_VERTICAL];
  }
  message Section {
    uint32 id= 1 [(tableau.field).name = "SectionID"];
    string name = 2 [(tableau.field).name = "SectionName"];
    repeated Item item_list = 3 [(tableau.field).name = "SectionItem"];
  }
}

Output with prefix

// demo_test.proto
message DemoActivity{
  map<uint32, Activity> activity_map = 1 [(key) = "ActivityID"];
  message Activity {
    uint32 activity_id= 1 [(tableau.field).name = "ActivityID"];
    string activity_name = 2 [(tableau.field).name = "ActivityName"];
    map<uint32, Chapter> chapter_map = 3 [(tableau.field).key = "ChapterID"];
  }
  message Chapter {
    uint32 chapter_id= 1 [(tableau.field).name = "ChapterID"];
    string chapter_name = 2 [(tableau.field).name = "ChapterName"];
    repeated Section section_list = 3 [(tableau.field).layout = LAYOUT_VERTICAL];
  }
  message Section {
    uint32 section_id= 1 [(tableau.field).name = "SectionID"];
    string section_name = 2 [(tableau.field).name = "SectionName"];
    repeated Item section_item_list = 3 [(tableau.field).name = "SectionItem"];
  }
}

Incell

workbook: (AliasTest)DemoTest, worksheet: (Env)Environment

IDNameIncellMessageIncellListIncellMapIncellMessageListIncellMessageMap
uint32string{int32 id,string desc,int32 value}Msg[]int32map<int32,string>[]{int32 id,string desc}Elemmap<int32,Value{int32 id,string desc}>
1Earth1,desc,1001,2,31:hello,2:world{1,hello},{2,world}1:{1,hello},2:{2,world}

IncellMessage

Syntax: TODO: EBNF Type: message type Value: comma seperated field values, e.g.: 1,desc,100 Rules:

Default TypeValue
int32can be parsed as number
stringcannot be parsed as number

IncellList

Syntax: []Type Type: any scalar type Value: comma seperated list items, e.g.: 1,2,3

IncellMap

Syntax: map<Type,Type> Type: any scalar type Value: comma seperated key-value pairs, and key-value is seperated by colon. e.g.: 1:hello,2:world

IncellMessageList

TODO…

IncellMessageMap

TODO…

Output

// demo_test.proto
message Env {
  uint32 ID = 1 [(tableau.field).name = "ID"];
  string name = 2 [(tableau.field).name = "Name"];
  Msg incell_message= 3 [(tableau.field).name = "IncellMessage"];
  repeated int32 incell_list= 4 [(tableau.field).name = "IncellList"];
  map<int32, string> incell_map = 5 [(tableau.field).name = "IncellMap"];
  repeated Elem incell_message_list= 6 [(tableau.field).name = "IncellMessageList"];
    map<int32, Value> incell_message_map = 7 [(tableau.field).name = "IncellMessageMap"];

    // defaut name: field + <tagid>
  message Msg {
    int32 id = 1;
    string desc= 2; 
    int32 value= 3;
  }
  message Elem {
    int32 id = 1;
    string desc= 2;
  }
  message Value {
    int32 id = 1;
    string desc= 2;
  }
}
  • Incell message: comma seperated sequence: {TYPE [NAME],TYPE [NAME]}, NAME is optional, and will be auto generated as field + <tagid> if not specified.
  • Incell list: []TYPE, TYPE must be scalar type.
  • Incell map: map[KEY]VALUE, KEY and VALUE must be scalar types.
  • Incell message list: []TYPE, TYPE must be message type.
  • Incell message map: map[KEY]VALUE, KEY is scalar, and VALUE must be message type.

Protoconf -> Workbook

TODO…