Skip to content

Commit 2cd0415

Browse files
committedAug 12, 2021
PIE update
1 parent cc7d1ef commit 2cd0415

File tree

17 files changed

+781
-40
lines changed

17 files changed

+781
-40
lines changed
 

‎js/easycoder/Browser.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,8 +2174,6 @@ const EasyCoder_Browser = {
21742174
switch (targetRecord.keyword) {
21752175
case `text`:
21762176
case `textarea`:
2177-
target.value = value;
2178-
break;
21792177
case `input`:
21802178
target.value = value;
21812179
break;

‎pie/pie/2 Designing a language/Commands.txt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,19 @@ If you are building a composite string this can be a little cumbersome, so I rec
2222
index Numbers to N
2323
print `The value of element ` cat N cat ` of Numbers is ` cat Numbers
2424
```
25-
All languages need a selection of conditional commands, the minimum being `if` and `while`. Because this language will probably lack any advanced structuring you'll also need the infamous `goto` command. To avoid repetition you'll most likely want `gosub` and `return` (or their equivalents). If you're feeling really adventurous you might include a `with` clause in your `gosub` so you can pass in private variables, but it could get rather complex as you'll need some way of identiying them at the entry point of a subroutine.
25+
All languages need a selection of flow control commands, the minimum being `if` and `while`. Because this language will probably lack any advanced structuring you'll also need the infamous `goto` command. To avoid repetition you'll most likely want `gosub` and `return` (or their equivalents). If you're feeling really adventurous you might include a `with` clause in your `gosub` so you can pass in private variables, but it could get rather complex as you'll need some way of identiying them at the entry point of a subroutine.
2626

27-
When you're programming browser-based apps you'll almost certainly want concurrent processing at some point. JavaScript itself is single-threaded but there are ways to implement concurrency (or at least something that looks and feels like it) in your scripts. All I'll say here is that with this capability you can implement commands like `wait` to pause execution for a given time, `fork` to run different parts of your script concurrently with another, and `run` to load and concurrently run different script modules. There's a whole chapter of this book devoted to the subject.
27+
When you're programming browser-based apps you'll almost certainly want concurrent processing at some point. JavaScript itself is single-threaded but there are ways to implement concurrency (or at least something that looks and feels like it) in your scripts. All I'll say here is that with this capability you can implement commands like `wait` to pause execution for a given time, `fork` to run different parts of your script concurrently with another, and `run` to load and concurrently run different script modules. I'll cover these a little later on in this book.
2828

29-
Another thing you'll almost certainly want at some point is a set of REST commands so you can GET data from your own or remote websites and POST data back again.
29+
Some other things you're likely to want at some point (some more important than others):
30+
- a set of REST commands so you can GET data from your own or remote websites and POST data back again
31+
- commands for handling JSON data; creating JSON objects adding and removing items from them etc.
32+
- commands for handling forms
33+
- a Google Maps module
34+
- floating-point arithmetic
35+
- other specialized domain-related commands
36+
37+
All of these can be handled by load-on-demand plugins (~sid:5 Plugins:See this section~) instead of rolling them into the main JS file~
3038

3139
## DOM commands
3240
A large number of commands in the browser domain will be those dealing with the Document Object Model. This is where the language is likely to be most frequently used; as a replacement for JavaScript when aspects of a web page need to be tweaked.
@@ -35,7 +43,7 @@ Each of the DOM variables such as `<div>` will be designed to handle actions on
3543
```
3644
attach Screen to `id_screen`
3745
```
38-
This assumes that in your page there is a `<div>` with an id of `id_screen`. Your runtime JavaScript looks for this id and "attaches" its owner to the script variable. You can now perform operations on the variable; the runtime instantly and faithfully mirrors changes on the `<div>` itself. Your language will have to recognise all the commands that are valid for this variable type, so you might decide to implement a `set` command to do the hard work, with constructs like
46+
This assumes that in your document there is a `<div>` with an id of `id_screen`. Your runtime JavaScript looks for this id and "attaches" its owner element to the script variable. You can now perform operations on the variable; the runtime instantly and faithfully mirrors changes on the `<div>` itself. Your language will have to recognise all the commands that are valid for this variable type, so you might decide to implement a `set` command to do the hard work, with constructs like
3947
```
4048
set the background of Screen to `yellow`
4149
```

‎pie/pie/2 Designing a language/Environment.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Rather than beat about the bush I'll come straight to the point. The techniques
66

77
The other decision concerns where the code is to run. If the compiler is coded in Python, the applications you build are basically restricted to running on a PC of some kind. I'm personally doubtful of the value of this, especially as Python is itself already a fairly high-level language and you'd get more bang from your buck by just learning that.
88

9-
Some might prefer to code in C++ or Java. Many compilers are written in C++ so there's something to be said for that, though if you want to distrbute your efforts you'll have to do versions for Windows, for Mac and for Linux.
9+
Some might prefer to code in C++ or Java. Many compilers are written in C++ so there's something to be said for that, though if you want to distribute your efforts you'll have to do versions for Windows, for Mac and for Linux.
1010

1111
There are many, many other alternatives but the one that stands out is JavaScript, because it runs in every browser on the planet. Many people, most notably Chromebook owners, rarely venture outside the browser. Your apps can reside on a webserver and be available simply by typing a URL. Add to this a superb graphics environment and the case is unanswerable.
1212

‎pie/pie/2 Designing a language/Example code.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ multiply C by B
4545
divide B by A giving C
4646
divide C by B
4747
```
48-
Note how I've shown multiply and divide having the two values in the opposite order to add and subtract. This of course regards B as a "primary" value and A as something being used to modify it, which might not always be the case, but it serves to point out how the new language can follow English quite closely.
48+
Note how I've shown multiply and divide having the two values in the opposite order to add and subtract. The way I've presented it treats B as a "primary" value and A as something being used to modify it, which might not always be the case, but it serves to point out how the new language can follow English quite closely.
4949

5050
~tid:Variables~

‎pie/pie/2 Designing a language/content.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This is pretty obvious really. If we can't be sure what a statement means, how c
1212

1313
2. A computer program is code, but sometimes there's a need to explain what's happening. A comment is there for human readers and the computer knows to ignore it when running the program. You can adopt any comment style you wish, but for the purposes of this book I'm going to use an exclamation mark (!) to indicate that what follows is a comment.
1414

15-
3. Modern programming is nearly always 'structured', but human language isn't. Human-written instructions such as cooking recipes or navigation directions use GOTO freely, but this has been anathema to programmers for some decades now. Here we have something of a dilemma. Structure can be applied without the use of symbols, for example using indentation as in Python, but then it's all too easy to get wrong in ways that are hard to spot, so in many respects it's little better than having no structural rules at all. Consider though that programs written in our new language are unlikely to be massively complex. Anything really large or complex requires a professional programmer, who will be using mainstream development tools, so it's arguable we don't need formal structure. Fortran managed without it in the early days of programming, so without any sense of shame we'll resurrect the much-maligned GOTO command. What we do need is something to identify a labelled point in a program, and for this I'll allocate a colon (:) symbol.
15+
3. Modern programming is nearly always 'structured', but human language isn't. Human-written instructions such as cooking recipes or navigation directions use GOTO freely, but this has been anathema to programmers for some decades now. Here we have something of a dilemma. Structure can be applied without the use of symbols, for example using indentation as in Python, but then it's all too easy to get wrong in ways that are hard to spot, so in many respects it's little better than having no structural rules at all. Consider also that programs written in our new language are unlikely to be massively complex. Anything really large or complex requires a team of professional programmers, who will be using mainstream development tools, so it's arguable we don't need formal structure. Fortran managed without it in the early days of programming, so without any sense of shame we'll resurrect the much-maligned GOTO command. What we do need is something to identify a labelled point in a program, and for this I'll allocate a colon (:) symbol.
1616

1717
4. When literal text is used it must be quoted so the compiler won't mistake it for code. This is normally done with single or double quotes, but when you want to include one of these characters in your quoted text it gets very messy. So I'll specify the little-used backtick (&#96;) to denote quoted text.
1818

@@ -22,6 +22,6 @@ And that's it - just 3 symbols.
2222

2323
To help with clarity it's useful to capitalize certain words. English is actually a poor example since capitals are used mainly to start sentences and for names. German takes things a lot further by capitalizing all nouns.
2424

25-
Computer languages generally use camel case, which does indeed improve readability, but then start all names with lower-case, contrary to usage in human languages. To keep closer to the latter I'll suggest using an initial capital letter on all variable names. It won't actually be a requirement but it makes statements much more readable. The initial word of every statement will start with a lower-case letter otherwise it could be confused with a variable having the same name. It also helps when you build a color-coded editor to have all the variables stand out in their own color.
25+
Computer languages generally use camel case, which does indeed improve readability, but then start all names with lower-case, contrary to usage in human languages. To keep closer to the latter I'll suggest using an initial capital letter on all variable names. It won't actually be a requirement but it makes statements much more readable. The initial word of every statement will start with a lower-case letter otherwise it could be confused with a variable having the same name. It also helps when you build a color-coded editor, to have all the variables stand out in their own color.
2626

2727
~tid:Environment:The coding environment~

‎pie/pie/3 Compilation/Compiler.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
# Keywords
1+
# The compiler
22

3-
In our custom language, every program statement starts with an English command verb such as `put`, `get`, `open`, `close`, `print` etc., plus a handful of other words like `if` and `while`. All of these words we call _keywords_ and each one of them has its own section of code in the compiler to handle it.
4-
5-
Most of the main compiler module is a collection of functions to do things that will be needed by the keyword processors. I'll show the ones implemented in _**EasyCoder**_; your language may need more or less of them.
3+
Most of the main compiler module is a collection of functions to do things that will be needed by the ~tid:Keywords:keyword handlers~. I'll show the ones implemented in _**EasyCoder**_; your language may need more or less of them.
64

75
The _**EasyCoder**_ compiler module looks like this:
86
```

‎pie/pie/3 Compilation/Handlers.txt renamed to ‎pie/pie/3 Compilation/Keywords.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Keyword handlers
22

3-
Here we come to the heart of the compiler. When the compiler starts to evaluate a command, the first word will be a command verb or a special word such as `if` or `while`. The compiler tries each of the available domains in turn until one reports it is able to handle that keyword. This is done by a call to a function inside the domain handler. The one for the "core" package looks like this:
3+
Here we come to the heart of the compiler.
4+
5+
In our custom language, every program statement starts with an English command verb such as `put`, `get`, `open`, `close`, `print` etc., plus a handful of other words like `if` and `while`. All of these words we call _keywords_ and each one of them has its own section of code in the compiler to handle it. These _handlers_ are distributed among the various domain modules that contribute to the language.
6+
7+
When the compiler starts to evaluate a command, the first word will be a command verb or a special word such as `if` or `while`. The compiler tries each of the available domains in turn until one reports it is able to handle that keyword. This is done by a call to a function inside the domain handler. The one for the "core" package looks like this:
48
```
59
getHandler: (name) => {
610
switch (name) {
@@ -30,7 +34,7 @@ Perhaps the most important command is `stop`, which causes the current thread of
3034

3135
There's also likely to be an `exit` command, which not only stops the current thread but terminates the entire program instance, which ideally should clean itself up and remove itself from the browser. You should choose what action should happen if execution reaches the end of a script without either `stop` or `exit` being found. The `exit` command is unlikely to be used by the mai program running in a browser as the page would stop operating, but if you've called in other scripts it's sometimes useful to be able to terminate them.
3236

33-
Let's continue by examining how to process an `add` command. Here's the handler:
37+
Let's continue by examining how to process an `add` command. Here's the handler referenced in the fragment above:
3438
```
3539
Add: {
3640

‎pie/pie/3 Compilation/Tokenizer.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# The tokenizer
22

3-
The tokenizer reads the entire script and converts if to a list of tokens. Since we're dealing with a variant of English, these tokens are words delimited by spaces or newlines. We'll decide to ignore the difference as for as possible and allow scripts to be written on one line or over several lines, according to the whim of the programmer.
3+
The tokenizer reads the entire script and converts it to a list of tokens. Since we're dealing with a variant of English, these tokens are words delimited by spaces or newlines. We'll decide to ignore the difference as far as possible and allow scripts to be written on one line or over several lines, according to the whim of the programmer.
44

55
The tokenizer scans along the source script, character by character. First it looks for a non-space, then it moves forward until it sees a space. The characters it passed over become a _token_ that gets pushed into an array, with a couple of exceptions:
66

7-
The first exception is comments. These start with an exclamation mark and continue to the end of a line. A comment can be the only thing on a line or it can be placed after a program instruction. So when the scanner sees an exclamation mark it removes everything from and including it to the end of the line.
7+
The first exception is comments. These start with an exclamation mark and continue to the end of a line. A comment might be the only thing on a line or it might be placed after a program instruction. So when the scanner sees an exclamation mark it removes everything from and including it to the end of the line.
88

9-
The second exception is quoted strings, which may themselves contain spaces. Quoted strings start and end with a backtick (&#96;) and they're not allowed to continue from one line to the next. So when the tokenizer sees a backtick it reads ahead until it sees a matching backtick, then the entire string (minus its backticks) is pushed to the list.
9+
The second exception is quoted strings, which may themselves contain spaces. Quoted strings start and end with a backtick (&#96;) and they're not allowed to continue from one line to the next. So when the tokenizer sees a backtick it reads ahead until it sees a matching backtick, then the entire string (minus its backticks) is pushed to the token array.
1010

1111
At the end of the scanning pass we have an array of tokens. However, there's a little more to do to make the programmer's life easier. If an error occurs it's polite to report it, showing where it happened in the original script. Rather than just push plain tokens onto our list we also push the index of the token in the list and the line number of the original script it came from. The latter is easy to get simply by counting newlines while scanning. We also build a second array, comprising the line number and the text of each line. This will let our error reporter show the line at which the error occurred and maybe a few lines leading up to it.
1212

‎pie/pie/3 Compilation/Variables.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Compiling variables
22

3-
Variables are a special kind of entity and require a special kind of entry in the program list. Each _domain_ (see ~tid:Compiler:the compiler~) handles its own types of variables but in the program list they all look much the same. In _**EasyCoder**_ the _compileVariable()_ function looks like this:
3+
Variables are a special kind of entity and require a special kind of entry in the program list. Each _domain_ (see ~tid:Domains:Domains~) handles its own types of variables but in the program list they all look much the same. In _**EasyCoder**_ the `compileVariable()` function looks like this:
44
```
55
compileVariable: function(domain, keyword, isVHolder = false, extra = null) {
66
this.next();
@@ -42,7 +42,7 @@ It starts by checking for a duplicate token name, then adds an entry to the symb
4242

4343
`used` - a boolan value that starts `false` and is changed to `true` as soon as the compiler comes across a command that assigns it a value. This allows a post-compilation check to be made for unused variables. Note that simply reading a variable does not set the flag (this would defeat the purpose of identifying it as unused). Unused variables are a frequent source of errors when you come to run your program.
4444

45-
`isVHolder` - tells the compiler that this variable is able to hold a numeric or a string value. Many variable types do not so this permits another compilation check to be made that a script is not attempting to assign a value to them.
45+
`isVHolder` - tells the compiler that this variable is able to hold a numeric or a string value. Many variable types do not so this permits another compilation check to be made that a script is not attempting to assign a value to one of them.
4646

4747
`name` - the name of the variable.
4848

@@ -56,6 +56,6 @@ It starts by checking for a duplicate token name, then adds an entry to the symb
5656

5757
`extra` - an unspecified extra object. This is used by the _browser_ plugin to signify that the variable is a DOM object. Other domains may use it for something else entirely.
5858

59-
Your language may have more or less fields than this, according to its requirements.
59+
Your language may have more or fewer fields than this, according to its requirements.
6060

61-
~tid:Handlers:Keyword handlers~
61+
~tid:Keywords:Keyword handlers~

‎pie/pie/3 Compilation/content.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The compiler I will describe here will generate code that can easily be interpre
1616

1717
## Compiler strategy
1818

19-
Books on compiler design generally describe a standard series of steps such as _lexical_, _syntax_ and _semantic analysis_, _code generation_ and _optimization_. The compiler examines the entire program to build an internal representation of if, then traverses the structure to generate and optimize the code. We're not going to do any of that. Not because it's wrong - it certainly isn't - but because it's not the only way. The technique I'll be describing here is based on observation of how human beings process language, particularly foreign language. As far as I know there are no other books on this techinique.
19+
Books on compiler design generally describe a standard series of steps such as _lexical_, _syntax_ and _semantic analysis_, _code generation_ and _optimization_. The compiler examines the entire program to build an internal representation of if, then traverses the structure to generate and optimize the code. We're not going to do any of that. Not because it's wrong - it certainly isn't - but because it's not the only way. The technique I'll be describing here is based on observation of how human beings process language, particularly foreign language. I have yet to find any other books describing this techinique.
2020

2121
Imagine you encounter some instructions written in some language other than English. Let's say you have a very basic understanding of the language but are in no way fluent, and that you have a dictionary to hand. How do you approach the task?
2222

@@ -32,7 +32,7 @@ Now I can't guarantee that all minds approach a problem the same way, but this h
3232

3333
The first word is _apri_. I look this up in the dictionary and discover it means _open_ (as a command verb). So I deduce that what follows must be something that needs to be opened. The next word is _la_ (_the_) which I will treat as syntactic noise that doesn't affect the overall meaning. Then comes _terza_ (_third_). I realize this isn't a thing at all; it's an ordinal number. So the thing that needs to be opened is the third of something. Save that information and move on to _porta_ (_door_). Yes, this makes sense; it's the third door.
3434

35-
At this point there are potentially several possibilities. Perhaps there's nothing at all, in which case I'll need to make some kind of assumption about which door to open. Perhaps it's telling me _how_ to open the door, with a adjective like _quickly_ or _slowly_? Nope, it's none of them. It's actually _a_ (_on the_). This is another bit of syntactic noise, but it gives me a good clue as to what to expect, that is, either _right_ or _left_. When I take the next token it's confirmed.
35+
At this point there are potentially several possibilities. Perhaps there's nothing at all, in which case I'll need to make some kind of assumption about which door to open. Perhaps it's telling me _how_ to open the door, with an adjective like _quickly_ or _slowly_? Nope, it's none of them. It's actually _a_ (_on the_). This is another bit of syntactic noise, but it gives me a good clue as to what to expect, that is, either _right_ or _left_. When I take the next token it's confirmed.
3636

3737
There are several points along the way where more than one possibility branches off from where I am. The technique I'm using tries each of these possibilities in turn until one is found that meets expectations, then I move on. If none of my expectations are met I may have taken a wrong turning earlier, so I back up to that point and try another path. Eventually I either find a complete match for the whole instruction or I conclude that it's nonsense - an error.
3838

@@ -42,7 +42,7 @@ However, all of the above assumes the program is presented to the compiler as a
4242

4343
## Function naming
4444

45-
When you add a JavaScript file to your web page you are adding all its global constants, variables and functions. It's important to name these in such as way that they're not likely to clash with names chosen for other JS modules that might also be added to the page. The best strategy is to keep to a minimum the number of distinct global names and to prefix each one with a unique identifier such as the name of your project. For example, _**EasyCoder**_ comprises a number of source files each of which contains a single function with the name `EasyCoder_`_something_. Here is the start of the main compiler module:
45+
When you add a JavaScript file to your web page you are adding all its global constants, variables and functions. It's important to name these in such as way that they're not likely to clash with names chosen for other JS modules that might also be added to the page. The best strategy is to keep to a minimum the number of distinct global names and to prefix each one with a unique identifier such as the name of your project. For example, _**EasyCoder**_ comprises a number of source files each of which contains a single object with the name `EasyCoder_`_something_. Here is the start of the main compiler module:
4646
```
4747
const EasyCoder_Compiler = {
4848

‎pie/pie/4 Runtime/DOM.txt

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Running DOM commands
2+
3+
Browser commands tend to be more complex than the simple ones we've just seen, with the notable exception of variable declarations, which just return the address of the next command.
4+
5+
Here's the browser `create` command. It's a simplified copy of the code in _**EasyCoder**_:
6+
```
7+
run: (program) => {
8+
const command = program[program.pc];
9+
const targetRecord = program.getSymbolRecord(command.name);
10+
let parent;
11+
if (command.parent === `body`) {
12+
parent = document.body;
13+
} else {
14+
const p = program;
15+
const parentRecord = p.getSymbolRecord(command.parent);
16+
parent = parentRecord.element[parentRecord.index];
17+
}
18+
targetRecord.element[targetRecord.index] = document.createElement(targetRecord.keyword);
19+
targetRecord.element[targetRecord.index].id =
20+
`ec-${targetRecord.name}-${targetRecord.index}-${EasyCoder.elementId++}`;
21+
if (targetRecord.keyword === `a`) {
22+
targetRecord.element[targetRecord.index].setAttribute(`href`, `#`);
23+
}
24+
parent.appendChild(targetRecord.element[targetRecord.index]);
25+
return command.pc + 1;
26+
}
27+
```
28+
As before, first we get the command, then the symbol table record for the target variable (for example a `<div>`).
29+
30+
When we create a DOM object we can make it a direct child of the page BODY or of another DOM element. In the latter case, the parent will have been specified in the script, so we can get its record, and from that the actual DOM element (which will only exist if the parent has also been `create`d or has been `attach`ed to a suitable element in the document).
31+
32+
Now we create a new element, giving it the type, e.g. `dom`. We must give it an ID that is guaranteed to be unique; I choose to use a 3-part name comprising the variable name, its array index and a global serial number that increments for every DOM element. This combination ID is very useful when using the browser debugger as it becomes quite clear how any element relates to the script. So a typical ID looks like
33+
```
34+
MyDiv-0-392
35+
```
36+
A special action is needed for hyperlinks; these must have an attribute set so they don't respond to clicks except via script commands (more of this later). And finally we append the new element to its parent.
37+
38+
### `on click`
39+
40+
This is the runtime code for the following command:
41+
```
42+
on click <DOM variable> <do something>
43+
```
44+
45+
This is one of the most complex parts of DOM handling. Here's a simplified version of the code extracted from the _**EasyCoder**_ handler:
46+
```
47+
targetRecord = program.getSymbolRecord(command.symbol);
48+
targetRecord.element.forEach(function (target, index) {
49+
if (target) {
50+
target.targetRecord = targetRecord;
51+
target.targetIndex = index;
52+
target.targetPc = command.pc + 2;
53+
target.onclick = function (event) {
54+
event.stopPropagation();
55+
EasyCoder_Browser.clickData = {
56+
target,
57+
clientX: event.clientX,
58+
clientY: event.clientY
59+
};
60+
if (program.length > 0) {
61+
const eventTarget = event.target;
62+
if (eventTarget.type != `radio`) {
63+
eventTarget.blur();
64+
}
65+
if (typeof eventTarget.targetRecord !== `undefined`) {
66+
eventTarget.targetRecord.index = eventTarget.targetIndex;
67+
setTimeout(function () {
68+
program.run(eventTarget.targetPc);
69+
}, 1);
70+
}
71+
}
72+
return false;
73+
};
74+
}
75+
});
76+
```
77+
In _**EasyCoder**_ it's common to create an array of DOM variables that respond identically to a mouse click, avoiding the need to set up a handler for each array element. So the code sets up listeners for each of the array elements.
78+
79+
Three items of information need to be retrieved when a mouse click occurs, so these are added to the DOM element as properties. Bear in mind that at the point this code runs there hasn't yet been a mouse click; we're just setting up for when they do happen.
80+
81+
Next we set up the `onclick` listener. The content of this is what will handle the actual click. We set up a global record to record the target element and the click location; this is needed for drag and drop operations. We 'blur' everything except radio buttons (to remove the focus), then we set the index of the DOM variable to the one that was clicked, allowing the `<do something>` code to know which element it's dealing with.
82+
83+
Finally, we request a new execution thread for the actions resulting from the click. It's best to do this after a short pause, otherwise the display doesn't have time to update before the actions start to take effect.
84+
85+
This code is not guaranteed to be optimal. Slight changes tend to have unforseen effects. All I can say is that it works pretty reliably and saves a massive amount of effort compared with having to do it in JavaScript each time.
86+
87+
## CSS
88+
89+
You have a choice of assigning CSS classes to DOM elements or of assigning specific style values. If you want to do the former you'll need something along the lines of:
90+
```
91+
symbol = program.getSymbolRecord(command.symbolName);
92+
target = symbol.element[symbol.index];
93+
if (!target) {
94+
targetId = program.getValue(symbol.value[symbol.index]);
95+
target = document.getElementById(targetId);
96+
}
97+
program.getValue(command.value).split(` `).forEach(function(item) {
98+
target.classList.remove(item);
99+
target.classList.add(item);
100+
});
101+
102+
```
103+
This gets the symbol record for the element, either directly from the symbol record or by using its saved ID and requesting it from the DOM. Then it splits the list of classes provided, removes each one from the target's class list and replaces it.
104+
105+
The alternative is to set styles directly:
106+
```
107+
symbol = program.getSymbolRecord(command.symbolName);
108+
target = symbol.element[symbol.index];
109+
if (!target) {
110+
const symbolElement = symbol.value[symbol.index];
111+
if (!symbolElement.type) {
112+
program.runtimeError(command.lino,
113+
`Variable '${symbol.name}' is not attached to a DOM element.`);
114+
return 0;
115+
}
116+
targetId = program.getValue(symbolElement);
117+
target = document.getElementById(targetId);
118+
}
119+
const styleValue = program.getValue(command.styleValue);
120+
if (!symbol.element[symbol.index]) {
121+
program.runtimeError(command.lino, `Variable '${symbol.name}' has no DOM element.`);
122+
return 0;
123+
}
124+
switch (command.type) {
125+
case `setStyle`:
126+
target.style[command.styleName.content] = styleValue;
127+
break;
128+
case `setStyles`:
129+
target.style.cssText = styleValue;
130+
break;
131+
}
132+
```
133+
This one presents an alternative way of getting the target element. It then either replaces all the styles on that element or just one of them, depending on what it finds in `command.type`.
134+
135+
~sid:5 Plugins:Plugins~

‎pie/pie/4 Runtime/Handlers.txt

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Runtime handlers
2+
3+
Every keyword has a runtime handler function alongside the compiler function described ~stid:3 Compilation/Handlers:earlier~. The handler for `add` looks like this:
4+
```
5+
run: program => {
6+
const command = program[program.pc];
7+
const value1 = command.value1;
8+
const value2 = command.value2;
9+
const target = program.getSymbolRecord(command.target);
10+
if (target.isVHolder) {
11+
const value = target.value[target.index];
12+
if (value2) {
13+
const result = program.getValue(value2) +
14+
program.getValue(value1);
15+
target.value[target.index] = {
16+
type: `constant`,
17+
numeric: true,
18+
content: result
19+
};
20+
} else {
21+
if (!value.numeric && isNaN(value.content)) {
22+
program.nonNumericValueError(command.lino);
23+
}
24+
const result = parseInt(value.content) + parseInt(program.getValue(value1));
25+
target.value[target.index] = {
26+
type: `constant`,
27+
numeric: true,
28+
content: result
29+
};
30+
}
31+
} else {
32+
program.variableDoesNotHoldAValueError(command.lino, target.name);
33+
}
34+
return command.pc + 1;
35+
}
36+
```
37+
We start by extracting the command from the program (passed in as the single parameter) at the index given by `program.pc`. Then we get from it the two values to be added and the name of the _target_ variable - that is, where the result is going. Next we get the symbol table record for that variable and check if it's able to hold a value. If not, we throw an error.
38+
39+
Next we get the current value of the target, by using its array index. Remember that every variable is an array although most only ever have a single element.
40+
41+
Now we check if there actually was a second value or whether the sum is of the form
42+
```
43+
C = C + A
44+
```
45+
If `value2` exists we can request the values to be added, do the addition and write the result back into the target array. Note that we're writing a new value specification, not just the result itself.
46+
47+
If the second value was not present we check that the target contains a numeric value, without which it's risky to do the addition. This is a safety measure to avoid hard-to-find type conversion errors. (You may think it should also be done for the first case too, but I'm assuming that `getValue(value)` will convert strings to numbers.) So we get the integer value of each of the 2 values, add them together and post them back to the target.
48+
49+
Finally, we return the value of the current program counter advanced by one.
50+
51+
There's nothing to say this is the only - or even the best - way to handle all the issues involved, but it's one that I've found in practice results in very few errors.
52+
53+
### `fork`
54+
55+
Here's a second example; this time of the `fork` command:
56+
```
57+
run: program => {
58+
const command = program[program.pc];
59+
try {
60+
program.run(program.symbols[command.label].pc);
61+
} catch (err) {
62+
console.log(err.message);
63+
alert(err.message);
64+
}
65+
return command.pc + 1;
66+
}
67+
```
68+
This function is very short; it extracts the command as before then starts a new program thread at the address of the label given before returning the next step as before. The new thread won't run immediately because this one is still active, so it just goes onto the queue and the current thread continues with the next command. The new thread will get its day in the sun when the current one reaches a `stop` or a `wait` command.
69+
70+
### `if`
71+
72+
And finally, here's how the `if` command is handled:
73+
```
74+
run: program => {
75+
const command = program[program.pc];
76+
const condition = command.condition;
77+
const test = program.condition.test(program, condition);
78+
if (test) {
79+
return command.pc + 1;
80+
}
81+
return command.else;
82+
}
83+
84+
```
85+
This one extracts the command then the condition and calls the condition processor to evaluate it as `true` or `false`. It then returns either with the address of the next command or with the `else` address.
86+
87+
~tid:DOM:Running DOM commands~

‎pie/pie/4 Runtime/content.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
Once compilation is complete, its output is a _program_; a list of commands. The job of the runtime engine is to take these commands in linear order or as directed otherwise and perform the duties each one encapsulates. You could regard the commands as an assembly language for a rather special kind of processor.
44

5-
The runtime engine starts by pushing onto a runtime queue the offset (the program counter) at which to start execution. If the queue was not empty, nothing else happens as the queue already contains one or more threads of execution handling previous run requests.
5+
The runtime engine starts by pushing onto a runtime queue the offset (the program counter) at which to start execution. If the queue was not empty, nothing else happens as the queue already contains one or more threads of execution, handling previous run requests.
66

77
Once an execution thread stops, by reaching a `stop` or `wait` command, the queue is popped and the next run request starts. If this is well-behaved it too will stop or wait at some point and allow another thread to start. This behaviour creates an illusion of multi-tasking.
88

9-
In a system where multiple scripts may running at the same time, each one can have its own program and its own queue or you may set up a gloabl queue where each entry comprises a program and its program counter.
9+
In a system where multiple scripts may run at the same time, you can arrange for each one to have its own program and its own queue or you may set up a global queue where each entry comprises a program and its program counter.
1010

11-
Each command contains a `domain` property. This identifies which plugin domain should handle the command. Here's the core of the runtime engine in its simplest form:
11+
Each command contains a `domain` property that identifies which plugin domain should handle the command. Here's the core of the runtime engine in its simplest form:
1212
```
1313
while (queue.length > 0) {
1414
program.pc = queue.shift();
@@ -41,4 +41,4 @@ If you add a `trace` command you can arrange for the program to stop at each com
4141

4242
Trace/debug is implemented in _**EasyCoder**_. The code is all in the `Runtime.js` file and there's a page in the [Codex](https://easycoder.github.io?s=step10) describing the tracer and how to use it.
4343

44-
~sid:5 Plugins:Plugins~
44+
~tid:Handlers:Runtime handlers~
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Export and import
2+
3+
As ~tid:Handling plugins:already mentioned~, the language can share variables between scripts, allowing almost any amount of functionality to be delegated. With such power comes a certain amount of danger as you may have no control over what the delegate will do with your variables.
4+
5+
The code to specify a list of exports looks like this:
6+
```
7+
// Get the exports, if any
8+
const imports = [];
9+
if (compiler.tokenIs(`with`)) {
10+
while (true) {
11+
if (compiler.nextIsSymbol(true)) {
12+
const symbolRecord = compiler.getSymbolRecord();
13+
imports.push(symbolRecord.name);
14+
compiler.next();
15+
if (!compiler.tokenIs(`and`)) {
16+
break;
17+
}
18+
}
19+
}
20+
}
21+
```
22+
It may seem odd to label the array `imports[]` but that's exactly what they will be when received by the child script Here we're specify what they are in the parent's space. It's simply a matter of listing the variable names. Here's an example:
23+
```
24+
run Calculator with Opcode and Arg1 and Arg2
25+
```
26+
On the receiving side, the child script would start with something like this:
27+
```
28+
import variable Opcode and variable Arg1 and variable Arg2
29+
```
30+
The first thing to say is that the names of the variable don't have to match what they were called in the parent script, but ther types must match, as must the number of shared variables. Here's the code to handle importing:
31+
```
32+
Import: {
33+
34+
compile: compiler => {
35+
const imports = compiler.imports;
36+
let caller = EasyCoder.scripts[imports.caller];
37+
const program = compiler.getProgram();
38+
if (imports.length) {
39+
for (const name of imports) {
40+
let symbolRecord = caller.getSymbolRecord(name);
41+
const thisType = compiler.nextToken();
42+
const exportedType = symbolRecord.keyword;
43+
if (thisType === exportedType) {
44+
const command = compiler.compileVariable(symbolRecord.domain, exportedType, true);
45+
const newRecord = program[compiler.getSymbols()[command.name].pc];
46+
newRecord.element = symbolRecord.element;
47+
newRecord.exporter = symbolRecord.exporter ? symbolRecord.exporter : caller.script;
48+
newRecord.exportedName = symbolRecord.name;
49+
newRecord.extra = symbolRecord.extra;
50+
newRecord.isVHolder = symbolRecord.isVHolder;
51+
if (symbolRecord.program) {
52+
newRecord.program = symbolRecord.program.script;
53+
}
54+
newRecord.imported = true;
55+
if (!compiler.tokenIs(`and`)) {
56+
break;
57+
}
58+
} else {
59+
throw new Error(`Mismatched import variable type for '${symbolRecord.name}'`);
60+
}
61+
}
62+
if (compiler.tokenIs(`and`)) {
63+
throw new Error(`Imports do not match exports`);
64+
}
65+
} else {
66+
compiler.next();
67+
}
68+
return true;
69+
},
70+
71+
run: program => {
72+
return program[program.pc].pc + 1;
73+
}
74+
},
75+
```
76+
This iterates the imports. For each one, we get its record from the symbol table of the calling program and compare its type with the corresponding item in the import command as shown above. If they match we then compile the variable as if it had been a regular declaration.
77+
78+
Next we pull out the record for the variable as `newRecord` and add information from the exported variable. The important thing is to ensure the two records keep in step with each other and there are a couple of different strategies to ensure this. One is to share the actual content of the parent and child; the other is to synchronize every time the variable is read or altered. When I translated this code from Java back in 2018 I was still new to JavaScript and I couldn't figure if sharing was even possible, so I went for the second strategy. You should not assume that _**EasyCoder**_ does it the best way; the code has been susequently governed by the rule "If it ain't broke, don't fix it".
79+
80+
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# Handling plugins
2+
3+
This is a fairly complex part of the language but well worth implementing as it gives a huge amount of power to the programmer.
4+
5+
In order to compile a script, all of the domain handlers for the keywords it contains must already be present. A script can't request a plugin for its own code because by then the compiler is already running, and to add a new domain would mean suspending compilation while the new domain is loading. Not impossible, perhaps, but I'm assuming this is not implemented. So instead we use a 2-stage bootstrap where the extra domain(s) are called in by a boot script which then loads the main script, compiles and runs it.
6+
7+
For this we need 2 keywords: `require` and `run`, plus a `module` variable type in the core language module. The first looks like this:
8+
```
9+
Require: {
10+
11+
compile: compiler => {
12+
const lino = compiler.getLino();
13+
const type = compiler.nextToken();
14+
if ([`css`, `js`].includes(type)) {
15+
const url = compiler.getNextValue();
16+
compiler.addCommand({
17+
domain: `core`,
18+
keyword: `require`,
19+
lino,
20+
type,
21+
url
22+
});
23+
return true;
24+
}
25+
throw new Error(`File type must be 'css' or 'js'`);
26+
},
27+
28+
// runtime
29+
30+
run: program => {
31+
const command = program[program.pc];
32+
program.require(command.type, program.getValue(command.url),
33+
function () {
34+
program.run(command.pc + 1);
35+
});
36+
return 0;
37+
}
38+
},
39+
```
40+
Note that program execution halts while the load is happening and resumes once it has completed.
41+
42+
The `program.require() function used by `run(program)` looks like this:
43+
```
44+
require: function(type, src, callback) {
45+
let prefix = ``;
46+
if (src[0] == `/`) {
47+
prefix = window.location + `/`;
48+
}
49+
const element = document.createElement(type === `css` ? `link` : `script`);
50+
switch (type) {
51+
case `css`:
52+
element.type = `text/css`;
53+
element.href = `${prefix}${src}`;
54+
element.rel = `stylesheet`;
55+
break;
56+
case `js`:
57+
element.type = `text/javascript`;
58+
element.src = `${prefix}${src}`;
59+
break;
60+
default:
61+
return;
62+
}
63+
element.onload = function () {
64+
console.log(`${Date.now() - EasyCoder.timestamp} ms: Library ${prefix}${src} loaded`);
65+
callback();
66+
};
67+
document.head.appendChild(element);
68+
},
69+
```
70+
This can load either CSS or JS files, so as well as your domains being load-on-demand capable, so also can be your CSS scripts.
71+
72+
## Running a child script
73+
74+
The `run()` handler looks like this:
75+
```
76+
Run: {
77+
78+
compile: compiler => {
79+
const lino = compiler.getLino();
80+
const script = compiler.getNextValue();
81+
// Get the exports, if any
82+
const imports = [];
83+
if (compiler.tokenIs(`with`)) {
84+
while (true) {
85+
if (compiler.nextIsSymbol(true)) {
86+
const symbolRecord = compiler.getSymbolRecord();
87+
imports.push(symbolRecord.name);
88+
compiler.next();
89+
if (!compiler.tokenIs(`and`)) {
90+
break;
91+
}
92+
}
93+
}
94+
}
95+
// Specify the module variable
96+
let module;
97+
if (compiler.tokenIs(`as`)) {
98+
if (compiler.nextIsSymbol(true)) {
99+
const moduleRecord = compiler.getSymbolRecord();
100+
// moduleRecord.program = program.script;
101+
compiler.next();
102+
if (moduleRecord.keyword !== `module`) {
103+
throw new Error(`'${moduleRecord.name}' is not a module`);
104+
}
105+
module = moduleRecord.name;
106+
}
107+
}
108+
let nowait = false;
109+
if (compiler.tokenIs(`nowait`)) {
110+
compiler.next();
111+
nowait = true;
112+
}
113+
const pc = compiler.getPc();
114+
compiler.addCommand({
115+
domain: `core`,
116+
keyword: `run`,
117+
lino,
118+
script,
119+
imports,
120+
module,
121+
nowait,
122+
then: 0
123+
});
124+
// Get the 'then' code, if any
125+
if (compiler.tokenIs(`then`)) {
126+
const goto = compiler.getPc();
127+
// Add a 'goto' to skip the 'then'
128+
compiler.addCommand({
129+
domain: `core`,
130+
keyword: `goto`,
131+
goto: 0
132+
});
133+
// Fixup the link to the 'then' branch
134+
compiler.getCommandAt(pc).then = compiler.getPc();
135+
// Process the 'then' branch
136+
compiler.next();
137+
compiler.compileOne(true);
138+
compiler.addCommand({
139+
domain: `core`,
140+
keyword: `stop`
141+
});
142+
// Fixup the 'goto'
143+
compiler.getCommandAt(goto).goto = compiler.getPc();
144+
}
145+
return true;
146+
},
147+
148+
// runtime
149+
150+
run: program => {
151+
program.nextPc = program.pc + 1;
152+
program.runScript(program);
153+
return 0;
154+
}
155+
```
156+
This may seem a little complex but it deals with several things. As well as just running a script you also have the option of
157+
158+
> a) adding a `with` clause to hand some of your variables to the child script
159+
> b) specifying an action to occur after the child script has completed
160+
161+
The first of these is equivalent to passing variables by reference in other programming languages, except it's not a subroutine or function that we're sharing with; it's a completely different script that's potentially running concurrently with the first one. Having a shared variable space is a powerful feature; I'll describe it in more detail later.
162+
163+
Since you are launching an independent program that could be sharing some of your variables, you may not be able to interact with it until it has completed some initialization. For example, imagine a child script whose sole job is to fetch the weather from a remote server when it receives a message from the parent. So we add a command that the child script runs when it is ready to interact with the parent - in this case to receive messages. I use `set ready` but you can choose any suitable syntax. The parent will block until this command is run.
164+
165+
The use of `nowait` overrides this functionality and allows the calling script to continue immediately.
166+
167+
Here is the `runScript(program)` function from _**EasyCoder**_:
168+
```
169+
runScript: function (program) {
170+
const command = program[program.pc];
171+
const script = program.getValue(command.script);
172+
const imports = command.imports;
173+
imports.caller = program.script;
174+
const moduleRecord = command.module ? program.getSymbolRecord(command.module) : null;
175+
try {
176+
EasyCoder.tokeniseAndCompile(script.split(`\n`), imports, moduleRecord, this.script, command.then);
177+
} catch (err) {
178+
EasyCoder.reportError(err, program, program.source);
179+
if (program.onError) {
180+
program.run(program.onError);
181+
} else {
182+
let parent = EasyCoder.scripts[program.parent];
183+
if (parent && parent.onError) {
184+
parent.run(parent.onError);
185+
}
186+
}
187+
return;
188+
}
189+
if (command.nowait) {
190+
EasyCoder.run(program.nextPc);
191+
}
192+
},
193+
```
194+
You can see this invokes the tokenizer and compiler to process the new script and then run it. The largest part of the function is in dealing with errors.
195+
196+
~tid:Export and import~

‎pie/pie/5 Plugins/content.txt

Lines changed: 231 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,233 @@
1-
# Plugins
1+
# Plugins and child scripts
22

3-
## An example plugin
3+
Plugins are JavaScript files containing domain handler code in a fixed format. The best way of illustrating this format is to write a plugin for a hypothetical domain. Let's assume your problem domain is that of logistics, and that one of your custom variable types is a `box`, which has features that make it useful when packing items for transport. The `box` has dimensions and weight so we need to be able to create a box, set up its properties and return information when requested. Here's a simple script that exercises all the features:
4+
```
5+
box Box52
6+
7+
create Box52
8+
width 60
9+
depth 40
10+
height 40
11+
weight 20
12+
13+
print the volume of Box52
14+
if Box52 is heavy print `Heavy: ` cat the weight of Box 52
15+
stop
16+
```
17+
Here's the complete plugin in a form that's compatible with _**EasyCoder**_. To adapt it for you own language you probably need to do no more than globally change _**EasyCoder**_ into the name of your project. The filname is `box.js`.
18+
```
19+
const EasyCoder_Box = {
420

21+
name: `EasyCoder_Box`,
22+
23+
BOX: {
24+
25+
compile: compiler => {
26+
compiler.compileVariable(`box`, `box`);
27+
return true;
28+
},
29+
30+
run: program => {
31+
return program[program.pc].pc + 1;
32+
}
33+
},
34+
35+
Create: {
36+
37+
compile: compiler => {
38+
const lino = compiler.getLino();
39+
if (compiler.nextIsSymbol())
40+
{
41+
const symbolRecord = compiler.getSymbolRecord();
42+
const keyword = symbolRecord.keyword;
43+
if (keyword === `box`) {
44+
let width;
45+
let depth;
46+
let height;
47+
let weight;
48+
compiler.next();
49+
while (true)
50+
{
51+
switch (compiler.getToken()) {
52+
case `width`:
53+
width = compiler.getNextValue();
54+
break;
55+
case `depth`:
56+
depth = compiler.getNextValue();
57+
break;
58+
case `height`:
59+
height = compiler.getNextValue();
60+
break;
61+
case `weight`:
62+
weight = compiler.getNextValue();
63+
break;
64+
default:
65+
compiler.addCommand({
66+
domain: `box`,
67+
keyword: `create`,
68+
name: symbolRecord.name,
69+
lino,
70+
width,
71+
depth,
72+
height,
73+
weight
74+
});
75+
return true;
76+
}
77+
}
78+
}
79+
}
80+
return false;
81+
},
82+
83+
run: program => {
84+
const command = program[program.pc];
85+
const width = program.getValue(command.width);
86+
const depth = program.getValue(command.depth);
87+
const height = program.getValue(command.height);
88+
const weight = program.getValue(command.weight);
89+
const box = program.getSymbolRecord(command.name);
90+
box.value[box.index] = {
91+
width,
92+
depth,
93+
height,
94+
weight
95+
};
96+
return command.pc + 1;
97+
}
98+
},
99+
100+
// Values
101+
value: {
102+
103+
compile: compiler => {
104+
compiler.skip(`the`);
105+
const type = compiler.getToken();
106+
if ([`width`, `depth`, `height`, `weight`, `volume`].includes(type)) {
107+
compiler.next();
108+
compiler.skip(`of`);
109+
const name = compiler.getToken();
110+
compiler.next();
111+
return {
112+
domain: `box`,
113+
name,
114+
type
115+
};
116+
};
117+
return null;
118+
},
119+
120+
get: (program, value) => {
121+
const symbolRecord = program.getSymbolRecord(value.name);
122+
const record = symbolRecord.value[symbolRecord.index];
123+
let content;
124+
switch (value.type) {
125+
case `width`:
126+
content = record.width;
127+
break;
128+
case `depth`:
129+
content = record.depth;
130+
break;
131+
case `height`:
132+
content = record.height;
133+
break;
134+
case `weight`:
135+
content = record.weight;
136+
break;
137+
case `volume`:
138+
content = record.width * record.depth * record.height / 100 / 100 / 100;
139+
break;
140+
}
141+
return {
142+
type: `constant`,
143+
numeric: true,
144+
content
145+
};
146+
return value;
147+
}
148+
},
149+
150+
// Conditions
151+
condition: {
152+
153+
compile: compiler => {
154+
if (compiler.isSymbol()) {
155+
const symbolRecord = compiler.getSymbolRecord();
156+
if (symbolRecord.keyword === `box`) {
157+
if (compiler.nextTokenIs(`is`)) {
158+
let sense = true;
159+
if (compiler.nextTokenIs(`not`)) {
160+
compiler.next();
161+
sense = false;
162+
}
163+
if (compiler.tokenIs(`heavy`)) {
164+
compiler.next();
165+
return {
166+
domain: `box`,
167+
type: `heavy`,
168+
name: symbolRecord.name,
169+
sense
170+
}
171+
}
172+
}
173+
return null;
174+
}
175+
}
176+
},
177+
178+
test: (program, condition) => {
179+
if (condition.type === `heavy`) {
180+
const record = program.getSymbolRecord(condition.name);
181+
const heavy = (record.value[record.index].weight >= 25);
182+
return condition.sense ? heavy : !heavy;
183+
}
184+
return false;
185+
}
186+
},
187+
188+
// Dispatcher
189+
getHandler: name => {
190+
switch (name) {
191+
case `box`:
192+
return EasyCoder_Box.BOX;
193+
case `create`:
194+
return EasyCoder_Box.Create;
195+
default:
196+
return false;
197+
}
198+
},
199+
200+
run: program => {
201+
const command = program[program.pc];
202+
const handler = EasyCoder_Box.getHandler(command.keyword);
203+
if (!handler) {
204+
program.runtimeError(command.lino, `Unknown keyword '${command.keyword}' in 'box' package`);
205+
}
206+
return handler.run(program);
207+
}
208+
};
209+
210+
// eslint-disable-next-line no-unused-vars
211+
EasyCoder.domain.box = EasyCoder_Box;
212+
```
213+
At the start is a property giving the name of the module. This is for the convenience of the programmer; it may help to locate the code during debugging.
214+
215+
Next there are handler objects for the 2 keywords implemented in this domain; `box` and `create`. Each of these contains 2 functions; `compile(compiler)` and `run(program)`.
216+
217+
The `box` command is a variable declaration and is implemented in a standard way.
218+
219+
The `create` command accepts the syntax shown in the example at the top of this section, where the item is first named and then followed by a set of attributes, which can be given in any order. The function returns an object with all these items.
220+
221+
At runtime, the command is read from the program array and the various items are extracted. An object is built to contain the current values of the items and is written to the variable.
222+
223+
Next we have the value handler object. This contains the compiler function `compile(compiler)` and the runtime function `get(program, value)`. Between them they provide the means to access any of the properties of the box variable, plus the volume, which is computed on the fly at runtime.
224+
225+
After this is the condition handler object. This contains the compiler function `compile(compiler)` and the runtime function `test(program, condition)`. Between then they implement a single test, which returns `true` if the box weight exceeds a fixed value. Note that the core condition handler will already have checked for `not`, which if given will negate the test, as in `if Box52 is not heavy ...`
226+
227+
Now we come to the `getHandler(name)`, the dispatcher function, which returns the handler object for any given keyword in this domain (or null if an unknown keyword is given).
228+
229+
This is followed by the runtime function that is called by the main runtime to execute the command at the current program counter.
230+
231+
Finally, the module posts itself into the list of domains held by the main program module, allowing the compiler to include it in searches for a suitable handler for any given keyword.
232+
233+
~tid:Handling plugins~

‎pie/pie/home/content.txt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
# Programming In English
22

3-
This book describes how to build a versatile computer language whose scripts look like a series of step-by-step English commands similar to cookery recipes, navigation instructions or car maintenance manuals. The language will be English stripped to the minimum; terse but understandable by anyone familiar with the problem domain. The environment will be that of a web browser and the language will be a replacement for JavaScript, permitting scripts to be embedded in any web page then compiled and run on the fly as the page loads.
3+
This book describes how to build a versatile computer language whose scripts look like a series of step-by-step English commands similar to cookery recipes, navigation instructions or car maintenance manuals. The syntax will be English stripped to the minimum; terse but understandable by anyone familiar with the problem domain. The environment will be that of a web browser and the language will be a comprehensive, capable and easy to use replacement for JavaScript, permitting scripts to be embedded in any web page then compiled and run on the fly as the page loads.
44

55
Programming is about communication; specifically about getting computers to do things. I'm assuming that you, my reader, are a programmer and that among your circle of friends and aquaintances there are others with whom you share specialized skills you've worked long and hard to gain.
66

77
You'll also know a lot of other people who aren't programmers, most of whom find your interests and skills baffling or even frightening. For them, computers are just tools to be used, preferably without the need to understand how they work any more than most of us know about what happens under the hood of a car, or indeed anywhere outside own own areas of speciality.
88

9-
Just as non-programmers find it hard to understand how programmers think, we often fail to fully appreciate the complexities of the world outside the computer, for which we build software systems. Better understanding makes for better, more reliable software, and communication is the key to understanding.
9+
Just as non-programmers find it hard to understand how programmers think, we often fail to fully appreciate the complexities of the world outside the computer, for which we build software systems. We have a regrettable tendency to disregard advice from users of a system because we all too often assume we know better than those users. Better understanding makes for better, more reliable software, and better communication is the key to better understanding.
1010

1111
## What is programming?
1212

13-
Computers are literal devices. They blindly follow the instructions given, without stopping to question if you really meant what you said. A programmer is someone who has the skill to create sets of instructions that are unambiguous, so the computer will do exactly what is intended. In order for this to happen, instructions are written in a computer _language_; a formal syntax that's understood by a computer and more importantly by the programmers tasked with ensuring the right things are done. Many computer languages have been invented, most of which are much more similar to each other than any of them are to human languages. But just as human language develops specialized dialects to suit the needs of a particular group, whether it be chemists, lawyers or footballers, new computer languages can always be created to provide a better match to problem domains of one kind or another.
13+
Computers are literal devices. They blindly follow the instructions given, without stopping to question if you really meant what you said. A programmer is someone who has the skill to create sets of instructions that are unambiguous, so the computer will do exactly what is intended. In order for this to happen, instructions are written in a computer _language_; a formal syntax that's understood by a computer and more importantly by the programmers tasked with ensuring the right things are done. Many computer languages have been invented, most of which are much more similar to each other than any of them are to human languages. And just as human language develops specialized dialects to suit the needs of a particular group, whether it be chemists, lawyers or footballers, new computer languages are always being created to provide a better match to problem domains of one kind or another.
1414

1515
## Why is programming so difficult?
1616

17-
Most computer languages are less than half a century old. Programming started by devising symbolic code that offered a human-readable alternative to coding in the hard-to-remember numbers required by the computing devices themselves. As time went by, more and more layers of abstraction were added, but in most cases they retained a fundamental characteristic, being more or less a system of mathematical symbols and expressions.
17+
Most if not all of our current computer languages are less than half a century old. Programming started by devising symbolic code that offered a human-readable alternative to coding in the hard-to-remember numbers required by the computing devices themselves. As time went by, more and more layers of abstraction were added, but in most cases they retained a fundamental characteristic, being more or less a system of mathematical symbols and expressions.
1818

1919
Take a few lines of any computer program written in C/C++, Java, Python or JavaScript, and try to read it out loud. It's almost impossible to do it in such a way that another human being - even another programmer - can make sense of what you are saying. The only sensible way to communicate computer code to another person is in writing. And this is one reason why non-programmers find programming so difficult. We learn to speak before we learn to read; most of us can "hear" written text when we see it and many people sub-vocalize to help them process the meaning, without even being aware of it. For them, coding feels unnatural; something to be left to those strange geeks.
2020

21-
It's all a matter of motivation. As programmers, you and I may be happy to live in a silent world of cryptic formulae and text, but the majority of people aren't built like that and for them it's a complete turn-off. It's not that they _can't_ become programmers; they just don't have any motivation for doing so. In fact, nearly everybody is a programmer, quite capable of handling cooking recipies or step by step car maintenance instructions that resemble simple computer programs. The human species developed complex societies long before we invented computer language. Lawyers and engineers have the mental discipline to organise facts in a coherent manner so that the outcome of an action can be safely predicted, but for some reason, when it comes to computers we suddenly feel the need to invent a whole new way of expressing things that's competely alien to our natural ways of thinking. Small wonder there's a shortage of good programmers.
21+
It's all a matter of motivation. As programmers, you and I may be happy to live in a silent world of cryptic formulae and text, but the majority of people aren't built like that and for them it's a complete turn-off. It's not that they _can't_ become programmers; they just don't have any motivation for doing so. In fact, nearly everybody is a programmer, quite capable of handling cooking recipies or step by step car maintenance instructions that resemble simple computer programs. The human species developed complex societies long before we invented computer language. There were programmes long before there was programming. Lawyers and engineers have the mental discipline to organise facts in a coherent manner so that the outcome of an action can be safely predicted, but for some reason, when it comes to computers we assume it's necessary to use a way of expressing things that's competely alien to our natural modes of thinking. Small wonder there's a shortage of good programmers.
2222

23-
It gets worse. Computer software is structurally complex, requiring a huge amount of training and experience to understand how a large program works or even where it begins. As a result, maintenance is often a time bomb waiting to blow up large systems. By the time a few years have passed, the original designers of a software system are long gone and maintenance is often left in the hands of people who are inadequately trained or for whom this is only one of many other responsibilities. Lacking the insights posessed by the original design team they inevitably make mistakes, causing invisible damage to the whole edifice. Slowly but surely the system begins to decay, with every attempt to fix it creating further problems. In the end, to avoid catastrophic failure the system will have to be replaced, often well before its originally projected lifetime.
23+
It gets worse. Computer software is structurally complex, requiring a huge amount of training and experience to understand how a large program works or even where it begins. As a result, maintenance is often a time bomb waiting to blow up big systems. By the time a few years have passed, the original designers of a software system are long gone and maintenance is often left in the hands of people who are inadequately trained or for whom this is only one of many other responsibilities. Lacking the insights posessed by the original design team they inevitably make mistakes, causing invisible damage to the whole edifice. Slowly but surely the system begins to decay, with every attempt to fix it creating further problems. In the end, to avoid catastrophic failure the system will have to be replaced, often well before its originally projected lifetime.
2424

2525
For centuries we have built complex and robust human societies without the need for computers or software. Language had a huge, vital part to play in this; without it we'd still be living in caves.
2626

@@ -43,6 +43,8 @@ And finally, before getting started, **_EasyCoder_** has its own website at [htt
4343

4444
This book can be read in a linear fashion; at the bottom of each page is a link to the next. Alternatively, you can choose any page from the table of contents below.
4545

46+
I'm assuming you have some experience with JavaScript and ES6. You don't need to be an expert but complete beginners will find it a little hard going in places as I expect you to be able to read sections of code without my having to spell out everything line by line. When I started the _**EasyCoder**_ project my level of JS knowledge was pretty low (and it shows in places).
47+
4648
~sid:2 Designing a language:Designing a language~
4749

4850
> ~stid:2 Designing a language/Environment:The coding environment~
@@ -67,7 +69,7 @@ This book can be read in a linear fashion; at the bottom of each page is a link
6769

6870
> ~stid:3 Compilation/Variables:Compiling variables~
6971

70-
> ~stid:3 Compilation/Handlers:Keyword handlers~
72+
> ~stid:3 Compilation/Keywords:Keyword handlers~
7173

7274
> ~stid:3 Compilation/If:The `if` handler~
7375

@@ -81,4 +83,8 @@ This book can be read in a linear fashion; at the bottom of each page is a link
8183

8284
~sid:4 Runtime:The runtime engine~
8385

84-
~sid:5 Plugins:Creating plugin modules~
86+
~sid:5 Plugins~
87+
88+
~stid:5 Plugins/Handling plugins:Handling plugins~
89+
90+
~stid:5 Plugins/Export and import:Export and import~

0 commit comments

Comments
 (0)
Please sign in to comment.