Co będzie potrzebne? Generalnie niezbyt wiele - jak zbudowany jest dany format, biegłość w operowaniu na stringach, jakieś obycie z state machine oraz świadomość że w programowaniu również używa się ifów :) no i dyscyplina do pisania wielu testów
ale wyjdźmy z przykładami
Może akurat nie csv, xml czy json ale weźmy na warsztat zwykły kod
namespace Test
public int Test(int a, int b)
{
if (a > b)
{
return a + b;
}
else
{
return a - b;
}
}
Na początku fajnie jakbyś zamienił tekst na jakąś abstrakcję - leksem / token / symbol / whatever
W podanym niżej podejściu prostu przechodzisz po tym inpucie i w zależności od złożoności formatu dodajesz jakąś logikę która to obsłuży, tu jakiś przykład:
public List<LexElement> Walk()
{
Reset();
do
{
if (char.IsWhiteSpace(_Current))
_State = LexingState.WhiteCharacter;
else if (char.IsLetter(_Current))
_State = LexingState.Word;
else if (char.IsNumber(_Current))
_State = LexingState.NumericalValue;
else if (_Current == '"')
_State = LexingState.String;
else if (LexingFacts.OtherTokens.Contains(_Current))
_State = LexingState.Character;
else
_State = LexingState.Unknown;
Handle(_State);
} while (MoveNext());
return _Elements;
}
private void Handle(LexingState state)
{
switch(state)
{
case LexingState.Word:
HandleWord();
break;
case LexingState.String:
HandleString();
break;
case LexingState.NumericalValue:
HandleNumericalValue();
break;
case LexingState.Character:
HandleOtherCharacter();
break;
case LexingState.WhiteCharacter:
HandleWhiteCharacter();
break;
case LexingState.Unknown:
throw new Exception("Unrecognized token");
}
}
private void HandleString()
{
var tmp = "";
while (MoveNext())
{
if (_Current == '"' && HasPreviousElement() && ElementAt(_Index - 1) != LexingFacts.EscapeChar)
{
var element = new LexStringLiteral(tmp, GetDiagnostics());
_Elements.Add(element);
return;
}
tmp += _Current;
}
Error("Unclosed string");
}
private void HandleWord()
{
var tmp = "";
void AddElement()
{
LexElement element;
if (LanguageFacts.KeywordMapper.TryGetValue(tmp, out var lexingElement))
{
element = new LexKeyword(tmp, lexingElement, GetDiagnosticsAtIndex(tmp.Length));
}
else
{
element = new LexWord(tmp, GetDiagnosticsAtIndex(tmp.Length));
}
_Elements.Add(element);
}
do
{
if (char.IsWhiteSpace(_Current) || IsLast())
{
if (IsLast()) tmp += _Current;
AddElement();
if (!IsLast())
MoveBehind();
return;
}
if (LexingFacts.OtherTokens.Contains(_Current))
{
AddElement();
MoveBehind();
return;
}
tmp += _Current;
} while (MoveNext());
}
i np. po czymś takim dostajesz listę już nie znaczków, a czegoś co już ma konkretniejsze znaczenie np.
Keyword: namespace of Kind: Namespace
Word: Test
Keyword: public of Kind: AccessibilityModifier
Keyword: int of Kind: Type
Word: Test
Character: OpenParenthesis
Keyword: int of Kind: Type
Word: a
Character: Comma
Keyword: int of Kind: Type
Word: b
Character: ClosedParenthesis
Character: OpenBracket
Gdy już masz taką wstępnie przetworzoną warstwę na której łatwiej ci będzie operować, to teraz wypadałoby zrobić to, co tam potrzebujesz
w przypadku CSV to pewnie będzie budowanie obiektów, a w przypadku języku programowania budowa np. drzewka
|-Root
| \-Namespace: 'Test'
| \-TypedFunction: 'Test'(int32). Args: (int32), (int32).
| \-Body
| \-TypedIf - ComplexTyped: bool; Operator: GreaterThan
| |-Body
| | \-TypedReturnStatement
| | \-ComplexTyped: int32; Operator: Addition
| | |-Typed Variable Use: a
| | \-Typed Variable Use: b
| \-Body
| \-TypedReturnStatement
| \-ComplexTyped: int32; Operator: Substraction
| |-Typed Variable Use: a
| \-Typed Variable Use: b
ale to już się o wiele bardziej komplikuje i wymaga praktyki
więc pewnie jakbyś wyszedł od prostych formatów typu ini
, później jakieś csvki mniej oraz bardziej skomplikowane (np. wartości ze średnikami oraz znakami nowej linii, etc)
podstawowy css
może, jakiś podzbiór html
/xml
, następnie może json
?