Skip to content

Commit 6d13e94

Browse files
committed
Go over Chapter 12
1 parent aa60d7f commit 6d13e94

3 files changed

Lines changed: 50 additions & 50 deletions

File tree

12_language.md

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -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
1919
some human inventions were so immensely clever and complicated that
2020
I'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

2525
We will build a programming language called Egg. It will be a tiny,
2626
simple 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
9191
identifier's name as a string. Finally, `"apply"` expressions
9292
represent 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

9696
The `>(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
112112
imagine the objects as dots and the links between them as lines
113113
between those dots, it has a ((tree))like shape. The fact that
114114
expressions 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
116116
again.
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

184185
After 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
188189
the input does not match one of these three forms, it is not a valid
189190
expression, and the parser throws an error. We use `SyntaxError`
190191
instead 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}
356358
specialForms.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}
393395
specialForms.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}
431433
specialForms.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

450452
To be able to use the `if` construct we just defined, we must have
451453
access 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
453455
the values `true` and `false` and use those.
454456

455457
```{includeCode: true}
@@ -471,13 +473,13 @@ console.log(evaluate(prog, topScope));
471473

472474
To supply basic ((arithmetic)) and ((comparison)) ((operator))s, we
473475
will 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
476478
individually.
477479

478480
```{includeCode: true}
479481
for (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

496498
That 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}
501503
function run(program) {
@@ -538,33 +540,29 @@ A programming language without functions is a poor programming
538540
language indeed.
539541

540542
Fortunately, 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-
549547
specialForms.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
610608
work as possible in advance. For example, in well-designed languages
611609
it is obvious, for each use of a ((binding)), which binding is being
612610
referred 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

616615
Traditionally, ((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

623622
It would be possible to write an alternative ((evaluation)) strategy
624623
for 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

629628
If you are interested in this topic and willing to spend some time on
630629
it, I encourage you to try to implement such a compiler as an
@@ -653,7 +652,8 @@ get real work done.
653652

654653
Such a language does not have to resemble a typical programming
655654
language. 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
683683
This is what is usually called a _((domain-specific language))_, a
684684
language tailored to express a narrow domain of knowledge. Such a
685685
language 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

848848
This ((ambiguity)) causes a problem. When you try to give a nonlocal
849849
binding 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

855855
Add a special form `set`, similar to `define`, which gives a binding a
856856
new value, updating the binding in an outer scope if it doesn't
857857
already 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

18_http.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,11 +1343,11 @@ if}}
13431343
Use `document.querySelector` or `document.getElementById` to get
13441344
access to the elements defined in your HTML. An event handler for
13451345
`"click"` or `"mousedown"` events on the button can get the `value`
1346-
property of the text field and call `new Function` on it.
1346+
property of the text field and call `Function` on it.
13471347

13481348
{{index "try keyword", "exception handling"}}
13491349

1350-
Make sure you wrap both the call to `new Function` and the call to its
1350+
Make sure you wrap both the call to `Function` and the call to its
13511351
result in a `try` block so that you can catch exceptions that it
13521352
produces. In this case, we really don't know what type of exception we
13531353
are looking for, so catch everything.

code/solutions/18_2_a_javascript_workbench.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
let code = document.querySelector("#code").value;
1010
let outputNode = document.querySelector("#output");
1111
try {
12-
let result = new Function(code)();
12+
let result = Function(code)();
1313
outputNode.innerText = String(result);
1414
} catch (e) {
1515
outputNode.innerText = "Error: " + e;

0 commit comments

Comments
 (0)