diff --git a/.github/workflows/deploy-api.yml b/.github/workflows/deploy-api.yml index 80408cb762b..96d8a36b2a3 100644 --- a/.github/workflows/deploy-api.yml +++ b/.github/workflows/deploy-api.yml @@ -14,11 +14,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Cancel previous runs - uses: styfle/cancel-workflow-action@0.12.1 + uses: styfle/cancel-workflow-action@0.13.1 with: workflow_id: 42688837 access_token: ${{ github.token }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: superfly/flyctl-actions/setup-flyctl@master with: version: 0.3.221 diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index b899f4958c2..acce8b3e14a 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -11,15 +11,15 @@ jobs: runs-on: macos-latest steps: - name: Cancel previous runs - uses: styfle/cancel-workflow-action@0.12.1 + uses: styfle/cancel-workflow-action@0.13.1 with: workflow_id: 42688838 access_token: ${{ github.token }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Cache ~/.npm for npm ci - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index e3ecf923f01..3b787810fad 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -9,4 +9,4 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 \ No newline at end of file + - uses: actions/labeler@v6 \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf5361d11c2..006cd70df15 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,12 +11,12 @@ jobs: name: test runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v6 with: go-version: "1.17.2" - - uses: actions/setup-node@v2 - - uses: actions/checkout@v2 - - uses: arduino/setup-task@v1 + - uses: actions/setup-node@v6 + - uses: actions/checkout@v6 + - uses: arduino/setup-task@v2 - run: go mod download - run: task generate diff --git a/frontend/content/releases/server/v1.1.0.2612.md b/frontend/content/releases/server/v1.1.0.2612.md index a53ea2c5a9f..7b37223a59e 100644 --- a/frontend/content/releases/server/v1.1.0.2612.md +++ b/frontend/content/releases/server/v1.1.0.2612.md @@ -108,4 +108,4 @@ There are so many fixes and additions, for those who don't know, they can read b * @Zorono made their first contribution in https://github.com/openmultiplayer/open.mp/pull/752 * @ReshiramZekrom1 made their first contribution in https://github.com/openmultiplayer/open.mp/pull/762 -**Full Changelog**: https://github.com/openmultiplayer/open.mp/compare/v0.0.11.2331...v1.1.0 +**Full Changelog**: https://github.com/openmultiplayer/open.mp/compare/v0.0.11.2331...v1.1.0.2612 diff --git a/frontend/content/releases/server/v1.3.1.2748.md b/frontend/content/releases/server/v1.3.1.2748.md index e13eb30aff9..9547bcdca2a 100644 --- a/frontend/content/releases/server/v1.3.1.2748.md +++ b/frontend/content/releases/server/v1.3.1.2748.md @@ -76,4 +76,4 @@ assets: * @myudev made their first contribution in https://github.com/openmultiplayer/open.mp/pull/919 * @atomlin-git made their first contribution in https://github.com/openmultiplayer/open.mp/pull/902 -**Full Changelog**: https://github.com/openmultiplayer/open.mp/compare/v1.2.0.2670...v1.3.1.2744 +**Full Changelog**: https://github.com/openmultiplayer/open.mp/compare/v1.2.0.2670...v1.3.1.2748 diff --git a/frontend/content/releases/server/v1.4.0.2779.md b/frontend/content/releases/server/v1.4.0.2779.md index 8b309a69bbf..7264d2e9593 100644 --- a/frontend/content/releases/server/v1.4.0.2779.md +++ b/frontend/content/releases/server/v1.4.0.2779.md @@ -59,4 +59,4 @@ assets: ## New Contributors * @f0Re3t made their first contribution in https://github.com/openmultiplayer/open.mp/pull/978 -**Full Changelog**: https://github.com/openmultiplayer/open.mp/compare/v1.3.1.2744...v1.4.0.2779 +**Full Changelog**: https://github.com/openmultiplayer/open.mp/compare/v1.3.1.2748...v1.4.0.2779 diff --git a/frontend/content/releases/server/v1.5.8.3079.md b/frontend/content/releases/server/v1.5.8.3079.md index b603c5d9af8..b57cee3d8b8 100644 --- a/frontend/content/releases/server/v1.5.8.3079.md +++ b/frontend/content/releases/server/v1.5.8.3079.md @@ -111,4 +111,4 @@ assets: * @Tiaansu made their first contribution in https://github.com/openmultiplayer/open.mp/pull/1151 * @nidi21 made their first contribution in https://github.com/openmultiplayer/open.mp/pull/1152 -**Full Changelog**: https://github.com/openmultiplayer/open.mp/compare/v1.4.0.2779...v1.5.8.3077 +**Full Changelog**: https://github.com/openmultiplayer/open.mp/compare/v1.4.0.2779...v1.5.8.3079 diff --git a/frontend/docs/client/_.md b/frontend/docs/client/_.md deleted file mode 100644 index e6dc1fedb9c..00000000000 --- a/frontend/docs/client/_.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Client -sidebar_label: Client -description: This category contains information about SA-MP client features and support. ---- - -This category contains information about SA-MP client features and support. diff --git a/frontend/docs/scripting/language/reference/00-Contents.md b/frontend/docs/scripting/language/reference/00-Contents.md index c0f57cbf397..3b70a170b8d 100644 --- a/frontend/docs/scripting/language/reference/00-Contents.md +++ b/frontend/docs/scripting/language/reference/00-Contents.md @@ -48,36 +48,36 @@ size of 11 points. --- -[Foreword](01-Foreword) - Page 3-5 +[Foreword](Foreword) - Page 3-5 -[A tutorial introduction](02-A-tutorial-introduction) - Page 5-62 +[A tutorial introduction](A-tutorial-introduction) - Page 5-62 -[Data and declarations](03-Data-and-declarations) - Page 62-70 +[Data and declarations](Data-and-declarations) - Page 62-70 -[Functions](04-Functions) - Page 70-93 +[Functions](Functions) - Page 70-93 -[The preprocessor](05-The-preprocessor) - Page 93-97 +[The preprocessor](The-preprocessor) - Page 93-97 -[General syntax](06-General-syntax) - Page 97-104 +[General syntax](General-syntax) - Page 97-104 -[Operators and expressions](07-Operators-and-expressions) - Page 104-112 +[Operators and expressions](Operators-and-expressions) - Page 104-112 -[Statements](08-Statements) - Page 112-117 +[Statements](Statements) - Page 112-117 -[Directives](09-Directives) - Page 117-124 +[Directives](Directives) - Page 117-124 -[Proposed function library](10-Proposed-function-library) - Page 124-134 +[Proposed function library](Proposed-function-library) - Page 124-134 -[Pitfalls: differences from C](11-Pitfalls-differences-from-C) - Page 134-137 +[Pitfalls: differences from C](Pitfalls-differences-from-C) - Page 134-137 -[Assorted tips](12-Assorted-tips) - Page 137-148 +[Assorted tips](Assorted-tips) - Page 137-148 -[Appendices](13-Appendices) - Page 148-183 +[Appendices](Appendices) - Page 148-183 -∟ [Error and warning messages](13-Appendices#error-and-warning-messages) - Page 148-168 +∟ [Error and warning messages](Appendices#error-and-warning-messages) - Page 148-168 -∟ [The compiler](13-Appendices#the-compiler) - Page 168-174 +∟ [The compiler](Appendices#the-compiler) - Page 168-174 -∟ [Rationale](13-Appendices#rationale) - Page 174-181 +∟ [Rationale](Appendices#rationale) - Page 174-181 -∟ [License](13-Appendices#license) - Page 181-183 +∟ [License](Appendices#license) - Page 181-183 diff --git a/frontend/docs/scripting/language/reference/01-Foreword.md b/frontend/docs/scripting/language/reference/01-Foreword.md index f02d77b2226..2edc46ad5d1 100644 --- a/frontend/docs/scripting/language/reference/01-Foreword.md +++ b/frontend/docs/scripting/language/reference/01-Foreword.md @@ -80,4 +80,4 @@ functions to manipulate objects in the host application. --- -[Go Back to Contents](00-Contents) +[Go Back to Contents](Contents) diff --git a/frontend/docs/scripting/language/reference/02-A-tutorial-introduction.md b/frontend/docs/scripting/language/reference/02-A-tutorial-introduction.md index 3294a666499..27f1e294472 100644 --- a/frontend/docs/scripting/language/reference/02-A-tutorial-introduction.md +++ b/frontend/docs/scripting/language/reference/02-A-tutorial-introduction.md @@ -13,37 +13,32 @@ string; printing “Hello world” is a classic example. In pawn, the program wo Listing: hello.p ```c - main() - printf "Hello world\n" - + printf "Hello world\n" ``` This manual assumes that you know how to run a pawn program; if not, please consult the application manual (more hints are at page 168). -A pawn program starts execution in an “entry” function∗ —in nearly all ex- -amples of this manual, this entry function is called “main”. Here, the function +A pawn program starts execution in an “entry” function\* —in nearly all examples of this manual, this entry function is called “main”. Here, the function main contains only a single instruction, which is at the line below the function head itself. Line breaks and indenting are insignificant; the invocation of the function print could equally well be on the same line as the head of function main. -The definition of a function requires that a pair of parentheses follow the func- -tion name. If a function takes parameters, their declarations appear between +The definition of a function requires that a pair of parentheses follow the function name. If a function takes parameters, their declarations appear between the parentheses. The function main does not take any parentheses. The rules are different for a function invocation (or a function call); parentheses are optional in the call to the print function. The single argument of the print function is a string, which must be enclosed in double quotes. The characters \n near the end of the string form an escape -sequence, in this case they indicate a “newline” symbol. When print encoun- -ters the newline escape sequence, it advances the cursor to the first column of +sequence, in this case they indicate a “newline” symbol. When print encounters the newline escape sequence, it advances the cursor to the first column of the next line. One has to use the \n escape sequence to insert a “newline” into the string, because a string may not wrap over multiple lines. --- -###### ∗ This should not be confused with the “state” entry functions, which are called entry, but serve a different purpose — see page 42. +###### \* This should not be confused with the “state” entry functions, which are called entry, but serve a different purpose — see page 42. `Compiling and running scripts: see page 168` @@ -64,28 +59,23 @@ pawn syntax (and it has the same semantics as the earlier example): Listing: hello.p — C style ```c - -#include +#include main() { - printf("Hello world\n"); + printf("Hello world\n"); } - ``` These first examples also reveal a few differences between pawn and the C language: - there is usually no need to include any system-defined “header file”; - - semicolons are optional (except when writing multiple statements on one line); - - when the body of a function is a single instruction, the braces (for a compound instruction) are optional; - - when you do not use the result of a function in an expression or assignment, parentheses around the function argument are optional. As an aside, the few preceding points refer to optional syntaxes. It is -your choice what syntax you wish to use: neither style is “deprecated” or +your choice what syntax you wish to use: neither style is “deprecated” or “considered harmful”. The examples in this manual position the braces and use an indentation that is known as the “Whitesmith’s style”, but pawn is a free format language and other indenting styles are just as good. @@ -98,7 +88,7 @@ available by the host application and be “declared” to the pawn parser. --- -###### ∗ In the language specification, the term “parser” refers to any implementation that processes and runs on conforming Pawn programs —either interpreters or compilers. +###### \* In the language specification, the term “parser” refers to any implementation that processes and runs on conforming Pawn programs —either interpreters or compilers. `More function descriptions at page 124` @@ -111,12 +101,12 @@ In some environments, the display or terminal must be enabled before any text can be output onto it. If this is the case, you must add a call to the function “console” before the first call to function print or printf. The console function also allows you to specify device characteristics, such as the number -of lines and columns of the display. The example programs in this +of lines and columns of the display. The example programs in this manual do not use the console functions, because many platforms do not require or provide it. ### • Arithmetic -Fundamental elements of most programs are calculations, decisions +Fundamental elements of most programs are calculations, decisions (conditional execution), iterations (loops) and variables to store input data, output data and intermediate results. The next program example illustrates many of these concepts. The program calculates the greatest common divisor of two @@ -125,29 +115,25 @@ values using an algorithm invented by Euclides. Listing: gcd.p ```c - -/* the greatest common divisor of two values, using Euclides’ -algorithm */ +/* the greatest common divisor of two values, using Euclides’ algorithm */ main() { - print "Input two values\n" - new a = getvalue() - new b = getvalue() - while (a != b) - if (a > b) - a = a - b + print "Input two values\n" + new a = getvalue() + new b = getvalue() + while (a != b) + if (a > b) + a = a - b else - b = b - a - printf "The greatest common divisor is %d\n", a + b = b - a + printf "The greatest common divisor is %d\n", a } - ``` Function main now contains more than just a single “print” statement. When the body of a function contains more than one statement, these statements -must be embodied in braces —the “\{” and “\}” characters. This groups the in- -structions to a single compound statement. The notion of grouping statements +must be embodied in braces —the “\{” and “\}” characters. This groups the instructions to a single compound statement. The notion of grouping statements in a compound statement applies as well to the bodies of if–else and loop instructions. The new keyword creates a variable. The name of the variable follows new. @@ -167,8 +153,7 @@ is a typeless language, all variables are numeric cells that can hold a signed integral value. The getvalue function name is followed by a pair of parentheses. These are -required because the value that getvalue returns is stored in a variable. Nor- -mally, the function’s arguments (or parameters) would appear between +required because the value that getvalue returns is stored in a variable. Normally, the function’s arguments (or parameters) would appear between the parentheses, but getvalue (as used in this program) does not take any explicit arguments. If you do not assign the result of a function to a variable or use it in a expression in another way, the parentheses are optional. For @@ -176,29 +161,26 @@ example, the result of the print and printf statements are not used. You may still use parentheses around the arguments, but it is not required. Loop instructions, like “while”, repeat a single instruction as long as the loop -condition (the expression between parentheses) is “true”. One can +condition (the expression between parentheses) is “true”. One can execute multiple instructions in a loop by grouping them in a compound statement. The if–else instruction has one instruction for the “true” clause and one for the “false”. -Observe that some statements, like while and if–else, contain (or +Observe that some statements, like while and if–else, contain (or “fold around”) another instruction —in the case of if–else even two other instructions. The complete bundle is, again, a single instruction. That is: - the assignment statements “a = a - b” below the if and “b = b - a” below the else are statements; - - the if–else statement folds around these two assignment statements and forms a single statement of itself; - - the while statement folds around the if–else statement and forms, again, a single statement. It is common to make the nesting of the statements explicit by indenting any -sub-statements below a statement in the source text. In the “Greatest Com- -mon Divisor” example, the left margin indent increases by four space characters -after the while statement, and again after the if and else keywords. State- -ments that belong to the same level, such as both printf invocations and the +sub-statements below a statement in the source text. In the “Greatest Common +Divisor” example, the left margin indent increases by four space characters +after the while statement, and again after the if and else keywords. Statements that belong to the same level, such as both printf invocations and the while loop, have the same indentation. The loop condition for the while loop is “(a != b)”; the symbol != is the -“not equal to” operator. That is, the if–else instruction is repeated +“not equal to” operator. That is, the if–else instruction is repeated until “a” equals “b”. It is good practice to indent the instructions that run under control of another statement, as is done in the preceding example. @@ -245,29 +227,26 @@ Symbolic constants look like variables, but they cannot be changed. Listing: sieve.p ```c - -/* Print all primes below 100, using the "Sieve of Eratosthenes" */ +/* Print all primes below 100, using the "Sieve of Eratosthenes" */ main() { - const max_primes = 100 - new series[max_primes] = { true, ... } - for (new i = 2; i < max_primes; ++i) - if (series[i]) + const max_primes = 100 + new series[max_primes] = { true, ... } + for (new i = 2; i < max_primes; ++i) + if (series[i]) { - printf "%d ", i - /* filter all multiples of this "prime" from the list */ - for (new j = 2 * i; j < max_primes; j += i) - series[j] = false + printf "%d ", i + /* filter all multiples of this "prime" from the list */ + for (new j = 2 * i; j < max_primes; j += i) + series[j] = false } } - ``` --- -When a program or sub-program has some fixed limit built-in, it is good prac- -tice create a symbolic constant for it. In the preceding example, the symbol +When a program or sub-program has some fixed limit built-in, it is good practice create a symbolic constant for it. In the preceding example, the symbol max_primes is a constant with the value 100. The program uses the symbol max_primes three times after its definition: in the declaration of the variable series and in both for loops. If we were to adapt the program to print all @@ -281,7 +260,7 @@ programmer types in the word “true” a hundred times. The symbols true and false are predefined constants. When a simple variable, like the variables i and j in the primes sieve example, -is declared in the first expression of a for loop, the variable is +is declared in the first expression of a for loop, the variable is valid only inside the loop. Variable declaration has its own rules; it is not a statement — although it looks like one. @@ -324,36 +303,33 @@ and the sequence is 1, 1, 2, 3, 5, 8, 13, 21,. . . Listing: fib.p ```c - -/* Calculation of Fibonacci numbers by iteration */ +/* Calculation of Fibonacci numbers by iteration */ main() { - print "Enter a value: " - new v = getvalue() - if (v > 0) - printf "The value of Fibonacci number %d is %d\n", v, fibonacci(v) + print "Enter a value: " + new v = getvalue() + if (v > 0) + printf "The value of Fibonacci number %d is %d\n", v, fibonacci(v) else - printf "The Fibonacci number %d does not exist\n", v + printf "The Fibonacci number %d does not exist\n", v } fibonacci(n) { - assert n > 0 - new a = 0, b = 1 - (new i = 2; i < n; i++) + assert n > 0 + new a = 0, b = 1 + (new i = 2; i < n; i++) { - new c = a + b - a = b - b = c + new c = a + b + a = b + b = c } - return a + b + return a + b } - ``` The assert instruction at the top of the fibonacci function deserves explicit -mention; it guards against “impossible” or invalid conditions. A negative Fi- -bonacci number is invalid, and the assert statement flags it as a programmer’s +mention; it guards against “impossible” or invalid conditions. A negative Fibonacci number is invalid, and the assert statement flags it as a programmer’s error if this case ever occurs. Assertions should only flag programmer’s errors, never user input errors. @@ -363,7 +339,7 @@ receives an input value through a parameter and it returns a value (it has a “result”). Function parameters are declared in the function header; the single parameter -in this example is “n”. Inside the function, a parameter behaves as a +in this example is “n”. Inside the function, a parameter behaves as a local variable, but one whose value is passed from the outside at the call to the function. @@ -381,14 +357,13 @@ like getvalue and printf, as well as the user-defined function fibonacci. From the perspective of calling a function (as in function main), there is no difference between user-defined and native functions. -The Fibonacci numbers sequence describes a surprising variety of natural phe- -nomena. For example, the two or three sets of spirals in pineapples, pine cones +The Fibonacci numbers sequence describes a surprising variety of natural phenomena. For example, the two or three sets of spirals in pineapples, pine cones and sunflowers usually have consecutive Fibonacci numbers between 5 and 89 as their number of spirals. The numbers that occur naturally in branching patterns (e.g. that of plants) are indeed Fibonacci numbers. Finally, although the Fibonacci sequence is not a geometric sequence, the further the sequence is extended, the more closely the ratio between successive terms approaches -the Golden Ratio, of 1.618. . . ∗ that appears so often in art and architecture. +the Golden Ratio, of 1.618. . . \* that appears so often in art and architecture. ### • Call-by-reference & call-by-value @@ -397,29 +372,26 @@ because the calenders that a date refers to have known such a diversity, through time and around the world. The “Julian Day Number” is attributed to Josephus Scaliger† and it counts -the number of days since November 24, 4714 BC (proleptic Gregorian cal- -endar‡). Scaliger chose that date because it marked the coincidence of three +the number of days since November 24, 4714 BC (proleptic Gregorian calendar‡). Scaliger chose that date because it marked the coincidence of three well-established cycles: the 28-year Solar Cycle (of the old Julian calendar), -the 19-year Metonic Cycle and the 15-year Indiction Cycle (periodic taxes or gov- -ernmental requisitions in ancient Rome), and because no literature or recorded +the 19-year Metonic Cycle and the 15-year Indiction Cycle (periodic taxes or governmental requisitions in ancient Rome), and because no literature or recorded history was known to predate that particular date in the remote past. Scaliger used this concept to reconcile dates in historic documents, later astronomers embraced it to calculate intervals between two events more easily. --- -###### ∗ The exact value for the Golden Ratio is 1/2(√5 + 1). The relation between Fibonacci numbers and the Golden Ratio also allows for a “direct” calculation of any sequence number, instead of the iterative method described here. +###### \* The exact value for the Golden Ratio is 1/2(√5 + 1). The relation between Fibonacci numbers and the Golden Ratio also allows for a “direct” calculation of any sequence number, instead of the iterative method described here. -###### ∗ There is some debate on exactly what Josephus Scaliger invented and who or what he called it after. +###### \* There is some debate on exactly what Josephus Scaliger invented and who or what he called it after. -###### ∗ The Gregorian calendar was decreed to start on 15 October 1582 by pope Gregory XIII, which means that earlier dates do not really exist in the Gregorian calendar. When extending the Gregorian calendar to days before 15 October 1582, we refer to it as the proleptic Gregorian calendar. +###### \* The Gregorian calendar was decreed to start on 15 October 1582 by pope Gregory XIII, which means that earlier dates do not really exist in the Gregorian calendar. When extending the Gregorian calendar to days before 15 October 1582, we refer to it as the proleptic Gregorian calendar. `Native function interface: 85` --- -Julian Day numbers (sometimes denoted with unit “jd”) should not be con- -fused with Julian Dates (the number of days since the start of the same year), +Julian Day numbers (sometimes denoted with unit “jd”) should not be confused with Julian Dates (the number of days since the start of the same year), or with the Julian calendar that was introduced by Julius Caesar. Below is a program that calculates the Julian Day number from a date in @@ -431,91 +403,87 @@ negative year values for BC years and positive (non-zero) values for AD years. Listing: julian.p ```c - -/* calculate Julian Day number from a date, and vice versa */ +/* calculate Julian Day number from a date, and vice versa */ main() { - new d, m, y, jdn - print "Give a date (dd-mm-yyyy): " + new d, m, y, jdn + print "Give a date (dd-mm-yyyy): " - d = getvalue(_, ’-’, ’/’) - m = getvalue(_, ’-’, ’/’) - y = getvalue() - jdn = DateToJulian(d, m, y) + d = getvalue(_, '-', '/') + m = getvalue(_, '-', '/') + y = getvalue() + jdn = DateToJulian(d, m, y) - printf("Date %d/%d/%d = %d JD\n", d, m, y, jdn) - print "Give a Julian Day Number: " + printf("Date %d/%d/%d = %d JD\n", d, m, y, jdn) + print "Give a Julian Day Number: " - jdn = getvalue() - JulianToDate jdn, d, m, y + jdn = getvalue() + JulianToDate jdn, d, m, y - printf "%d JD = %d/%d/%d\n", jdn, d, m, y + printf "%d JD = %d/%d/%d\n", jdn, d, m, y } -DateToJulian(day, month, year) +DateToJulian(day, month, year) { - /* The first year is 1. Year 0 does not exist: it is 1 BC (or -1) */ + /* The first year is 1. Year 0 does not exist: it is 1 BC (or -1) */ - assert year != 0 - if (year < 0) + assert year != 0 + if (year < 0) year++ - /* move January and February to the end of the previous year */ + /* move January and February to the end of the previous year */ - if (month <= 2) - year--, month += 12 - new jdn = 365*year + year/4 - year/100 + year/400 - + (153*month - 457) / 5 - + day + 1721119 + if (month <= 2) + year--, month += 12 + new jdn = 365*year + year/4 - year/100 + year/400 + + (153*month - 457) / 5 + + day + 1721119 - return jdn + return jdn } -JulianToDate(jdn, &day, &month, &year) +JulianToDate(jdn, &day, &month, &year) { - jdn -= 1721119 + jdn -= 1721119 - /* approximate year, then adjust in a loop */ + /* approximate year, then adjust in a loop */ - year = (400 * jdn) / 146097 - while (365*year + year/4 - year/100 + year/400 < jdn) + year = (400 * jdn) / 146097 + while (365*year + year/4 - year/100 + year/400 < jdn) year++ year-- - /* determine month */ + /* determine month */ - jdn -= 365*year + year/4 - year/100 + year/400 - month = (5*jdn + 457) / 153 + jdn -= 365*year + year/4 - year/100 + year/400 + month = (5*jdn + 457) / 153 - /* determine day */ + /* determine day */ - day = jdn - (153*month - 457) / 5 + day = jdn - (153*month - 457) / 5 - /* move January and February to start of the year */ + /* move January and February to start of the year */ - if (month > 12) - month -= 12, year++ + if (month > 12) + month -= 12, year++ - /* adjust negative years (year 0 must become 1 BC, or -1) */ + /* adjust negative years (year 0 must become 1 BC, or -1) */ - if (year <= 0) + if (year <= 0) year-- } - ``` --- Function main starts with creating variables to hold the day, month and year, and the calculated Julian Day number. Then it reads in a date —three calls to -getvalue— and calls function DateToJulian to calculate the day number. Af- -ter calculating the result, main prints the date that you entered and the Julian +getvalue— and calls function DateToJulian to calculate the day number. After calculating the result, main prints the date that you entered and the Julian Day number for that date. Now, let us focus on function DateToJulian. . . Near the top of function DateToJulian, it increments the year value if it is negative; it does this to cope with the absence of a “zero” year in the -proleptic Gregorian calendar. In other words, function DateToJulian modifies its func- -tion arguments (later, it also modifies month). Inside a function, an argument +proleptic Gregorian calendar. In other words, function DateToJulian modifies its function arguments (later, it also modifies month). Inside a function, an argument behaves like a local variable: you may modify it. These modifications remain local to the function DateToJulian, however. Function main passes the values of d, m and y into DateToJulian, who maps them to its function arguments @@ -524,8 +492,7 @@ month, it does not change y and m in function main; it only changes local copies of y and m. This concept is called “call by value”. The example intentionally uses different names for the local variables in the -functions main and DateToJulian, for the purpose of making the above ex- -planation easier. Renaming main’s variables d, m and y to day, month +functions main and DateToJulian, for the purpose of making the above explanation easier. Renaming main’s variables d, m and y to day, month and year respectively, does not change the matter: then you just happen to have two local variables called day, two called month and two called year, which is perfectly valid in pawn. @@ -566,7 +533,7 @@ more output values, you use reference arguments. You may combine the two inside a single function, for example in a function that returns its “normal” output via a reference argument and an error code in its return value. -As an aside, many desktop application use conversions to and from +As an aside, many desktop application use conversions to and from Julian Day numbers (or varieties of it) to conveniently calculate the number of days between to dates or to calculate the date that is 90 days from now —for example.--- @@ -588,26 +555,24 @@ floating-point and a fixed-point module, and the details (and trade-offs) for these modules in their respective documentation. The issue is, however, that a host application may implement either floating-point or fixed-point, or both -or neither.∗ The program below requires that at least either kind of rational +or neither.\* The program below requires that at least either kind of rational number support is available; it will fail to run if the host application does not support rational numbers at all. Listing: c2f.p ```c - main() { - new Rational: Celsius - new Rational: Fahrenheit - print "Celsius\t Fahrenheit\n" - for (Celsius = 5; Celsius <= 25; Celsius++) + new Rational: Celsius + new Rational: Fahrenheit + print "Celsius\tFahrenheit\n" + for (Celsius = 5; Celsius <= 25; Celsius++) { - Fahrenheit = (Celsius * 1.8) + 32 - printf "%r \t %r\n", Celsius, Fahrenheit + Fahrenheit = (Celsius * 1.8) + 32 + printf "%r \t %r\n", Celsius, Fahrenheit } } - ``` The example program converts a table of degrees Celsius to degrees Fahrenheit. @@ -622,7 +587,7 @@ case for rational numbers, its memory lay-out. --- -###### ∗ Actually, this is already true of all native functions, including all native functions that the examples in this manual use. +###### \* Actually, this is already true of all native functions, including all native functions that the examples in this manual use. `Tag names: 68` @@ -636,7 +601,7 @@ The equation for obtaining degrees Fahrenheit from degrees Celsius is °F = ⁹⁄₅ + 32 °C The program uses the value 1.8 for the quotient 9/₅. When rational number -support is enabled, pawn supports values with a fractional part behind +support is enabled, pawn supports values with a fractional part behind the decimal point. The only other non-trivial change from earlier programs is that the format @@ -671,29 +636,27 @@ operation: applying it twice on the same text reveals the original. Listing: rot13.p ```c - -/* Simple encryption, using ROT13 */ +/* Simple encryption, using ROT13 */ main() { - printf "Please type the string to mangle: " + printf "Please type the string to mangle: " - new str[100] - getstring str, sizeof str - rot13 str + new str[100] + getstring str, sizeof str + rot13 str - printf "After mangling, the string is: \"%s\"\n", str + printf "After mangling, the string is: \"%s\"\n", str } rot13(string[]) { - for (new index = 0; string[index]; index++) - if (’a’ <= string[index] <= ’z’) - string[index] = (string[index] - ’a’ + 13) % 26 + ’a’ - else if (’A’ <= string[index] <= ’Z’) - string[index] = (string[index] - ’A’ + 13) % 26 + ’A’ + for (new index = 0; string[index]; index++) + if ('a' <= string[index] <= 'z') + string[index] = (string[index] - 'a' + 13) % 26 + 'a' + else if ('A' <= string[index] <= 'Z') + string[index] = (string[index] - 'A' + 13) % 26 + 'A' } - ``` In the function header of rot13, the parameter “string” is declared as an @@ -705,8 +668,7 @@ allows the function to be called with arrays of any size. You must then have some other means of determining the (maximum) size of the array. In the case of a string parameter, one can simply search for the zero terminator. -The for loop that walks over the string is typical for string processing func- -tions. Note that the loop condition is “string[index]”. The rule for true/ +The for loop that walks over the string is typical for string processing functions. Note that the loop condition is “string[index]”. The rule for true/ false conditions in pawn is that any value is “true”, except zero. That is, when the array cell at string[index] is zero, it is “false” and the loop aborts. @@ -732,10 +694,9 @@ parameter (called “string” in the example) without needing to declare as a reference argument. Another point of interest are the conditions in the two if statements. The first -if, for example, holds the condition “’a’ \<= string[index] \<= ’z’”, which -means that the expression is true if (and only if) both ’a’ \<= string[index] -and string[index] \<= ’z’ are true. In the combined expression, the rela- -tional operators are said to be “chained”, as they chain multiple comparisons +if, for example, holds the condition `“'a' <= string[index] <= 'z'”`, which +means that the expression is true if (and only if) both `'a' <= string[index]` +and `string[index] <= 'z'` are true. In the combined expression, the relational operators are said to be “chained”, as they chain multiple comparisons in one condition. Finally, note how the last printf in function main uses the escape sequence @@ -749,51 +710,49 @@ that shows a few new features of the pawn language. Listing: wcount.p ```c - -/* word count: count words on a string that the user types */ +/* word count: count words on a string that the user types */ main() { - print "Please type a string: " - new string[100] - getstring string, sizeof string + print "Please type a string: " + new string[100] + getstring string, sizeof string - new count = 0 + new count = 0 - new word[20] - new index - for ( ;; ) + new word[20] + new index + for ( ;; ) { - word = strtok(string, index) - if (strlen(word) == 0) + word = strtok(string, index) + if (strlen(word) == 0) break count++ - printf "Word %d: ’%s’\n", count, word + printf "Word %d: '%s'\n", count, word } - printf "\nNumber of words: %d\n", count + printf "\nNumber of words: %d\n", count } -strtok(const string[], &index) +strtok(const string[], &index) { - new length = strlen(string) + new length = strlen(string) - /* skip leading white space */ - while (index < length && string[index] <= ’ ’) + /* skip leading white space */ + while (index < length && string[index] <= ' ') index++ - /* store the word letter for letter */ - new offset = index /* save start position of token */ - new result[20] /* string to store the word in */ - while (index < length - && string[index] > ’ ’ - && index - offset < sizeof result - 1) + /* store the word letter for letter */ + new offset = index /* save start position of token */ + new result[20] /* string to store the word in */ + while (index < length + && string[index] > ' ' + && index - offset < sizeof result - 1) { - result[index - offset] = string[index] + result[index - offset] = string[index] index++ } - result[index - offset] = EOS /* zero-terminate the string */ - return result + result[index - offset] = EOS /* zero-terminate the string */ + return result } - ``` --- @@ -802,7 +761,7 @@ strtok(const string[], &index) `Escape sequence: 99` -Function main first displays a message and retrieves a string that +Function main first displays a message and retrieves a string that the user must type. Then it enters a loop: writing “for (;;)” creates a loop without initialisation, without increment and without test —it is an infinite loop, @@ -810,16 +769,14 @@ equivalent to “while (true)”. However, where the pawn parser will give you a warning if you type “while (true)” (something along the line “redundant test expression; always true”), “for (;;)” passes the parser without warning. -A typical use for an infinite loop is a case where you need a loop +A typical use for an infinite loop is a case where you need a loop with the test in the middle —a hybrid between a while and a do. . . while loop, so to -speak. pawn does not support loops-with-a-test-in-the middle directly, +speak. pawn does not support loops-with-a-test-in-the middle directly, but you can imitate one by coding an infinite loop with a conditional break. In this example program, the loop: - gets a word from the string —code before the test ; - - tests wether a new word is available, and breaks out of the loop if not —the test in the middle; - - prints the word and its sequence number —code after the test . As is apparent from the line “word = strtok(string, index)” (and the @@ -838,96 +795,94 @@ which it then returns. ### • Arrays and enumerations (structured data) -In a typeless language, we might assign a different purpose to some +In a typeless language, we might assign a different purpose to some array elements than to other elements in the same array. pawn supports enumerated constants with an extension that allows it to mimic some functionality that other languages implement with “structures” or “records”. -The example to illustrate enumerations and arrays is longer than +The example to illustrate enumerations and arrays is longer than previous pawn programs, and it also displays a few other features, such as global variables and named parameters. Listing: queue.p ```c +/* Priority queue (for simple text strings) */ -/* Priority queue (for simple text strings) */ - -enum message +enum message { - text[40 char], + text[40 char], priority } main() { - new msg[message] + new msg[message] - /* insert a few items (read from console input) */ + /* insert a few items (read from console input) */ - printf "Please insert a few messages and their priorities; \ - end with an empty string\n" - for ( ;; ) + printf "Please insert a few messages and their priorities; \ + end with an empty string\n" + for ( ;; ) { - printf "Message: " - getstring .string = msg[text], .maxlength = 40, .pack = true - if (strlen(msg[text]) == 0) + printf "Message: " + getstring .string = msg[text], .maxlength = 40, .pack = true + if (strlen(msg[text]) == 0) break - printf "Priority: " - msg[priority] = getvalue() - if (!insert(msg)) + printf "Priority: " + msg[priority] = getvalue() + if (!insert(msg)) { - printf "Queue is full, cannot insert more items\n" + printf "Queue is full, cannot insert more items\n" break } } - /* now print the messages extracted from the queue */ - printf "\nContents of the queue:\n" - while (extract(msg)) - printf "[%d] %s\n", msg[priority], msg[text] + /* now print the messages extracted from the queue */ + printf "\nContents of the queue:\n" + while (extract(msg)) + printf "[%d] %s\n", msg[priority], msg[text] } -const queuesize = 10 -new queue[queuesize][message] -new queueitems = 0 -insert(const item[message]) +const queuesize = 10 +new queue[queuesize][message] +new queueitems = 0 +insert(const item[message]) { - /* check if the queue can hold one more message */ - if (queueitems == queuesize) - return false /* queue is full */ + /* check if the queue can hold one more message */ + if (queueitems == queuesize) + return false /* queue is full */ - /* find the position to insert it to */ - new pos = queueitems /* start at the bottom */ - while (pos > 0 && item[priority] > queue[pos-1][priority]) - --pos /* higher priority: move one position up */ + /* find the position to insert it to */ + new pos = queueitems /* start at the bottom */ + while (pos > 0 && item[priority] > queue[pos-1][priority]) + --pos /* higher priority: move one position up */ - /* make place for the item at the insertion spot */ - for (new i = queueitems; i > pos; --i) - queue[i] = queue[i-1] + /* make place for the item at the insertion spot */ + for (new i = queueitems; i > pos; --i) + queue[i] = queue[i-1] - /* add the message to the correct slot */ - queue[pos] = item + /* add the message to the correct slot */ + queue[pos] = item queueitems++ - return true + return true } extract(item[message]) { - /* check whether the queue has one more message */ - if (queueitems == 0) - return false /* queue is empty */ + /* check whether the queue has one more message */ + if (queueitems == 0) + return false /* queue is empty */ - /* copy the topmost item */ - item = queue[0] + /* copy the topmost item */ + item = queue[0] --queueitems - /* move the queue one position up */ - for (new i = 0; i < queueitems; ++i) - queue[i] = queue[i+1] + /* move the queue one position up */ + for (new i = 0; i < queueitems; ++i) + queue[i] = queue[i+1] - return true + return true } - ``` --- @@ -938,18 +893,14 @@ extract(item[message]) `"char" operator: 110` -Near the top of the program listing is the declaration of the enumeration mes- -sage. This enumeration defines two constants: text, which is zero, and pri- -ority, which is 11 (assuming a 32-bit cell). The idea behind an enumeration +Near the top of the program listing is the declaration of the enumeration message. This enumeration defines two constants: text, which is zero, and priority, which is 1 (assuming a 32-bit cell). The idea behind an enumeration is to quickly define a list of symbolic constants without duplicates. By -default, every constant in the list is 1 higher than its predecessor and the very +default, every constant in the list is 1 higher than its predecessor and the very first constant in the list is zero. However, you may give an extra increment for a constant so that the successor has a value of 1 plus that extra increment. The - text constant specifies an extra increment of 40 char. In pawn, char is an operator, it returns the number of cells needed to hold a packed string of the - specified number of characters. Assuming a 32-bit cell and a 8-bit character, 10 cells can hold 40 packed characters. @@ -960,9 +911,8 @@ plus the optional extra increment for that last element. So in this example, message is 12. That is to say, array msg is declared to hold 12 cells. Further in main are two loops. The for loop reads strings and priority values -from the console and inserts them in a queue. The while loop below -that -extracts element by element from the queue and prints the information on the +from the console and inserts them in a queue. The while loop below +that extracts element by element from the queue and prints the information on the screen. The point to note, is that the for loop stores both the string and the priority number (an integer) in the same variable msg; indeed, function main declares only a single variable. Function getstring stores the message text @@ -971,70 +921,56 @@ that you type starting at array msg[text] while the priority value is stored in the while loop reads the string and the value from those positions as well. At the same time, the msg array is an entity on itself: it is passed in its -entirety -to function insert. That function, near the end, says “queue[queueitems] - -= item”, where item is an array with size message and queue is a -two- -dimensional array that holds queuesize elements of size message. The decla- -ration of queue and queuesize are just above function insert. - -The example implements a “priority queue”. You can insert a number -of -messages into the queue and when these messages all have the same priority, -they are extracted from the queue in the same order. However, when -the -messages have different priorities, the one with the highest priority comes out +entirety to function insert. That function, near the end, says “queue[queueitems] += item”, where item is an array with size message and queue is a +two-dimensional array that holds queuesize elements of size message. The declaration of queue and queuesize are just above function insert. + +The example implements a “priority queue”. You can insert a number +of messages into the queue and when these messages all have the same priority, +they are extracted from the queue in the same order. However, when +the messages have different priorities, the one with the highest priority comes out first. The “intelligence” for this operation is inside function insert: it -first -determines the position of the new message to add, then moves a few messages +first determines the position of the new message to add, then moves a few messages one position upward to make space for the new message. Function extract simply always retrieves the first element of the queue and shifts all remaining elements down by one position. Note that both functions insert and extract work on two shared variables, -queue and queueitems. A variable that is declared inside a function, -like -variable msg in function main can only be accessed from within that function. +queue and queueitems. A variable that is declared inside a function, +like variable msg in function main can only be accessed from within that function. A “global variable” is accessible by all functions, and that variable is -declared -outside the scope of any function. Variables must still be declared before they +declared outside the scope of any function. Variables must still be declared before they are used, so main cannot access variables queue and queueitems, but both insert and extract can. --- -Function extract returns the messages with the highest priority via its func- -tion argument item. That is, it changes its function argument by copying the +Function extract returns the messages with the highest priority via its function argument item. That is, it changes its function argument by copying the first element of the queue array into item. Function insert copies in the other direction and it does not change its function argument item. In such a case, it is advised to mark the function argument as “const”. This helps the pawn parser to both check for errors and to generate better (more compact, quicker) code. A final remark on this latest sample is the call to getstring in function main: -note how the parameters are attributed with a description. The first param- -eter is labeled “.string”, the second “.maxlength” and the third “.pack”. -Function getstring receives “named parameters” rather than positional pa- -rameters. The order in which named parameters are listed is not important. -Named parameters are convenient in specifying —and deciphering— long pa- -rameter lists. +note how the parameters are attributed with a description. The first parameter is labeled “.string”, the second “.maxlength” and the third “.pack”. +Function getstring receives “named parameters” rather than positional parameters. The order in which named parameters are listed is not important. +Named parameters are convenient in specifying —and deciphering— long parameter lists. --- `Named parameters: 74` -### • Bit operations to manipulate ‘‘sets’’ +### • Bit operations to manipulate "sets" A few algorithms are most easily solved with “set operations”, like -intersection, -union and inversion. In the figure below, for example, we want to design an +intersection, union and inversion. In the figure below, for example, we want to design an algorithm that returns us the points that can be reached from some other point in a specified maximum number of steps. For example, if we ask it to return the points that can be reached in two steps starting from B, the algorithm has to return C, D, E and F, but not G because G takes three steps from B. Our approach is to keep, for each point in the graph, the set of other points -that it can reach in one step —this is the “next_step” set. We +that it can reach in one step —this is the “next_step” set. We also have a “result” set that keeps all points that we have found so far. We start by setting the result set equal to the next_step set for the departure point. Now we have in the result set all points that one can reach in one step. Then, for @@ -1050,7 +986,6 @@ the result set. The first point that we encounter in the set is D, and we check what points can be reached from D in one step: these are C and F. So we add C and F to the result set. We knew that the points that can be reached from D in one step are C and F, because C and F are in the next_step set for - D. So what we do is to merge the next_step set for point D into the result set. The merge is called a “union” in set theory. That handles D. The original result set also contained point E, but the next_step set for E is empty, so @@ -1061,7 +996,6 @@ no more point is added. The new result set therefore now contains C, D, E and F. A set is a general purpose container for elements. The only information that a set holds of an element is whether it is present in the set or not. The order of elements in a set is insignificant and a set cannot contain the same element - multiple times. The pawn language does not provide a “set” data type or operators that work on sets. However, sets with up to 32 elements can be simulated by bit operations. It takes just one bit to store a “present/absent” @@ -1078,7 +1012,7 @@ lower case letter for an element from that set. | union | A ∪ B | A I B | | complement | A | ~A | | empty set | ε | 0 | -| membership | x ∈ A | (1 \<< x ) & A | +| membership | x ∈ A | `(1 << x) & A` | --- @@ -1087,7 +1021,7 @@ element, create a set with just one element and take the intersection. If the result is 0 (the empty set) the element is not in the set. Bit numbering starts typically at zero; the lowest bit is bit 0 and the highest bit in a 32-bit cell is bit 31. To make a cell with only bit 7 set, shift the value 1 left by seven -—or in a pawn expression: “1 \<< 7”. +—or in a pawn expression: `“1 << 7”`. Below is the program that implements the algorithm described earlier to find all points that can be reached from a specific departure in a given number of @@ -1096,55 +1030,52 @@ steps. The algorithm is completely in the findtargets function. Listing: set.p ```c - -/* Set operations, using bit arithmetic */ +/* Set operations, using bit arithmetic */ main() { - enum (<<= 1) { A = 1, B, C, D, E, F, G } - new nextstep[] = - - { C | E, /* A can reach C and E */ - D | E, /* B " " D and E */ - G, /* C " " G */ - C | F, /* D " " C and F */ - 0, /* E " " none */ - 0, /* F " " none */ - E | F, /* G " " E and F */ + enum (<<= 1) { A = 1, B, C, D, E, F, G } + new nextstep[] = + { C | E, /* A can reach C and E */ + D | E, /* B " " D and E */ + G, /* C " " G */ + C | F, /* D " " C and F */ + 0, /* E " " none */ + 0, /* F " " none */ + E | F, /* G " " E and F */ } - #pragma unused A, B + #pragma unused A, B - print "The departure point: " - new source = clamp( .value = toupper(getchar()) - ’A’, - .min = 0, - .max = sizeof nextstep - 1 + print "The departure point: " + new source = clamp( .value = toupper(getchar()) - 'A', + .min = 0, + .max = sizeof nextstep - 1 ) - print "\nThe number of steps: " - new steps = getvalue() + print "\nThe number of steps: " + new steps = getvalue() - /* make the set */ - new result = findtargets(source, steps, nextstep) - printf "The points reachable from %c in %d steps: ", source+’A’, + /* make the set */ + new result = findtargets(source, steps, nextstep) + printf "The points reachable from %c in %d steps: ", source+'A', steps - for (new i = 0; i < sizeof nextstep; i++) - if (result & 1 << i) - printf "%c ", i + ’A’ + for (new i = 0; i < sizeof nextstep; i++) + if (result & 1 << i) + printf "%c ", i + 'A' } -findtargets(source, steps, nextstep[], numpoints = sizeof nextstep) +findtargets(source, steps, nextstep[], numpoints = sizeof nextstep) { - new result = 0 - new addedpoints = nextstep[source] - while (steps-- > 0 && result != addedpoints) + new result = 0 + new addedpoints = nextstep[source] + while (steps-- > 0 && result != addedpoints) { - result = addedpoints - for (new i = 0; i < numpoints; i++) - if (result & 1 << i) - addedpoints |= nextstep[i] + result = addedpoints + for (new i = 0; i < numpoints; i++) + if (result & 1 << i) + addedpoints |= nextstep[i] } - return result + return result } - ``` --- @@ -1153,20 +1084,18 @@ The enum statement just below the header of the main function declares the constants for the nodes A to G, but with a twist. Usually, the enum starts counting from zero; here, the value of the first constant, A, is explicitly set to -1. More noteworthy is the expression “(\<\<= 1)” between the enum keyword - and the opening brace that starts the constant list: it specifies a “bit - shifting” increment. By default, every constant in an enum list gets a value that is - 1 above its predecessor, but you can specify every successive constant - in an enumeration to have a value that is: - -_its predecessor incremented by any value (not just 1) —e.g., “(+= 5)”;_ +1. More noteworthy is the expression `“(<<= 1)”` between the enum keyword + and the opening brace that starts the constant list: it specifies a “bit + shifting” increment. By default, every constant in an enum list gets a value that is + 1 above its predecessor, but you can specify every successive constant + in an enumeration to have a value that is: -_its predecessor multiplied by any value —e.g., “(_= 3)”;\_ - -_its predecessor bit-shifted to the left by any value —e.g., “(\<\<= 1)”;_ +- its predecessor incremented by any value (not just 1) —e.g., “(+= 5)”; +- its predecessor multiplied by any value —e.g., “(\*= 3)”; +- its predecessor bit-shifted to the left by any value —e.g., `“(<<= 1)”`; Note that, in binary arithmetic, shifting left by one bit amounts to the same -as multiplying by two, meaning that `(/*= 2)` and `(<<= 1)` do the same thing. +as multiplying by two, meaning that `(*= 2)` and `(<<= 1)` do the same thing. When working with sets, a typical task that pops up is to determine the number of elements in the set. A straightforward function that does this is below: @@ -1174,37 +1103,33 @@ of elements in the set. A straightforward function that does this is below: Listing: simple bitcount function ```c - bitcount(set) { - new count = 0 - for (new i = 0; i < cellbits; i++) - if (set & (1 << i)) + new count = 0 + for (new i = 0; i < cellbits; i++) + if (set & (1 << i)) count++ - return count + return count } - ``` With a cell size of 32 bits, this function’s loop iterates 32 times to check for a single bit at each iteration. With a bit of binary arithmetic magic, we can -reduce it to loop only for the number of bits that are “set”. +reduce it to loop only for the number of bits that are “set”. That is, the following function iterates only once if the input value has only one bit set: Listing: improved bitcount function ```c - bitcount(set) { - new count = 0 - if (set) + new count = 0 + if (set) do count++ - while ((set = set & (set - 1))) - return count + while ((set = set & (set - 1))) + return count } - ``` --- @@ -1218,34 +1143,30 @@ bitcount(set) ### • A simple RPN calculator The common mathematical notation, with expressions like “26 3 (5 + 2)”, -is known as the algebraic notation. It is a compact notation and -we have -grown accustomed to it. pawn and by far most other programming languages +is known as the algebraic notation. It is a compact notation and +we have grown accustomed to it. pawn and by far most other programming languages use the algebraic notation for their programming expressions. The algebraic - notation does have a few disadvantages, though. For instance, it occasionally requires that the order of operations is made explicit by folding a part of the -expression in parentheses. The expression at the top of this paragraph -can -be rewritten to eliminate the parentheses, but at the cost of nearly doubling +expression in parentheses. The expression at the top of this paragraph +can be rewritten to eliminate the parentheses, but at the cost of nearly doubling its length. In practice, the algebraic notation is augmented with precedence level rules that say, for example, that multiplication goes before addition and -subtraction.∗ Precedence levels greatly reduce the need for parentheses, but it +subtraction.\* Precedence levels greatly reduce the need for parentheses, but it does not fully avoid them. Worse is that when the number of operators grows large, -the hierarchy of precedence levels and the particular precedence +the hierarchy of precedence levels and the particular precedence level for each operator becomes hard to memorize —which is why an operator-rich language as APL does away with precedence levels altogether. Around 1920, the Polish mathematician Jan ´Lukasiewicz demonstrated that by putting the operators in front of their operands, instead of between them, - precedence levels became redundant and parentheses were never necessary. This notation became known as the “Polish Notation”.† Charles Hamblin proposed later to put operators behind the operands, calling it the “Reverse Polish Notation”. --- -###### ∗ These rules are often summarized in a mnemonic like “Please Excuse My Dear Aunt Sally” (Parentheses, Exponentiation, Multiplication, Division, Addition, Subtraction) +###### \* These rules are often summarized in a mnemonic like “Please Excuse My Dear Aunt Sally” (Parentheses, Exponentiation, Multiplication, Division, Addition, Subtraction) ###### \* Polish Notation is completely unrelated to “Hungarian Notation” —which is just the habit of adding “type” or “purpose” identification warts to names of variables or functions @@ -1258,16 +1179,15 @@ proposed later to put operators behind the operands, calling it the “Reverse P --- The advantage of reversing the order is that the operators -are listed in the same order as they must be executed: when +are listed in the same order as they must be executed: when reading the operators from the left to the right, you also have the operations to perform in that order. The algebraic expression from the beginning of this section would read in rpn as: `26 3 5 2 + × −` -When looking at the operators only, we have: first an addition, then a mul- -tiplication and finally a subtraction. The operands of each operator are read -from right to left: the operands for the + operator are the values 5 +When looking at the operators only, we have: first an addition, then a multiplication and finally a subtraction. The operands of each operator are read +from right to left: the operands for the + operator are the values 5 and 2, those for the operator are the result of the previous addition and the value 3, and so on. @@ -1275,7 +1195,6 @@ It is helpful to imagine the values to be stacked on a pile, where the operators take one or more operands from the top of the pile and put a result back on top of the pile. When reading through the rpn expression, the values 26, 3, 5 and 2 are “stacked” in that order. The operator + removes the top two elements - from the stack (5 and 2) and pushes the sum of these values back —the stack now reads “26 3 7”. Then, the operator removes 3 and 7 and pushes the product of the values onto the stack —the stack is “26 21”. Finally, the @@ -1292,113 +1211,107 @@ The main program for a Reverse Polish Notation calculator is below: Listing: rpn.p ```c - -/* a simple RPN calculator */ -#include strtok -#include stack -#include rpnparse +/* a simple RPN calculator */ +#include strtok +#include stack +#include rpnparse main() { - print "Type an expression in Reverse Polish Notation: " - new string[100] - getstring string, sizeof string - rpncalc string + print "Type an expression in Reverse Polish Notation: " + new string[100] + getstring string, sizeof string + rpncalc string } - ``` --- -The main program contains very little code itself; instead it includes the re- -quired code from three other files, each of which implements a few functions +The main program contains very little code itself; instead it includes the required code from three other files, each of which implements a few functions that, together, build the rpn calculator. When programs or scripts get larger, it is usually advised to spread the implementation over several files, in order to make maintenance easier. Function main first puts up a prompt and calls the native function getstring to read an expression that the user types. Then it calls the custom function -rpncalc to do the real work. Function rpncalc is implemented in the +rpncalc to do the real work. Function rpncalc is implemented in the file rpnparse.inc, reproduced below: Listing: rpnparse.inc -````c - -/* main rpn parser and lexical analysis, part of the RPN calculator */ -#include -#include +```c +/* main rpn parser and lexical analysis, part of the RPN calculator */ +#include +#include -enum token +enum token { - t_type, /* operator or token type */ - Rational: t_value, /* value, if t_type is "Number" */ - t_word[20], /* raw string */ + t_type, /* operator or token type */ + Rational: t_value, /* value, if t_type is "Number" */ + t_word[20], /* raw string */ } -const Number = ’0’ -const EndOfExpr = ’#’ +const Number = '0' +const EndOfExpr = '#' -rpncalc(const string[]) +rpncalc(const string[]) { - new index - new field[token] - for ( ;; ) + new index + new field[token] + for ( ;; ) { - field = gettoken(string, index) - switch (field[t_type]) + field = gettoken(string, index) + switch (field[t_type]) { - case Number: - push field[t_value] - case ’+’: - push pop() + pop() - case ’-’: - push - pop() + pop() - case ’*’: - push pop() * pop() - case ’/’, ’:’: - push 1.0 / pop() * pop() - case EndOfExpr: - break /* exit "for" loop */ + case Number: + push field[t_value] + case '+': + push pop() + pop() + case '-': + push - pop() + pop() + case '*': + push pop() * pop() + case '/', ':': + push 1.0 / pop() * pop() + case EndOfExpr: + break /* exit "for" loop */ default: - printf "Unknown operator ’%s’\n", field[t_word] + printf "Unknown operator '%s'\n", field[t_word] } } - printf "Result = %r\n", pop() - if (clearstack()) - print "Stack not empty\n", red + printf "Result = %r\n", pop() + if (clearstack()) + print "Stack not empty\n", red } -gettoken(const string[], &index) +gettoken(const string[], &index) { - /* first get the next "word" from the string */ - new word[20] - word = strtok(string, index) - - /* then parse it */ - new field[token] - field[t_word] = word - if (strlen(word) == 0) + /* first get the next "word" from the string */ + new word[20] + word = strtok(string, index) + + /* then parse it */ + new field[token] + field[t_word] = word + if (strlen(word) == 0) { - field[t_type] = EndOfExpr /* special "stop" symbol */ - field[t_value] = 0 + field[t_type] = EndOfExpr /* special "stop" symbol */ + field[t_value] = 0 } - else if (’0’ <= word[0] <= ’9’) + else if ('0' <= word[0] <= '9') { - field[t_type] = Number - field[t_value] = rationalstr(word) + field[t_type] = Number + field[t_value] = rationalstr(word) } else { - field[t_type] = word[0] - field[t_value] = 0 + field[t_type] = word[0] + field[t_value] = 0 } - return field - + return field } - -```*** +``` The rpn calculator uses rational number support and rpnparse.inc includes the “rational” file for that purpose. Almost all of the operations on rational @@ -1412,36 +1325,23 @@ declaration, where one element has a tag (t_field) and the other element has a s the enumeration symbol as its size. Behind the screens, this declaration does more than just create an array with 22 cells: -/*The index tag of the array is set to the tag name “token:”. This means that you can index the array with any of the elements from the enumeration, but not with values that have a different tag. In other words, field[t_type] is okay, but field[1] gives a parser diagnostic. +/\* The index tag of the array is set to the tag name “token:”. This means that you can index the array with any of the elements from the enumeration, but not with values that have a different tag. In other words, field[t_type] is okay, but field[1] gives a parser diagnostic. -/*The tag name of the enumeration overrules the tag name of the array variable, if any. The field variable is untagged, but field[t_value] has the tag Rational:, because the enumeration element t_value is declared as such. This, hence, allows you to create an array whose elements have different tag names. +/\* The tag name of the enumeration overrules the tag name of the array variable, if any. The field variable is untagged, but field[t_value] has the tag Rational:, because the enumeration element t_value is declared as such. This, hence, allows you to create an array whose elements have different tag names. -/*When the enumeration element has a size, the array element indicated with that element is sometimes treated as a sub-array. In rpncalc, expression “field[t_type]” is a single cell, “field[t_value]” is a single cell, but “field[t_word]” is a one-dimensional array of 20 cells. We see that specifically in the line: +/\* When the enumeration element has a size, the array element indicated with that element is sometimes treated as a sub-array. In rpncalc, expression “field[t_type]” is a single cell, “field[t_value]” is a single cell, but “field[t_word]” is a one-dimensional array of 20 cells. We see that specifically in the line: - printf "Unknown operator ’%s’\n", field[t_word] + printf "Unknown operator '%s'\n", field[t_word] where the format code %s expects a string —a zero-terminated array. - - - - `Rational numbers, see also the “Celsius to Fahrenheit” example on page page 16` `“enum” statement: 101` `Another example of an index tag: page 68` - - -// came to here - - - - -*** - -If you know C/C⁺⁺ or Java, you may want to look at the switch statement. +If you know C/C⁺⁺ or Java, you may want to look at the switch statement. The switch statement differs in a number of ways from the other languages that provide it. The cases are not fall-through, for example, which in turn means that the break statement for the case EndOfExpr breaks out of the @@ -1462,7 +1362,6 @@ array size of word. Listing: strtok.inc ```c - /* extract words from a string (words must be separated by white space) */ #include @@ -1471,14 +1370,14 @@ strtok(const string[], &index) new length = strlen(string) /* skip leading white space */ - while (index < length && string[index] <= ’ ’) + while (index < length && string[index] <= ' ') index++ /* store the word letter for letter */ new offset = index /* save start position of token */ new result[20] /* string to store the word in */ while (index < length - && string[index] > ’ ’ + && string[index] > ' ' && index - offset < sizeof result - 1) { result[index - offset] = string[index] @@ -1488,8 +1387,7 @@ strtok(const string[], &index) return result } - -```` +``` --- @@ -1500,23 +1398,21 @@ strtok(const string[], &index) Function strtok is the same as the one used in the wcount.p example. It is implemented in a separate file for the rpn calculator program. Note that the strtok function as it is implemented here can only handle words with up to 19 - characters —the 20th character is the zero terminator. A truly general purpose re-usable implementation of an strtok function would pass the destination array as a parameter, so that it could handle words of any size. Supporting -both packed and unpack strings would also be a useful feature of a +both packed and unpack strings would also be a useful feature of a general purpose function. When discussing the merits of Reverse Polish Notation, I mentioned that a -stack is both an aid in “visualizing” the algorithm as well as a +stack is both an aid in “visualizing” the algorithm as well as a convenient method to implement an rpn parser. This example rpn calculator, uses -a stack with the ubiquitous functions push and pop. For error checking +a stack with the ubiquitous functions push and pop. For error checking and resetting the stack, there is a third function that clears the stack. Listing: stack.inc ```c - /* stack functions, part of the RPN calculator */ #include @@ -1543,7 +1439,6 @@ clearstack() stackidx = 0 return true } - ``` --- @@ -1558,11 +1453,10 @@ support), but it does not do any harm either and, for the sake of code re-use, it is better to make any file include the definitions of the libraries that it depends on. -Notice how the two global variables stack and stackidx are declared +Notice how the two global variables stack and stackidx are declared as “static” variables; using the keyword static instead of new. Doing this makes -the global variables “visible” in that file only. For all other files in a -larger project, the symbols stack and stackidx are invisible and they cannot (ac- -cidentally) modify the variables. It also allows the other modules to declare +the global variables “visible” in that file only. For all other files in a +larger project, the symbols stack and stackidx are invisible and they cannot (accidentally) modify the variables. It also allows the other modules to declare their own private variables with these names, so it avoids name clashing. The rpn calculator is actually still a fairly small program, but it has been set @@ -1573,8 +1467,7 @@ implemented more compactly. ### • Event-driven programming All of the example programs that were developed in this chapter so far, have -used a “lineal” programming model: they start with main and the code deter- -mines what to do and when to request input. This programming model is easy +used a “lineal” programming model: they start with main and the code determines what to do and when to request input. This programming model is easy to understand and it nicely fits most programming languages, but it is also a model does not fit many “real life” situations. Quite often, a program cannot simply process data and suggest that the user provides input only when it is @@ -1582,8 +1475,7 @@ ready for him/her. Instead, it is the user who decides when to provide input, and the program or script should be prepared to process it in an acceptable time, regardless of what it was doing at the moment. -The above description suggests that a program should therefore be able to in- -terrupt its work and do other things before picking up the original task. In +The above description suggests that a program should therefore be able to interrupt its work and do other things before picking up the original task. In early implementations, this was indeed how such functionality was implemented: a multi-tasking system where one task (or thread) managed the background tasks and a second task/thread that sits in a loop continuously requesting user input. @@ -1605,7 +1497,7 @@ sub-systems such as timers and clocks, and all kinds of other equipment that you may have attached to your system. Many of the apparatus that produce input, just send it. The arrival of such input is an event, just like a key press. -If you do not catch the event, a few of them may be stored in an +If you do not catch the event, a few of them may be stored in an internal system queue, but once the queue is saturated the events are simply dropped. @@ -1613,27 +1505,20 @@ pawn directly supports the event-driven model, because it supports multiple entry points. The sole entry point of a lineal program is main; an event-driven program has an entry point for every event that it captures. When compared to the lineal model, event-driven programs often appear “bottom-up”: instead of - your program calling into the host application and deciding what to do next, -your program is being called from the outside and it is required to -respond -appropriately and promptly. +your program is being called from the outside and it is required to +respond appropriately and promptly. pawn does not specify a standard library, and so there is no guarantee that in a particular implementation, functions like printf and getvalue. Although it is suggested that every implementation provides a minimal console/terminal interface with a these functions, their availability is ultimately -implementation- - -dependent. The same holds for the public functions —the entry points for a -script. It is implementation-dependent which public functions a host applica- -tion supports. The script in this section may therefore not run on your platform +implementation-dependent. The same holds for the public functions —the entry points for a +script. It is implementation-dependent which public functions a host application supports. The script in this section may therefore not run on your platform (even if all previous scripts ran fine). The tools in the standard distribution of the pawn system support all scripts developed in this manual, provided that - your operating system or environment supports standard terminal functions - such as setting the cursor position. An early programming language that was developed solely for teaching the @@ -1650,55 +1535,51 @@ to create drawings by properly programming the turtle —it became known as turt --- -The term “turtle graphics” was also used for drawing inter- -actively with the arrow keys on the keyboard and a “turtle” for the current +The term “turtle graphics” was also used for drawing interactively with the arrow keys on the keyboard and a “turtle” for the current position. This method of drawing pictures on the computer was briefly popular before the advent of the mouse. Listing: turtle.p ```c - @keypressed(key) { - /_ get current position */ + /* get current position */ new x, y wherexy x, y - /_ determine how the update the current position */ + /* determine how the update the current position */ switch (key) { - case ’u’: y-- /_ up */ - case ’d’: y++ /_ down */ - case ’l’: x-- /_ left */ - case ’r’: x++ /_ right */ - case ’\e’: exit /_ Escape = exit */ + case 'u': y-- /* up */ + case 'd': y++ /* down */ + case 'l': x-- /* left */ + case 'r': x++ /* right */ + case '\e': exit /* Escape = exit */ } - /_ adjust the cursor position and draw something */ + /* adjust the cursor position and draw something */ moveturtle x, y } moveturtle(x, y) { gotoxy x, y - print ’/*’ + print '*' gotoxy x, y } - ``` The entry point of the above program is @keypressed —it is called on a -key press. If you run the program and do not type any key, the +key press. If you run the program and do not type any key, the function @keypressed never runs; if you type ten keys, @keypressed runs ten times. Contrast this behaviour with main: function main runs immediately after you start the script and it runs only once. It is still allowed to add a main function to an event-driven program: the main function will then serve for one-time initialization. A simple addition to this -example program is to add a main function, in order to clear the -console/ -terminal window on entry and perhaps set the initial position of the “turtle” +example program is to add a main function, in order to clear the +console/terminal window on entry and perhaps set the initial position of the “turtle” to the centre. Support for function keys and other special keys (e.g. the arrow keys) is highly @@ -1713,18 +1594,17 @@ does not mean, however, that special keys are beyond pawn’s capabilities. In the “turtle” script, the “Escape” key terminates the host application through the instruction exit. For a simple pawn run-time host, this will indeed work. -With host applications where the script is an add-on, or +With host applications where the script is an add-on, or host-applications - that are embedded in a device, the script usually cannot terminate the host application. ### • Multiple events The advantages of the event-driven programming model, for building reactive programs, become apparent in the presence of multiple events. In fact, -the event-driven model is only useful if you have more that one entry +the event-driven model is only useful if you have more that one entry point; if your script just handles a single event, it might as well enter a polling -loop for that single event. The more events need to be handled, the +loop for that single event. The more events need to be handled, the harder the lineal programming model becomes. The script below implements a bare-bones “chat” program, using only two events: one for sending and one for receiving. The script allows users on a network (or perhaps over another connection) to @@ -1734,7 +1614,6 @@ The script depends on the host application to provide the native and public functions for sending and receiving “datagrams” and for responding to keys that are typed in. How the host application sends its messages, over a serial line or using TCP/IP, the host application may decide itself. The tools in the - standard pawn distribution push the messages over the TCP/IP network, and allow for a “broadcast” mode so that more than two people can chat with each other. @@ -1743,7 +1622,6 @@ allow for a “broadcast” mode so that more than two people can chat with each Listing: chat.p ```c - #include @receivestring(const message[], const source[]) @@ -1754,18 +1632,18 @@ printf "[%s] says: %s\n", source, message static string[100 char] static index - if (key == ’\e’) - exit /* quit on ’Esc’ key */ + if (key == '\e') + exit /* quit on 'Esc' key */ echo key - if (key == ’\r’ || key == ’\n’ || index char == sizeof string) + if (key == '\r' || key == '\n' || index char == sizeof string) { - string{index} = ’\0’ /* terminate string */ + string{index} = '\0' /* terminate string */ sendstring string index = 0 - string[index] = ’\0’ + string[index] = '\0' } else string{index++} = key @@ -1774,13 +1652,12 @@ printf "[%s] says: %s\n", source, message echo(key) { new string[2 char] = { 0 } - string{0} = key == ’\r’ ? ’\n’ : key + string{0} = key == '\r' ? '\n' : key printf string } - ``` -The bulk of the above script handles gathering received key-presses +The bulk of the above script handles gathering received key-presses into a string and sending that string after seeing the enter key. The “Escape” key ends the program. The function echo serves to give visual feedback of what the user types: it builds a zero-terminated string from the key and prints it. @@ -1789,7 +1666,7 @@ Despite its simplicity, this script has the interesting property that there is no fixed or prescribed order in which the messages are to be sent or received — there is no query–reply scheme where each host takes its turn in talking & listening. A new message may even be received while the user is typing its -own message.∗ +own message.\* • State programming @@ -1802,7 +1679,7 @@ other signals that take part in the protocol. --- -###### ∗ As this script makes no attempt to separate received messages from typed messages (for example, in two different scrollable regions), the terminal/console will look confusing when this happens. With an improved user-interface, this simple script could indeed be a nice message-base chat program +###### \* As this script makes no attempt to separate received messages from typed messages (for example, in two different scrollable regions), the terminal/console will look confusing when this happens. With an improved user-interface, this simple script could indeed be a nice message-base chat program --- @@ -1812,7 +1689,7 @@ the event-driven program must follow a precise hand-shaking protocol. To adhere to a protocol, an event-driven program must respond to each event in compliance with the (recent) history of events received earlier and the responses to those events. In other words, the handling of one event may set -up a “condition” or “environment” for the handling any one or more subsequent events. +up a condition or environment for the handling any one or more subsequent events. A simple, but quite effective, abstraction for constructing reactive systems that need to follow (partially) sequential protocols, is that of the “automaton” or @@ -1832,7 +1709,7 @@ implementation from a “state diagram”. ![State diagram](https://i.ibb.co/k3kWVvy/image.png) -In a state diagram, the states are usually represented as circles or +In a state diagram, the states are usually represented as circles or rounded rectangles and the arrows represent the transitions. As transitions are the response of the automaton to events, an arrow may also be seen as an event “that does something”. @@ -1849,21 +1726,17 @@ with `*/`. There are states for plain text and for text inside a comment, plus two states for tentative entry into or exit from a comment. The automaton is intended to parse the comments interactively, from characters that the user types on the keyboard. Therefore, the only events that the automaton reacts -on are key presses. Actually, there is only one event (“key-press”) and +on are key presses. Actually, there is only one event (“key-press”) and the state switches are determined by event’s parameter: the key. -pawn supports automatons and states directly in the language. Every func- -tion∗ may optionally have one or more states assigned to it. pawn also supports +pawn supports automatons and states directly in the language. Every function\* may optionally have one or more states assigned to it. pawn also supports multiple automatons, and each state is part of a particular automaton. The - -following script implements the preceding state diagram (in a single, anony- -mous, automaton). To differentiate plain text from comments, both are output +following script implements the preceding state diagram (in a single, anonymous, automaton). To differentiate plain text from comments, both are output in a different colour. Listing: comment.p ```c - /* parse C comments interactively, using events and a state machine */ main() @@ -1871,31 +1744,31 @@ main() @keypressed(key) { - state (key == ’/’) slash - if (key != ’/’) + state (key == '/') slash + if (key != '/') echo key } @keypressed(key) { - state (key != ’/’) plain - state (key == ’/*’) comment - echo ’/’ /* print ’/’ held back from previous state */ - if (key != ’/’) + state (key != '/') plain + state (key == '*') comment + echo '/' /* print '/' held back from previous state */ + if (key != '/') echo key } @keypressed(key) { echo key - state (key == ’/*’) star + state (key == '*') star } @keypressed(key) { echo key - state (key != ’/*’) comment - state (key == ’/’) plain + state (key != '*') comment + state (key == '/') plain } echo(key) @@ -1909,17 +1782,16 @@ printchar(ch, colour) setattr .foreground = colour printf "%c", ch } - ``` --- -###### ∗ With the exception of “native functions” and user-defined operators. +###### \* With the exception of “native functions” and user-defined operators. --- -Function main sets the starting state to main and exits; all logic -is eventdriven. when a key arrives in state plain, the program checks for +Function main sets the starting state to main and exits; all logic +is event-driven. when a key arrives in state plain, the program checks for a slash and conditionally prints the received key. The interaction between the states plain and slash demonstrates a complexity that is typical for automatons: you must decide how to respond to an event when it arrives, without being @@ -1950,16 +1822,14 @@ That said, an automaton must be prepared to handle all events in any state. Typically, the automaton has neither control over which events arrive nor over when they arrive, so not handling an event in some state could lead to wrong decisions. It frequently happens, then, that a some events are meaningful only -in a few specific states and that they should trigger an error or “reset” pro- -cedure in all other cases. The function for handling the event in such “error” +in a few specific states and that they should trigger an error or “reset” procedure in all other cases. The function for handling the event in such “error” condition might then hold a lot of state names, if you were to mention them explicitly. There is a shorter way: by not mentioning any name between the -angle brackets, the function matches all states that have not explicit imple- -mentation elsewhere. So, for example, you could use the signature `echo(key) <>` +angle brackets, the function matches all states that have not explicit implementation elsewhere. So, for example, you could use the signature `echo(key) <>` for either of the two implementations (but not for both). A single anonymous automaton is pre-defined. If a program contains more -than one automaton, the others must be explicitly mentioned, both in +than one automaton, the others must be explicitly mentioned, both in the state classifier of the function and in the state instruction. To do so, add the name of the automaton in front of the state name and separate the names of the automaton and the state with a colon. That is, “parser:slash” stands @@ -1970,16 +1840,14 @@ of different automatons. ### • Entry functions and automata theory -State machines, and the foundation of “automata theory”, originate from me- -chanical design and pneumatic/electric switching circuits (using relays rather +State machines, and the foundation of “automata theory”, originate from mechanical design and pneumatic/electric switching circuits (using relays rather than transistors). Typical examples are coin acceptors, traffic light control -and communication switching circuits. In these applications, robustness and pre- -dictability are paramount, and it was found that these goals were best achieved +and communication switching circuits. In these applications, robustness and predictability are paramount, and it was found that these goals were best achieved when actions (output) were tied to the states rather than to the events (input). --- -###### \* A function that has the same implementation for all states, does not need a state classifierat all —see printchar. +###### \* A function that has the same implementation for all states, does not need a state classifier at all —see printchar. --- @@ -1995,15 +1863,15 @@ do not carry out other operations. In a pedestrian crossing lights system, the lights for the vehicles and the pedestrians must be synchronized. Obviously, the combination of a green light for the traffic and a “walk” sign for the pedestrians is recipe for disaster. We -can also immediately dismiss the combination of yellow /walk as too -dangerous. Thus, four combinations remain to be handled. The figure below is a +can also immediately dismiss the combination of yellow /walk as too +dangerous. Thus, four combinations remain to be handled. The figure below is a state diagram for the pedestrian crossing lights. The entire process is activated with a button, and operates on a timer. ![pcl](https://i.ibb.co/9wNR3ry/image.png) When the state red/walk times out, the state cannot immediately go back to -green/wait, because the pedestrians that are busy crossing the road at +green/wait, because the pedestrians that are busy crossing the road at that moment need some time to clear the road —the state red/wait allows for this. @@ -2014,8 +1882,8 @@ functionality that when a pedestrian pushes the button while the light for the traffic is already red, the time that the pedestrian has for crossing is lengthened. If the state is red/wait and the button is pressed, it switches back to red/walk. The englobing box around the states red/walk and red/wait for handling the -button event is just a notational convenience: I could also have -drawn two arrows from either state back to red/walk. The script source code +button event is just a notational convenience: I could also have +drawn two arrows from either state back to red/walk. The script source code (which follows below) reflects this same notational convenience, though. In the implementation in the pawn language, the event functions now always @@ -2025,8 +1893,8 @@ must be handled in the script; hence, the “fall-back” event functions that d The output, in this example program only messages printed on the console, is all done in the special functions entry. The function entry may -be seen as a main for a state: it is implicitly called when the -state that it is attached to is entered. Note that the entry function is also called +be seen as a main for a state: it is implicitly called when the +state that it is attached to is entered. Note that the entry function is also called when “switching” to the state that the automaton is already in: when the state is red_walk an invocation of the @keypressed sets the state to red_walk (which it is already in) and causes the entry function of red_walk to run —this is a @@ -2035,7 +1903,6 @@ re-entry of the state. Listing: traffic.p ```c - /* traffic light synchronizer, using states in an event-driven model */ #include