Translate

jueves, 20 de abril de 2023

CSharpSyntaxRewriter

En el post anterior hablamos de CSharpSyntaxWalker y cómo podríamos navegar el árbol de sintaxis con el patrón de visitor. Ahora vamos un paso más allá con CSharpSyntaxRewriter y "modificamos" el árbol sintactico a medida que lo recorremos. Es importante tener en cuenta que en realidad no estamos mutando el árbol sintactico original, ya que los árboles de Roslyn son inmutables. En su lugar, CSharpSyntaxRewriter crea un nuevo árbol como resultado de nuestros cambios.

CSharpSyntaxRewriter puede visitar todos los nodos, tokens o trivias dentro de un árbol sintactico. Al igual que CSharpSyntaxVisitor, podemos elegir de forma selectiva qué fragmentos de sintaxis nos gustaría visitar. Hacemos esto sobreescribiendo varios métodos y devolviendo lo siguiente:

  • El nodo, token o trivia original, sin cambios.
  • Nulo, que indica que se debe eliminar el nodo, el token o la trivia.
  • Un nuevo nodo de sintaxis, token o trivia.

Al igual que con la mayoría de las API, CSharpSyntaxRewriter se comprende mejor a través de ejemplos. Una pregunta reciente sobre Stack Overflow preguntó ¿Cómo puedo eliminar los puntos y comas redundantes en el código con SyntaxRewriter?

Roslyn trata todos los puntos y coma redundantes como parte de un nodo EmptyStatementSyntax. A continuación, demostramos cómo resolver el caso base: un punto y coma innecesario en una línea propia.


public class EmtpyStatementRemoval : CSharpSyntaxRewriter

{

    public override SyntaxNode VisitEmptyStatement(EmptyStatementSyntax node)

    {

        //Simply remove all Empty Statements

        return null;

    }

}


public static void Main(string[] args)

{

    //A syntax tree with an unnecessary semicolon on its own line

    var tree = CSharpSyntaxTree.ParseText(@"

    public class Sample

    {

       public void Foo()

       {

          Console.WriteLine();

          ;

        }

    }");


    var rewriter = new EmtpyStatementRemoval();

    var result = rewriter.Visit(tree.GetRoot());

    Console.WriteLine(result.ToFullString());

}


La salida de este programa produce un programa simple sin ningún punto y coma redundante.


public class Sample

{

   public void Foo()

   {

      Console.WriteLine();

    }

}

Sin embargo, cuando hay trivia inicial o final, esta trivia se elimina. Esto significa que se eliminarán los comentarios por encima y por debajo del punto y coma. Al construir un EmptyStatementSyntax con un token faltante en lugar de un punto y coma, podemos eliminar el punto y coma del árbol original:

public class EmtpyStatementRemoval : CSharpSyntaxRewriter

{

    public override SyntaxNode VisitEmptyStatement(EmptyStatementSyntax node)

    {

        //Construct an EmptyStatementSyntax with a missing semicolon

        return node.WithSemicolonToken(

            SyntaxFactory.MissingToken(SyntaxKind.SemicolonToken)

                .WithLeadingTrivia(node.SemicolonToken.LeadingTrivia)

                .WithTrailingTrivia(node.SemicolonToken.TrailingTrivia));

    }

}


public static void Main(string[] args)

{

    var tree = CSharpSyntaxTree.ParseText(@"

    public class Sample

    {

       public void Foo()

       {

          Console.WriteLine();

          #region SomeRegion

          //Some other code

          #endregion

          ;

        }

    }");


    var rewriter = new EmtpyStatementRemoval();

    var result = rewriter.Visit(tree.GetRoot());

    Console.WriteLine(result.ToFullString());

}


El resultado de este enfoque es:


public class Sample

{

   public void Foo()

   {

      Console.WriteLine();

      #region SomeRegion

      //Some other code

      #endregion

    }

}

Este enfoque tiene el efecto secundario de dejar una línea en blanco donde haya un punto y coma redundante. Dicho esto, creo que probablemente valga la pena el intercambio, ya que de lo contrario no parece haber una forma de retener las trivias. En última instancia, la trivia solo se puede conservar si se adjunta a un nodo y luego se devuelve ese nodo.

La única forma de eliminar el nodo y conservar las trivias es construir un nodo de reemplazo. El mejor candidato para el reemplazo probablemente sea un EmptyStatementSyntax al que le falte un punto y coma.

Esto también podría indicar una limitación con CSharpSyntaxRewriter. Parece que debería ser más fácil eliminar nodos, manteniendo sus trivias.

miércoles, 19 de abril de 2023

Le preguntamos a ChatGPT ¿cual es el lenguaje de programación que debo estudiar?


Estuve jugando un rato con chatGPT y les cuento : 

YO: ¿cual es el lenguaje de programación que debo estudiar?

ChatGPT : La elección del lenguaje de programación a estudiar dependerá de tus objetivos y del tipo de aplicación que quieras desarrollar. Aquí te presento algunas opciones populares y sus características principales:

Python: Es un lenguaje de programación popular en la actualidad debido a su facilidad de aprendizaje, su versatilidad y su amplio uso en aplicaciones de inteligencia artificial, ciencia de datos y análisis estadístico.

JavaScript: Es un lenguaje de programación utilizado en aplicaciones web para crear interacciones dinámicas en la interfaz de usuario, también es muy utilizado en el desarrollo de aplicaciones móviles.

Java: Es un lenguaje de programación orientado a objetos y es utilizado en aplicaciones empresariales, de escritorio y móviles.

C++: Es un lenguaje de programación de bajo nivel que se utiliza en aplicaciones de sistemas y de rendimiento.

Swift: Es el lenguaje de programación utilizado para desarrollar aplicaciones para dispositivos Apple, como iPhone y iPad.

Te recomiendo investigar más sobre estas opciones y elegir la que mejor se adapte a tus objetivos y necesidades. También es importante tener en cuenta que el aprendizaje de un lenguaje de programación no es algo que se logre de la noche a la mañana, sino que requiere práctica y dedicación.


YO: Con cual encontraré trabajo más rápido?

ChatGPT : No hay una respuesta definitiva a esta pregunta, ya que el mercado laboral y las oportunidades de trabajo varían de acuerdo a la ubicación geográfica, las tendencias de la industria y la experiencia previa que tengas.

Sin embargo, algunos lenguajes de programación son más demandados que otros en la actualidad. Por ejemplo, Python es muy utilizado en la industria de la inteligencia artificial, análisis de datos y aprendizaje automático, lo que puede hacer que sea más fácil encontrar trabajo en estas áreas.

También es importante destacar que el dominio de un solo lenguaje de programación no garantiza la obtención de trabajo en el campo de la programación. Muchas empresas buscan programadores que tengan habilidades en múltiples lenguajes y tecnologías, así como una comprensión profunda de los conceptos de programación y las metodologías de desarrollo de software.

En resumen, la elección del lenguaje de programación que te ayudará a encontrar trabajo más rápido dependerá de tus habilidades, intereses y las oportunidades laborales disponibles en tu área geográfica. Lo mejor es enfocarse en aprender bien un lenguaje de programación y tener una sólida comprensión de los fundamentos de la programación para destacar en el mercado laboral.


YO: Y cual te gusta más?

ChatGPT : Como modelo de lenguaje de programación, como soy, no tengo emociones ni preferencias. Mi función es brindar información objetiva y responder a preguntas de manera imparcial.

Sin embargo, en la comunidad de programadores, cada uno tiene sus propias preferencias y opiniones personales sobre qué lenguaje de programación es mejor para diferentes aplicaciones. La elección del lenguaje de programación dependerá de las necesidades del proyecto y del programador en sí. Lo importante es que el lenguaje de programación elegido sea adecuado para el propósito del proyecto y que el programador tenga la experiencia y habilidades necesarias para trabajar con él de manera efectiva.


Interesante conversación que les quería compartír, ustedes que opinan?



domingo, 16 de abril de 2023

Analizando el flujo de datos con Roslyn

Esta API se puede usar para inspeccionar cómo se leen y escriben las variables dentro de un bloque de código determinado. Tal vez le gustaría crear una extensión de Visual Studio que capture y registre todas las asignaciones a una determinada variable. Se puede usar la API de análisis de flujo de datos para encontrar las declaraciones y un reescritor para registrarlas.

Para demostrar las capacidades de esta API, podemos analizar el bucle for en el siguiente código:


var tree = CSharpSyntaxTree.ParseText(@"

public class Sample

{

   public void Foo()

   {

        int[] outerArray = new int[10] { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4};

        for (int index = 0; index < 10; index++)

        {

             int[] innerArray = new int[10] { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4 };

             index = index + 2;

             outerArray[index – 1] = 5;

        }

   }

}");

 

var Mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);

 

var compilation = CSharpCompilation.Create("MyCompilation",

    syntaxTrees: new[] { tree }, references: new[] { Mscorlib });

var model = compilation.GetSemanticModel(tree);

 

var forStatement = tree.GetRoot().DescendantNodes().OfType<ForStatementSyntax>().Single();

DataFlowAnalysis result = model.AnalyzeDataFlow(forStatement);


En este punto, tenemos acceso a un objeto DataFlowAnalysis.


Quizás la propiedad más importante de este objeto es Succeeded. Esto le indica si el análisis de flujo de datos se completó correctamente. En mi experiencia, la API ha sido bastante buena para lidiar con código semánticamente inválido. Ni las invocaciones a métodos faltantes ni el uso de variables no declaradas parecían hacer tropezar. La documentación señala que si la región analizada no abarca una sola expresión o declaración, es probable que el análisis falle.

El objeto DataFlowAnalysis expone una API bastante rica. Expone información sobre direcciones inseguras, variables locales capturadas por métodos anónimos y mucho más.

En nuestro caso, estamos interesados en las siguientes propiedades:

  • DataFlowAnalysis.AlwaysAssigned: el conjunto de variables locales para las que siempre se asigna un valor dentro de una región.
  • DataFlowAnalysis.ReadInside: el conjunto de variables locales que se leen dentro de una región.
  • DataFlowAnalysis.WrittenOutside: el conjunto de variables locales que se escriben fuera de una región.
  • DataFlowAnalysis.WrittenInside: el conjunto de variables locales que se escriben dentro de una región.
  • DataFlowAnalysis.VariablesDeclared: el conjunto de variables locales que se declaran dentro de una región. Tenga en cuenta que la región debe estar delimitada por el cuerpo de un método o el inicializador de un campo, por lo que los símbolos de parámetros nunca se incluyen en el resultado.


Los resultados del análisis son los siguientes:


AlwaysAssigned: index

index siempre se asigna a, ya que está contenido en el inicializador del bucle for, que se ejecuta incondicionalmente.

WrittenInside: index, innerArray

Tanto index como innerArray están claramente escritos dentro del bucle.

Un punto importante es que externalArray no. Mientras estamos mutando la matriz, no estamos mutando la referencia contenida dentro de la variable outsideArray. Por lo tanto, no aparece en esta lista.

WrittenOutside: outerArray, this

outsideArray está claramente escrito fuera del bucle for.

Sin embargo, me sorprendió que esto apareciera como un símbolo de parámetro dentro de la lista de WriteOutside. Parece como si esto se pasara como un parámetro a la clase y su miembro, lo que significa que también aparece aquí. Esto parece ser por diseño, aunque sospecho que la mayoría de los consumidores de esta API se sorprenderán y probablemente ignoren este valor.

ReadInside: index, outerArray

Está claro que el valor del índice se lee dentro del ciclo.

Me sorprendió que se considere que outsideArray se "lee" dentro del ciclo, ya que no estamos leyendo su valor directamente. Supongo que, técnicamente, primero debemos leer el valor de externalArray para calcular el desplazamiento y recuperar la dirección correcta para el elemento dado de la matriz. Así que estamos realizando una especie de "lectura implícita" dentro del ciclo aquí.


VariablesDeclared: index, innerArray

Esto es bastante sencillo. index se declara dentro del inicializador de bucle e innerArray dentro del cuerpo del bucle for.

La rareza general de la API de análisis de flujo de datos hace que no le vea mucha utilidad, se les ocurre un lugar para usarla?

dotnet new gitignore


Cuando creo un nuevo proyecto en GitHub, lo primero que agrego en el repositorio inicial es el archivo .gitignore porque no quiero tener basura en mi repo. 

No mucha gente sabe que dotnet cli proporciona un comando para crear un archivo .gitignore, prácticamente perfecto.

En la carpeta raíz del proyecto, ejecutamos el siguiente comando :

dotnet new gitignore


Y listo! Se genera un archivo .gitignore con el siguiente contenido : 


## Ignore Visual Studio temporary files, build results, and

## files generated by popular Visual Studio add-ons.

##

## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore


# User-specific files

*.rsuser

*.suo

*.user

*.userosscache

*.sln.docstates


# User-specific files (MonoDevelop/Xamarin Studio)

*.userprefs


# Mono auto generated files

mono_crash.*


# Build results

[Dd]ebug/

[Dd]ebugPublic/

[Rr]elease/

[Rr]eleases/

x64/

x86/

[Ww][Ii][Nn]32/

[Aa][Rr][Mm]/

[Aa][Rr][Mm]64/

bld/

[Bb]in/

[Oo]bj/

[Ll]og/

[Ll]ogs/


# Visual Studio 2015/2017 cache/options directory

.vs/

# Uncomment if you have tasks that create the project's static files in wwwroot

#wwwroot/


# Visual Studio 2017 auto generated files

Generated\ Files/


# MSTest test Results

[Tt]est[Rr]esult*/

[Bb]uild[Ll]og.*


# NUnit

*.VisualState.xml

TestResult.xml

nunit-*.xml


# Build Results of an ATL Project

[Dd]ebugPS/

[Rr]eleasePS/

dlldata.c


# Benchmark Results

BenchmarkDotNet.Artifacts/


# .NET

project.lock.json

project.fragment.lock.json

artifacts/


# Tye

.tye/


# ASP.NET Scaffolding

ScaffoldingReadMe.txt


# StyleCop

StyleCopReport.xml


# Files built by Visual Studio

*_i.c

*_p.c

*_h.h

*.ilk

*.meta

*.obj

*.iobj

*.pch

*.pdb

*.ipdb

*.pgc

*.pgd

*.rsp

*.sbr

*.tlb

*.tli

*.tlh

*.tmp

*.tmp_proj

*_wpftmp.csproj

*.log

*.tlog

*.vspscc

*.vssscc

.builds

*.pidb

*.svclog

*.scc


# Chutzpah Test files

_Chutzpah*


# Visual C++ cache files

ipch/

*.aps

*.ncb

*.opendb

*.opensdf

*.sdf

*.cachefile

*.VC.db

*.VC.VC.opendb


# Visual Studio profiler

*.psess

*.vsp

*.vspx

*.sap


# Visual Studio Trace Files

*.e2e


# TFS 2012 Local Workspace

$tf/


# Guidance Automation Toolkit

*.gpState


# ReSharper is a .NET coding add-in

_ReSharper*/

*.[Rr]e[Ss]harper

*.DotSettings.user


# TeamCity is a build add-in

_TeamCity*


# DotCover is a Code Coverage Tool

*.dotCover


# AxoCover is a Code Coverage Tool

.axoCover/*

!.axoCover/settings.json


# Coverlet is a free, cross platform Code Coverage Tool

coverage*.json

coverage*.xml

coverage*.info


# Visual Studio code coverage results

*.coverage

*.coveragexml


# NCrunch

_NCrunch_*

.*crunch*.local.xml

nCrunchTemp_*


# MightyMoose

*.mm.*

AutoTest.Net/


# Web workbench (sass)

.sass-cache/


# Installshield output folder

[Ee]xpress/


# DocProject is a documentation generator add-in

DocProject/buildhelp/

DocProject/Help/*.HxT

DocProject/Help/*.HxC

DocProject/Help/*.hhc

DocProject/Help/*.hhk

DocProject/Help/*.hhp

DocProject/Help/Html2

DocProject/Help/html


# Click-Once directory

publish/


# Publish Web Output

*.[Pp]ublish.xml

*.azurePubxml

# Note: Comment the next line if you want to checkin your web deploy settings,

# but database connection strings (with potential passwords) will be unencrypted

*.pubxml

*.publishproj


# Microsoft Azure Web App publish settings. Comment the next line if you want to

# checkin your Azure Web App publish settings, but sensitive information contained

# in these scripts will be unencrypted

PublishScripts/


# NuGet Packages

*.nupkg

# NuGet Symbol Packages

*.snupkg

# The packages folder can be ignored because of Package Restore

**/[Pp]ackages/*

# except build/, which is used as an MSBuild target.

!**/[Pp]ackages/build/

# Uncomment if necessary however generally it will be regenerated when needed

#!**/[Pp]ackages/repositories.config

# NuGet v3's project.json files produces more ignorable files

*.nuget.props

*.nuget.targets


# Microsoft Azure Build Output

csx/

*.build.csdef


# Microsoft Azure Emulator

ecf/

rcf/


# Windows Store app package directories and files

AppPackages/

BundleArtifacts/

Package.StoreAssociation.xml

_pkginfo.txt

*.appx

*.appxbundle

*.appxupload


# Visual Studio cache files

# files ending in .cache can be ignored

*.[Cc]ache

# but keep track of directories ending in .cache

!?*.[Cc]ache/


# Others

ClientBin/

~$*

*~

*.dbmdl

*.dbproj.schemaview

*.jfm

*.pfx

*.publishsettings

orleans.codegen.cs


# Including strong name files can present a security risk

# (https://github.com/github/gitignore/pull/2483#issue-259490424)

#*.snk


# Since there are multiple workflows, uncomment next line to ignore bower_components

# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)

#bower_components/


# RIA/Silverlight projects

Generated_Code/


# Backup & report files from converting an old project file

# to a newer Visual Studio version. Backup files are not needed,

# because we have git ;-)

_UpgradeReport_Files/

Backup*/

UpgradeLog*.XML

UpgradeLog*.htm

ServiceFabricBackup/

*.rptproj.bak


# SQL Server files

*.mdf

*.ldf

*.ndf


# Business Intelligence projects

*.rdl.data

*.bim.layout

*.bim_*.settings

*.rptproj.rsuser

*- [Bb]ackup.rdl

*- [Bb]ackup ([0-9]).rdl

*- [Bb]ackup ([0-9][0-9]).rdl


# Microsoft Fakes

FakesAssemblies/


# GhostDoc plugin setting file

*.GhostDoc.xml


# Node.js Tools for Visual Studio

.ntvs_analysis.dat

node_modules/


# Visual Studio 6 build log

*.plg


# Visual Studio 6 workspace options file

*.opt


# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)

*.vbw


# Visual Studio 6 auto-generated project file (contains which files were open etc.)

*.vbp


# Visual Studio 6 workspace and project file (working project files containing files to include in project)

*.dsw

*.dsp


# Visual Studio 6 technical files

*.ncb

*.aps


# Visual Studio LightSwitch build output

**/*.HTMLClient/GeneratedArtifacts

**/*.DesktopClient/GeneratedArtifacts

**/*.DesktopClient/ModelManifest.xml

**/*.Server/GeneratedArtifacts

**/*.Server/ModelManifest.xml

_Pvt_Extensions


# Paket dependency manager

.paket/paket.exe

paket-files/


# FAKE - F# Make

.fake/


# CodeRush personal settings

.cr/personal


# Python Tools for Visual Studio (PTVS)

__pycache__/

*.pyc


# Cake - Uncomment if you are using it

# tools/**

# !tools/packages.config


# Tabs Studio

*.tss


# Telerik's JustMock configuration file

*.jmconfig


# BizTalk build output

*.btp.cs

*.btm.cs

*.odx.cs

*.xsd.cs


# OpenCover UI analysis results

OpenCover/


# Azure Stream Analytics local run output

ASALocalRun/


# MSBuild Binary and Structured Log

*.binlog


# NVidia Nsight GPU debugger configuration file

*.nvuser


# MFractors (Xamarin productivity tool) working folder

.mfractor/


# Local History for Visual Studio

.localhistory/


# Visual Studio History (VSHistory) files

.vshistory/


# BeatPulse healthcheck temp database

healthchecksdb


# Backup folder for Package Reference Convert tool in Visual Studio 2017

MigrationBackup/


# Ionide (cross platform F# VS Code tools) working folder

.ionide/


# Fody - auto-generated XML schema

FodyWeavers.xsd


# VS Code files for those working on multiple tools

.vscode/*

!.vscode/settings.json

!.vscode/tasks.json

!.vscode/launch.json

!.vscode/extensions.json

*.code-workspace


# Local History for Visual Studio Code

.history/


# Windows Installer files from build outputs

*.cab

*.msi

*.msix

*.msm

*.msp


# JetBrains Rider

*.sln.iml


##

## Visual studio for Mac

##



# globs

Makefile.in

*.userprefs

*.usertasks

config.make

config.status

aclocal.m4

install-sh

autom4te.cache/

*.tar.gz

tarballs/

test-results/


# Mac bundle stuff

*.dmg

*.app


# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore

# General

.DS_Store

.AppleDouble

.LSOverride


# Icon must end with two \r

Icon



# Thumbnails

._*


# Files that might appear in the root of a volume

.DocumentRevisions-V100

.fseventsd

.Spotlight-V100

.TemporaryItems

.Trashes

.VolumeIcon.icns

.com.apple.timemachine.donotpresent


# Directories potentially created on remote AFP share

.AppleDB

.AppleDesktop

Network Trash Folder

Temporary Items

.apdisk


# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore

# Windows thumbnail cache files

Thumbs.db

ehthumbs.db

ehthumbs_vista.db


# Dump file

*.stackdump


# Folder config file

[Dd]esktop.ini


# Recycle Bin used on file shares

$RECYCLE.BIN/


# Windows Installer files

*.cab

*.msi

*.msix

*.msm

*.msp


# Windows shortcuts

*.lnk


martes, 11 de abril de 2023

CSharpSyntaxWalker

En el post anterior, exploramos diferentes enfoques para separar partes del árbol de sintaxis. Este enfoque funciona bien cuando solo está interesado en partes específicas de la sintaxis (métodos, clases, declaración de lanzamiento, etc.). Es excelente para identificar ciertas partes del árbol de sintaxis para una mayor investigación.

Sin embargo, a veces nos gustaría operar en todos los nodos y tokens dentro de un árbol. Alternativamente, el orden en que visita estos nodos puede ser importante. Quizás esté intentando convertir C# en VB.Net. O tal vez le gustaría analizar un archivo C# y generar un archivo HTML estático con la coloración correcta. Ambos programas requerirían que visitáramos todos los nodos y tokens dentro de un árbol de sintaxis en el orden correcto.

La clase abstracta CSharpSyntaxWalker nos permite construir nuestro propio objeto que recorre la sintaxis y que puede visitar todos los nodos, tokens y trivia. Simplemente podemos heredar de CSharpSyntaxWalker y sobreescribir el método Visit() para visitar todos los nodos dentro del árbol.


public class CustomWalker : CSharpSyntaxWalker

{

    static int Tabs = 0;

    public override void Visit(SyntaxNode node)

    {

        Tabs++;

        var indents = new String('\t', Tabs);

        Console.WriteLine(indents + node.Kind());

        base.Visit(node);

        Tabs—;

    }

}


static void Main(string[] args)

{

    var tree = CSharpSyntaxTree.ParseText(@"

        public class MyClass

        {

            public void MyMethod()

            {

            }

            public void MyMethod(int n)

            {

            }

       ");

    

    var walker = new CustomWalker();

    walker.Visit(tree.GetRoot());

}


Este breve ejemplo contiene una implementación de CSharpSyntaxWalker llamada CustomWalker. CustomWalker sobreescribe el método Visit() e imprime el tipo de nodo que se está visitando actualmente. Es importante tener en cuenta que CustomWalker.Visit() también llama al método base.Visit(SyntaxNode). Esto permite que CSharpSyntaxWalker visite todos los nodos secundarios del nodo actual.


La salida para este programa:


Podemos ver claramente los distintos nodos del árbol de sintaxis y su relación entre sí. Hay dos MethodDeclarations hermanos que comparten la misma ClassDeclaration principal.

Este ejemplo anterior solo visita los nodos de un árbol de sintaxis, pero también podemos modificar CustomWalker para visitar tokens y trivias. La clase abstracta CSharpSyntaxWalker tiene un constructor que nos permite especificar la profundidad con la que queremos visitar.

Podemos modificar el ejemplo anterior para imprimir los nodos y sus tokens correspondientes en cada profundidad del árbol de sintaxis.


public class DeeperWalker : CSharpSyntaxWalker

{

    static int Tabs = 0;

    //NOTE: Make sure you invoke the base constructor with 

    //the correct SyntaxWalkerDepth. Otherwise VisitToken() will never get run.

    public DeeperWalker() : base(SyntaxWalkerDepth.Token)

    {

    }

    public override void Visit(SyntaxNode node)

    {

        Tabs++;

        var indents = new String('\t', Tabs);

        Console.WriteLine(indents + node.Kind());

        base.Visit(node);

        Tabs—;

    }


    public override void VisitToken(SyntaxToken token)

    {

        var indents = new String('\t', Tabs);

        Console.WriteLine(indents + token);

        base.VisitToken(token);

    }

}


Es importante pasar el argumento SyntaxWalkerDepth adecuado a CSharpSyntaxWalker. De lo contrario, nunca se llama al método VisitToken().


El resultado cuando usamos este CSharpSyntaxWalker:



La muestra anterior y esta comparten el mismo árbol de sintaxis. La salida contiene los mismos nodos de sintaxis, pero agregamos los tokens de sintaxis correspondientes para cada nodo.

En los ejemplos anteriores, visitamos todos los nodos y todos los tokens dentro de un árbol de sintaxis. Sin embargo, a veces solo nos gustaría visitar ciertos nodos, pero en el orden predefinido que proporciona CSharpSyntaxWalker. Afortunadamente, la API nos permite filtrar los nodos que nos gustaría visitar según su sintaxis.

En lugar de visitar todos los nodos como hicimos en ejemplos anteriores, lo siguiente solo visita los nodos ClassDeclarationSyntax y MethodDeclarationSyntax. Es extremadamente simple, simplemente imprime la concatenación del nombre de la clase con el nombre del método.


public class ClassMethodWalker : CSharpSyntaxWalker

{

    string className = String.Empty;

    public override void VisitClassDeclaration(ClassDeclarationSyntax node)

    {

        className = node.Identifier.ToString();

        base.VisitClassDeclaration(node);

    }


    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)

    {

        string methodName = node.Identifier.ToString();

        Console.WriteLine(className + '.' + methodName);

        base.VisitMethodDeclaration(node);

    }

}


static void Main(string[] args)

{

    var tree = CSharpSyntaxTree.ParseText(@"

    public class MyClass

    {

        public void MyMethod()

        {

        }

    }

    public class MyOtherClass

    {

        public void MyMethod(int n)

        {

        }

    }

   ");


    var walker = new ClassMethodWalker();

    walker.Visit(tree.GetRoot());

}


Esta muestra simplemente genera:

MiClase.MiMétodo

MiOtraClase.MiMétodo


CSharpSyntaxWalker actúa como una gran API para analizar árboles de sintaxis. Permite lograr mucho sin recurrir al modelo semántico y forzar una compilación (posiblemente) costosa. Siempre que sea importante inspeccionar los árboles de sintaxis y el orden, CSharpSyntaxWalker suele ser lo que está buscando.


Tendencias 2023 de datos y AI

 

jueves, 6 de abril de 2023

Primeros pasos con Phoenix parte 7

 


Agreguemos un poco de complejidad a nuestra aplicación. Vamos a agregar una nueva página que reconocerá una parte de la URL, la etiquetará como "mensajero" y la pasará a través del controlador a la plantilla para que nuestro mensajero pueda saludar.

Como hicimos la última vez, lo primero que haremos será crear una nueva ruta.

Vamos a reutilizar HelloController creado en el post anterior y agregaremos una nueva acción de mostrar. Agregaremos una línea justo debajo de nuestra última ruta, así:


scope "/", HelloWeb do

  pipe_through :browser

  get "/", PageController, :home

  get "/hello", HelloController, :index

  get "/hello/:messenger", HelloController, :show

end


Usamos la sintaxis :messenger en la ruta. Phoenix tomará cualquier valor que aparezca en esa posición en la URL y lo convertirá en un parámetro. Por ejemplo, si apuntamos el navegador a: http://localhost:4000/hello/Frank, el valor de "messenger" será "Frank".

Las solicitudes a nuestra nueva ruta serán manejadas por la acción show HelloWeb.HelloController. Ya tenemos el controlador en lib/hello_web/controllers/hello_controller.ex, así que todo lo que tenemos que hacer es editar ese controlador y agregarle una acción show. Esta vez, necesitaremos extraer el messenger de los parámetros para que podamos pasarlo a la plantilla. Para hacer eso, agregamos esta función show al controlador:

def show(conn, %{"messenger" => messenger}) do

  render(conn, :show, messenger: messenger)

end


Dentro del cuerpo de la acción show, también pasamos un tercer argumento a la función render, un par clave-valor donde :messenger es la clave, y la variable messenger se pasa como el valor.

Si el cuerpo de la acción necesita acceso al mapa completo de parámetros enlazados a la variable params, además de la variable messenger enlazada, podríamos definir show/2 así:


def show(conn, %{"messenger" => messenger} = params) do

  ...

end


Es bueno recordar que las claves del mapa de parámetros siempre serán cadenas, y que el signo igual no representa una asignación, sino una afirmación de coincidencia de patrón.

Ahora necesitaremos una nueva plantilla. Dado que es para la acción de mostrar de HelloController, irá al directorio lib/hello_web/controllers/hello_html y se llamará show.html.heex. Se parecerá sorprendentemente a nuestra plantilla index.html.heex, excepto que necesitaremos mostrar el nombre de nuestro mensajero.

Para hacer eso, usaremos las etiquetas HEEx especiales para ejecutar expresiones Elixir: <%= %>. Observe que la etiqueta inicial tiene un signo igual como este: <%= . Eso significa que se ejecutará cualquier código de Elixir que se encuentre entre esas etiquetas, y el valor resultante reemplazará la etiqueta en la salida HTML. Si faltara el signo igual, el código aún se ejecutaría, pero el valor no aparecería en la página.

Nuestras plantillas están escritas en HEEx (HTML+EEx). HEEx es un superconjunto de EEx, por lo que comparte la sintaxis <%= %>.


Y así es como debería verse la plantilla:


<section>

  <h2>Hello World, from <%= @messenger %>!</h2>

</section>


Nuestro messenger aparece como @messenger.

Los valores que pasamos a la vista desde el controlador se denominan colectivamente nuestras "asignaciones". Podríamos acceder a nuestro valor de mensajería a través de asignaciones.messenger pero a través de alguna metaprogramación, Phoenix nos brinda la sintaxis @ mucho más limpia para usar en plantillas.

Si vamos a http://localhost:4000/hello/emanuel, debería ver una página similar a esta:

Hello World, from emanuel!

miércoles, 5 de abril de 2023

Analizando el Syntax Trees con LINQ


La idea principal sobre  Syntax Trees es que dada una cadena que contiene código C#, el compilador crea una representación de árbol (llamada Árbol de sintaxis) de la cadena. El poder de Roslyn es que nos permite consultar este árbol de sintaxis con LINQ.

Aquí hay una muestra en la que usamos Roslyn para crear un árbol de sintaxis a partir de una cadena. Debemos agregar referencias a Microsoft.CodeAnalysis y Microsoft.CodeAnalysis.CSharp. 


using Microsoft.CodeAnalysis.CSharp;

using Microsoft.CodeAnalysis.CSharp.Syntax;


var tree = CSharpSyntaxTree.ParseText(@"

    public class MyClass

    {

        public void MyMethod()

        {

        }

    }");


var syntaxRoot = tree.GetRoot();

var MyClass = syntaxRoot.DescendantNodes().OfType<ClassDeclarationSyntax>().First();

var MyMethod = syntaxRoot.DescendantNodes().OfType<MethodDeclarationSyntax>().First();


Console.WriteLine(MyClass.Identifier.ToString());

Console.WriteLine(MyMethod.Identifier.ToString());


Primero comenzamos analizando una cadena que contiene código C# y obteniendo la raíz de este árbol de sintaxis. Desde este punto, es extremadamente fácil recuperar elementos que nos gustaría usar LINQ. Dada la raíz del árbol, miramos todos los objetos descendientes y los filtramos por su tipo. Si bien solo hemos usado ClassDeclarationSyntax y MethodDeclarationSyntax, hay piezas correspondientes de sintaxis para cualquier característica de C#.

Intellisense de Visual Studio es extremadamente valioso para explorar los diversos tipos de sintaxis de C# que podemos usar.

Podemos componer expresiones LINQ más avanzadas como cabría esperar:


var tree = CSharpSyntaxTree.ParseText(@"

    public class MyClass

    {

        public void MyMethod()

        {

        }

        public void MyMethod(int n)

        {

        }

    }");


var syntaxRoot = tree.GetRoot();

var MyMethod = syntaxRoot.DescendantNodes().OfType<MethodDeclarationSyntax>()

    .Where(n => n.ParameterList.Parameters.Any()).First();


//Find the type that contains this method

var containingType = MyMethod.Ancestors().OfType<TypeDeclarationSyntax>().First();


Console.WriteLine(containingType.Identifier.ToString());

Console.WriteLine(MyMethod.ToString());


Arriba, comenzamos buscando todos los métodos y luego filtramos por aquellos que aceptan parámetros. Luego tomamos este método y avanzamos hacia arriba a través del árbol con el método Ancestors(), buscando el primer tipo que contiene este método.

Existen algunas limitaciones en el tipo de información que puede descubrir a un nivel puramente sintáctico y para superarlas debemos hacer uso del modelo semántico de Roslyn.

martes, 4 de abril de 2023

Roslyn, Compiler-as-a-Service parte 2

Los principales tipos de Syntax Nodes se derivan de la clase base SyntaxNode. El conjunto de Syntax Nodes no es extensible. Estas clases forman la construcción para crear declaraciones, sentencias, cláusulas y expresiones. No son terminales, lo que significa que tienen subnodos por los que se puede navegar a través de propiedades o métodos escritos.


/* tree is given after parsing the Hello World example with the

Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText method */

var syntaxRoot = tree.GetRoot();

var Demo = syntaxRoot.DescendantNodes().OfType<ClassDeclarationSyntax>().First();


Los Syntax Tokens son terminales y no pueden ser padres de otros nodos o tipos de tokens. Representan identificadores, palabras clave, literales y puntuaciones. Ofrece propiedades para acceder a los valores analizados desde el código fuente de entrada.

Syntax Trivia representan porciones de texto insignificantes, que pueden aparecer como espacios en blanco, comentarios y directivas de preprocesador en cualquier posición dentro de la entrada de origen. No forman parte de la sintaxis del lenguaje normal y no se agregarán como elementos secundarios de los nodos de sintaxis. Tampoco tienen un nodo principal. La clase base se llama SyntaxTrivia.

Los nodos, tokens y trivias contienen Spans para el posicionamiento y la cantidad de ocurrencias de caracteres para asociar la ubicación correcta desde la fuente de entrada. Esto se puede utilizar para la depuración o la determinación de información de errores.

Los Kinds o tipos se utilizan como propiedades para nodos, tokens y trivias para distinguir los tipos correspondientes y proporcionar las conversiones correctas. La clase SyntaxKind se puede representar como un tipo de enumeración en el lenguaje de destino C# o VB.

Al analizar el código fuente, pueden ocurrir errores, que pueden ubicarse y marcarse como incorrectos o incompletos, por ejemplo, falta un token o no es válido. El analizador puede omitir tokens no válidos y continuar buscando el siguiente token válido para continuar con el análisis. Los tokens omitidos se adjuntarán como Trivia Node of Kind SkippedToken.

Roslyn proporciona una rica API para crear unidades de compilación AST por código. Utiliza el patrón de fábrica y proporciona muchas clases y métodos estáticos o enumeraciones, que se pueden usar a través del encadenamiento para construir estas soluciones. El encadenamiento es un diseño de llamada de método fluido utilizado en lenguajes de programación orientados a objetos, donde cada método devuelve un objeto, lo que permite llamar a la siguiente declaración sin requerir variables que almacenen los resultados intermedios. El siguiente código cortado muestra un ejemplo de cómo usar la API de Roslyn para hacer una declaración de clase simple.

var tree = SyntaxFactory.CompilationUnit().AddMembers(

    SyntaxFactory.NamespaceDeclaration(

        SyntaxFactory.IdentifierName("Example")).AddMembers(

            SyntaxFactory.ClassDeclaration("Demo").AddMembers(

                SyntaxFactory.MethodDeclaration(

                    SyntaxFactory.PredefinedType(

                        SyntaxFactory.Token(SyntaxKind.VoidKeyword)), "Main")

                    .AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword))

                    .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))

                    .WithBody(SyntaxFactory.Block()).AddBodyStatements(

                         SyntaxFactory.ReturnStatement())

           )

     )

);

Aunque la representación del árbol sintáctico del código fuente de entrada ofrece muchas funciones, solo analiza las estructuras léxica y sintáctica. Para cubrir completamente todos los aspectos de los lenguajes de programación también es necesario definir su semántica, que marca su comportamiento. Las designaciones de variables locales y miembro pueden superponerse entre sí y diferir entre sus alcances y las reglas de acción deben integrarse dentro de esos cierres. Para ello, Roslyn utiliza símbolos. Un símbolo representa un elemento, que lleva los metadatos recibidos de la fuente de entrada. Se puede acceder a estos a través de la tabla de símbolos proporcionada, también representada en una estructura de árbol, comenzando desde el elemento raíz, que es el espacio de nombres global. Los símbolos se derivan de la interfaz ISymbol, mientras que el compilador proporciona las propiedades y los métodos. Los símbolos ofrecen espacios de nombres, tipos y miembros entre el código fuente y los metadatos y sus conceptos de lenguaje son similares a la API de Reflection utilizada por el sistema de tipos CLR. También es importante saber que el acceso al árbol del modelo semántico desencadena una compilación, lo que significa que es más costoso en comparación con el acceso al árbol sintáctico.

Cada símbolo contiene información sobre

  • la ubicación de la declaración (en el fuente o en los metadatos)
  • en qué espacio de nombres o tipo existe este símbolo
  • la información si el símbolo es abstracto, estático, sellado, etc.

El siguiente fragmento de código muestra cómo agregar información semántica al ejemplo de Hello World declarado anteriormente.


/* tree is given after parsing the Hello World example with the Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText method */ 

var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly. Location); 

var compilation = CSharpCompilation.Create("DemoCompilation", syntaxTrees: new[] { tree }, references: new[] { mscorlib }); 

var model = compilation.GetSemanticModel(tree)


Roslyn es una rica plataforma de servicio de compilación que permite un acceso profundo al marco .NET de Microsoft y sus subconjuntos de lenguajes admitidos. Aunque Roslyn ofrece una variedad de posibilidades de bajo nivel para construir AST, se abstrae muy bien de estas capas y proporciona una API limpia e intuitiva. 


Inscripciones Abiertas a Cursos Gugler 2023

 

PRENSA GUGLER  LAB 2023

  NOTICIAS ACTUALES

informacion  OFERTA ACADÉMICA


 Se encuentran abiertas las inscripciones para el primer cuatrimestre del año 2023, para todas las capacitaciones dictadas por el Laboratorio de Investigación Gugler. Podés asegurar tu lugar en el curso y comisión que desees !!!.

Las clases inician:

Martes 18/04 , Miércoles 19/04, Jueves 20/04 o Sábado 22/04, según el curso que elegiste

 

Inscribirteclic aquí

Cursos, Horarios y comisionesclic aquí.

 

 

Dictamos nuestros cursos en la Facultad de Ciencia y Tecnología, perteneciente a la Universidad Autónoma de Entre Ríos. En nuestro portafolio de capacitación encontrarás:

MODALIDAD PRESENCIAL

  • Cursos de Programación:
    Programación en Python.
    Programación web frontend.
     
  • Cursos de Mantenimiento/Reparación:
    Reparación y Mantenimiento de PC.

 MODALIDAD DISTANCIA

  • Cursos de Programación:
    Programación en PHP.
    Programación en Java.
informacion   MÁS INFORMACIÓN 

informacion LABORATORIO DE INVESTIGACIÓN GUGLER

Si deseas comunicarte con nosotros, te recordamos que podes hacerlo a través de los siguientes portales, en los cuales encontrarás información sobre Gugler.

TEL: (0343) - 4975066 Interno 119

Sitio Oficial:  www.gugler.com.ar

Campus:  campusvirtual.gugler.com.ar

Sistema de Gestión Cursos:  sgc.gugler.com.ar

Sistema de Gestión Cursos Móvil: Aquí 

Sistema de Documentación:  sgd.gugler.com.ar

Sistema de Validación:  giua.gugler.com.ar

                   Twitter                Facebook

Laboratorio Gugler

partir del 2012, la Facultad de Ciencia y Tecnología nos declaro:  "Laboratorio de Investigación".

 

El laboratorio ha ampliado las incumbencias de Gugler, ya que además de la capacitación, la promoción y difusión del software libre, ahora abarcará actividades como publicaciones y proyectos de investigación, así como también el dictado y participación en conferencias o exposiciones de ámbitos académicos y científicos.

 

Ante cualquier duda comunicarse con nosotros a gugler_contacto@uader.edu.ar

GUGLER PRESS

 


Roslyn, Compiler-as-a-Service

 


Roslyn expone el análisis del código del compilador para desarrolladores de C# y Visual Basic a través de tres capas principales de API. 

La capa Compiler Pipeline maneja el análisis de nivel inferior, el análisis de símbolos y el procesamiento de metadatos, incluidos los enlaces y la emisión de IL. La capa Compiler API ofrece acceso a la representación AST (abstract syntax tree) de los datos procesados y funciona como una abstracción de nivel superior para los desarrolladores de C# o Visual Basic. La capa Language Service proporciona un conjunto de herramientas de nivel superior para operar en los datos de la API del Compilador, incluidas las operaciones de navegación, la información y las posibilidades de formato.

Compiler Pipeline: Compiler Pipeline procesa la fuente de entrada en diferentes fases, comenzando con el proceso de análisis, donde la fuente se tokeniza en las reglas de lenguaje gramatical correspondientes. Posteriormente, los metadatos se analizan para extraer los símbolos requeridos, también llamada fase de declaración. El Binder hace coincidir los símbolos con los identificadores correspondientes y, al final, el Emisor construye un conjunto completo.

Compiler API: en equivalencia con las fases descritas anteriormente de Compiler Pipeline, Compiler API ofrece un modelo de objeto representativo en el lenguaje de destino actual para acceder a la información necesaria. La representación de la fase Parser es traducida por la Syntax Tree API como modelo AST, seguida de modelos de metadatos para los símbolos y enlaces, y la Emit API se abstrae del productor de código de bytes IL correspondiente.

Language Service: El Servicio Lingüístico se adapta a las capas y fases subyacentes. Por ejemplo, utiliza la tabla de símbolos para el Examinador de objetos o las funciones de navegación, y la representación del árbol de sintaxis para las funciones de esquematización y formato.

Roslyn está construido como un conjunto de APIs de dos capas, la capa de API del compilador y la capa de API del espacio de trabajo. La capa del compilador contiene los modelos de objetos que consisten en la información extraída sobre las fases individuales y los datos sintácticos y semánticos analizados desde el fuente. Ofrece una instantánea inmutable de una única invocación de un compilador. Esta capa es independiente de cualquier componente de Visual Studio. Esto permite que las herramientas de diagnóstico definidas por el usuario se conecten al proceso de compilación real para obtener información sobre errores y advertencias. La API del espacio de trabajo resuelve las dependencias del proyecto y utiliza la capa del compilador para proporcionar modelos de objetos para soluciones de análisis y refactorización.

La API del árbol de sintaxis es el componente central del marco de Roslyn. Vincula todas las funciones proporcionadas a las clases de modelos de objetos abstractos, conocidos como AST. El análisis de código, la refactorización, los componentes IDE, la información de origen, las construcciones gramaticales y muchas más funciones convergen en un punto, que es la API del árbol sintáctico. También es importante saber que todos los resultados analizados se pueden revertir a su texto de origen original (completamente de ida y vuelta). Además, el árbol de sintaxis es seguro para subprocesos e inmutable, lo que permite que múltiples usuarios interactúen simultáneamente sin interferir. Esto también significa que no se pueden enviar modificaciones en los árboles de sintaxis. Los métodos de fábricación proporcionan instantáneas adicionales del árbol de sintaxis para operar indirectamente con ellos, a costa de poca sobrecarga de memoria. Para ilustrar los principales componentes y características de la API del árbol sintáctico, el siguiente programa Hello World escrito en C# servirá como una ilustración práctica.


using System;

    namespace Demo {

        class Program {

            static void Main() {

                Console.WriteLine("Hello World!");

            }

        }

}

Roslyn también brinda posibilidades para visualizar secciones de código existentes como un árbol de sintaxis utilizando el SDK de la plataforma del compilador .NET, que está disponible como un paquete NuGet para Visual Studio. Después de instalar el paquete adicional, Visual Studio agrega un nuevo elemento de menú en la pestaña Ver/Otras ventanas/Visualizador de sintaxis. El visualizador de sintaxis crea el siguiente árbol de sintaxis a partir del código mostrado anteriormente, como se muestra en la figura 


También es posible ver una representación visual del árbol de sintaxis como se muestra en la figura. 


La leyenda en el lado derecho proporciona una descripción general de los elementos del árbol de sintaxis resultantes de un procedimiento de análisis.

Cada árbol de sintaxis de Roslyn consta de los siguientes elementos:

Nodos de sintaxis para declaraciones, sentencias, cláusulas y expresiones 

Tokens de sintaxis para representar terminales, los fragmentos de código más pequeños 

Sintaxis Trivia para espacios en blanco, comentarios y directivas de preprocesador, que son los elementos de código insignificantes que se pueden omitir después del análisis

Spanns para el posicionamiento del texto y la cantidad de caracteres utilizados por un nodo, token o trivia 

Tipos para identificar el elemento de sintaxis exacto correspondiente, que se puede convertir en enumeraciones específicas del lenguaje

Los errores de sintaxis, que no se ajustan a la gramática y se pueden adjuntar elementos de diagnóstico adicionales para el desarrollo o la depuración


lunes, 3 de abril de 2023

Primeros pasos con Phoenix parte 6


Hay un par de cosas interesantes para notar sobre el post anterior. No necesitábamos detener y reiniciar el servidor mientras realizábamos los cambios. ¡Sí, Phoenix tiene recarga de código caliente! Además, aunque nuestro archivo index.html.heex consta de una sola etiqueta de sección, la página que obtenemos es un documento HTML completo. La plantilla index en realidad se representa una pagina compuesta por el diseño globlal de lib/hello_web/components/layouts/root.html.heex y lib/hello_web/components/layouts/app.html.heex. Si abrimos este archivo, verá una línea que se ve así en la parte inferior:

<%= @inner_content %>

Que es donde se inyecta el html particular. 

Plug es una biblioteca y una especificación para unir aplicaciones web. Es una parte esencial de cómo Phoenix maneja las solicitudes. Cada conector define una parte del procesamiento de solicitudes. En el endpoint encontrarás un esqueleto más o menos así:


defmodule HelloWeb.Endpoint do

  use Phoenix.Endpoint, otp_app: :hello


  plug Plug.Static, ...

  plug Plug.RequestId

  plug Plug.Telemetry, ...

  plug Plug.Parsers, ...

  plug Plug.MethodOverride

  plug Plug.Head

  plug Plug.Session, ...

  plug HelloWeb.Router

end


Cada uno de estos plug tiene una responsabilidad específica. El último complemento es precisamente el módulo HelloWeb.Router. Esto permite que el endpoint delegue todo el procesamiento de solicitudes adicional al enrutador. Como sabemos ahora, su responsabilidad principal es mapear pares de verbo/ruta a controladores. Luego, el controlador le dice a una vista que represente una plantilla.

En este momento, puede estar pensando que pueden ser muchos pasos para simplemente renderizar una página. Sin embargo, a medida que nuestra aplicación crezca en complejidad, veremos que cada capa tiene un propósito distinto:

Endpoint (Phoenix.Endpoint): contiene la ruta común e inicial por la que pasan todas las solicitudes. Si desea que suceda algo en todas las solicitudes, se dirige al punto final.

Router (Phoenix.Router): es responsable de enviar verbos/ruta a los controladores. El enrutador también nos permite medir la funcionalidad. Por ejemplo, algunas páginas de su aplicación pueden requerir la autenticación del usuario, otras no.

Controlador (Phoenix.Controller): el trabajo del controlador es recuperar la información solicitada, hablar con su dominio comercial y preparar datos para la capa de presentación.

Vista: la vista maneja los datos estructurados del controlador y los convierte en una presentación para mostrar a los usuarios. Las vistas a menudo reciben el nombre del formato de contenido que representan.

Hagamos un resumen rápido y cómo los últimos tres componentes funcionan juntos agregando otra página.