@@ -18,14 +18,14 @@ The main thing I want to show in this chapter is that there is no
1818((magic)) involved in building your own language. I've often felt that
1919some human inventions were so immensely clever and complicated that
2020I'd never be able to understand them. But with a little reading and
21- experimenting, things often turn out to be quite mundane.
21+ experimenting, they often turn out to be quite mundane.
2222
2323{{index "Egg language"}}
2424
2525We will build a programming language called Egg. It will be a tiny,
2626simple language—but one that is powerful enough to express any
27- computation you can think of. It will also allow simple
28- ((abstraction)) based on ((function))s.
27+ computation you can think of. It will allow simple ((abstraction))
28+ based on ((function))s.
2929
3030{{id parsing}}
3131
@@ -90,8 +90,8 @@ represent. Expressions of type `"word"` are used for identifiers
9090(names). Such objects have a ` name ` property that holds the
9191identifier's name as a string. Finally, ` "apply" ` expressions
9292represent applications. They have an ` operator ` property that refers
93- to the expression that is being applied, and they have an ` args `
94- property that holds an array of argument expressions.
93+ to the expression that is being applied, and an ` args ` property that
94+ holds an array of argument expressions.
9595
9696The ` >(x, 5) ` part of the previous program would be represented like this:
9797
@@ -112,7 +112,7 @@ Such a ((data structure)) is called a _((syntax tree))_. If you
112112imagine the objects as dots and the links between them as lines
113113between those dots, it has a ((tree))like shape. The fact that
114114expressions contain other expressions, which in turn might contain
115- more expressions, is similar to the way branches split and split
115+ more expressions, is similar to the way tree branches split and split
116116again.
117117
118118{{figure {url: "img/syntax_tree.svg", alt: "The structure of a syntax tree",width: "5cm"}}}
@@ -175,21 +175,23 @@ function skipSpace(string) {
175175
176176{{index "skipSpace function"}}
177177
178- Because Egg allows any amount of ((whitespace)) between its elements,
179- we have to repeatedly cut the whitespace off the start of the program
180- string. That is what the ` skipSpace ` function helps with.
178+ Because Egg, like JavaScript, allows any amount of ((whitespace))
179+ between its elements, we have to repeatedly cut the whitespace off the
180+ start of the program string. That is what the ` skipSpace ` function
181+ helps with.
181182
182183{{index "literal expression", "SyntaxError type"}}
183184
184185After skipping any leading space, ` parseExpression ` uses three
185- ((regular expression))s to spot the three simple ( atomic) elements
186- that Egg supports: strings, numbers, and words. The parser constructs
187- a different kind of data structure depending on which one matches. If
186+ ((regular expression))s to spot the three atomic elements that Egg
187+ supports: strings, numbers, and words. The parser constructs a
188+ different kind of data structure depending on which one matches. If
188189the input does not match one of these three forms, it is not a valid
189190expression, and the parser throws an error. We use ` SyntaxError `
190191instead of ` Error ` as exception constructor, which is another standard
191- error type, which is a little more specific—it is also the error type
192- thrown when an attempt is made to run an invalid JavaScript program.
192+ error type, because it is a little more specific—it is also the error
193+ type thrown when an attempt is made to run an invalid JavaScript
194+ program.
193195
194196{{index "parseApply function"}}
195197
@@ -355,7 +357,7 @@ currently empty. Let's add `if`.
355357``` {includeCode: true}
356358specialForms.if = (args, scope) => {
357359 if (args.length != 3) {
358- throw new SyntaxError("Bad number of args to if");
360+ throw new SyntaxError("Wrong number of args to if");
359361 } else if (evaluate(args[0], scope) !== false) {
360362 return evaluate(args[1], scope);
361363 } else {
@@ -375,9 +377,9 @@ produces a value, namely the result of the second or third argument.
375377
376378{{index Boolean}}
377379
378- Egg differs from JavaScript in how it handles the condition value to
379- ` if ` . It will not treat things like zero or the empty string as false,
380- but only the precise value ` false ` .
380+ Egg also differs from JavaScript in how it handles the condition value
381+ to ` if ` . It will not treat things like zero or the empty string as
382+ false, only the precise value ` false ` .
381383
382384{{index "short-circuit evaluation"}}
383385
@@ -392,7 +394,7 @@ The `while` form is similar.
392394``` {includeCode: true}
393395specialForms.while = (args, scope) => {
394396 if (args.length != 2) {
395- throw new SyntaxError("Bad number of args to while");
397+ throw new SyntaxError("Wrong number of args to while");
396398 }
397399 while (evaluate(args[0], scope) !== false) {
398400 evaluate(args[1], scope);
@@ -430,7 +432,7 @@ must return a value. We'll make it return the value that was assigned
430432``` {includeCode: true}
431433specialForms.define = (args, scope) => {
432434 if (args.length != 2 || args[0].type != "word") {
433- throw new SyntaxError("Bad use of define");
435+ throw new SyntaxError("Incorrect use of define");
434436 }
435437 let value = evaluate(args[1], scope);
436438 scope[args[0].name] = value;
@@ -449,7 +451,7 @@ represent the ((global scope)).
449451
450452To be able to use the ` if ` construct we just defined, we must have
451453access to ((Boolean)) values. Since there are only two Boolean values,
452- we do not need special syntax for them. We simply bind two bindings to
454+ we do not need special syntax for them. We simply bind two names to
453455the values ` true ` and ` false ` and use those.
454456
455457``` {includeCode: true}
@@ -471,13 +473,13 @@ console.log(evaluate(prog, topScope));
471473
472474To supply basic ((arithmetic)) and ((comparison)) ((operator))s, we
473475will also add some function values to the ((scope)). In the interest
474- of keeping the code short, we'll use ` new Function` to synthesize a
475- bunch of operator functions in a loop, rather than defining them all
476+ of keeping the code short, we'll use ` Function ` to synthesize a bunch
477+ of operator functions in a loop, rather than defining them
476478individually.
477479
478480``` {includeCode: true}
479481for (let op of ["+", "-", "*", "/", "==", "<", ">"]) {
480- topScope[op] = new Function("a, b", `return a ${op} b;`);
482+ topScope[op] = Function("a, b", `return a ${op} b;`);
481483}
482484```
483485
@@ -494,8 +496,8 @@ topScope.print = value => {
494496{{index parsing, "run function"}}
495497
496498That gives us enough elementary tools to write simple programs. The
497- following ` run ` function provides a convenient way to parse a program
498- and run it in a fresh scope.
499+ following function provides a convenient way to parse a program and
500+ run it in a fresh scope.
499501
500502``` {includeCode: true}
501503function run(program) {
@@ -538,33 +540,29 @@ A programming language without functions is a poor programming
538540language indeed.
539541
540542Fortunately, it isn't hard to add a ` fun ` construct, which treats its
541- last argument as the function's body and treats all the arguments
542- before that as the names of the function's arguments .
543+ last argument as the function's body and uses all arguments before
544+ that as the names of the function's parameters .
543545
544546``` {includeCode: true}
545- function argName(expr) {
546- }
547-
548-
549547specialForms.fun = (args, scope) => {
550548 if (!args.length) {
551549 throw new SyntaxError("Functions need a body");
552550 }
553551 let body = args[args.length - 1];
554- let argNames = args.slice(0, args.length - 1).map(expr => {
552+ let params = args.slice(0, args.length - 1).map(expr => {
555553 if (expr.type != "word") {
556- throw new SyntaxError("Arg names must be words");
554+ throw new SyntaxError("Parameter names must be words");
557555 }
558556 return expr.name;
559557 });
560558
561559 return function() {
562- if (arguments.length != argNames .length) {
560+ if (arguments.length != params .length) {
563561 throw new TypeError("Wrong number of arguments");
564562 }
565563 let localScope = Object.create(scope);
566564 for (let i = 0; i < arguments.length; i++) {
567- localScope[argNames [i]] = arguments[i];
565+ localScope[params [i]] = arguments[i];
568566 }
569567 return evaluate(body, localScope);
570568 };
@@ -610,8 +608,9 @@ into something that can be evaluated more efficiently by doing as much
610608work as possible in advance. For example, in well-designed languages
611609it is obvious, for each use of a ((binding)), which binding is being
612610referred to, without actually running the program. This can be used to
613- avoid looking up the binding by name every time it is accessed and to
614- directly fetch it from some predetermined ((memory)) location.
611+ avoid looking up the binding by name every time it is accessed,
612+ instead directly fetching it from some predetermined ((memory))
613+ location.
615614
616615Traditionally, ((compilation)) involves converting the program to
617616((machine code)), the raw format that a computer's processor can
@@ -622,9 +621,9 @@ representation can be thought of as compilation.
622621
623622It would be possible to write an alternative ((evaluation)) strategy
624623for Egg, one that first converts the program to a JavaScript program,
625- uses ` new Function` to invoke the JavaScript compiler on it, and then
626- runs the result. When done right, this would make Egg run very fast
627- while still being quite simple to implement.
624+ uses ` Function ` to invoke the JavaScript compiler on it, and then runs
625+ the result. When done right, this would make Egg run very fast while
626+ still being quite simple to implement.
628627
629628If you are interested in this topic and willing to spend some time on
630629it, I encourage you to try to implement such a compiler as an
@@ -653,7 +652,8 @@ get real work done.
653652
654653Such a language does not have to resemble a typical programming
655654language. If JavaScript didn't come equipped with regular expressions,
656- you could write your own parser and evaluator for such a sublanguage.
655+ for example, you could write your own parser and evaluator for regular
656+ expressions.
657657
658658{{index "artificial intelligence"}}
659659
@@ -683,8 +683,8 @@ behavior attack
683683This is what is usually called a _ ((domain-specific language))_ , a
684684language tailored to express a narrow domain of knowledge. Such a
685685language can be more expressive than a general-purpose language
686- because it is designed to express exactly the things that need
687- expressing in its domain and nothing else.
686+ because it is designed to describe exactly the things that need to be
687+ described in its domain, and nothing else.
688688
689689## Exercises
690690
@@ -847,15 +847,15 @@ existing ones a new value.
847847
848848This ((ambiguity)) causes a problem. When you try to give a nonlocal
849849binding a new value, you will end up defining a local one with the
850- same name instead. ( Some languages work like this by design, but I've
851- always found it an awkward way to handle ((scope)).)
850+ same name instead. Some languages work like this by design, but I've
851+ always found it an awkward way to handle ((scope)).
852852
853853{{index "ReferenceError type"}}
854854
855855Add a special form ` set ` , similar to ` define ` , which gives a binding a
856856new value, updating the binding in an outer scope if it doesn't
857857already exist in the inner scope. If the binding is not defined at
858- all, throw a ` ReferenceError ` (which is another standard error type).
858+ all, throw a ` ReferenceError ` (another standard error type).
859859
860860{{index "hasOwnProperty method", prototype, "getPrototypeOf function"}}
861861
0 commit comments