Como posso transformar uma grande árvore de união discriminada em uma forma legível?
-
26-09-2019 - |
Pergunta
O tipo a seguir é claramente bastante grande, portanto, escrever o código manualmente para convertê -lo em um formulário legível seria tedioso. Eu gostaria de saber a maneira mais simples de exibir a árvore de forma legível.
type Element =
| Nil
| Token of Token
| Expression of Element * Element
| ExpressionNoIn of Element * Element
| AssignmentExpression of Element * AssignmentOperator * Element
| AssignmentExpressionNoIn of Element * AssignmentOperator * Element
| ConditionalExpression of Element * Element * Element
| ConditionalExpressionNoIn of Element * Element * Element
| LogicalORExpression of Element * Element
| LogicalORExpressionNoIn of Element * Element
| LogicalANDExpression of Element * Element
| LogicalANDExpressionNoIn of Element * Element
| BitwiseORExpression of Element * Element
| BitwiseORExpressionNoIn of Element * Element
| BitwiseXORExpression of Element * Element
| BitwiseXORExpressionNoIn of Element * Element
| BitwiseANDExpression of Element * Element
| BitwiseANDExpressionNoIn of Element * Element
| EqualityExpression of Element * EqualityOperator * Element
| EqualityExpressionNoIn of Element * EqualityOperator * Element
| RelationalExpression of Element * RelationalOperator * Element
| RelationalExpressionNoIn of Element * RelationalOperator * Element
| ShiftExpression of Element * BitwiseShiftOperator * Element
| AdditiveExpression of Element * AdditiveOperator * Element
| MultiplicativeExpression of Element * MultiplicativeOperator * Element
| UnaryExpression of UnaryOperator * Element
| PostfixExpression of Element * PostfixOperator
| MemberExpression of Element * Element
| Arguments of Element * Element
| ArgumentList of Element
| CallExpression of Element * Element
| NewExpression of NewOperator * Element
| LeftHandSideExpression of Element
| PrimaryExpression of Element
| ObjectLiteral of Element
| PropertyNameAndValueList of Element * Element
| PropertyAssignment of Element * Element * Element
| PropertyName of Element
| PropertySetParameterList of Element
| ArrayLiteral of Element * Element
| Elision of Element * Element
| ElementList of Element * Element * Element
| Statement of Element
| Block of Element
| StatementList of Element * Element
| VariableStatement of Element
| VariableDeclarationList of Element * Element
| VariableDeclarationListNoIn of Element * Element
| VariableDeclaration of Element * Element
| VariableDeclarationNoIn of Element * Element
| Initialiser of Element
| InitialiserNoIn of Element
| EmptyStatement
| ExpressionStatement of Element
| IfStatement of Element * Element * Element
| IterationStatement of Element * Element * Element * Element
| ContinueStatement of Element
| BreakStatement of Element
| ReturnStatement of Element
| WithStatement of Element * Element
| SwitchStatement of Element * Element
| CaseBlock of Element * Element * Element
| CaseClauses of Element * Element
| CaseClause of Element * Element
| DefaultClause of Element
| LabelledStatement of Element * Element
| ThrowStatement of Element
| TryStatement of Element * Element * Element
| Catch of Element * Element
| Finally of Element
| DebuggerStatement
| FunctionDeclaration of Element * Element * Element
| FunctionExpression of Element * Element * Element
| FormalParameterList of Element * Element
| FunctionBody of Element
| SourceElement of Element
| SourceElements of Element * Element
| Program of Element
Aqui está um exemplo de como isso pode ser exibido. (É um pouco diferente como eu produzi há um tempo atrás.)
<Expression>
<AssignmentExpression>
<ConditionalExpression>
<LogicalORExpression>
<LogicalORExpression>
<LogicalANDExpression>
<BitwiseORExpression>
<BitwiseXORExpression>
<BitwiseANDExpression>
<EqualityExpression>
<EqualityExpression>
<RelationalExpression>
<ShiftExpression>
<AdditiveExpression>
<MultiplicativeExpression>
<MultiplicativeExpression>
<UnaryExpression>
<PostfixExpression>
<LeftHandSideExpression>
<NewExpression>
<MemberExpression>
<PrimaryExpression>
<TokenNode Value="i" Line="9" Column="13" />
</PrimaryExpression>
</MemberExpression>
</NewExpression>
</LeftHandSideExpression>
</PostfixExpression>
</UnaryExpression>
</MultiplicativeExpression>
<TokenNode Value="%" Line="9" Column="15" />
<UnaryExpression>
<PostfixExpression>
<LeftHandSideExpression>
<NewExpression>
<MemberExpression>
<PrimaryExpression>
<TokenNode Value="3" Line="9" Column="17" />
</PrimaryExpression>
</MemberExpression>
</NewExpression>
</LeftHandSideExpression>
</PostfixExpression>
</UnaryExpression>
</MultiplicativeExpression>
</AdditiveExpression>
</ShiftExpression>
</RelationalExpression>
</EqualityExpression>
<TokenNode Value="===" Line="9" Column="19" />
<RelationalExpression>
<ShiftExpression>
<AdditiveExpression>
<MultiplicativeExpression>
<UnaryExpression>
<PostfixExpression>
<LeftHandSideExpression>
<NewExpression>
<MemberExpression>
<PrimaryExpression>
<TokenNode Value="0" Line="9" Column="23" />
</PrimaryExpression>
</MemberExpression>
</NewExpression>
</LeftHandSideExpression>
</PostfixExpression>
</UnaryExpression>
</MultiplicativeExpression>
</AdditiveExpression>
</ShiftExpression>
</RelationalExpression>
</EqualityExpression>
</BitwiseANDExpression>
</BitwiseXORExpression>
</BitwiseORExpression>
</LogicalANDExpression>
</LogicalORExpression>
<TokenNode Value="||" Line="9" Column="25" />
<LogicalANDExpression>
<BitwiseORExpression>
<BitwiseXORExpression>
<BitwiseANDExpression>
<EqualityExpression>
<EqualityExpression>
<RelationalExpression>
<ShiftExpression>
<AdditiveExpression>
<MultiplicativeExpression>
<MultiplicativeExpression>
<UnaryExpression>
<PostfixExpression>
<LeftHandSideExpression>
<NewExpression>
<MemberExpression>
<PrimaryExpression>
<TokenNode Value="i" Line="9" Column="28" />
</PrimaryExpression>
</MemberExpression>
</NewExpression>
</LeftHandSideExpression>
</PostfixExpression>
</UnaryExpression>
</MultiplicativeExpression>
<TokenNode Value="%" Line="9" Column="30" />
<UnaryExpression>
<PostfixExpression>
<LeftHandSideExpression>
<NewExpression>
<MemberExpression>
<PrimaryExpression>
<TokenNode Value="5" Line="9" Column="32" />
</PrimaryExpression>
</MemberExpression>
</NewExpression>
</LeftHandSideExpression>
</PostfixExpression>
</UnaryExpression>
</MultiplicativeExpression>
</AdditiveExpression>
</ShiftExpression>
</RelationalExpression>
</EqualityExpression>
<TokenNode Value="===" Line="9" Column="34" />
<RelationalExpression>
<ShiftExpression>
<AdditiveExpression>
<MultiplicativeExpression>
<UnaryExpression>
<PostfixExpression>
<LeftHandSideExpression>
<NewExpression>
<MemberExpression>
<PrimaryExpression>
<TokenNode Value="0" Line="9" Column="38" />
</PrimaryExpression>
</MemberExpression>
</NewExpression>
</LeftHandSideExpression>
</PostfixExpression>
</UnaryExpression>
</MultiplicativeExpression>
</AdditiveExpression>
</ShiftExpression>
</RelationalExpression>
</EqualityExpression>
</BitwiseANDExpression>
</BitwiseXORExpression>
</BitwiseORExpression>
</LogicalANDExpression>
</LogicalORExpression>
</ConditionalExpression>
</AssignmentExpression>
</Expression>
Solução
Se você deseja escrever um código de processamento sindical genérico que não precisará listar todos os casos da União, provavelmente precisará usar a API de reflexão F#. Aqui está um exemplo simples.
o formatUnion
A função usa a reflexão F#. Assume que o parâmetro de tipo 'T
é um tipo de sindicato e usa GetUnionFields
Para obter uma tupla contendo o nome do caso atual e dos argumentos. Ele imprime o nome do estojo atual e itera sobre todos os argumentos. Se alguns dos argumentos são o valor do tipo 'T
(o que significa que é união recursiva), imprimimos recursivamente informações sobre o valor:
let rec formatUnion indent (value:'T) = //'
// Get name and arguments of the current union case
let info, args = Reflection.FSharpValue.GetUnionFields(value, typeof<'T>) //'
// Print current name (with some indentation)
printfn "%s%s" indent info.Name
for a in args do
match box a with
| :? 'T as v ->
// Recursive use of the same union type..
formatUnion (indent + " ") v
| _ -> ()
O exemplo a seguir executa a função em um valor de união muito simples:
type Element = | Nil | And of Element * Element | Or of Element * Element
formatUnion "" (And(Nil, Or(Nil, Nil)))
// Here is the expected output:
// And
// Nil
// Or
// Nil
// Nil
Como uma nota lateral, acho que você poderia simplificar amplamente sua união discriminada por ter casos para BinaryOperator
e UnaryOperator
(com um parâmetro adicional) em vez de listar todos os tipos de elementos explicitamente. Então você provavelmente poderia implementar a função diretamente, porque seria bastante simples. Algo como:
type BinaryOperator = LogicalOr | LogicalAnd | BitwiseOr // ...
type UnaryOperator = Statement | Block | Initializer // ...
type Element =
| BinaryOperator of BinaryOperator * Element * Element
| UnaryOperator of UnaryOperator * Element