Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
El árbol de sintaxis es una estructura de datos inmutable fundamental expuesta por las API del compilador. Estos árboles representan la estructura léxica y sintáctica del código fuente. Sirven para dos propósitos importantes:
- Para permitir herramientas, como un IDE, complementos, herramientas de análisis de código y refactorizaciones, para ver y procesar la estructura sintáctica del código fuente en el proyecto de un usuario.
- Para habilitar herramientas, como refactorizaciones y un IDE, para crear, modificar y reorganizar código fuente de forma natural sin tener que usar ediciones de texto directo. Al crear y manipular árboles, las herramientas pueden crear y reorganizar fácilmente el código fuente.
Árboles de sintaxis
Los árboles de sintaxis son la estructura principal que se usa para la compilación, el análisis de código, el enlace, la refactorización, las características del IDE y la generación de código. No se entiende ninguna parte del código fuente sin que se identifique y clasifique primero en uno de los muchos elementos de lenguaje estructural conocidos.
Nota:
RoslynQuoter es una herramienta de código abierto que muestra las llamadas API de generador de sintaxis usadas para construir el árbol de sintaxis de un programa. Para probarlo en directo, consulte http://roslynquoter.azurewebsites.net.
Los árboles de sintaxis tienen tres atributos clave:
- Contienen toda la información de origen con fidelidad completa. La fidelidad completa significa que el árbol de sintaxis contiene todos los fragmentos de información que se encuentran en el texto de origen, cada construcción gramatical, cada token léxico y todo lo demás entre, incluidos los espacios en blanco, los comentarios y las directivas de preprocesador. Por ejemplo, cada literal mencionado en el origen se representa exactamente como se ha escrito. Los árboles de sintaxis también capturan errores en el código fuente cuando el programa está incompleto o con un formato incorrecto mediante la representación de tokens omitidos o que faltan.
- Pueden generar el texto exacto mediante el que se analizaron. Desde cualquier nodo de sintaxis, es posible obtener la representación de texto del subárbol raíz en ese nodo. Esta capacidad significa que los árboles de sintaxis se pueden usar como una manera de construir y editar texto de origen. Al crear un árbol que tienes, por implicación, has creado el texto equivalente, y al hacer un nuevo árbol a partir de cambios en un árbol existente, efectivamente has editado el texto.
- Son inmutables y seguros para subprocesos. Una vez obtenido un árbol, se trata de una instantánea del estado actual del código y nunca cambia. Esto permite a varios usuarios interactuar con el mismo árbol de sintaxis al mismo tiempo en subprocesos diferentes sin bloquear ni duplicar. Dado que los árboles son inmutables y no se pueden realizar modificaciones directamente en un árbol, los métodos de fábrica ayudan a crear y modificar árboles de sintaxis mediante la creación de instantáneas adicionales del árbol. Los árboles son eficaces en la forma en que reutilizan los nodos subyacentes, por lo que se puede volver a generar una nueva versión rápidamente y con poca memoria adicional.
Un árbol de sintaxis es, literalmente, una estructura de datos en forma de árbol, donde los elementos estructurales no terminales secundan a otros elementos. Cada árbol de sintaxis se compone de nodos, tokens y trivia.
Nodos de sintaxis
Los nodos de sintaxis son uno de los elementos principales de los árboles de sintaxis. Estos nodos representan construcciones sintácticas, como declaraciones, instrucciones, cláusulas y expresiones. Cada categoría de nodos de sintaxis se representa mediante una clase independiente derivada de Microsoft.CodeAnalysis.SyntaxNode. El conjunto de clases de nodo no es extensible.
Todos los nodos de sintaxis son nodos no terminales en el árbol de sintaxis, lo que significa que siempre tienen otros nodos y símbolos como hijos. Como elemento secundario de otro nodo, cada nodo tiene un nodo primario al que se puede acceder a través de la SyntaxNode.Parent propiedad . Dado que los nodos y árboles son inmutables, el elemento primario de un nodo nunca cambia. La raíz del árbol tiene un elemento primario nulo.
Cada nodo tiene un SyntaxNode.ChildNodes() método , que devuelve una lista de nodos secundarios en orden secuencial en función de su posición en el texto de origen. Esta lista no contiene tokens. Cada nodo también tiene métodos para examinar descendientes, como DescendantNodes, DescendantTokenso DescendantTrivia , que representan una lista de todos los nodos, tokens o trivia que existen en el subárbol raíz de ese nodo.
Además, cada subclase de nodos de sintaxis expone los mismos elementos secundarios mediante propiedades fuertemente tipadas. Por ejemplo, una BinaryExpressionSyntax clase de nodo tiene tres propiedades adicionales específicas de los operadores binarios: Left, OperatorTokeny Right. El tipo de Left y Right es ExpressionSyntaxy el tipo de OperatorToken es SyntaxToken.
Algunos nodos de sintaxis tienen hijos opcionales. Por ejemplo, un IfStatementSyntax tiene un ElseClauseSyntax opcional. Si el elemento secundario no está presente, la propiedad devuelve null.
Tokens de sintaxis
Los tokens de sintaxis son los terminales de la gramática del lenguaje, que representan los fragmentos sintácticos más pequeños del código. Nunca son elementos principales de otros nodos o tokens. Los tokens de sintaxis constan de palabras clave, identificadores, literales y puntuación.
Con fines de eficacia, el SyntaxToken tipo es un tipo de valor CLR. Por lo tanto, a diferencia de los nodos de sintaxis, solo hay una estructura para todos los tipos de tokens con una combinación de propiedades que tienen significado en función del tipo de token que se representa.
Por ejemplo, un token literal entero representa un valor numérico. Además del texto de origen sin formato que abarca el token, el token de literal tiene una propiedad Value que indica el valor entero descodificado exacto. Esta propiedad se escribe como Object porque puede ser uno de muchos tipos primitivos.
La ValueText propiedad indica la misma información que la Value propiedad; sin embargo, esta propiedad siempre se escribe como String. Un identificador en el texto de origen de C# puede incluir caracteres de escape Unicode, pero la sintaxis de la propia secuencia de escape no se considera parte del nombre del identificador. Por lo tanto, aunque el texto bruto abarcado por el token incluye la secuencia de escape, la propiedad ValueText no lo hace. En su lugar, incluye los caracteres Unicode que identifica el escape. Por ejemplo, si el texto de origen contiene un identificador escrito como \u03C0
, la ValueText propiedad de este token devolverá π
.
Trivia de sintaxis
La trivia de sintaxis representa las partes del texto de origen que son en gran medida insignificantes para comprender normalmente el código, como los espacios en blanco, los comentarios y las directivas de preprocesador. Al igual que los tokens de sintaxis, la trivia es un tipo de valor. El tipo único Microsoft.CodeAnalysis.SyntaxTrivia se usa para describir todos los tipos de trivia.
Dado que la trivia no forma parte de la sintaxis de lenguaje normal y puede aparecer en cualquier lugar entre dos tokens, no se incluyen en el árbol de sintaxis como elemento secundario de un nodo. Sin embargo, dado que son importantes al implementar una característica como la refactorización y mantener la fidelidad completa con el texto de origen, existen como parte del árbol de sintaxis.
Puede acceder a trivias revisando las colecciones SyntaxToken.LeadingTrivia o SyntaxToken.TrailingTrivia de un token. Cuando se analiza el texto de origen, las secuencias de trivia se asocian a tokens. En general, un token es propietario de cualquier curiosidad que le preceda en la misma línea hasta el siguiente token. Cualquier trivia después de esa línea está asociada al siguiente token. El primer token del archivo de origen obtiene toda la información inicial, y la última secuencia de información del archivo se añade al token de fin de archivo, que de otro modo tendría un ancho de cero.
A diferencia de los nodos de sintaxis y los tokens, los elementos triviales de sintaxis no tienen padres. Sin embargo, dado que forman parte del árbol y cada uno está asociado a un solo token, puede acceder al token al que está asociado mediante la SyntaxTrivia.Token propiedad .
Intervalos
Cada nodo, token o trivia conoce su posición dentro del texto de origen y el número de caracteres de los que consta. Una posición de texto se representa como un entero de 32 bits, que es un índice de base char
cero. Un TextSpan objeto es la posición inicial y un recuento de caracteres, ambos representados como enteros. Si TextSpan tiene una longitud cero, hace referencia a una ubicación entre dos caracteres.
Cada nodo tiene dos TextSpan propiedades: Span y FullSpan.
La Span propiedad es el intervalo de texto desde el principio del primer token del subárbol del nodo hasta el final del último token. Este intervalo no incluye ninguna curiosidad inicial ni final.
La FullSpan propiedad es el intervalo de texto que incluye el intervalo normal del nodo, además del intervalo de cualquier trivia inicial o final.
Por ejemplo:
if (x > 3)
{
|| // this is bad
|throw new Exception("Not right.");| // better exception?||
}
El nodo de la instrucción dentro del bloque tiene un intervalo indicado por las plecas (|). Incluye los caracteres throw new Exception("Not right.");
. El intervalo completo se indica mediante las barras verticales dobles (||). Incluye los mismos caracteres que el intervalo y los caracteres asociados a las curiosidades inicial y final.
Tipos
Cada nodo, token o trivia tiene una SyntaxNode.RawKind propiedad, de tipo System.Int32, que identifica el elemento de sintaxis exacto representado. Este valor se puede convertir en una enumeración específica del lenguaje. Cada lenguaje, C# o Visual Basic, tiene una sola SyntaxKind
enumeración (Microsoft.CodeAnalysis.CSharp.SyntaxKind y Microsoft.CodeAnalysis.VisualBasic.SyntaxKind, respectivamente) que enumera todos los nodos, tokens y elementos trivia posibles en la gramática. Esta conversión se puede realizar automáticamente accediendo a los métodos de extensión CSharpExtensions.Kind o VisualBasicExtensions.Kind.
La RawKind propiedad permite desambiguar fácilmente los tipos de nodo de sintaxis que comparten la misma clase de nodo. Para tokens y trivia, esta propiedad es la única manera de distinguir un tipo de elemento de otro.
Por ejemplo, una sola BinaryExpressionSyntax clase tiene Left, OperatorTokeny Right como elementos secundarios. La propiedad Kind distingue si es un nodo de sintaxis de tipo AddExpression, SubtractExpression o MultiplyExpression.
Sugerencia
Se recomienda utilizar métodos de extensión IsKind (para C#) o IsKind (para VB) para comprobar los tipos.
Errores
Incluso cuando el texto de origen contiene errores de sintaxis, se expone un árbol de sintaxis completo que se puede recorrer de ida y vuelta al origen. Cuando el analizador encuentra código que no se ajusta a la sintaxis definida del lenguaje, usa una de estas dos técnicas para crear un árbol de sintaxis:
Si el analizador espera un tipo determinado de token pero no lo encuentra, puede insertar un token que falta en el árbol de sintaxis en la ubicación en la que se esperaba el token. Un token que falta representa el token real esperado, pero tiene un intervalo vacío y su SyntaxNode.IsMissing propiedad devuelve
true
.El analizador puede omitir los tokens hasta que encuentre uno en el que pueda continuar el análisis. En este caso, los tokens omitidos se adjuntan como un nodo de trivia con el tipo SkippedTokensTrivia.