keytab_token.dart 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import 'dart:math' show min;
  2. enum KeytabTokenType {
  3. keyDefine,
  4. keyboard,
  5. keyName,
  6. mode,
  7. modeStatus,
  8. colon,
  9. input,
  10. shortcut,
  11. }
  12. class KeytabToken {
  13. KeytabToken(this.type, this.value);
  14. final KeytabTokenType type;
  15. final String value;
  16. @override
  17. String toString() {
  18. return '$type<$value>';
  19. }
  20. }
  21. class LineReader {
  22. LineReader(this.line);
  23. final String line;
  24. var _pos = 0;
  25. bool get done => _pos > line.length - 1;
  26. String? take([int count = 1]) {
  27. final result = peek(count);
  28. _pos += count;
  29. return result;
  30. }
  31. String? peek([int count = 1]) {
  32. if (done) return null;
  33. final end = min(_pos + count, line.length);
  34. final result = line.substring(_pos, end);
  35. return result;
  36. }
  37. void skipWhitespace() {
  38. while (peek() == ' ' || peek() == '\t') {
  39. _pos += 1;
  40. }
  41. }
  42. String readString() {
  43. final buffer = StringBuffer();
  44. final pattern = RegExp(r'\w|_');
  45. while (!done && line[_pos].contains(pattern)) {
  46. buffer.write(line[_pos]);
  47. _pos++;
  48. }
  49. return buffer.toString();
  50. }
  51. String readUntil(Pattern pattern, {bool inclusive = false}) {
  52. final buffer = StringBuffer();
  53. while (!done && !line[_pos].contains(pattern)) {
  54. buffer.write(line[_pos]);
  55. _pos++;
  56. }
  57. if (!done && inclusive) {
  58. buffer.write(line[_pos]);
  59. _pos++;
  60. }
  61. return buffer.toString();
  62. }
  63. }
  64. class TokenizeError {}
  65. Iterable<KeytabToken> tokenize(String source) sync* {
  66. final lines = source.split('\n');
  67. for (var i = 0; i < lines.length; i++) {
  68. var line = lines[i].trim();
  69. line = line.replaceFirst(RegExp('#.*'), '');
  70. if (line == '') {
  71. continue;
  72. }
  73. if (_isKeyboardNameDefine(line)) {
  74. yield* _parseKeyboardNameDefine(line);
  75. }
  76. if (_isKeyDefine(line)) {
  77. yield* _parseKeyDefine(line);
  78. }
  79. }
  80. }
  81. bool _isKeyboardNameDefine(String line) {
  82. return line.startsWith('keyboard ');
  83. }
  84. bool _isKeyDefine(String line) {
  85. return line.startsWith('key ');
  86. }
  87. Iterable<KeytabToken> _parseKeyboardNameDefine(String line) sync* {
  88. final reader = LineReader(line.trim());
  89. if (reader.readString() == 'keyboard') {
  90. yield KeytabToken(KeytabTokenType.keyboard, 'keyboard');
  91. } else {
  92. throw TokenizeError();
  93. }
  94. reader.skipWhitespace();
  95. yield _readInput(reader);
  96. }
  97. Iterable<KeytabToken> _parseKeyDefine(String line) sync* {
  98. final reader = LineReader(line.trim());
  99. if (reader.readString() == 'key') {
  100. yield KeytabToken(KeytabTokenType.keyDefine, 'key');
  101. } else {
  102. throw TokenizeError();
  103. }
  104. reader.skipWhitespace();
  105. final keyName = reader.readString();
  106. yield KeytabToken(KeytabTokenType.keyName, keyName);
  107. reader.skipWhitespace();
  108. while (reader.peek() == '+' || reader.peek() == '-') {
  109. final modeStatus = reader.take()!;
  110. yield KeytabToken(KeytabTokenType.modeStatus, modeStatus);
  111. final mode = reader.readString();
  112. yield KeytabToken(KeytabTokenType.mode, mode);
  113. reader.skipWhitespace();
  114. }
  115. if (reader.take() == ':') {
  116. yield KeytabToken(KeytabTokenType.colon, ':');
  117. } else {
  118. throw TokenizeError();
  119. }
  120. reader.skipWhitespace();
  121. if (reader.peek() == '"') {
  122. yield _readInput(reader);
  123. } else {
  124. final action = reader.readString();
  125. yield KeytabToken(KeytabTokenType.shortcut, action);
  126. }
  127. }
  128. KeytabToken _readInput(LineReader reader) {
  129. reader.skipWhitespace();
  130. if (reader.take() != '"') {
  131. throw TokenizeError();
  132. }
  133. final value = reader.readUntil('"');
  134. reader.take();
  135. return KeytabToken(KeytabTokenType.input, value);
  136. }