From 4b319bc857676347783ff28b5eee4d7015da757a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Olazagoitia?= Date: Sun, 26 Mar 2017 13:30:16 +0200 Subject: [PATCH 1/3] =?UTF-8?q?Solucionar=20visualizaci=C3=B3n=20del=20cap?= =?UTF-8?q?itulo=207?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- chapters/07_elife.html | 313 ++++++++++++++++++++++------------------- 2 files changed, 172 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index aca5a1a..e1af95a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# Eloquent-Javscritp-es-online +# Eloquent-Javscript-es-online Eloquent JavaScript 2 en español será publicado aquí para mostrar avances y que se pueda ir leyendo y haciendo sugerencias. diff --git a/chapters/07_elife.html b/chapters/07_elife.html index 0a68d77..dc8c80e 100644 --- a/chapters/07_elife.html +++ b/chapters/07_elife.html @@ -362,7 +362,7 @@

Una simulación

Si los bichos sólo se mueven y comen unos a otros, el mundo pronto sucumbirá a la ley de entropía creciente, se quedará sin energía, y se convertirá en un desierto sin vida. Para evitar que esto suceda (demasiado rápido, al menos), añadimos plantas al mundo. Las plantas no se mueven. Simplemente utilizan la fotosíntesis para crecer (es decir, aumentar su energía) y reproducirse.

Para que esto funcione, necesitaremos un mundo con un método letAct diferente. Podríamos simplemente reemplazar el método del (prototipo world), pero me he vuelto muy apegado a nuestra simulación con las criaturas que siguen a la pared y odiaría romper ese viejo mundo.

Una solución es usar la (herencia). Creamos un nuevo (constructor), LifelikeWorld, cuyo prototipo se basa en el (prototipo World) pero que anula el método letAct. El nuevo método letAct delega el trabajo de realizar una acción a varias funciones almacenadas en el objeto actionTypes.

-
function LifelikeWorld(map, legend) {
+
function LifelikeWorld(map, legend) {
   World.call(this, map, legend);
 }
 LifelikeWorld.prototype = Object.create(World.prototype);
@@ -380,147 +380,176 @@ 

Una simulación if (critter.energy <= 0) this.grid.set(vector, null); } -}; - ---- -(((electronic life)))(((function,as value)))(((call method)))(((this)))El nuevo método _letAct_ comprueba primero si se ha devuelto una acción, si existe una (función de controlador) para este tipo de acción y, por último, si ese controlador devuelve _true_, lo que indica que se ha tratado correctamente la acción. - -Tenga en cuenta el uso de _call_ para dar al manejador acceso al mundo a través de _this_. -Si la acción no funcionó por alguna razón, la acción predeterminada es que la criatura simplemente espere. Pierde un quinto punto de energía, y si su nivel de energía es igual o menor que cero, la criatura muere y se borra de la cuadrícula. - -== Manejadores de acciones == - -(((photosynthesis)))La acción más simple que una criatura puede realizar es "crecer" (_grow_), usada por las plantas. Cuando se devuelve un objeto de acción como _{type: "grow"}_, se llamará al siguiente método del controlador: - -// include_code - -[source,javascript]

-

actionTypes.grow = function(critter) { - critter.energy += 0.5; - return true; -};

-
Crecer siempre tiene éxito y añade medio punto al nivel de energía (_energy_) de la planta.
-Moverse es más complicado.
-
-// include_code
-
-[source,javascript]
-

actionTypes.move = function(critter, vector, action) { - var dest = this.checkDestination(action, vector); - if (dest == null || - critter.energy ⇐ 1 || - this.grid.get(dest) != null) - return false; - critter.energy -= 1; - this.grid.set(vector, null); - this.grid.set(dest, critter); - return true; -};

-
(((validation)))Esta acción comprueba primero, utilizando el método _checkDestination_ definido anteriormente, si la acción proporciona un destino válido. Si no, o si el destino no está vacío, o si el _critter_ carece de la energía requerida, _move_ devuelve false para indicar que no se tomó ninguna acción. De lo contrario, se mueve el _critter_ y resta el costo de la energía.
-
-(((food)))Además de moverse, las criaturas pueden comer.
-
-// include_code
-
-[source,javascript]
-

actionTypes.eat = function(critter, vector, action) { - var dest = this.checkDestination(action, vector); - var atDest = dest != null && this.grid.get(dest); - if (!atDest || atDest.energy == null) - return false; - critter.energy += atDest.energy; - this.grid.set(dest, null); - return true; -};

-
(((validation)))Comer otra criatura (_critter_) también implica proporcionar un cuadrado de destino válido. Esta vez, el destino no debe estar vacío y debe contener algo con energía, como un _critter_ (pero no una pared - las paredes no son comestibles). Si es así, la energía de la comida es transferida al comedor, y la víctima es removida de la cuadrícula.
-
-(((reproduction)))Y finalmente, permitimos que nuestras criaturas se reproduzcan.
-
-// include_code
-
-[source,javascript]
-

actionTypes.reproduce = function(critter, vector, action) { - var baby = elementFromChar(this.legend, - critter.originChar); - var dest = this.checkDestination(action, vector); - if (dest == null || - critter.energy ⇐ 2 * baby.energy || - this.grid.get(dest) != null) - return false; - critter.energy -= 2 * baby.energy; - this.grid.set(dest, baby); - return true; -};

-
(((electronic life)))Reproducirse cuesta dos veces el nivel de energía de un _critter_ recién nacido. Así que primero creamos un bebé (hipotético) usando _elementFromChar_ en el propio carácter de origen del _critter_. Una vez que tenemos un bebé, podemos encontrar su nivel de energía y probar si el padre tiene suficiente energía para llevarlo con éxito al mundo. También necesitamos un destino válido (y vacío).
-
-(((reproduction)))Si todo está bien, el bebé es puesto en la cuadrícula (ya no es hipotético), y la energía se gasta.
-
-== Llenar el nuevo mundo ==
-
-(((Plant type)))(((electronic life)))Ahora tenemos un marco (_framework_) para simular estas criaturas en forma más realista. Podríamos poner las criaturas del viejo mundo en ella, pero sólo morirían ya que no tienen una propiedad de energía. Así que hagamos nuevos. Primero escribiremos una planta (_Plant_), que es una forma de vida bastante simple.
-
-// include_code
-
-[source,javascript]
-

function Plant() { - this.energy = 3 + Math.random() * 4; +};

+

El nuevo método letAct comprueba primero si se ha devuelto una acción, si existe una (función de controlador) para este tipo de acción y, por último, si ese controlador devuelve true, lo que indica que se ha tratado correctamente la acción.

+

Tenga en cuenta el uso de call para dar al manejador acceso al mundo a través de this. +Si la acción no funcionó por alguna razón, la acción predeterminada es que la criatura simplemente espere. Pierde un quinto punto de energía, y si su nivel de energía es igual o menor que cero, la criatura muere y se borra de la cuadrícula.

+

Manejadores de acciones

+

+
actionTypes.grow = function(critter) {
+  critter.energy += 0.5;
+  return true;
+};
+

Crecer siempre tiene éxito y añade medio punto al nivel de energía (energy) de la planta. +Moverse es más complicado.

+
actionTypes.move = function(critter, vector, action) {
+  var dest = this.checkDestination(action, vector);
+  if (dest == null ||
+      critter.energy <= 1 ||
+      this.grid.get(dest) != null)
+    return false;
+  critter.energy -= 1;
+  this.grid.set(vector, null);
+  this.grid.set(dest, critter);
+  return true;
+};
+

Esta acción comprueba primero, utilizando el método checkDestination definido anteriormente, si la acción proporciona un destino válido. Si no, o si el destino no está vacío, o si el critter carece de la energía requerida, move devuelve false para indicar que no se tomó ninguna acción. De lo contrario, se mueve el critter y resta el costo de la energía.

+

Además de moverse, las criaturas pueden comer.

+
actionTypes.eat = function(critter, vector, action) {
+  var dest = this.checkDestination(action, vector);
+  var atDest = dest != null && this.grid.get(dest);
+  if (!atDest || atDest.energy == null)
+    return false;
+  critter.energy += atDest.energy;
+  this.grid.set(dest, null);
+  return true;
+};
+

Comer otra criatura (critter) también implica proporcionar un cuadrado de destino válido. Esta vez, el destino no debe estar vacío y debe contener algo con energía, como un critter (pero no una pared - las paredes no son comestibles). Si es así, la energía de la comida es transferida al comedor, y la víctima es removida de la cuadrícula.

+

Y finalmente, permitimos que nuestras criaturas se reproduzcan.

+
actionTypes.reproduce = function(critter, vector, action) {
+  var baby = elementFromChar(this.legend,
+                             critter.originChar);
+  var dest = this.checkDestination(action, vector);
+  if (dest == null ||
+      critter.energy <= 2 * baby.energy ||
+      this.grid.get(dest) != null)
+    return false;
+  critter.energy -= 2 * baby.energy;
+  this.grid.set(dest, baby);
+  return true;
+};
+

Reproducirse cuesta dos veces el nivel de energía de un critter recién nacido. Así que primero creamos un bebé (hipotético) usando elementFromChar en el propio carácter de origen del critter. Una vez que tenemos un bebé, podemos encontrar su nivel de energía y probar si el padre tiene suficiente energía para llevarlo con éxito al mundo. También necesitamos un destino válido (y vacío).

+

Si todo está bien, el bebé es puesto en la cuadrícula (ya no es hipotético), y la energía se gasta.

+

Llenar el nuevo mundo

+

Ahora tenemos un marco (framework) para simular estas criaturas en forma más realista. Podríamos poner las criaturas del viejo mundo en ella, pero sólo morirían ya que no tienen una propiedad de energía. Así que hagamos nuevos. Primero escribiremos una planta (Plant), que es una forma de vida bastante simple.

+
function Plant() {
+  this.energy = 3 + Math.random() * 4;
 }
-Plant.prototype.act = function(view) {
-  if (this.energy > 15) {
-    var space = view.find(" ");
-    if (space)
+Plant.prototype.act = function(view) {
+  if (this.energy > 15) {
+    var space = view.find(" ");
+    if (space)
+      return {type: "reproduce", direction: space};
   }
-  if (this.energy < 20)
-};

-
(((reproduction)))(((photosynthesis)))(((random
-number)))(((Math.random function)))Las plantas comienzan con un nivel de energía entre 3 y 7, aleatorizado para que no se reproduzcan todos en el mismo turno (_turn_). Cuando una planta alcanza 15 puntos de energía y hay un espacio vacío cerca, se reproduce en ese espacio vacío. Si una planta no puede reproducirse, simplemente crece hasta alcanzar el nivel de energía 20.
-
-(((critter)))(((PlantEater type)))(((herbivore)))(((food chain)))Ahora definimos un comedor de plantas (_PlantEater_).
-
-// include_code
-
-[source,javascript]
-

function PlantEater() { - this.energy = 20; + if (this.energy < 20) + return {type: "grow"}; +};

+

Las plantas comienzan con un nivel de energía entre 3 y 7, aleatorizado para que no se reproduzcan todos en el mismo turno (turn). Cuando una planta alcanza 15 puntos de energía y hay un espacio vacío cerca, se reproduce en ese espacio vacío. Si una planta no puede reproducirse, simplemente crece hasta alcanzar el nivel de energía 20.

+

Ahora definimos un comedor de plantas (PlantEater).

+
function PlantEater() {
+  this.energy = 20;
 }
-PlantEater.prototype.act = function(view) {
-  var space = view.find(" ");
-  if (this.energy > 60 && space)
-  var plant = view.find("*");
-  if (plant)
-  if (space)
-};

-
Usaremos el carácter _*_ para las plantas (_Plant_), así que eso es lo que esta criatura buscará cuando busque comida.
-
-== Darle vida ==
-
-(((electronic life)))Y eso nos da suficientes elementos para probar nuestro nuevo mundo. Imagínese el siguiente mapa como un valle cubierto de hierba con una manada de herbívoros en ella, algunas rocas y una exuberante vegetación en todas partes.
-
-// include_code
-
-[source,javascript]
-

var valley = new LifelikeWorld( - ["#####", - " #", - " * ", - " O *", - "# O ", - " O ", - " ", - " O * #", - "* O #", - "* O ", - "* # ", - "######"], - {"#": Wall, - "O": PlantEater, - "": Plant} -);

-
(((animation)))(((simulation)))Veamos ver ocurre cuando ejecutamos este codigo.
-(!book These snapshots illustrate a typical run of this world.!)
-
-
-// start_code
-// test: no
-
-[source,javascript]
-

animateWorld(valley);

+PlantEater.prototype.act = function(view) { + var space = view.find(" "); + if (this.energy > 60 && space) + return {type: "reproduce", direction: space}; + var plant = view.find("*"); + if (plant) + return {type: "eat", direction: plant}; + if (space) + return {type: "move", direction: space}; +};
+

Usaremos el carácter * para las plantas (Plant), así que eso es lo que esta criatura buscará cuando busque comida.

+

Darle vida

+

Y eso nos da suficientes elementos para probar nuestro nuevo mundo. Imagínese el siguiente mapa como un valle cubierto de hierba con una manada de herbívoros en ella, algunas rocas y una exuberante vegetación en todas partes.

+
var valley = new LifelikeWorld(
+  ["############################",
+   "#####                 ######",
+   "##   ***                **##",
+   "#   *##**         **  O  *##",
+   "#    ***     O    ##**    *#",
+   "#       O         ##***    #",
+   "#                 ##**     #",
+   "#   O       #*             #",
+   "#*          #**       O    #",
+   "#***        ##**    O    **#",
+   "##****     ###***       *###",
+   "############################"],
+  {"#": Wall,
+   "O": PlantEater,
+   "*": Plant}
+);
+

Veamos ver ocurre cuando ejecutamos este codigo.

+
animateWorld(valley);
+

La mayoría de las veces, las plantas se multiplican y se expanden con bastante rapidez, pero luego la abundancia de alimentos causa una explosión poblacional de los herbívoros, que proceden a eliminar todas o casi todas las plantas, resultando en un hambre masiva de las criaturas. A veces, el ecosistema se recupera y comienza otro ciclo. En otras ocasiones, una de las especies muere completamente. Si son los herbívoros, todo el espacio se llenará de plantas. Si son las plantas, las criaturas restantes mueren de hambre, y el valle se convierte en una tierra baldía desolada. Ah, la crueldad de la naturaleza.

+

Ejercicios

+

Estupidez artificial

+

Tener extinguidos a los habitantes de nuestro mundo después de unos minutos es algo deprimente. Para hacer frente a esto, podríamos tratar de crear un comedor de plantas más inteligente (smarter plant eater).

+

Hay varios problemas obvios con nuestros herbívoros. En primer lugar, son terriblemente codiciosos, tragan cada planta que ven hasta que han borrado toda la vida vegetal. En segundo lugar, su movimiento al azar (recuerde que el método view.find: devuelve una dirección al azar cuando coinciden varias direcciones) hace que tropiecen ineficazmente y mueren de hambre si no encuentran ninguna planta cerca. Y finalmente, se reproducen muy rápido, lo que hace que los ciclos entre la abundancia y el hambre sean bastante intensos.

+

Crea un nuevo tipo de critter intentando abordar uno o más de estos puntos y sustituyelo por el antiguo tipo PlantEater en el mundo del valle. Haga algunos ajustes mas si lo ve necesario.

+
// Your code here
+function SmartPlantEater() {}
+
+animateWorld(new LifelikeWorld(
+  ["############################",
+   "#####                 ######",
+   "##   ***                **##",
+   "#   *##**         **  O  *##",
+   "#    ***     O    ##**    *#",
+   "#       O         ##***    #",
+   "#                 ##**     #",
+   "#   O       #*             #",
+   "#*          #**       O    #",
+   "#***        ##**    O    **#",
+   "##****     ###***       *###",
+   "############################"],
+  {"#": Wall,
+   "O": SmartPlantEater,
+   "*": Plant}
+));
+
+

El problema de la codicia puede ser atacado de varias maneras. Las criaturas podrían dejar de comer cuando alcanzan un cierto nivel de energía. O pueden comer sólo cada N vueltas (manteniendo un contador de las vueltas desde su última comida en una propiedad en el objeto de criatura). O, para asegurarse de que las plantas nunca se extingan por completo, los animales podrían negarse a comer una planta a menos que vean por lo menos una planta cercana (usando el método findAll en la vista). Una combinación de estos, o alguna estrategia totalmente diferente, también podría funcionar

+

El problema de la codicia puede ser atacado de varias maneras. Las criaturas podrían dejar de comer cuando alcanzan un cierto nivel de energía (energy). O pueden comer sólo cada N vueltas (manteniendo un contador de las vueltas desde su última comida en una propiedad en el objeto critters). O, para asegurarse de que las plantas nunca se extingan por completo, los animales podrían negarse a comer una planta a menos que vean por lo menos una planta cercana (usando el método findAll en la vista). Una combinación de estos, o alguna estrategia totalmente diferente, también podría funcionar.

+

Hacer que los bichos se muevan más eficazmente se podría hacer copiando una de las estrategias de movimiento de las criaturas en nuestro viejo mundo sin energías. Tanto el comportamiento de rebote (bouncing) como el comportamiento de seguimiento de la pared (wall-following) mostraron un rango de movimiento mucho más amplio que el escalonamiento completamente aleatorio.

+

Hacer que las criaturas se reproduzcan más lentamente es trivial. Sólo aumentar el nivel mínimo de energía en la que se reproducen. Por supuesto, hacer el ecosistema más estable también lo hace más aburrido. Si tienes un puñado de criaturas gordas e inmóviles reproducirse pero siempre comiendo en un mar de plantas, tienes un ecosistema muy estable, pero muy aburrido.

+
+

Depredadores

+

Cualquier ecosistema serio tiene una cadena alimentaria más larga que un solo eslabón. Escriba otro critter que sobreviva comiendose al critter herbívoro. Usted notará que la estabilidad es aún más difícil de lograr ahora que hay ciclos en múltiples niveles. Trate de encontrar una estrategia para hacer que el ecosistema funcione sin problemas durante al menos un tiempo.

+

Una cosa que ayudará es hacer el mundo más grande. De esta manera, los auges o bajas de la población local son menos propensos a eliminar completamente una especie, y hay espacio para la población de presas relativamente grande que se necesita para mantener una pequeña población de depredadores.

+
// Your code here
+function Tiger() {}
+
+animateWorld(new LifelikeWorld(
+  ["####################################################",
+   "#                 ####         ****              ###",
+   "#   *  @  ##                 ########       OO    ##",
+   "#   *    ##        O O                 ****       *#",
+   "#       ##*                        ##########     *#",
+   "#      ##***  *         ****                     **#",
+   "#* **  #  *  ***      #########                  **#",
+   "#* **  #      *               #   *              **#",
+   "#     ##              #   O   #  ***          ######",
+   "#*            @       #       #   *        O  #    #",
+   "#*                    #  ######                 ** #",
+   "###          ****          ***                  ** #",
+   "#       O                        @         O       #",
+   "#   *     ##  ##  ##  ##               ###      *  #",
+   "#   **         #              *       #####  O     #",
+   "##  **  O   O  #  #    ***  ***        ###      ** #",
+   "###               #   *****                    ****#",
+   "####################################################"],
+  {"#": Wall,
+   "@": Tiger,
+   "O": SmartPlantEater, // from previous exercise
+   "*": Plant}
+));
+
+

Muchos de los mismos trucos que trabajaron para el ejercicio anterior también se aplican aquí. Se recomienda hacer que los depredadores sean grandes (mucha energía) y que se reproduzcan lentamente. Eso los hará menos vulnerables a los períodos de hambre cuando los herbívoros son escasos.

+

Más allá de mantenerse vivo, mantener vivo su alimento es el principal objetivo de un depredador. Encuentre alguna manera de hacer que los depredadores cazan de manera más agresiva cuando hay muchos herbívoros y cazan más lentamente (o no) cuando la presa es rara. Dado que los comedores de plantas se mueven, el simple truco de comer uno solo cuando otros están cerca no es probable que funcione, eso sucederá tan rara vez que su depredador se muera de hambre. Pero usted podría seguir la pista de observaciones en vueltas anteriores, en una cierta estructura de datos guardada en los objetos del depredador, y tenerla basar su comportamiento (behavior) en lo que ha visto recientemente.

+
+ + From 14bb5779020bb0e2309800a94dbee2ee118101b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Olazagoitia?= Date: Thu, 30 Mar 2017 23:03:16 +0200 Subject: [PATCH 2/3] add translation chapter 11 --- chapters/11_language.html | 460 +++++++++----------------------------- 1 file changed, 102 insertions(+), 358 deletions(-) diff --git a/chapters/11_language.html b/chapters/11_language.html index f6a91b1..9fb18af 100644 --- a/chapters/11_language.html +++ b/chapters/11_language.html @@ -29,62 +29,27 @@

Capítulo 11
Project: A Programming Language

-

The evaluator, which determines the meaning of expressions in a -programming language, is just another program.

+

El evaluador, que determina el significado de las expresiones en un lenguaje de programación, es simplemente otro programa.

Hal Abelson and Gerald Sussman, Structure and Interpretation of Computer Programs
-

When a student asked the master about the nature of the cycle of Data -and Control, Yuan-Ma replied ‘Think of a compiler, compiling itself.’

+

Cuando un estudiante le preguntó al maestro acerca de la naturaleza del ciclo de datos y control, Yuan-Ma respondió "Piensa en un compilador, compilándose."

Master Yuan-Ma, The Book of Programming
-

Building your own -programming language is surprisingly easy (as long as you do not -aim too high) and very enlightening.

-

The main thing I want to show in this chapter is that there is no -magic involved in building your own language. I’ve often felt that -some human inventions were so immensely clever and complicated that -I’d never be able to understand them. But with a little reading and -tinkering, such things often turn out to be quite mundane.

-

We will build a programming language called Egg. It -will be a tiny, simple language but one that is powerful enough to -express any computation you can think of. It will also allow simple -abstraction based on functions.

-

Parsing

-

The most immediately visible part of a -programming language is its syntax, or notation. A parser is a -program that reads a piece of text and produces a data structure that -reflects the structure of the program contained in that text. If the -text does not form a valid program, the parser should complain and -point out the error.

-

Our language will have a simple and uniform -syntax. Everything in Egg is an expression. An expression can be a -variable, a number, a string, or an application. Applications are -used for function calls but also for constructs such as if or while.

-

To -keep the parser simple, strings in Egg do not support anything like -backslash escapes. A string is simply a sequence of characters that -are not double quotes, wrapped in double quotes. A number is a -sequence of digits. Variable names can consist of any character that -is not whitespace and does not have a special meaning in the -syntax.

-

Applications are written the way they are in -JavaScript, by putting parentheses after an expression and having -any number of arguments between those parentheses, separated by -commas.

+

Construir su propio lenguaje de programación es sorprendentemente fácil (siempre y cuando no apunte demasiado alto) y muy esclarecedor.

+

Lo principal que quiero mostrar en este capítulo es que no hay magia involucrada en la construcción de su propio idioma. A menudo he sentido que algunas invenciones humanas eran tan inmensamente inteligentes y complicadas que nunca sería capaz de entenderlas. Pero con un poco de lectura y retoques, tales cosas a menudo resultan ser bastante mundano.

+

Construiremos un lenguaje de programación llamado "Egg". Será un lenguaje pequeño y sencillo, pero que sea lo suficientemente poderoso para expresar cualquier cálculo que se pueda imaginar. También permitirá la abstracción simple basada en funciones.

+

Analizando

+

La parte más inmediatamente visible de un lenguaje de programación es su sintaxis, o notación. Un analizador es un programa que lee una parte del texto y produce una estructura de datos que refleja la estructura del programa contenido en ese texto. Si el texto no forma un programa válido, el analizador debe quejarse y señalar el error.

+

Nuestro lenguaje tendrá una sintaxis simple y uniforme. Todo en "Egg" es una expresión. Una expresión puede ser una variable, un número, una cadena o una aplicación. Las aplicaciones se utilizan para llamar a funciones, pero también para construcciones como if o while.

+

Para mantener el analizador simple, las cadenas en "Egg" no soportan nada como escapes de barra invertida. Una cadena es simplemente una secuencia de caracteres que no son comillas dobles, envueltos en comillas dobles. Un número es una secuencia de dígitos. Los nombres de las variables pueden consistir en cualquier carácter que no sea un espacio en blanco y no tenga un significado especial en la sintaxis.

+

Las aplicaciones se escriben de la forma en que están en JavaScript, poniendo paréntesis después de una expresión pudiendo tener cualquier número de argumentos entre esos paréntesis, separados por comas.

do(define(x, 10),
    if(>(x, 5),
       print("large"),
       print("small")))
-

The uniformity of the Egg language means that -things that are operators in JavaScript (such as >) are normal -variables in this language, applied just like other functions. And -since the syntax has no concept of a block, we need a do -construct to represent doing multiple things in sequence.

-

The data structure that the parser will -use to describe a program will consist of expression objects, each -of which has a type property indicating the kind of expression it is -and other properties to describe its content.

+

La uniformidad del lenguaje "Egg" significa que las cosas que son operadores en JavaScript (como >) son variables normales en este lenguaje, aplicadas como otras funciones. Y puesto que la sintaxis no tiene concepto de un bloque, necesitamos una construcción do para representar hacer varias cosas en secuencia.

+

La estructura de datos que el analizador utilizará para describir un programa consistirá en objetos de expresión, cada uno de los cuales tiene una propiedad type que indica el tipo de expresión que es y otras propiedades para describir su contenido.

Expressions of type "value" represent literal strings or numbers. Their value property contains the string or number value that they represent. Expressions of type "word" are used for @@ -93,7 +58,8 @@

Par represent applications. They have an operator property that refers to the expression that is being applied, and they have an args property that refers to an array of argument expressions.

-

The >(x, 5) part of the previous program would be represented like this:

+

Las expresiones de tipo "value" representan cadenas literales o números. Su propiedad value contiene el valor de cadena o número que representan. Las expresiones del tipo "word" se utilizan para identificadores (nombres). Tales objetos tienen una propiedad de name que contiene una cadena con su nombre. Finalmente, las expresiones "apply" representan aplicaciones. Tienen una propiedad operator que hace referencia a la expresión que se está aplicando y tienen una propiedad args que hace referencia a una matriz con sus argumentos.

+

La parte >(x, 5) del programa anterior se representa así:

{
   type: "apply",
   operator: {type: "word", name: ">"},
@@ -102,36 +68,15 @@ 

Par {type: "value", value: 5} ] }

-

Such a data structure is called a syntax tree. If you -imagine the objects as dots and the links between them as lines -between those dots, it has a treelike shape. The fact that -expressions contain other expressions, which in turn might contain -more expressions, is similar to the way branches split and split again.

+

Esta estructura de datos se llama un estructura de árbol. Si usted imagina los objetos como puntos y los enlaces entre ellos como líneas entre esos puntos, tiene una forma de árbol. El hecho de que las expresiones contengan otras expresiones, que a su vez podrían contener más expresiones, es similar al modo en que las ramas se dividen y se dividen de nuevo.

The structure of a syntax tree
-

Contrast this to the parser we wrote for the -configuration file format in Chapter 9, which -had a simple structure: it split the input into lines and -handled those lines one at a time. There were only a few simple forms -that a line was allowed to have.

-

Here we must find a -different approach. Expressions are not separated into lines, and they -have a recursive structure. Application expressions contain other -expressions.

-

Fortunately, this problem can be solved elegantly by -writing a parser function that is recursive in a way that reflects the -recursive nature of the language.

-

We define a function -parseExpression, which takes a string as input and returns an -object containing the data structure for the expression at the start -of the string, along with the part of the string left after parsing -this expression. When parsing subexpressions (the argument to an -application, for example), this function can be called again, yielding -the argument expression as well as the text that remains. This text -may in turn contain more arguments or may be the closing parenthesis -that ends the list of arguments.

-

This is the first part of the parser:

+

Compare esto con el analizador que escribimos para el formato de archivo de configuración en el Capítulo 9, que tenía una estructura simple: dividió la entrada en líneas y manejó esas líneas una a la vez. Solamente permitia tener alguna forma simples en una linea.

+

Aquí debemos encontrar un enfoque diferente. Las expresiones no se separan en líneas y tienen una estructura recursiva. Las expresiones de la aplicación contienen otras expresiones.

+

Afortunadamente, este problema se puede resolver elegantemente escribiendo una función recursiva en el analizador una manera que refleja la naturaleza recursiva de la lengua.

+

Definimos la función parseExpression, que toma una cadena como entrada y devuelve un objeto que contiene la estructura de datos para la expresión al principio de la cadena, junto con la parte de la cadena que queda después de analizar esta expresión. Al analizar las subexpresiones (el argumento a una aplicación, por ejemplo), esta función puede ser llamada de nuevo, dando la expresión del argumento, así como el texto que permanece. Este texto puede a su vez contener más argumentos o puede ser el paréntesis de cierre que termina la lista de argumentos.

+

Esta es la primera parte del analizador:

function parseExpression(program) {
   program = skipSpace(program);
   var match, expr;
@@ -152,23 +97,9 @@ 

Par if (first == -1) return ""; return string.slice(first); }

-

Because Egg allows any amount of -whitespace between its elements, we have to repeatedly cut the -whitespace off the start of the program string. This is what the -skipSpace function helps with.

-

After skipping any -leading space, parseExpression uses three regular expressions to -spot the three simple (atomic) elements that Egg supports: strings, -numbers, and words. The parser constructs a different kind of data -structure depending on which one matches. If the input does not match -one of these three forms, it is -not a valid expression, and the parser throws an error. SyntaxError is a -standard error object type, which is raised when an attempt is made to -run an invalid JavaScript program.

-

We can then cut off the part that we matched -from the program string and pass that, along with the object for the -expression, to parseApply, which checks whether the expression is an -application. If so, it parses a parenthesized list of arguments.

+

Debido a que "Egg" permite cualquier cantidad de espacio en blanco entre sus elementos, tenemos que cortar repetidamente el espacio en blanco desde el inicio de la cadena del programa. Para esto nos ayuda la función skipSpace.

+

Después de salterse cualquier espacio en blanco, parseExpression utiliza tres expresiones regulares para detectar los tres elementos simples (atomic) que "Egg" soporta: cadenas, números y palabras. El analizador construye un tipo diferente de estructura de datos dependiendo de cuál coincide. Si la entrada no coincide con una de estas tres formas, no es una expresión válida y el analizador genera un error. SyntaxError es un tipo de objeto de error estándar, que se genera cuando se intenta ejecutar un programa no válido en JavaScript.

+

Podemos entonces cortar la parte que coincide con una cadena en el programa y pasar que, junto con el objeto de la expresión, a parseApply, que comprueba si la expresión es una aplicación. Si es así, analiza una lista de argumentos entre paréntesis.

function parseApply(expr, program) {
   program = skipSpace(program);
   if (program[0] != "(")
@@ -187,24 +118,15 @@ 

Par } return parseApply(expr, program.slice(1)); }

-

If the next character in the program is not an opening -parenthesis, this is not an application, and parseApply simply -returns the expression it was given.

-

Otherwise, it skips the opening parenthesis and -creates the syntax tree object for this application expression. It -then recursively calls parseExpression to parse each argument until a -closing parenthesis is found. The recursion is indirect, through -parseApply and parseExpression calling each other.

+

Si el siguiente carácter del programa no es un paréntesis de apertura, esto no es una aplicación y parseApply simplemente devuelve la expresión que se le dio.

+

De lo contrario, omite el paréntesis de apertura y crea el objeto de estructura de árbol para esta expresión de aplicación. A continuación, recursivamente llama a ParseExpression para analizar cada argumento hasta que se encuentra un paréntesis de cierre. La recursión es indirecta, a través de parseApply y parseExpression llamándose entre sí.

Because an application expression can itself be applied (such as in multiplier(2)(1)), parseApply must, after it has parsed an application, call itself again to check whether another pair of parentheses follows.

-

This is all we -need to parse Egg. We wrap it in a convenient parse function that -verifies that it has reached the end of the input string after parsing -the expression (an Egg program is a single expression), and that -gives us the program’s data structure.

-
function parse(program) {
+

Porque una expresión de aplicación puede aplicarse por sí misma (como en multiplier(2)(1)), parseApply debe, después de haber analizado una aplicación, volver a llamar para comprobar si hay otro par de paréntesis.

+

Esto es todo lo que necesita el analizador "Egg". Lo envolvemos en una conveniente función de análisis que verifica que ha llegado al final de la cadena de entrada después de analizar la expresión (un programa "Egg" es una sola expresión), y que nos da la estructura de datos del programa.

+
function parse(program) {
   var result = parseExpression(program);
   if (skipSpace(result.rest).length > 0)
     throw new SyntaxError("Unexpected text after program");
@@ -216,16 +138,9 @@ 

Par // operator: {type: "word", name: "+"}, // args: [{type: "word", name: "a"}, // {type: "value", value: 10}]}

-

It works! It doesn’t give us very helpful -information when it fails and doesn’t store the line and column on -which each expression starts, which might be helpful when reporting -errors later, but it’s good enough for our purposes.

-

The evaluator

-

What can we do with the syntax tree for a -program? Run it, of course! And that is what the evaluator does. You -give it a syntax tree and an environment object that associates names -with values, and it will evaluate the expression that the tree -represents and return the value that this produces.

+

¡Funciona! No nos da información muy útil cuando falla y no almacena la línea y la columna en la que empieza cada expresión, lo que podría ser útil cuando se reportan errores más tarde, pero es lo suficientemente bueno para nuestros propósito.

+

El evaluador

+

¿Qué podemos hacer con la estructura de árbol del programa? Correrla, por supuesto! Y eso es lo que hace el evaluador. Se le asigna una estructura de árbol y un objeto de entorno que asocia nombres con valores y evaluará la expresión que representa el árbol y devolverá el valor que produce.

function evaluate(expr, env) {
   switch(expr.type) {
     case "value":
@@ -252,36 +167,13 @@ 

The evaluatorvar specialForms = Object.create(null);

-

The evaluator has code for -each of the expression types. A literal value expression simply -produces its value. (For example, the expression 100 just evaluates -to the number 100.) For a variable, we must check whether it is -actually defined in the environment and, if it is, fetch the -variable’s value.

-

Applications are more involved. If they are -a special form, like if, we do not evaluate anything and simply -pass the argument expressions, along with the environment, to the -function that handles this form. If it is a normal call, we evaluate -the operator, verify that it is a function, and call it with the -result of evaluating the arguments.

-

We will use plain JavaScript function values to represent Egg’s -function values. We will come back to this -later, when the special form called -fun is defined.

-

The recursive structure of -evaluate resembles the similar structure of the parser. Both mirror -the structure of the language itself. It would also be possible to -integrate the parser with the evaluator and evaluate during parsing, -but splitting them up this way makes the program more readable.

-

This is really all that is -needed to interpret Egg. It is that simple. But without defining a few -special forms and adding some useful values to the environment, -you can’t do anything with this language yet.

+

El evaluador tiene código para cada uno de los tipos de expresiones. Una expresión de valor literal simplemente produce su valor. (Por ejemplo, la expresión 100 equivale al número 100.) Para una variable, debemos comprobar si está realmente definida en el entorno y, si es así, buscar el valor de la variable.

+

Las aplicaciones están más comprometidas. Si son una forma especial, como if, no evaluamos nada y simplemente pasamos las expresiones de argumento, junto con el entorno, a la función que maneja esta estructura. Si es una llamada normal, evaluamos al operador, verificamos que es una función y lo llamamos con el resultado de evaluar los argumentos.

+

Utilizaremos funciones simple de JavaScript para representar los valores de funciones de "Egg". Volveremos a esto más adelante, cuando definamos la forma especial de llamar funciones.

+

La estructura recursiva de evaluate se asemeja a la estructura similar de parse. Ambos reflejan la estructura del lenguaje mismo. También sería posible integrar el parse con el evaluate y evaluar durante el análisis sintáctico, pero dividirlo de esta manera hace que el programa sea más legible.

+

Esto es realmente todo lo que se necesita interpretar "Egg". Así de simple. Sin embargo, sin definir algunas formas especiales y agregar algunas utilidades al entorno, todavía no se puede hacer nada con este lenguaje.

Special forms

-

The specialForms object -is used to define special syntax in Egg. It associates words with -functions that evaluate such special forms. It is currently empty. -Let’s add some forms.

+

El objeto specialForms se utiliza para definir sintaxis especial en "Egg". Asocia palabras con funciones que evalúan tales formas especiales. Actualmente está vacío. Añadamos algunas formas.

specialForms["if"] = function(args, env) {
   if (args.length != 3)
     throw new SyntaxError("Bad number of args to if");
@@ -291,36 +183,22 @@ 

Special formselse return evaluate(args[2], env); };

-

Egg’s if construct expects exactly three -arguments. It will evaluate the first, and if the result isn’t the -value false, it will evaluate the second. Otherwise, the third gets -evaluated. This if form is more similar to JavaScript’s ternary ?: -operator than to JavaScript’s if. It is an expression, not a statement, -and it produces a value, namely, the result of the second or third -argument.

-

Egg differs from JavaScript in how it handles the -condition value to if. It will not treat things like zero or the -empty string as false, but only the precise value false.

-

The reason we need to represent if as -a special form, rather than a regular function, is that all arguments -to functions are evaluated before the function is called, whereas -if should evaluate only either its second or its third argument, -depending on the value of the first.

-

The while form is similar.

-
specialForms["while"] = function(args, env) {
+

El Constructor if de "Egg" espera exactamente tres argumentos. Se evaluará el primero, y si el resultado no es el valor false, se evaluará el segundo. Si no, el tercero se evaluara. Esta forma es más similar a operador ternario ?: que al de JavaScript if. Es una expresión, no una declaración, y produce un valor, es decir, el resultado del segundo o tercer argumento.

+

"Egg" difiere de JavaScript en cómo maneja el valor de la condición if. No tratará las cosas como cero o la cadena vacía como false, sólo el valor preciso false.

+

La razón por la que tenemos que representar if como una estructura especial, en lugar de una función regular, es que todos los argumentos a las funciones se evalúan antes de que se llame a la función, mientras que if debería evaluar sólo su segundo o su tercer argumento, dependiendo del valor del primero.

+

La estructura de while es similar.

+
specialForms["while"] = function(args, env) {
   if (args.length != 2)
     throw new SyntaxError("Bad number of args to while");
 
   while (evaluate(args[0], env) !== false)
     evaluate(args[1], env);
 
-  // Since undefined does not exist in Egg, we return false,
-  // for lack of a meaningful result.
+  // Como undefined no existe en Egg, devolvemos false,
+  // para la falta de resultados válidos
   return false;
 };
-

Another basic building block is do, which executes all its arguments -from top to bottom. Its value is the value produced by the last -argument.

+

Otro bloque de construcción básico es do, que ejecuta todos sus argumentos de arriba a abajo. Su valor es el valor producido por el último argumento.

specialForms["do"] = function(args, env) {
   var value = false;
   args.forEach(function(arg) {
@@ -328,12 +206,7 @@ 

Special formsreturn value; };

-

To be able to create variables and give them new -values, we also create a form called define. It expects a word as -its first argument and an expression producing the value to assign to -that word as its second argument. Since define, like everything, is -an expression, it must return a value. We’ll make it return the value -that was assigned (just like JavaScript’s = operator).

+

Para poder crear variables y darles nuevos valores, también creamos una specialForms llamada define. Espera una palabra como su primer argumento y una expresión que produce el valor para asignar a esa palabra como su segundo argumento. Dado que define, como toda una expresión, debe devolver un valor. Vamos a hacer que devuelva el valor que se asignó (Como el operador = de JavaScript)

specialForms["define"] = function(args, env) {
   if (args.length != 2 || args[0].type != "word")
     throw new SyntaxError("Bad use of define");
@@ -341,70 +214,45 @@ 

Special formsenv[args[0].name] = value; return value; };

-

The environment

-

The environment accepted -by evaluate is an object with properties whose names correspond to -variable names and whose values correspond to the values those -variables are bound to. Let’s define an environment object to -represent the global scope.

-

To be able to use the if construct we just defined, we must -have access to Boolean values. Since there are only two -Boolean values, we do not need special syntax for them. We simply bind -two variables to the values true and false and use those.

+

El entorno

+

El entorno aceptado por el evaluador es un objeto con propiedades cuyos nombres corresponden a nombres de variables y cuyos valores corresponden a los valores a los que están vinculadas. Vamos a definir un objeto de entorno para representar el ámbito global.

+

Para poder usar la construcción if que acabamos de definir, debemos tener acceso a valores booleanos. Como sólo tiene dos valores, no necesitamos sintaxis especial para ellos. Simplemente enlazamos dos variables a los valores true y false y los usamos.

var topEnv = Object.create(null);
 
 topEnv["true"] = true;
 topEnv["false"] = false;
-

We can now evaluate a simple expression that negates a Boolean value.

-
var prog = parse("if(true, false, true)");
+

Ahora podemos evaluar un expresión y devolver un valor boleano.

+
var prog = parse("if(true, false, true)");
 console.log(evaluate(prog, topEnv));
 // → false
-

To supply basic -arithmetic and comparison operators, we will also add some -function values to the environment. In the interest of keeping the -code short, we’ll use new Function to synthesize a bunch of operator -functions in a loop, rather than defining them all individually.

+

Para facilitar aritmética y operadores de comparación, también agregaremos algunos valores de función al entorno. En interés de mantener el código corto, vamos a utilizar new Function para sintetizar un montón de funciones de operador en un bucle, en lugar de definir todos ellos individualmente.

["+", "-", "*", "/", "==", "<", ">"].forEach(function(op) {
   topEnv[op] = new Function("a, b", "return a " + op + " b;");
 });
-

A way to output values is also very useful, so we’ll wrap -console.log in a function and call it print.

+

También es muy útiluna forma de mostrar valores de salida, así que vamos a envolver console.log en una función y lo llamamos print.

topEnv["print"] = function(value) {
   console.log(value);
   return value;
 };
-

That gives us enough elementary tools -to write simple programs. The following run function provides a -convenient way to write and run them. It creates a fresh environment -and parses and evaluates the strings we give it as a single program.

+

Eso nos da suficientes herramientas para escribir programas sencillos. La siguiente función run proporciona una forma conveniente de escribirlos y ejecutarlos. Crea un nuevo ámbito y analiza y evalúa las cadenas que le damos como un solo programa.

function run() {
   var env = Object.create(topEnv);
   var program = Array.prototype.slice
     .call(arguments, 0).join("\n");
   return evaluate(parse(program), env);
 }
-

The use of -Array.prototype.slice.call is a trick to turn an array-like object, such as arguments, into a real array so that we can call -join on it. It takes all the arguments given to run and treats -them as the lines of a program.

-
run("do(define(total, 0),",
+

El uso de Array.prototype.slice.call es un truco para convertir un objeto en array-like(similar o que se asemeja a un array), con tantos elementos como arguments, en una Array así que podemos llamar a join. Toma todos los arguments dados para ejecutarlos y los trata como las líneas de un programa.

+
run("do(define(total, 0),",
     "   define(count, 1),",
     "   while(<(count, 11),",
     "         do(define(total, +(total, count)),",
     "            define(count, +(count, 1)))),",
     "   print(total))");
 // → 55
-

This is the program we’ve seen -several times before, which computes the sum of the numbers 1 to 10, -expressed in Egg. It is clearly uglier than the equivalent JavaScript -program but not bad for a language implemented in less than 150 -lines of code.

-

Functions

-

A programming language without -functions is a poor programming language indeed.

-

Fortunately, it is not hard to add a fun construct, which treats its -last argument as the function’s body and treats all the arguments before that as -the names of the function’s arguments.

+

Este es el programa que hemos visto varias veces antes, que calcula la suma de los números 1 a 10, expresada en "Egg". Es claramente más feo que el programa JavaScript equivalente, pero no está mal para un lenguaje implementado en menos de 150 líneas de código.

+

Funciones

+

Un lenguaje de programación sin funciones es un lenguaje verdaderamente pobre

+

Afortunadamente, no es difícil añadir una constructor fun, que trata su último argumento como el cuerpo de la función y todos los argumentos anteriores como los nombres de los argumentos de la función.

specialForms["fun"] = function(args, env) {
   if (!args.length)
     throw new SyntaxError("Functions need a body");
@@ -425,16 +273,9 @@ 

Fun return evaluate(body, localEnv); }; };

-

Functions -in Egg have their own local environment, just like in JavaScript. We -use Object.create to make a new object that has access to the -variables in the outer environment (its prototype) but that can also -contain new variables without modifying that outer scope.

-

The function -created by the fun form creates this local environment and adds the -argument variables to it. It then evaluates the function body in this -environment and returns the result.

-
run("do(define(plusOne, fun(a, +(a, 1))),",
+

Las funciones en "Egg" tienen su propio entorno local, al igual que en JavaScript. Utilizamos Object.create para crear un nuevo objeto que tenga acceso a las variables del entorno externo (su prototipo) pero que también pueda contener nuevas variables sin modificar al ámbito global.

+

La función creada por el objecto fun crea este entorno local y le agrega las variables por argumentos. A continuación, evalúa el cuerpo de la función en este entorno y devuelve el resultado.

+
run("do(define(plusOne, fun(a, +(a, 1))),",
     "   print(plusOne(10)))");
 // → 11
 
@@ -444,54 +285,18 @@ 

Fun " *(base, pow(base, -(exp, 1)))))),", " print(pow(2, 10)))"); // → 1024

-

Compilation

-

What we have built is an -interpreter. During evaluation, it acts directly on the representation -of the program produced by the parser.

-

Compilation is the process of -adding another step between the parsing and the running of a program, -which transforms the program into something that can be evaluated more -efficiently by doing as much work as possible in advance. For example, -in well-designed languages it is obvious, for each use of a -variable, which variable is being referred to, without actually -running the program. This can be used to avoid looking up the variable -by name every time it is accessed and to directly fetch it from some -predetermined memory location.

-

Traditionally, compilation involves converting the program to -machine code, the raw format that a computer’s processor can -execute. But any process that converts a program to a different -representation can be thought of as compilation.

-

It would -be possible to write an alternative evaluation strategy for Egg, -one that first converts the program to a JavaScript program, uses new -Function to invoke the JavaScript compiler on it, and then runs the -result. When done right, this would make Egg run very fast while -still being quite simple to implement.

-

If you are interested in this topic and willing to spend some time on -it, I encourage you to try to implement such a compiler as an -exercise.

-

Cheating

-

When we defined if and while, you probably -noticed that they were more or less trivial wrappers around -JavaScript’s own if and while. Similarly, the values in Egg are -just regular old JavaScript values.

-

If you compare the implementation of Egg, built on top of JavaScript, -with the amount of work and complexity required to build a programming -language directly on the raw functionality provided by a machine, the -difference is huge. Regardless, this example hopefully gave you an -impression of the way programming languages work.

-

And when it comes to getting something done, cheating is more -effective than doing everything yourself. Though the toy language in -this chapter doesn’t do anything that couldn’t be done better in -JavaScript, there are situations where writing small languages helps -get real work done.

-

Such a language does not have to resemble a typical programming -language. If JavaScript didn’t come equipped with regular expressions, -you could write your own parser and evaluator for such a sublanguage.

-

Or imagine you are building a giant -robotic dinosaur and need to program its behavior. JavaScript -might not be the most effective way to do this. You might instead opt -for a language that looks like this:

+

Compilación

+

Lo que hemos construido es un intérprete. Durante la evaluación, actúa directamente sobre la representación del programa producido por el analizador.

+

Compilación es el proceso de añadir otro paso entre el análisis y el funcionamiento de un programa, que transforma el programa en algo que puede ser evaluado de manera más eficiente haciendo todo el trabajo posible de antemano. Por ejemplo, en lenguajes bien diseñados es obvio, para cada uso de una variable, a qué variable se refiere, sin ejecutar realmente el programa. Esto puede usarse para evitar buscar la variable por nombre cada vez que se accede y para buscarla directamente desde una ubicación de memoria predeterminada.

+

Traditionally, compilationTradicionalmente, la compilación implica convertir el programa en código de máquina, el formato en bruto que el procesador de una computadora puede ejecutar. Pero cualquier proceso que convierte un programa en una representación diferente puede considerarse como compilación.

+

Sería posible escribir una estrategia de evaluación alternativa para "Egg", una que primero convierta el programa a un programa JavaScript, utilice una nueva Función para invocar al compilador de JavaScript y luego ejecute el resultado. Cuando se haga bien, esto hará que "Egg" corra muy rápido sin dejar de ser muy sencillo de implementar.

+

Si está interesado en este tema y está dispuesto a dedicar algo de tiempo a ello, le animo a que intente implementar un compilador como un ejercicio.

+

Trampeando

+

Cuando definimos if y while, probablemente notó que eran envolturas más o menos triviales alrededor de los propios if y while de JavaScript. Del mismo modo, los valores de "Egg" son sólo antiguos valores de JavaScript.

+

Si comparas la implementación de Egg, construida por encima de JavaScript, con la cantidad de trabajo y complejidad requerida para construir un lenguaje de programación directamente en la funcionalidad bruta proporcionada por una máquina, la diferencia es enorme. Independientemente, este ejemplo esperamos le dio una impresión de la forma en que funcionan los lenguajes de programación.

+

Y cuando se trata de hacer algo, hacer trampa es más eficaz que hacer todo tú mismo. Aunque el lenguaje de juguete de este capítulo no hace nada que no se pueda hacer mejor en JavaScript, hay situaciones en las que escribir lenguajes pequeños ayuda a realizar un trabajo real.

+

Tal lenguaje no tiene que parecerse a un lenguaje de programación típico. Si JavaScript no viene equipado con expresiones regulares, puede escribir su propio analizador y evaluador para tal sublenguaje.

+

O imagínese que está construyendo un gigantesco dinosaurio robótico y necesita programar su comportamiento. JavaScript podría no ser la forma más efectiva de hacerlo. En su lugar, puede optar por un idioma que tenga el siguiente aspecto:

behavior walk
   perform when
     destination ahead
@@ -505,18 +310,11 @@ 

Cheating

actions fire laser-eyes launch arm-rockets
-

This is what is usually called a domain-specific language, a language tailored to express a narrow domain of -knowledge. Such a language can be more expressive than a -general-purpose language because it is designed to express exactly the -things that need expressing in its domain and nothing else.

-

Exercises

+

Esto es lo que se suele llamar un lenguaje específico del dominio, un lenguaje adaptado para expresar un estrecho dominio del conocimiento. Tal lenguaje puede ser más expresivo que un lenguaje de propósito general porque está diseñado para expresar exactamente las cosas que necesitan expresarse en su dominio y nada más.

+

Ejercicios

Arrays

-

Add support for arrays to Egg by adding the -following three functions to the top scope: array(...) to -construct an array containing the argument values, length(array) to -get an array’s length, and element(array, n) to fetch the nth -element from an array.

-
// Modify these definitions...
+

Agregue soporte para arrays a "Egg" agregando las siguientes tres funciones al ámbito superior: array (...) para construir una matriz que contenga el valor de sus argumentos, length(array) para obtener la longitud de una matriz y element(array, n) para buscar la posición del elemento en el array.

+
// Modify these definitions...
 
 topEnv["array"] = "...";
 
@@ -534,50 +332,24 @@ 

Arrays

" print(sum(array(1, 2, 3))))"); // → 6
-

The easiest way to do this is to represent Egg arrays -with JavaScript arrays.

-

The values added to the top environment must be -functions. Array.prototype.slice can be used to convert an -arguments array-like object into a regular array.

+

La forma más sencilla de hacerlo es representar arrays de "Egg" con arrays de JavaScript.

+

Los valores agregados al entorno superior deben ser funciones. Array.prototype.slice se puede utilizar para convertir arguments en un objecto array.

Closure

-

The way we have defined fun allows -functions in Egg to “close over” the surrounding environment, allowing -the function’s body to use local values that were visible at the time -the function was defined, just like JavaScript functions do.

-

The following program illustrates this: function f returns a function -that adds its argument to f's argument, meaning that it needs access -to the local scope inside f to be able to use variable a.

-
run("do(define(f, fun(a, fun(b, +(a, b)))),",
+

La forma en que hemos definido fun permite a las funciones de "Egg" "cerrar" el entorno, permitiendo que el cuerpo de la función use valores locales que eran visibles en el momento en que se definió la función, al igual que las funciones de JavaScript.

+

El siguiente programa lo ilustra: la función f devuelve una función que añade su argumento como argumentos de f, lo que significa que necesita acceso al ámbito local dentro de f para poder utilizar la variable a.

+
run("do(define(f, fun(a, fun(b, +(a, b)))),",
     "   print(f(4)(5)))");
 // → 9
-

Go back to the definition of the fun form and explain which -mechanism causes this to work.

+

Vuelva a la definición de fun y explique qué mecanismo hace que funcione.

-

Again, we are riding along on a JavaScript mechanism to -get the equivalent feature in Egg. Special forms are passed the local -environment in which they are evaluated so that they can evaluate -their subforms in that environment. The function returned by fun -closes over the env argument given to its enclosing function and -uses that to create the function’s local environment when it is -called.

-

This means that the prototype of the local -environment will be the environment in which the function was created, -which makes it possible to access variables in that environment from -the function. This is all there is to implementing closure (though to -compile it in a way that is actually efficient, you’d need to do some -more work).

+

Una vez más, estamos montando sobre un mecanismo JavaScript para obtener la característica equivalente en "Egg". Se pasan funciones especiales al entorno local en el que se evalúan para que puedan evaluar subfunciones dentro de ese entorno. La función devuelta por fun actua sobre el argumento env que se da a su función enclosing y usa eso, para crear el entorno local de la función cuando es llamada.

+

Esto significa que el prototype del entorno local será el entorno en el que se creó la función, lo que hace posible acceder a las variables en ese entorno desde la función. Esto es todo lo que hay que hacer para implementar el Closure (aunque para compilarlo de una manera que es realmente eficiente, tendría que hacer un poco más de trabajo).

-

Comments

-

It would be nice if we could -write comments in Egg. For example, whenever we find a hash sign -(#), we could treat the rest of the line as a comment and ignore it, -similar to // in JavaScript.

-

We do not have to make any big changes to the -parser to support this. We can simply change skipSpace to skip -comments like they are whitespace so that all the points where -skipSpace is called will now also skip comments. Make this change.

-
// This is the old skipSpace. Modify it...
+

Comentarios

+

Sería bueno si pudiéramos escribir comentarios en "Egg". Si nosotros encontramos el hash (#), podríamos tratar el resto de la línea como un comentario e ignorarlo, similar a // en JavaScript.

+

No tenemos que hacer grandes cambios en el analizador para apoyar esto. Podemos simplemente cambiar skipSpace para omitir los comentarios como si fueran espacios en blanco para que todos los puntos donde skipSpace se llama ahora también saltarán los comentarios. Haga este cambio.

+
// This is the old skipSpace. Modify it...
 function skipSpace(string) {
   var first = string.search(/\S/);
   if (first == -1) return "";
@@ -592,38 +364,17 @@ 

Comments

// operator: {type: "word", name: "a"}, // args: []}
-

Make sure your solution handles multiple comments in a -row, with potentially whitespace between or after them.

-

A regular expression is probably the easiest way to solve this. -Write something that matches “whitespace or a comment, zero or more -times”. Use the exec or match method and look at the length of -the first element in the returned array (the whole match) to find out -how many characters to slice off.

+

Asegúrese de que su solución maneja varios comentarios en una fila, pudiendo aparecer espacios en blanco entre o después de ellos.

+

A regular expressionUna expresión regular es probablemente la manera más fácil de resolver esto. Escriba algo que coincida con "espacios en blanco o un comentario, cero o más veces". Utilice el método exec o match y observe la longitud del primer elemento del Array devuelto (la coincidencia completa) para averiguar cuántos caracteres quitar.

-

Fixing scope

-

Currently, the only way to -assign a variable a value is define. This construct acts as -a way both to define new variables and to give existing ones a new value.

-

This ambiguity causes a problem. When you try -to give a nonlocal variable a new value, you will end up defining a -local one with the same name instead. (Some languages work like this -by design, but I’ve always found it a silly way to handle scope.)

-

Add a special form set, similar to -define, which gives a variable a new value, updating the variable in -an outer scope if it doesn’t already exist in the inner scope. If the -variable is not defined at all, throw a ReferenceError (which is -another standard error type).

-

The technique of representing scopes as simple objects, -which has made things convenient so far, will get in your way a -little at this point. You might want to use the -Object.getPrototypeOf function, which returns the prototype of an -object. Also remember that scopes do not derive from -Object.prototype, so if you want to call hasOwnProperty on them, -you have to use this clumsy expression:

+

Fijando el alcance

+

Actualmente, la única forma de asignar una variable a un valor es define. Esta construcción actúa para definir nuevas variables como para dar a las existentes un nuevo valor.

+

This ambiguityEsta ambigüedad causa un problema. Cuando intenta dar una variable no local un nuevo valor, terminará definiendo una en local con el mismo nombre en su lugar. (Algunos lenguajes funcionan de esta manera por diseño, pero siempre he encontrado una manera simple de manejar el alcance.)

+

Agregue una functión especial set, similar a define, la cual da a una variable un nuevo valor, actualizando la variable en un ámbito externo si aún no existe en el ámbito interno. Si la variable no está definida en absoluto, lance un ReferenceError (El cual es otro tipo de error estándar).

+

La técnica de representar los ámbitos como objetos simples, ha hecho las cosas convenientes hasta ahora, se pondrá en contra un poco en este punto. Es posible que desee utilizar la función Object.getPrototypeOf, que devuelve el prototipo de un objeto. También recuerde que los ámbitos no derivan de Object.prototype, así que si quieres llamar a hasOwnProperty en ellos, debes usar esta expresión:

Object.prototype.hasOwnProperty.call(scope, name);
-

This fetches the hasOwnProperty method from the Object prototype -and then calls it on a scope object.

-
specialForms["set"] = function(args, env) {
+

Esto recupera el método hasOwnProperty del prototipo de Object y lo llama a un objeto scope.

+
specialForms["set"] = function(args, env) {
   // Your code here.
 };
 
@@ -635,15 +386,8 @@ 

Fixing scope

run("set(quux, true)"); // → Some kind of ReferenceError
-

You will have to loop through -one scope at a time, using Object.getPrototypeOf to go the next -outer scope. For each scope, use hasOwnProperty to find out whether the -variable, indicated by the name property of the first argument to -set, exists in that scope. If it does, set it to the result of -evaluating the second argument to set and then return that value.

-

If the outermost scope is -reached (Object.getPrototypeOf returns null) and we haven’t found -the variable yet, it doesn’t exist, and an error should be thrown.

+

Tendrá que realizar un bucle sobre un scope a la vez, utilizando Object.getPrototypeOf para ir al siguiente scope externo. Para cada scope, use hasOwnProperty para averiguar si la variable, indicada por la propiedad name del primer argumento de set, Si lo hace, póngalo en el resultado de evaluar el segundo argumento a set y luego devolver ese valor.

+

Si alcanzamos el ámbito más alejado (Object.getPrototypeOf devuelve null) y no hemos encontrado la variable todavía, no existe, y se debe lanzar un error.

-

Capítulo 11
Project: A Programming Language

+

Capítulo 11
Proyecto: Un lenguaje de programación

El evaluador, que determina el significado de las expresiones en un lenguaje de programación, es simplemente otro programa.

Hal Abelson and Gerald Sussman, Structure and Interpretation of Computer Programs
diff --git a/index.html b/index.html index 7dbb744..d9b7689 100644 --- a/index.html +++ b/index.html @@ -56,6 +56,12 @@

Contenido

  • Expresiones Regulares
  • +
  • + Módulos(Pendiente) +
  • +
  • + Proyecto: Un lenguaje de programación +