Skip to content

Commit 63ef9fe

Browse files
committed
Integrate copyediting for chapter 12
1 parent 4142888 commit 63ef9fe

File tree

1 file changed

+23
-29
lines changed

1 file changed

+23
-29
lines changed

12_language.md

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ quote}}
1414

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

17-
The main thing I want to show in this chapter is that there is no ((magic)) involved in building a programming 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 experimenting, they often turn out to be quite mundane.
17+
The main thing I want to show in this chapter is that there's no ((magic)) involved in building a programming 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 experimenting, they often turn out to be quite mundane.
1818

1919
{{index "Egg language", [abstraction, "in Egg"]}}
2020

@@ -49,7 +49,7 @@ do(define(x, 10),
4949

5050
{{index block, [syntax, "of Egg"]}}
5151

52-
The ((uniformity)) of the ((Egg language)) means that things that are ((operator))s in JavaScript (such as `>`) are normal bindings in this language, applied just like other ((function))s. And since the syntax has no concept of a block, we need a `do` construct to represent doing multiple things in sequence.
52+
The ((uniformity)) of the ((Egg language)) means that things that are ((operator))s in JavaScript (such as `>`) are normal bindings in this language, applied just like other ((function))s. Since the syntax has no concept of a block, we need a `do` construct to represent doing multiple things in sequence.
5353

5454
{{index "type property", parsing, ["data structure", tree]}}
5555

@@ -74,7 +74,7 @@ The `>(x, 5)` part of the previous program would be represented like this:
7474

7575
{{indexsee "abstract syntax tree", "syntax tree", ["data structure", tree]}}
7676

77-
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 ((tree))like shape. The fact that expressions contain other expressions, which in turn might contain more expressions, is similar to the way tree branches split and split again.
77+
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, as shown in the following diagram, the structure has a ((tree))like shape. The fact that expressions contain other expressions, which in turn might contain more expressions, is similar to the way tree branches split and split again.
7878

7979
{{figure {url: "img/syntax_tree.svg", alt: "A diagram showing the structure of the syntax tree for the example program. The root is labeled 'do' and has two children, one labeled 'define' and one labeled 'if'. Those in turn have more children, describing their content.", width: "5cm"}}}
8080

@@ -92,7 +92,7 @@ Fortunately, this problem can be solved very well by writing a parser function t
9292

9393
{{index "parseExpression function", "syntax tree"}}
9494

95-
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.
95+
We define a function `parseExpression` that takes a string as input. It 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.
9696

9797
This is the first part of the parser:
9898

@@ -122,11 +122,11 @@ function skipSpace(string) {
122122

123123
{{index "skipSpace function", [whitespace, syntax]}}
124124

125-
Because Egg, like JavaScript, allows any amount of whitespace between its elements, we have to repeatedly cut the whitespace off the start of the program string. That is what the `skipSpace` function helps with.
125+
Because Egg, like JavaScript, allows any amount of whitespace between its elements, we have to repeatedly cut the whitespace off the start of the program string. The `skipSpace` function helps with this.
126126

127127
{{index "literal expression", "SyntaxError type"}}
128128

129-
After skipping any leading space, `parseExpression` uses three ((regular expression))s to spot the three 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. We use the `SyntaxError` constructor here. This is an exception class defined by the standard, like `Error`, but more specific.
129+
After skipping any leading space, `parseExpression` uses three ((regular expression))s to spot the three atomic elements that Egg supports: strings, numbers, and words. The parser constructs a different kind of data structure depending on which expression matches. If the input does not match one of these three forms, it is not a valid expression, and the parser throws an error. We use the `SyntaxError` constructor here. This is an exception class defined by the standard, like `Error`, but more specific.
130130

131131
{{index "parseApply function"}}
132132

@@ -155,13 +155,9 @@ function parseApply(expr, program) {
155155
}
156156
```
157157

158-
{{index parsing}}
159-
160-
If the next character in the program is not an opening parenthesis, this is not an application, and `parseApply` returns the expression it was given.
158+
{{index parsing, recursion}}
161159

162-
{{index recursion}}
163-
164-
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.
160+
If the next character in the program is not an opening parenthesis, this is not an application, and `parseApply` 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.
165161

166162
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.
167163

@@ -227,21 +223,21 @@ function evaluate(expr, scope) {
227223

228224
{{index "literal expression", scope}}
229225

230-
The evaluator has code for each of the ((expression)) types. A literal value expression produces its value. (For example, the expression `100` just evaluates to the number 100.) For a binding, we must check whether it is actually defined in the scope and, if it is, fetch the binding's value.
226+
The evaluator has code for each of the ((expression)) types. A literal value expression produces its value. (For example, the expression `100` evaluates to the number 100.) For a binding, we must check whether it is actually defined in the scope and, if it is, fetch the binding's value.
231227

232228
{{index [function, application]}}
233229

234-
Applications are more involved. If they are a ((special form)), like `if`, we do not evaluate anything and pass the argument expressions, along with the scope, 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 evaluated arguments.
230+
Applications are more involved. If they are a ((special form)), like `if`, we do not evaluate anything—we just and pass the argument expressions, along with the scope, 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 evaluated arguments.
235231

236-
We use plain JavaScript function values to represent Egg's function values. We will come back to this [later](language#egg_fun), when the special form called `fun` is defined.
232+
We use plain JavaScript function values to represent Egg's function values. We will come back to this [later](language#egg_fun), when the special form `fun` is defined.
237233

238234
{{index readability, "evaluate function", recursion, parsing}}
239235

240-
The recursive structure of `evaluate` resembles the similar structure of the parser, and both mirror the structure of the language itself. It would also be possible to combine the parser and the evaluator into one function, and evaluate during parsing. But splitting them up this way makes the program clearer and more flexible.
236+
The recursive structure of `evaluate` resembles the structure of the parser, and both mirror the structure of the language itself. It would also be possible to combine the parser and the evaluator into one function and evaluate during parsing, but splitting them up this way makes the program clearer and more flexible.
241237

242238
{{index "Egg language", interpretation}}
243239

244-
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 much with this language yet.
240+
This is really all that's needed to interpret Egg. It's that simple. But without defining a few special forms and adding some useful values to the ((environment)), you can't do much with this language yet.
245241

246242
## Special forms
247243

@@ -267,11 +263,11 @@ Egg's `if` construct expects exactly three arguments. It will evaluate the first
267263

268264
{{index Boolean}}
269265

270-
Egg also 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, only the precise value `false`.
266+
Egg also differs from JavaScript in how it handles the condition value to `if`. It will treat only the value `false` as false, not things like zero or the empty string.
271267

272268
{{index "short-circuit evaluation"}}
273269

274-
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.
270+
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.
275271

276272
The `while` form is similar.
277273

@@ -342,15 +338,15 @@ console.log(evaluate(prog, topScope));
342338

343339
{{index arithmetic, "Function constructor"}}
344340

345-
To supply basic ((arithmetic)) and ((comparison)) ((operator))s, we will also add some function values to the ((scope)). In the interest of keeping the code short, we'll use `Function` to synthesize a bunch of operator functions in a loop, instead of defining them individually.
341+
To supply basic ((arithmetic)) and ((comparison)) ((operator))s, we will also add some function values to the ((scope)). In the interest of keeping the code short, we'll use `Function` to synthesize a bunch of operator functions in a loop instead of defining them individually.
346342

347343
```{includeCode: true}
348344
for (let op of ["+", "-", "*", "/", "==", "<", ">"]) {
349345
topScope[op] = Function("a, b", `return a ${op} b;`);
350346
}
351347
```
352348

353-
A way to ((output)) values is also useful, so we'll wrap `console.log` in a function and call it `print`.
349+
It is also useful to have a way to ((output)) values, so we'll wrap `console.log` in a function and call it `print`.
354350

355351
```{includeCode: true}
356352
topScope.print = value => {
@@ -387,17 +383,15 @@ do(define(total, 0),
387383

388384
{{index "summing example", "Egg language"}}
389385

390-
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)).
386+
This is the program we've seen several times before that 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)).
391387

392388
{{id egg_fun}}
393389

394390
## Functions
395391

396392
{{index function, "Egg language"}}
397393

398-
A programming language without functions is a poor programming language indeed.
399-
400-
Fortunately, it isn't hard to add a `fun` construct, which treats its last argument as the function's body and uses all arguments before that as the names of the function's parameters.
394+
A programming language without functions is a poor programming language indeed. Fortunately, it isn't hard to add a `fun` construct, which treats its last argument as the function's body and uses all arguments before that as the names of the function's parameters.
401395

402396
```{includeCode: true}
403397
specialForms.fun = (args, scope) => {
@@ -460,15 +454,15 @@ Traditionally, ((compilation)) involves converting the program to ((machine code
460454

461455
{{index simplicity, "Function constructor", transpilation}}
462456

463-
It would be possible to write an alternative ((evaluation)) strategy for Egg, one that first converts the program to a JavaScript program, uses `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.
457+
It would be possible to write an alternative ((evaluation)) strategy for Egg, one that first converts the program to a JavaScript program, uses `Function` to invoke the JavaScript compiler on it, and runs the result. When done right, this would make Egg run very fast while still being quite simple to implement.
464458

465459
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.
466460

467461
## Cheating
468462

469463
{{index "Egg language"}}
470464

471-
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. Bridging the gap to a more primitive system, such as the machine code the processor understands, is more effort—but the way it works resembles what we are doing here.
465+
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. Bridging the gap to a more primitive system, such as the machine code the processor understands, takes more effort—but the way it works resembles what we are doing here.
472466

473467
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.
474468

@@ -492,15 +486,15 @@ application = expr '(' (expr (',' expr)*)? ')'
492486

493487
{{index expressivity}}
494488

495-
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 describe exactly the things that need to be described in its domain, and nothing else.
489+
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 describe exactly the things that need to be described in its domain and nothing else.
496490

497491
## Exercises
498492

499493
### Arrays
500494

501495
{{index "Egg language", "arrays in egg (exercise)", [array, "in Egg"]}}
502496

503-
Add support for arrays to Egg by adding the following three functions to the top scope: `array(...values)` to construct an array containing the argument values, `length(array)` to get an array's length, and `element(array, n)` to fetch the n^th^ element from an array.
497+
Add support for arrays to Egg by adding the following three functions to the top scope: `array(...values)` to construct an array containing the argument values, `length(array)` to get an array's length, and `element(array, n)` to fetch the *n*th element from an array.
504498

505499
{{if interactive
506500

0 commit comments

Comments
 (0)