The code below answers the question.
Here is a usage example:
parser = GetTextParser[template];
parser[text]
(*Returns <|"FirstName"->"Tom","Age"->20,"LastName"->"TomTom"|> *)
I was thinking it would be practical if Mathematica had something similar built-in. It's the inverse functionality of StringTemplate.
I've found that what I've done is a basic parser of semi-structured text. Some resources for other languages can be found here: http://sonalake.com/latest/utah-open-source-semi-structured-text-parser/
This code started as a simpler prototype but I managed to generalize it so that different template patterns can be used (in this case %VariableName% and the BlankNullSequence ___
).
What this code does is parse a template into intermediate variables, create variable names and symbols that will be used to generate Mathematica string patterns and finally define how to convert the matched strings into results.
templateVariables=
{
"%"~~x:(WordCharacter..)~~"%":>variable[x]
,
"___"->variable["Blank"]
};
variableName[variable[key_]]:=key;
leftTemplatePattern[variable[key_],variableSymbols_]:=
With[{symbol = variableSymbols[key]},
Shortest[Pattern[symbol,BlankSequence[]]]
];
rightTemplatePattern[variable[key_],variableSymbols_]:=
With[{symbol = variableSymbols[key]},
ConvertString@symbol &
];
variableName[variable["Blank"]]:=Nothing;
leftTemplatePattern[variable["Blank"],variableSymbols_]:=___;
rightTemplatePattern[variable["Blank"],variableSymbols_]:=Nothing;
GetTextParser[template_]:=
Module[
{stringSplited,variableNames,variableSymbols,leftPatterns,rightPatterns,
stringPattern,variables},
stringSplited=StringSplit[template,templateVariables];
variables=Cases[stringSplited,_variable];
variableNames=variableName/@variables;
variableSymbols=AssociationThread[variableNames,Symbol/@variableNames];
leftPatterns=stringSplited/.x_variable:>leftTemplatePattern[x,variableSymbols]//Apply@StringExpression;
rightPatterns=rightTemplatePattern[#,variableSymbols]&/@variables;
stringPattern=leftPatterns:>Evaluate@rightPatterns;
Function[text,
StringCases[text,stringPattern]//
If[Length@#==1, First@#, #]&//
Map[If[Head@# === Function, #[], #]&]//
If[# =!= {}, AssociationThread[variableNames,#], Association[]]&
]
];
SetAttributes[ConvertString, Listable];
ConvertString[s_String]:=
Module[
{simported},
If[StringCount[s, "/"] > 0 || StringCount[s, "\\"] > 0,
s
,
simported=ToExpression[s];
Which[
NumericQ[simported]||simported===True||simported===False,
simported
,
ListQ[simported],
Map[ConvertString[ToString[#]]&, simported]
,
DownValues[simported] =!= {},
simported,
True, (*string case*)
s
]
]
];