abstract class TlToken { static Iterable tokenize(String file) sync* { var lastWasNone = false; for (final untrimmedLine in file.split(RegExp(r'[\r\n]'))) { final line = untrimmedLine.trim(); if (line.isEmpty) { if (!lastWasNone) { yield TlTokenNone(); lastWasNone = true; } continue; } else { lastWasNone = false; if (line.startsWith('//')) { // Comment if (!line.contains('@')) { yield TlTokenCommentLine(line); continue; } // Skip to first @tag final interestLine = line.substring(line.indexOf('@')); for (final unTrimmedTaggedChunk in interestLine.split('@')) { final taggedChunk = unTrimmedTaggedChunk.trim(); if (taggedChunk.isEmpty) { continue; } final firstWs = taggedChunk.indexOf(RegExp(r'\s')); final tagName = taggedChunk.substring(0, firstWs); final tagValue = taggedChunk.substring(firstWs).trim(); yield TlTokenCommentTag(name: tagName, value: tagValue); } } else if (line == '---functions---') { yield TlTokenFunctionsDelimiter(); } else { // class // Split by space final chunksIt = line.split(RegExp(r'\s+')).iterator; // First is name chunksIt.moveNext(); final name = chunksIt.current; // Then there are parameters until = final parameters = []; do { chunksIt.moveNext(); if (chunksIt.current == '=') { break; } // Params are defined as name:type // Ignore oddities if (!chunksIt.current.contains(':')) { continue; } final splitted = chunksIt.current.split(':'); parameters.add(TlTokenClassParam( name: splitted[0], type: splitted[1] )); } while (true); // Finally, there is the base type (abstract class) chunksIt.moveNext(); String removeSemicolon(String input) { while (input.codeUnits.last == ';'.codeUnits[0]) { input = input.substring(0, input.length - 1); } return input; } final baseType = removeSemicolon(chunksIt.current); yield TlTokenClassTag( name: name, parameters: parameters, baseType: baseType, ); } } } } } class TlTokenFunctionsDelimiter extends TlToken { @override String toString() { return 'TlToken: ---functions---'; } } class TlTokenNone extends TlToken { @override String toString() { return 'TlToken.'; } } class TlTokenCommentLine extends TlToken { /// The content of the comment including the leading slashes final String content; TlTokenCommentLine(this.content); @override String toString() { return 'TlToken: //$content'; } } class TlTokenCommentTag extends TlToken { /// The name of the tag, excluding the ampersand: description final String name; /// The value of the tag: Provides ... final String value; TlTokenCommentTag({required this.name, required this.value}); @override String toString() { return 'TlToken: //@$name $value'; } } class TlTokenClassTag extends TlToken { /// The name of the class final String name; final List parameters; final String baseType; TlTokenClassTag({required this.name, required this.parameters, required this.baseType}); @override String toString() { final params = parameters.join(', '); return 'TlToken: $name($params) = $baseType'; } } class TlTokenClassParam { final String name; final String type; TlTokenClassParam({required this.name, required this.type}); @override String toString() { return '$name:$type'; } }