From 96efeda4005223eee18019d8d4d7d34b40b00926 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 13 Oct 2021 20:33:43 +0700 Subject: [PATCH 001/145] Revert "The development is moved to GitLab" This reverts commit 5b38fb3ed750a096bf50bd309eb49ec092b54cc4. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 585e89dd..aad4900c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Porth -**WARNING! THE DEVELOPMENT IS MOVED TO GITLAB: https://gitlab.com/tsoding/porth** +**WARNING! THIS LANGUAGE IS A WORK IN PROGRESS! ANYTHING CAN CHANGE AT ANY MOMENT WITHOUT ANY NOTICE! USE THIS LANGUAGE AT YOUR OWN RISK!** It's like [Forth](https://en.wikipedia.org/wiki/Forth_(programming_language)) but written in [Python](https://www.python.org/). But I don't actually know for sure since I never programmed in Forth, I only heard that it's some sort of stack-based programming language. Porth is also stack-based programming language. Which makes it just like Forth am I rite? From 8fdbb051c3ac9970b6250e985b08a94bff8b2ef2 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 13 Oct 2021 22:18:24 +0700 Subject: [PATCH 002/145] Update Language Reference --- README.md | 223 ++++++++++++------------------------------------------ 1 file changed, 49 insertions(+), 174 deletions(-) diff --git a/README.md b/README.md index aad4900c..8003e7d3 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ By default the compiler searches files to include in `./` and `./std/`. You can This is what the language supports so far. **Since the language is a work in progress everything in this section is the subject to change.** -### Data Types +### Literals #### Integer @@ -188,184 +188,52 @@ Example: This program pushes integer `69` onto the stack (since the ASCII code of letter `E` is `69`) and prints it with the `print` operation. -### Built-in Words +### Intrinsics (Built-in Words) #### Stack Manipulation -- `dup` - duplicate an element on top of the stack. -``` -a = pop() -push(a) -push(a) -``` -- `swap` - swap 2 elements on the top of the stack. -``` -a = pop() -b = pop() -push(a) -push(b) -``` -- `drop` - drops the top element of the stack. -``` -pop() -``` -- `print` - print the element on top of the stack in a free form to stdout and remove it from the stack. -``` -a = pop() -print(a) -``` -- `over` -``` -a = pop() -b = pop() -push(b) -push(a) -push(b) -``` -- `rot` - rotate the top three stack elements. -``` -a = pop() -b = pop() -c = pop() -push(b) -push(a) -push(c) -``` +- `dup (a -- a a)` - duplicate an element on top of the stack. +- `swap (a b -- b a)` - swap 2 elements on the top of the stack. +- `drop (a b -- a)` - drops the top element of the stack. +- `print (a b -- a)` - print the element on top of the stack in a free form to stdout and remove it from the stack. +- `over (a b -- a b a)` - copy the element below the top of the stack +- `rot (a b c -- b c a)` - rotate the top three stack elements. #### Comparison -- `=` - checks if two elements on top of the stack are equal. Removes the elements from the stack and pushes `1` if they are equal and `0` if they are not. -``` -a = pop() -b = pop() -push(int(a == b)) -``` -- `!=` - checks if two elements on top of the stack are not equal. -``` -a = pop() -b = pop() -push(int(a != b)) -``` -- `>` - checks if the element below the top greater than the top. -``` -b = pop() -a = pop() -push(int(a > b)) -``` -- `<` - checks if the element below the top less than the top. -``` -b = pop() -a = pop() -push(int(a < b)) -``` -- `>=` -``` -b = pop() -a = pop() -push(int(a >= b)) -``` -- `<=` -``` -b = pop() -a = pop() -push(int(a >= b)) -``` +- `= ([a: int] [b: int] -- [a == b: bool])` - checks if two elements on top of the stack are equal. +- `!= ([a: int] [b: int] -- [a != b: bool])` - checks if two elements on top of the stack are not equal. +- `> ([a: int] [b: int] -- [a > b: bool])` - applies the greater comparison on top two elements. +- `< ([a: int] [b: int] -- [a < b: bool])` - applies the less comparison on top two elements. +- `>= ([a: int] [b: int] -- [a >= b: bool])` - applies the greater or equal comparison on top two elements +- `<= ([a: int] [b: int] -- [a <= b: bool])` - applies the greater or equal comparison on top two elements. #### Arithmetic -- `+` - sums up two elements on the top of the stack. -``` -a = pop() -b = pop() -push(a + b) -``` -- `-` - subtracts the top of the stack from the element below. -``` -a = pop() -b = pop() -push(b - a) -``` -- `*` - multiples the top of the stack with the element below the top of the stack -``` -a = pop() -b = pop() -push(b * a) -``` -- `divmod` -``` -a = pop() -b = pop() -push(b // a) -push(b % a) -``` +- `+ ([a: int] [b: int] -- [a + b: int])` - sums up two elements on the top of the stack. +- `- ([a: int] [b: int] -- [a - b: int])` - subtracts two elements on the top of the stack +- `* ([a: int] [b: int] -- [a * b: int])` - multiples two elements on top of the stack +- `divmod ([a: int] [b: int] -- [a / b: int] [a % b: int])` - perform [Euclidean division](https://en.wikipedia.org/wiki/Euclidean_division) between two elements on top of the stack. #### Bitwise -- `shr` -``` -a = pop() -b = pop() -push(b >> a) -``` -- `shl` -``` -a = pop() -b = pop() -push(b << a) -``` -- `or` -``` -a = pop() -b = pop() -push(b | a) -``` -- `and` -``` -a = pop() -b = pop() -push(b & a) -``` -- `not` -``` -a = pop() -push(~a) -``` - -#### Control Flow - -- `if do else end` - pops the element on top of the stack and if the element is not `0` executes the ``, otherwise ``. -- `while do end` - keeps executing both `` and `` until `` produces `0` at the top of the stack. Checking the result of the `` removes it from the stack. +- `shr ([a: int] [b: int] -- [a >> b: int])` - right **unsigned** bit shift. +- `shl ([a: int] [b: int] -- [a << b: int])` - light bit shift. +- `or ([a: int] [b: int] -- [a | b: int])` - bit `or`. +- `and ([a: int] [b: int] -- [a & b: int])` - bit `and`. +- `not ([a: int] -- [~a: int])` - bit `not`. #### Memory -- `mem` - pushes the address of the beginning of the memory where you can read and write onto the stack. -``` -push(mem_addr) -``` -- `.` - store a given byte at the address on the stack. -``` -byte = pop() -addr = pop() -store(addr, byte) -``` -- `,` - load a byte from the address on the stack. -``` -addr = pop() -byte = load(addr) -push(byte) -``` -- `.64` - store an 8-byte word at the address on the stack. -``` -word = pop() -addr = pop() -store(addr, word) -``` -- `,64` - load an 8-byte word from the address on the stack. -``` -word = pop() -byte = load(word) -push(byte) -``` +- `mem (-- [mem: ptr])` - pushes the address of the beginning of the memory where you can read and write onto the stack. +- `. ([place: ptr] [byte: int] --)` - store a given byte at the address on the stack. +- `, ([place: ptr] -- [byte: int])` - load a byte from the address on the stack. +- `! ([byte: int] [place: ptr] -- )` - store a given byte at the address on the stack. Same as `.` but the arguments swapped. +- `@ ([place: ptr] -- [byte: int])` - load a byte from the address on the stack. Synonym to `,`. +- `.64 ([place: ptr] [byte: int] --)` - store an 8-byte word at the address on the stack. +- `,64 ([place: ptr] -- [byte: int])` - load an 8-byte word from the address on the stack. +- `!64 ([place: ptr] [byte: int] --)` - store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. +- `@64 ([place: ptr] -- [byte: int])` - load an 8-byte word from the address on the stack. Synonym to `,64`. #### System @@ -379,6 +247,23 @@ for i in range(n): ``` +#### Misc + +- `here (-- [len: int] [str: ptr])` - pushes a string `"::"` where `` is the path to the file where `here` is located, `` is the row on which `here` is located and `` is the column from which `here` starts. It is useful for reporting developer errors: + +```pascal +include "std.porth" + +here puts ": TODO: not implemented\n" puts 1 exit +``` +- `argc (-- [argc: int])` +- `argv (-- [argv: ptr])` + +### Control Flow + +- `if do else end` - pops the element on top of the stack and if the element is not `0` executes the ``, otherwise ``. +- `while do end` - keeps executing both `` and `` until `` produces `0` at the top of the stack. Checking the result of the `` removes it from the stack. + ### Macros Define a new word `write` that expands into a sequence of tokens `stdout SYS_write syscall3` during the compilation. @@ -396,13 +281,3 @@ Include tokens of file `file.porth` ``` include "file.porth" ``` - -### Misc - -- `here` - pushes a string `"::"` where `` is the path to the file where `here` is located, `` is the row on which `here` is located and `` is the column from which `here` starts. It is useful for reporting developer errors: - -```pascal -include "std.porth" - -here puts ": TODO: not implemented\n" puts 1 exit -``` From f1073f18df979e3e2533f4dc9529dbdfe3414cc9 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 13 Oct 2021 22:21:56 +0700 Subject: [PATCH 003/145] Try out intrinsics documentation in form of a table --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8003e7d3..771d21ff 100644 --- a/README.md +++ b/README.md @@ -192,12 +192,14 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter #### Stack Manipulation -- `dup (a -- a a)` - duplicate an element on top of the stack. -- `swap (a b -- b a)` - swap 2 elements on the top of the stack. -- `drop (a b -- a)` - drops the top element of the stack. -- `print (a b -- a)` - print the element on top of the stack in a free form to stdout and remove it from the stack. -- `over (a b -- a b a)` - copy the element below the top of the stack -- `rot (a b c -- b c a)` - rotate the top three stack elements. +| Name | Signature | Description | +| --- | --- | --- | +| `dup` | `(a -- a a)` | duplicate an element on top of the stack. | +| `swap` | `(a b -- b a)` | swap 2 elements on the top of the stack. | +| `drop` | `(a b -- a)` | drops the top element of the stack. | +| `print` | `(a b -- a)` | print the element on top of the stack in a free form to stdout and remove it from the stack. | +| `over` | `(a b -- a b a)` | copy the element below the top of the stack | +| `rot` | `(a b c -- b c a)` | rotate the top three stack elements. | #### Comparison From a8cda8ef822a0e056de6c2df74d1b204148b6da9 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 13 Oct 2021 22:23:18 +0700 Subject: [PATCH 004/145] Drop parens in the intrinsics signatures --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 771d21ff..0513ab6e 100644 --- a/README.md +++ b/README.md @@ -192,14 +192,14 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter #### Stack Manipulation -| Name | Signature | Description | -| --- | --- | --- | -| `dup` | `(a -- a a)` | duplicate an element on top of the stack. | -| `swap` | `(a b -- b a)` | swap 2 elements on the top of the stack. | -| `drop` | `(a b -- a)` | drops the top element of the stack. | -| `print` | `(a b -- a)` | print the element on top of the stack in a free form to stdout and remove it from the stack. | -| `over` | `(a b -- a b a)` | copy the element below the top of the stack | -| `rot` | `(a b c -- b c a)` | rotate the top three stack elements. | +| Name | Signature | Description | +| --- | --- | --- | +| `dup` | `a -- a a` | duplicate an element on top of the stack. | +| `swap` | `a b -- b a` | swap 2 elements on the top of the stack. | +| `drop` | `a b -- a` | drops the top element of the stack. | +| `print` | `a b -- a` | print the element on top of the stack in a free form to stdout and remove it from the stack. | +| `over` | `a b -- a b a` | copy the element below the top of the stack | +| `rot` | `a b c -- b c a` | rotate the top three stack elements. | #### Comparison From 12e9a584a342bb4d52cd0ca0480c19c8ced3dff2 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 13 Oct 2021 22:56:34 +0700 Subject: [PATCH 005/145] Check how bitwise section is rendered --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0513ab6e..2719cdb5 100644 --- a/README.md +++ b/README.md @@ -219,11 +219,13 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter #### Bitwise -- `shr ([a: int] [b: int] -- [a >> b: int])` - right **unsigned** bit shift. -- `shl ([a: int] [b: int] -- [a << b: int])` - light bit shift. -- `or ([a: int] [b: int] -- [a | b: int])` - bit `or`. -- `and ([a: int] [b: int] -- [a & b: int])` - bit `and`. -- `not ([a: int] -- [~a: int])` - bit `not`. +| Name | Signature | Description | +| --- | --- | --- | +| `shr` | `([a: int] [b: int] -- [a >> b: int])` | right **unsigned** bit shift. | +| `shl` | `([a: int] [b: int] -- [a << b: int])` | light bit shift. | +| `or` | `([a: int] [b: int] -- [a | b: int])` | bit `or`. | +| `and` | `([a: int] [b: int] -- [a & b: int])` | bit `and`. | +| `not` | `([a: int] -- [~a: int])` | bit `not`. | #### Memory From 1bde7e924d7a8568e62fd8687765a79b12a839c7 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 13 Oct 2021 22:57:22 +0700 Subject: [PATCH 006/145] Try to fix the rendering of the Bitwise section --- README.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2719cdb5..2adcd1c4 100644 --- a/README.md +++ b/README.md @@ -203,19 +203,23 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter #### Comparison -- `= ([a: int] [b: int] -- [a == b: bool])` - checks if two elements on top of the stack are equal. -- `!= ([a: int] [b: int] -- [a != b: bool])` - checks if two elements on top of the stack are not equal. -- `> ([a: int] [b: int] -- [a > b: bool])` - applies the greater comparison on top two elements. -- `< ([a: int] [b: int] -- [a < b: bool])` - applies the less comparison on top two elements. -- `>= ([a: int] [b: int] -- [a >= b: bool])` - applies the greater or equal comparison on top two elements -- `<= ([a: int] [b: int] -- [a <= b: bool])` - applies the greater or equal comparison on top two elements. +| Name | Signature | Description | +| --- | --- | --- | +| `= ` | `[a: int] [b: int] -- [a == b : bool]` | checks if two elements on top of the stack are equal. | +| `!=` | `[a: int] [b: int] -- [a != b : bool]` | checks if two elements on top of the stack are not equal. | +| `> ` | `[a: int] [b: int] -- [a > b : bool]` | applies the greater comparison on top two elements. | +| `< ` | `[a: int] [b: int] -- [a < b : bool]` | applies the less comparison on top two elements. | +| `>=` | `[a: int] [b: int] -- [a >= b : bool]` | applies the greater or equal comparison on top two elements | +| `<=` | `[a: int] [b: int] -- [a <= b : bool]` | applies the greater or equal comparison on top two elements. | #### Arithmetic -- `+ ([a: int] [b: int] -- [a + b: int])` - sums up two elements on the top of the stack. -- `- ([a: int] [b: int] -- [a - b: int])` - subtracts two elements on the top of the stack -- `* ([a: int] [b: int] -- [a * b: int])` - multiples two elements on top of the stack -- `divmod ([a: int] [b: int] -- [a / b: int] [a % b: int])` - perform [Euclidean division](https://en.wikipedia.org/wiki/Euclidean_division) between two elements on top of the stack. +| Name | Signature | Description | +| --- | --- | --- | +| `+` | `([a: int] [b: int] -- [a + b: int])` | sums up two elements on the top of the stack. | +| `-` | `([a: int] [b: int] -- [a - b: int])` | subtracts two elements on the top of the stack | +| `*` | `([a: int] [b: int] -- [a * b: int])` | multiples two elements on top of the stack | +| `divmod` | `([a: int] [b: int] -- [a / b: int] [a % b: int])` | perform [Euclidean division](https://en.wikipedia.org/wiki/Euclidean_division) between two elements on top of the stack. | #### Bitwise @@ -223,7 +227,7 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter | --- | --- | --- | | `shr` | `([a: int] [b: int] -- [a >> b: int])` | right **unsigned** bit shift. | | `shl` | `([a: int] [b: int] -- [a << b: int])` | light bit shift. | -| `or` | `([a: int] [b: int] -- [a | b: int])` | bit `or`. | +| `or` | `([a: int] [b: int] -- [a \| b: int])` | bit `or`. | | `and` | `([a: int] [b: int] -- [a & b: int])` | bit `and`. | | `not` | `([a: int] -- [~a: int])` | bit `not`. | From e4be390e3fb3da4389ec2227a2449dcd8f73b593 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 13 Oct 2021 23:00:31 +0700 Subject: [PATCH 007/145] Use table for the Memory section --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2adcd1c4..9d26c64c 100644 --- a/README.md +++ b/README.md @@ -227,21 +227,23 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter | --- | --- | --- | | `shr` | `([a: int] [b: int] -- [a >> b: int])` | right **unsigned** bit shift. | | `shl` | `([a: int] [b: int] -- [a << b: int])` | light bit shift. | -| `or` | `([a: int] [b: int] -- [a \| b: int])` | bit `or`. | +| `or` | `([a: int] [b: int] -- [a \| b: int])` | bit `or`. | | `and` | `([a: int] [b: int] -- [a & b: int])` | bit `and`. | | `not` | `([a: int] -- [~a: int])` | bit `not`. | #### Memory -- `mem (-- [mem: ptr])` - pushes the address of the beginning of the memory where you can read and write onto the stack. -- `. ([place: ptr] [byte: int] --)` - store a given byte at the address on the stack. -- `, ([place: ptr] -- [byte: int])` - load a byte from the address on the stack. -- `! ([byte: int] [place: ptr] -- )` - store a given byte at the address on the stack. Same as `.` but the arguments swapped. -- `@ ([place: ptr] -- [byte: int])` - load a byte from the address on the stack. Synonym to `,`. -- `.64 ([place: ptr] [byte: int] --)` - store an 8-byte word at the address on the stack. -- `,64 ([place: ptr] -- [byte: int])` - load an 8-byte word from the address on the stack. -- `!64 ([place: ptr] [byte: int] --)` - store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. -- `@64 ([place: ptr] -- [byte: int])` - load an 8-byte word from the address on the stack. Synonym to `,64`. +| Name | Signature | Description | +| --- | --- | --- | +| `mem` | `(-- [mem: ptr])` | pushes the address of the beginning of the memory where you can read and write onto the stack. | +| `.` | `([place: ptr] [byte: int] --)` | store a given byte at the address on the stack. | +| `,` | `([place: ptr] -- [byte: int])` | load a byte from the address on the stack. | +| `!` | `([byte: int] [place: ptr] -- )` | store a given byte at the address on the stack. Same as `.` but the arguments swapped. | +| `@` | `([place: ptr] -- [byte: int])` | load a byte from the address on the stack. Synonym to `,`. | +| `.64` | `([place: ptr] [byte: int] --)` | store an 8-byte word at the address on the stack. | +| `,64` | `([place: ptr] -- [byte: int])` | load an 8-byte word from the address on the stack. | +| `!64` | `([place: ptr] [byte: int] --)` | store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. | +| `@64` | `([place: ptr] -- [byte: int])` | load an 8-byte word from the address on the stack. Synonym to `,64`. | #### System From 595f21342506902a8fe467e43c87d67d08cd2e59 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 13 Oct 2021 23:02:24 +0700 Subject: [PATCH 008/145] Remove parenthesis from the signatures --- README.md | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 9d26c64c..1292986e 100644 --- a/README.md +++ b/README.md @@ -203,47 +203,47 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter #### Comparison -| Name | Signature | Description | -| --- | --- | --- | -| `= ` | `[a: int] [b: int] -- [a == b : bool]` | checks if two elements on top of the stack are equal. | +| Name | Signature | Description | +| --- | --- | --- | +| `= ` | `[a: int] [b: int] -- [a == b : bool]` | checks if two elements on top of the stack are equal. | | `!=` | `[a: int] [b: int] -- [a != b : bool]` | checks if two elements on top of the stack are not equal. | -| `> ` | `[a: int] [b: int] -- [a > b : bool]` | applies the greater comparison on top two elements. | -| `< ` | `[a: int] [b: int] -- [a < b : bool]` | applies the less comparison on top two elements. | +| `> ` | `[a: int] [b: int] -- [a > b : bool]` | applies the greater comparison on top two elements. | +| `< ` | `[a: int] [b: int] -- [a < b : bool]` | applies the less comparison on top two elements. | | `>=` | `[a: int] [b: int] -- [a >= b : bool]` | applies the greater or equal comparison on top two elements | | `<=` | `[a: int] [b: int] -- [a <= b : bool]` | applies the greater or equal comparison on top two elements. | #### Arithmetic -| Name | Signature | Description | -| --- | --- | --- | -| `+` | `([a: int] [b: int] -- [a + b: int])` | sums up two elements on the top of the stack. | -| `-` | `([a: int] [b: int] -- [a - b: int])` | subtracts two elements on the top of the stack | -| `*` | `([a: int] [b: int] -- [a * b: int])` | multiples two elements on top of the stack | -| `divmod` | `([a: int] [b: int] -- [a / b: int] [a % b: int])` | perform [Euclidean division](https://en.wikipedia.org/wiki/Euclidean_division) between two elements on top of the stack. | +| Name | Signature | Description | +| --- | --- | --- | +| `+` | `[a: int] [b: int] -- [a + b: int]` | sums up two elements on the top of the stack. | +| `-` | `[a: int] [b: int] -- [a - b: int]` | subtracts two elements on the top of the stack | +| `*` | `[a: int] [b: int] -- [a * b: int]` | multiples two elements on top of the stack | +| `divmod` | `[a: int] [b: int] -- [a / b: int] [a % b: int]` | perform [Euclidean division](https://en.wikipedia.org/wiki/Euclidean_division) between two elements on top of the stack. | #### Bitwise -| Name | Signature | Description | -| --- | --- | --- | -| `shr` | `([a: int] [b: int] -- [a >> b: int])` | right **unsigned** bit shift. | -| `shl` | `([a: int] [b: int] -- [a << b: int])` | light bit shift. | -| `or` | `([a: int] [b: int] -- [a \| b: int])` | bit `or`. | -| `and` | `([a: int] [b: int] -- [a & b: int])` | bit `and`. | -| `not` | `([a: int] -- [~a: int])` | bit `not`. | +| Name | Signature | Description | +| --- | --- | --- | +| `shr` | `[a: int] [b: int] -- [a >> b: int]` | right **unsigned** bit shift. | +| `shl` | `[a: int] [b: int] -- [a << b: int]` | light bit shift. | +| `or` | `[a: int] [b: int] -- [a \| b: int]` | bit `or`. | +| `and` | `[a: int] [b: int] -- [a & b: int]` | bit `and`. | +| `not` | `[a: int] -- [~a: int]` | bit `not`. | #### Memory -| Name | Signature | Description | -| --- | --- | --- | -| `mem` | `(-- [mem: ptr])` | pushes the address of the beginning of the memory where you can read and write onto the stack. | -| `.` | `([place: ptr] [byte: int] --)` | store a given byte at the address on the stack. | -| `,` | `([place: ptr] -- [byte: int])` | load a byte from the address on the stack. | -| `!` | `([byte: int] [place: ptr] -- )` | store a given byte at the address on the stack. Same as `.` but the arguments swapped. | -| `@` | `([place: ptr] -- [byte: int])` | load a byte from the address on the stack. Synonym to `,`. | -| `.64` | `([place: ptr] [byte: int] --)` | store an 8-byte word at the address on the stack. | -| `,64` | `([place: ptr] -- [byte: int])` | load an 8-byte word from the address on the stack. | -| `!64` | `([place: ptr] [byte: int] --)` | store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. | -| `@64` | `([place: ptr] -- [byte: int])` | load an 8-byte word from the address on the stack. Synonym to `,64`. | +| Name | Signature | Description | +| --- | --- | --- | +| `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | +| `.` | `[place: ptr] [byte: int] --` | store a given byte at the address on the stack. | +| `,` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. | +| `!` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. Same as `.` but the arguments swapped. | +| `@` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. Synonym to `,`. | +| `.64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. | +| `,64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. | +| `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. | +| `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. Synonym to `,64`. | #### System From 4d623378c28d4f647cf09b37e0e6f43ccff512f4 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 14 Oct 2021 00:39:28 +0700 Subject: [PATCH 009/145] Remove the unreachable code and fix the unreachable asserts --- porth.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/porth.py b/porth.py index 5195cbbd..a63c0bb9 100755 --- a/porth.py +++ b/porth.py @@ -1006,14 +1006,6 @@ def type_check_program(program: Program): compiler_note(op.token.loc, 'Expected types: %s' % expected_types) compiler_note(op.token.loc, 'Actual types: %s' % actual_types) exit(1) - elif block_type == OpType.ELIF: - expected_types = list(map(lambda x: x[0], block_snapshot)) - actual_types = list(map(lambda x: x[0], stack)) - if expected_types != actual_types: - compiler_error_with_expansion_stack(op.token, 'all branches of the if-block must produce the same types of the arguments on the data stack') - compiler_note(op.token.loc, 'Expected types: %s' % expected_types) - compiler_note(op.token.loc, 'Actual types: %s' % actual_types) - exit(1) elif block_type == OpType.DO: begin_snapshot, begin_type = block_stack.pop() @@ -1032,6 +1024,17 @@ def type_check_program(program: Program): expected_types = list(map(lambda x: x[0], begin_snapshot)) actual_types = list(map(lambda x: x[0], stack)) + if expected_types != actual_types: + compiler_error_with_expansion_stack(op.token, 'else-less if block is not allowed to alter the types of the arguments on the data stack') + compiler_note(op.token.loc, 'Expected types: %s' % expected_types) + compiler_note(op.token.loc, 'Actual types: %s' % actual_types) + exit(1) + + stack = block_snapshot + elif begin_type == OpType.ELIF: + expected_types = list(map(lambda x: x[0], begin_snapshot)) + actual_types = list(map(lambda x: x[0], stack)) + if expected_types != actual_types: compiler_error_with_expansion_stack(op.token, 'else-less if block is not allowed to alter the types of the arguments on the data stack') compiler_note(op.token.loc, 'Expected types: %s' % expected_types) @@ -1040,9 +1043,9 @@ def type_check_program(program: Program): stack = block_snapshot else: - assert "unreachable" + assert False, "unreachable" else: - assert "unreachable" + assert False, "unreachable" elif op.typ == OpType.ELSE: do_snapshot, do_type = block_stack.pop() assert do_type == OpType.DO From 234f336c5e4cce17a3c75b49a011cdc9e91216dd Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 14 Oct 2021 02:42:24 +0700 Subject: [PATCH 010/145] Rename test case else-less-if -> if-else-less --- tests/.gitignore | 2 +- tests/else-less-if-fail.txt | 11 ----------- ...lse-less-if-fail.porth => if-else-less-fail.porth} | 0 tests/if-else-less-fail.txt | 11 +++++++++++ tests/{else-less-if.porth => if-else-less.porth} | 0 tests/{else-less-if.txt => if-else-less.txt} | 0 6 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 tests/else-less-if-fail.txt rename tests/{else-less-if-fail.porth => if-else-less-fail.porth} (100%) create mode 100644 tests/if-else-less-fail.txt rename tests/{else-less-if.porth => if-else-less.porth} (100%) rename tests/{else-less-if.txt => if-else-less.txt} (100%) diff --git a/tests/.gitignore b/tests/.gitignore index 6b321c22..a64dab01 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -8,7 +8,7 @@ macros dead-recursive-macro argv if-else -else-less-if +if-else-less while here memory-forth-style diff --git a/tests/else-less-if-fail.txt b/tests/else-less-if-fail.txt deleted file mode 100644 index 6f2fde4e..00000000 --- a/tests/else-less-if-fail.txt +++ /dev/null @@ -1,11 +0,0 @@ -:i argc 0 -:b stdin 0 - -:i returncode 1 -:b stdout 0 - -:b stderr 274 -./tests/else-less-if-fail.porth:1:23: ERROR: else-less if block is not allowed to alter the types of the arguments on the data stack -./tests/else-less-if-fail.porth:1:23: NOTE: Expected types: [] -./tests/else-less-if-fail.porth:1:23: NOTE: Actual types: [] - diff --git a/tests/else-less-if-fail.porth b/tests/if-else-less-fail.porth similarity index 100% rename from tests/else-less-if-fail.porth rename to tests/if-else-less-fail.porth diff --git a/tests/if-else-less-fail.txt b/tests/if-else-less-fail.txt new file mode 100644 index 00000000..75a3846f --- /dev/null +++ b/tests/if-else-less-fail.txt @@ -0,0 +1,11 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 274 +./tests/if-else-less-fail.porth:1:23: ERROR: else-less if block is not allowed to alter the types of the arguments on the data stack +./tests/if-else-less-fail.porth:1:23: NOTE: Expected types: [] +./tests/if-else-less-fail.porth:1:23: NOTE: Actual types: [] + diff --git a/tests/else-less-if.porth b/tests/if-else-less.porth similarity index 100% rename from tests/else-less-if.porth rename to tests/if-else-less.porth diff --git a/tests/else-less-if.txt b/tests/if-else-less.txt similarity index 100% rename from tests/else-less-if.txt rename to tests/if-else-less.txt From 7559ca195112e48245327107b79c9664ddef3326 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 14 Oct 2021 08:32:20 +0700 Subject: [PATCH 011/145] Implement a better Type Checking strategy The strategy is based on check all of the possible execution paths --- README.md | 6 + porth.py | 445 ++++++++++++++++-------------------- tests/if-else-fail.txt | 6 +- tests/if-else-less-fail.txt | 6 +- tests/while-fail.txt | 6 +- 5 files changed, 210 insertions(+), 259 deletions(-) diff --git a/README.md b/README.md index 1292986e..79773c98 100644 --- a/README.md +++ b/README.md @@ -291,3 +291,9 @@ Include tokens of file `file.porth` ``` include "file.porth" ``` + +### Type Checking + +TBD + + diff --git a/porth.py b/porth.py index a63c0bb9..a1c905a4 100755 --- a/porth.py +++ b/porth.py @@ -541,305 +541,313 @@ def not_enough_arguments(op: Op): DataStack=List[Tuple[DataType, Token]] -# TODO: `if 1 10 < do 69 32 elif 2 10 < do 420 end` does not properly type check +@dataclass +class Context: + stack: DataStack + ip: OpAddr + def type_check_program(program: Program): - stack: DataStack = [] - block_stack: List[Tuple[DataStack, OpType]] = [] - for ip in range(len(program)): - op = program[ip] + visited_dos: Dict[OpAddr, DataStack] = {} + contexts: List[Context] = [Context(stack=[], ip=0)] + while len(contexts) > 0: + ctx = contexts[-1]; + op = program[ctx.ip] assert len(OpType) == 10, "Exhaustive ops handling in type_check_program()" if op.typ == OpType.PUSH_INT: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) + ctx.ip += 1 elif op.typ == OpType.PUSH_STR: - stack.append((DataType.INT, op.token)) - stack.append((DataType.PTR, op.token)) + ctx.stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.PTR, op.token)) + ctx.ip += 1 elif op.typ == OpType.PUSH_CSTR: - stack.append((DataType.PTR, op.token)) + ctx.stack.append((DataType.PTR, op.token)) + ctx.ip += 1 elif op.typ == OpType.INTRINSIC: assert len(Intrinsic) == 41, "Exhaustive intrinsic handling in type_check_program()" assert isinstance(op.operand, Intrinsic), "This could be a bug in compilation step" if op.operand == Intrinsic.PLUS: assert len(DataType) == 3, "Exhaustive type handling in PLUS intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == DataType.INT and b_type == DataType.INT: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) elif a_type == DataType.INT and b_type == DataType.PTR: - stack.append((DataType.PTR, op.token)) + ctx.stack.append((DataType.PTR, op.token)) elif a_type == DataType.PTR and b_type == DataType.INT: - stack.append((DataType.PTR, op.token)) + ctx.stack.append((DataType.PTR, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument types for PLUS intrinsic. Expected INT or PTR") exit(1) elif op.operand == Intrinsic.MINUS: assert len(DataType) == 3, "Exhaustive type handling in MINUS intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and (a_type == DataType.INT or a_type == DataType.PTR): - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) elif b_type == DataType.PTR and a_type == DataType.INT: - stack.append((DataType.PTR, op.token)) + ctx.stack.append((DataType.PTR, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument types fo MINUS intrinsic: %s" % [b_type, a_type]) exit(1) elif op.operand == Intrinsic.MUL: assert len(DataType) == 3, "Exhaustive type handling in MUL intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument types fo MUL intrinsic. Expected INT.") exit(1) elif op.operand == Intrinsic.DIVMOD: assert len(DataType) == 3, "Exhaustive type handling in DIVMOD intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.INT, op.token)) - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument types fo DIVMOD intrinsic. Expected INT.") exit(1) elif op.operand == Intrinsic.EQ: assert len(DataType) == 3, "Exhaustive type handling in EQ intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.BOOL, op.token)) + ctx.stack.append((DataType.BOOL, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument types fo EQ intrinsic. Expected INT.") exit(1) elif op.operand == Intrinsic.GT: assert len(DataType) == 3, "Exhaustive type handling in GT intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.BOOL, op.token)) + ctx.stack.append((DataType.BOOL, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for GT intrinsic") exit(1) elif op.operand == Intrinsic.LT: assert len(DataType) == 3, "Exhaustive type handling in LT intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.BOOL, op.token)) + ctx.stack.append((DataType.BOOL, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for LT intrinsic") exit(1) elif op.operand == Intrinsic.GE: assert len(DataType) == 3, "Exhaustive type handling in GE intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.BOOL, op.token)) + ctx.stack.append((DataType.BOOL, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for GE intrinsic") exit(1) elif op.operand == Intrinsic.LE: assert len(DataType) == 3, "Exhaustive type handling in LE intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.BOOL, op.token)) + ctx.stack.append((DataType.BOOL, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for LE intrinsic") exit(1) elif op.operand == Intrinsic.NE: assert len(DataType) == 3, "Exhaustive type handling in NE intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.BOOL, op.token)) + ctx.stack.append((DataType.BOOL, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for NE intrinsic") exit(1) elif op.operand == Intrinsic.SHR: assert len(DataType) == 3, "Exhaustive type handling in SHR intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for SHR intrinsic") exit(1) elif op.operand == Intrinsic.SHL: assert len(DataType) == 3, "Exhaustive type handling in SHL intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for SHL intrinsic") exit(1) elif op.operand == Intrinsic.OR: assert len(DataType) == 3, "Exhaustive type handling in OR intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) elif a_type == b_type and a_type == DataType.BOOL: - stack.append((DataType.BOOL, op.token)) + ctx.stack.append((DataType.BOOL, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for OR intrinsic") exit(1) elif op.operand == Intrinsic.AND: assert len(DataType) == 3, "Exhaustive type handling in AND intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == b_type and a_type == DataType.INT: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) elif a_type == b_type and a_type == DataType.BOOL: - stack.append((DataType.BOOL, op.token)) + ctx.stack.append((DataType.BOOL, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for AND intrinsic") exit(1) elif op.operand == Intrinsic.NOT: assert len(DataType) == 3, "Exhaustive type handling in NOT intrinsic" - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() if a_type == DataType.INT: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) elif a_type == DataType.BOOL: - stack.append((DataType.BOOL, op.token)) + ctx.stack.append((DataType.BOOL, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for NOT intrinsic") exit(1) elif op.operand == Intrinsic.PRINT: - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - stack.pop() + ctx.stack.pop() elif op.operand == Intrinsic.DUP: - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - a = stack.pop() - stack.append(a) - stack.append(a) + a = ctx.stack.pop() + ctx.stack.append(a) + ctx.stack.append(a) elif op.operand == Intrinsic.SWAP: - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a = stack.pop() - b = stack.pop() - stack.append(a) - stack.append(b) + a = ctx.stack.pop() + b = ctx.stack.pop() + ctx.stack.append(a) + ctx.stack.append(b) elif op.operand == Intrinsic.DROP: - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - stack.pop() + ctx.stack.pop() elif op.operand == Intrinsic.OVER: - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a = stack.pop() - b = stack.pop() - stack.append(b) - stack.append(a) - stack.append(b) + a = ctx.stack.pop() + b = ctx.stack.pop() + ctx.stack.append(b) + ctx.stack.append(a) + ctx.stack.append(b) elif op.operand == Intrinsic.ROT: - if len(stack) < 3: + if len(ctx.stack) < 3: not_enough_arguments(op) exit(1) - a = stack.pop() - b = stack.pop() - c = stack.pop() - stack.append(b) - stack.append(a) - stack.append(c) + a = ctx.stack.pop() + b = ctx.stack.pop() + c = ctx.stack.pop() + ctx.stack.append(b) + ctx.stack.append(a) + ctx.stack.append(c) elif op.operand == Intrinsic.MEM: - stack.append((DataType.PTR, op.token)) + ctx.stack.append((DataType.PTR, op.token)) elif op.operand == Intrinsic.LOAD: assert len(DataType) == 3, "Exhaustive type handling in LOAD intrinsic" - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() if a_type == DataType.PTR: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD intrinsic: %s" % a_type) exit(1) elif op.operand == Intrinsic.STORE: assert len(DataType) == 3, "Exhaustive type handling in STORE intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == DataType.INT and b_type == DataType.PTR: pass @@ -848,24 +856,24 @@ def type_check_program(program: Program): exit(1) elif op.operand == Intrinsic.FORTH_LOAD: assert len(DataType) == 3, "Exhaustive type handling in LOAD intrinsic" - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() if a_type == DataType.PTR: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD intrinsic: %s" % a_type) exit(1) elif op.operand == Intrinsic.FORTH_STORE: assert len(DataType) == 3, "Exhaustive type handling in STORE intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if a_type == DataType.PTR and b_type == DataType.INT: pass @@ -874,24 +882,24 @@ def type_check_program(program: Program): exit(1) elif op.operand == Intrinsic.LOAD64: assert len(DataType) == 3, "Exhaustive type handling in LOAD64 intrinsic" - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() if a_type == DataType.PTR: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD64 intrinsic") exit(1) elif op.operand == Intrinsic.STORE64: assert len(DataType) == 3, "Exhaustive type handling in STORE64 intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if (a_type == DataType.INT or a_type == DataType.PTR) and b_type == DataType.PTR: pass @@ -900,24 +908,24 @@ def type_check_program(program: Program): exit(1) elif op.operand == Intrinsic.FORTH_LOAD64: assert len(DataType) == 3, "Exhaustive type handling in LOAD64 intrinsic" - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() if a_type == DataType.PTR: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) else: compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD64 intrinsic") exit(1) elif op.operand == Intrinsic.FORTH_STORE64: assert len(DataType) == 3, "Exhaustive type handling in STORE64 intrinsic" - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) - a_type, a_loc = stack.pop() - b_type, b_loc = stack.pop() + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() if (b_type == DataType.INT or b_type == DataType.PTR) and a_type == DataType.PTR: pass @@ -925,177 +933,121 @@ def type_check_program(program: Program): compiler_error_with_expansion_stack(op.token, "invalid argument type for STORE64 intrinsic: %s" % [b_type, a_type]) exit(1) elif op.operand == Intrinsic.CAST_PTR: - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - a_type, a_token = stack.pop() + a_type, a_token = ctx.stack.pop() - stack.append((DataType.PTR, a_token)) + ctx.stack.append((DataType.PTR, a_token)) elif op.operand == Intrinsic.ARGC: - stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.INT, op.token)) elif op.operand == Intrinsic.ARGV: - stack.append((DataType.PTR, op.token)) + ctx.stack.append((DataType.PTR, op.token)) elif op.operand == Intrinsic.HERE: - stack.append((DataType.INT, op.token)) - stack.append((DataType.PTR, op.token)) + ctx.stack.append((DataType.INT, op.token)) + ctx.stack.append((DataType.PTR, op.token)) # TODO: figure out how to type check syscall arguments and return types elif op.operand == Intrinsic.SYSCALL0: - if len(stack) < 1: + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) for i in range(1): - stack.pop() - stack.append((DataType.INT, op.token)) + ctx.stack.pop() + ctx.stack.append((DataType.INT, op.token)) elif op.operand == Intrinsic.SYSCALL1: - if len(stack) < 2: + if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) for i in range(2): - stack.pop() - stack.append((DataType.INT, op.token)) + ctx.stack.pop() + ctx.stack.append((DataType.INT, op.token)) elif op.operand == Intrinsic.SYSCALL2: - if len(stack) < 3: + if len(ctx.stack) < 3: not_enough_arguments(op) exit(1) for i in range(3): - stack.pop() - stack.append((DataType.INT, op.token)) + ctx.stack.pop() + ctx.stack.append((DataType.INT, op.token)) elif op.operand == Intrinsic.SYSCALL3: - if len(stack) < 4: + if len(ctx.stack) < 4: not_enough_arguments(op) exit(1) for i in range(4): - stack.pop() - stack.append((DataType.INT, op.token)) + ctx.stack.pop() + ctx.stack.append((DataType.INT, op.token)) elif op.operand == Intrinsic.SYSCALL4: - if len(stack) < 5: + if len(ctx.stack) < 5: not_enough_arguments(op) exit(1) for i in range(5): - stack.pop() - stack.append((DataType.INT, op.token)) + ctx.stack.pop() + ctx.stack.append((DataType.INT, op.token)) elif op.operand == Intrinsic.SYSCALL5: - if len(stack) < 6: + if len(ctx.stack) < 6: not_enough_arguments(op) exit(1) for i in range(6): - stack.pop() - stack.append((DataType.INT, op.token)) + ctx.stack.pop() + ctx.stack.append((DataType.INT, op.token)) elif op.operand == Intrinsic.SYSCALL6: - if len(stack) < 7: + if len(ctx.stack) < 7: not_enough_arguments(op) exit(1) for i in range(7): - stack.pop() - stack.append((DataType.INT, op.token)) + ctx.stack.pop() + ctx.stack.append((DataType.INT, op.token)) else: assert False, "unreachable" + ctx.ip += 1 elif op.typ == OpType.IF: - block_stack.append((copy(stack), op.typ)) + ctx.ip += 1 elif op.typ == OpType.WHILE: - block_stack.append((copy(stack), op.typ)) + ctx.ip += 1 elif op.typ == OpType.END: - block_snapshot, block_type = block_stack.pop() - assert len(OpType) == 10, "Exhaustive handling of op types" - if block_type == OpType.ELSE: - expected_types = list(map(lambda x: x[0], block_snapshot)) - actual_types = list(map(lambda x: x[0], stack)) - if expected_types != actual_types: - compiler_error_with_expansion_stack(op.token, 'all branches of the if-block must produce the same types of the arguments on the data stack') - compiler_note(op.token.loc, 'Expected types: %s' % expected_types) - compiler_note(op.token.loc, 'Actual types: %s' % actual_types) - exit(1) - elif block_type == OpType.DO: - begin_snapshot, begin_type = block_stack.pop() - - if begin_type == OpType.WHILE: - expected_types = list(map(lambda x: x[0], begin_snapshot)) - actual_types = list(map(lambda x: x[0], stack)) - - if expected_types != actual_types: - compiler_error_with_expansion_stack(op.token, 'while-do body is not allowed to alter the types of the arguments on the data stack') - compiler_note(op.token.loc, 'Expected types: %s' % expected_types) - compiler_note(op.token.loc, 'Actual types: %s' % actual_types) - exit(1) - - stack = block_snapshot - elif begin_type == OpType.IF: - expected_types = list(map(lambda x: x[0], begin_snapshot)) - actual_types = list(map(lambda x: x[0], stack)) - - if expected_types != actual_types: - compiler_error_with_expansion_stack(op.token, 'else-less if block is not allowed to alter the types of the arguments on the data stack') - compiler_note(op.token.loc, 'Expected types: %s' % expected_types) - compiler_note(op.token.loc, 'Actual types: %s' % actual_types) - exit(1) - - stack = block_snapshot - elif begin_type == OpType.ELIF: - expected_types = list(map(lambda x: x[0], begin_snapshot)) - actual_types = list(map(lambda x: x[0], stack)) - - if expected_types != actual_types: - compiler_error_with_expansion_stack(op.token, 'else-less if block is not allowed to alter the types of the arguments on the data stack') - compiler_note(op.token.loc, 'Expected types: %s' % expected_types) - compiler_note(op.token.loc, 'Actual types: %s' % actual_types) - exit(1) - - stack = block_snapshot - else: - assert False, "unreachable" - else: - assert False, "unreachable" + assert isinstance(op.operand, OpAddr) + ctx.ip = op.operand elif op.typ == OpType.ELSE: - do_snapshot, do_type = block_stack.pop() - assert do_type == OpType.DO - - pre_do_snapshot, pre_do_type = block_stack.pop() - assert pre_do_type == OpType.IF or pre_do_type == OpType.ELIF, pre_do_type - - if pre_do_type == OpType.ELIF: - expected_types = list(map(lambda x: x[0], pre_do_snapshot)) - actual_types = list(map(lambda x: x[0], stack)) - if expected_types != actual_types: - compiler_error_with_expansion_stack(op.token, 'all branches of the if-block must produce the same types of the arguments on the data stack') - compiler_note(op.token.loc, 'Expected types: %s' % expected_types) - compiler_note(op.token.loc, 'Actual types: %s' % actual_types) - exit(1) - - block_stack.append((copy(stack), op.typ)) - stack = do_snapshot + assert isinstance(op.operand, OpAddr) + ctx.ip = op.operand elif op.typ == OpType.ELIF: - do_snapshot, do_type = block_stack.pop() - assert do_type == OpType.DO - - pre_do_snapshot, pre_do_type = block_stack.pop() - assert pre_do_type == OpType.IF or pre_do_type == OpType.ELIF, pre_do_type - - if pre_do_type == OpType.ELIF: - expected_types = list(map(lambda x: x[0], pre_do_snapshot)) - actual_types = list(map(lambda x: x[0], stack)) - if expected_types != actual_types: - compiler_error_with_expansion_stack(op.token, 'all branches of the if-block must produce the same types of the arguments on the data stack') - compiler_note(op.token.loc, 'Expected types: %s' % expected_types) - compiler_note(op.token.loc, 'Actual types: %s' % actual_types) - exit(1) - - block_stack.append((copy(stack), op.typ)) - stack = do_snapshot + assert isinstance(op.operand, OpAddr) + ctx.ip = op.operand elif op.typ == OpType.DO: - if len(stack) < 1: + assert isinstance(op.operand, OpAddr) + if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) - a_type, a_token = stack.pop() + a_type, a_token = ctx.stack.pop() if a_type != DataType.BOOL: compiler_error_with_expansion_stack(op.token, "Invalid argument for the while-do condition. Expected BOOL.") exit(1) - block_stack.append((copy(stack), op.typ)) + if ctx.ip in visited_dos: + expected_types = list(map(lambda x: x[0], visited_dos[ctx.ip])) + actual_types = list(map(lambda x: x[0], ctx.stack)) + if expected_types != actual_types: + compiler_error_with_expansion_stack(op.token, 'Loops are not allowed to alter types and amount of elements on the stack.') + compiler_note(op.token.loc, 'Expected elements: %s' % expected_types) + compiler_note(op.token.loc, 'Actual elements: %s' % actual_types) + exit(1) + contexts.pop() + if len(contexts) > 0: + ctx = contexts[-1] + else: + continue + else: + visited_dos[ctx.ip] = copy(ctx.stack) + ctx.ip += 1 + contexts.append(Context(stack=copy(ctx.stack), ip=op.operand)) + ctx = contexts[-1] else: assert False, "unreachable" - if len(stack) != 0: - compiler_error_with_expansion_stack(stack[-1][1], "unhandled data on the stack: %s" % list(map(lambda x: x[0], stack))) - exit(1) + + if ctx.ip >= len(program): + if len(ctx.stack) != 0: + compiler_error_with_expansion_stack(ctx.stack[-1][1], "unhandled data on the ctx.stack: %s" % list(map(lambda x: x[0], ctx.stack))) + exit(1) + contexts.pop() def generate_nasm_linux_x86_64(program: Program, out_file_path: str): strs: List[bytes] = [] @@ -1740,7 +1692,6 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp else: assert False, 'unreachable' - if len(stack) > 0: compiler_error_with_expansion_stack(program[stack.pop()].token, 'unclosed block') exit(1) diff --git a/tests/if-else-fail.txt b/tests/if-else-fail.txt index 4647f4ee..1c144e38 100644 --- a/tests/if-else-fail.txt +++ b/tests/if-else-fail.txt @@ -4,8 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 263 -./tests/if-else-fail.porth:1:38: ERROR: all branches of the if-block must produce the same types of the arguments on the data stack -./tests/if-else-fail.porth:1:38: NOTE: Expected types: [] -./tests/if-else-fail.porth:1:38: NOTE: Actual types: [] +:b stderr 93 +./tests/if-else-fail.porth:1:34: ERROR: unhandled data on the ctx.stack: [] diff --git a/tests/if-else-less-fail.txt b/tests/if-else-less-fail.txt index 75a3846f..9caa9cd7 100644 --- a/tests/if-else-less-fail.txt +++ b/tests/if-else-less-fail.txt @@ -4,8 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 274 -./tests/if-else-less-fail.porth:1:23: ERROR: else-less if block is not allowed to alter the types of the arguments on the data stack -./tests/if-else-less-fail.porth:1:23: NOTE: Expected types: [] -./tests/if-else-less-fail.porth:1:23: NOTE: Actual types: [] +:b stderr 92 +./tests/if-else-less-fail.porth:1:27: ERROR: not enough arguments for the `print` intrinsic diff --git a/tests/while-fail.txt b/tests/while-fail.txt index 2266b5ea..4b26ba50 100644 --- a/tests/while-fail.txt +++ b/tests/while-fail.txt @@ -4,8 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 281 -./tests/while-fail.porth:4:1: ERROR: while-do body is not allowed to alter the types of the arguments on the data stack -./tests/while-fail.porth:4:1: NOTE: Expected types: [] -./tests/while-fail.porth:4:1: NOTE: Actual types: [, ] +:b stderr 90 +./tests/while-fail.porth:1:1: ERROR: unhandled data on the ctx.stack: [] From f46a35cf1c24f83c383b6444481282dbf1ce5e4a Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 14 Oct 2021 08:50:01 +0700 Subject: [PATCH 012/145] Add solution for 8th Project Euler Problem --- euler/.gitignore | 3 ++- euler/problem08.porth | 33 +++++++++++++++++++++++++++++++++ euler/problem08.txt | 9 +++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 euler/problem08.porth create mode 100644 euler/problem08.txt diff --git a/euler/.gitignore b/euler/.gitignore index 9735f282..58ef1aaa 100644 --- a/euler/.gitignore +++ b/euler/.gitignore @@ -4,4 +4,5 @@ problem03 problem04 problem05 problem06 -problem07 \ No newline at end of file +problem07 +problem08 \ No newline at end of file diff --git a/euler/problem08.porth b/euler/problem08.porth new file mode 100644 index 00000000..be78a07a --- /dev/null +++ b/euler/problem08.porth @@ -0,0 +1,33 @@ +include "std.porth" + +macro N 13 end + +macro str mem end +macro len str 8 + end +macro acc len 8 + end +macro ans acc 8 + end + +"7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450" + +str !64 +len !64 + +0 ans !64 + +0 while dup len @64 N - 1 + < do + 1 acc !64 + + 0 while dup N < do + 2dup + str @64 cast(ptr) + @ '0' - + acc @64 * acc !64 + 1 + + end drop + + if acc @64 ans @64 > do + acc @64 ans !64 + end + + 1 + +end drop + +ans @64 print diff --git a/euler/problem08.txt b/euler/problem08.txt new file mode 100644 index 00000000..42b0fc54 --- /dev/null +++ b/euler/problem08.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 12 +23514624000 + +:b stderr 0 + From e7df1ef4e604f5b31ea415ef3639b9a1d36e00b0 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 14 Oct 2021 15:08:08 +0700 Subject: [PATCH 013/145] Add solution for 9th Project Euler Problem --- euler/.gitignore | 3 ++- euler/problem09.porth | 24 ++++++++++++++++++++++++ euler/problem09.txt | 9 +++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 euler/problem09.porth create mode 100644 euler/problem09.txt diff --git a/euler/.gitignore b/euler/.gitignore index 58ef1aaa..db06909d 100644 --- a/euler/.gitignore +++ b/euler/.gitignore @@ -5,4 +5,5 @@ problem04 problem05 problem06 problem07 -problem08 \ No newline at end of file +problem08 +problem09 \ No newline at end of file diff --git a/euler/problem09.porth b/euler/problem09.porth new file mode 100644 index 00000000..c76109ea --- /dev/null +++ b/euler/problem09.porth @@ -0,0 +1,24 @@ +include "std.porth" + +macro a mem end +macro b a 8 + end +macro c b 8 + end + +1 while dup 1000 < do + dup a !64 + 1 while dup a @64 + 1000 < do + dup b !64 + 1000 a @64 - b @64 - c !64 + + if a @64 dup * b @64 dup * + c @64 dup * = do + a @64 b @64 * c @64 * print + 0 exit + end + + 1 + + end drop + 1 + +end drop + +here eputs ": unreachable\n" eputs +1 exit diff --git a/euler/problem09.txt b/euler/problem09.txt new file mode 100644 index 00000000..05b8a609 --- /dev/null +++ b/euler/problem09.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 9 +31875000 + +:b stderr 0 + From 8f61b6aeb9073e8a7be74915a2064a0229feecb8 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 14 Oct 2021 16:41:48 +0700 Subject: [PATCH 014/145] Report "no pre-do" error properly --- porth.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/porth.py b/porth.py index a1c905a4..c5f7b2c8 100755 --- a/porth.py +++ b/porth.py @@ -1626,8 +1626,13 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp ip += 1 elif token.value == Keyword.DO: program.append(Op(typ=OpType.DO, token=token)) + if len(stack) == 0: + compiler_error_with_expansion_stack(token, "`do` is not preceded by `if`, `while` or `elif`") + exit(1) pre_do_ip = stack.pop() - assert program[pre_do_ip].typ == OpType.WHILE or program[pre_do_ip].typ == OpType.IF or program[pre_do_ip].typ == OpType.ELIF + if program[pre_do_ip].typ != OpType.WHILE and program[pre_do_ip].typ != OpType.IF and program[pre_do_ip].typ != OpType.ELIF: + compiler_error_with_expansion_stack(token, "`do` is not preceded by `if`, `while` or `elif`") + exit(1) program[ip].operand = pre_do_ip stack.append(ip) ip += 1 From 30597e0f33c96c522d6742766ad6a20fe9242f40 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 15 Oct 2021 09:33:39 +0700 Subject: [PATCH 015/145] Deprecate old style 64 bit memory access intrinsics --- euler/problem02.porth | 4 +- euler/problem04.porth | 6 +-- euler/problem05.porth | 18 ++++---- euler/problem07.porth | 12 ++--- examples/cat.porth | 8 ++-- examples/cat.txt | 10 ++--- examples/gol.porth | 16 +++---- examples/reverse-linked-list.porth | 22 +++++----- examples/seq.porth | 6 +-- porth.py | 70 +++--------------------------- std/std.porth | 10 +++-- tests/memory.porth | 4 +- 12 files changed, 68 insertions(+), 118 deletions(-) diff --git a/euler/problem02.porth b/euler/problem02.porth index 3f501a17..8ab15ff4 100644 --- a/euler/problem02.porth +++ b/euler/problem02.porth @@ -4,9 +4,9 @@ macro acc mem end 1 2 while over 4000000 < do if over 2 mod 0 = do - over acc ,64 + acc swap .64 + over acc @64 + acc !64 end swap over + end 2drop -acc ,64 print +acc @64 print diff --git a/euler/problem04.porth b/euler/problem04.porth index 30ebcc90..60684363 100644 --- a/euler/problem04.porth +++ b/euler/problem04.porth @@ -17,8 +17,8 @@ macro ans mem end // a b if 2dup = do - if dup ans ,64 > do - ans over .64 + if dup ans @64 > do + ans over swap !64 end end @@ -29,4 +29,4 @@ macro ans mem end 1 + end drop -ans ,64 print +ans @64 print diff --git a/euler/problem05.porth b/euler/problem05.porth index 72839c86..6e2540f9 100644 --- a/euler/problem05.porth +++ b/euler/problem05.porth @@ -11,7 +11,7 @@ macro ans tmp 8 N * + end // clean up the tmp table 0 while dup N < do - tmp over 8 * + 0 .64 + tmp over 8 * + 0 swap !64 1 + end drop @@ -26,13 +26,13 @@ macro ans tmp 8 N * + end end 2drop 0 while dup N < do - if dup 8 * acc + ,64 - over 8 * tmp + ,64 + if dup 8 * acc + @64 + over 8 * tmp + @64 < do dup 8 * acc + - over 8 * tmp + ,64 - .64 + over 8 * tmp + @64 + swap !64 end 1 + @@ -41,15 +41,15 @@ macro ans tmp 8 N * + end 1 + end drop -ans 1 .64 +1 ans !64 0 while dup N < do - acc over 8 * + ,64 while dup 0 > do - over ans ,64 * ans swap .64 + acc over 8 * + @64 while dup 0 > do + over ans @64 * ans !64 1 - end drop 1 + end drop -ans ,64 print +ans @64 print diff --git a/euler/problem07.porth b/euler/problem07.porth index ebbd77e2..b4e7a960 100644 --- a/euler/problem07.porth +++ b/euler/problem07.porth @@ -8,28 +8,28 @@ macro primes primes-count 8 + end macro is-prime 0 while - if 2dup 8 * primes + ,64 dup * >= do - 2dup 8 * primes + ,64 mod 0 != + if 2dup 8 * primes + @64 dup * >= do + 2dup 8 * primes + @64 mod 0 != else false end do 1 + end - 8 * primes + ,64 dup * < + 8 * primes + @64 dup * < end macro add-prime - primes primes-count ,64 8 * + swap .64 + primes primes-count @64 8 * + !64 primes-count inc64 end 2 add-prime -3 while primes-count ,64 N < do +3 while primes-count @64 N < do if dup is-prime do dup add-prime end 1 + end drop -primes N 1 - 8 * + ,64 print +primes N 1 - 8 * + @64 print diff --git a/examples/cat.porth b/examples/cat.porth index f089d802..a8d73b33 100644 --- a/examples/cat.porth +++ b/examples/cat.porth @@ -7,13 +7,13 @@ macro fd mem end macro buffer fd 8 + end macro cat_fd - while BUFFER_CAP buffer fd ,64 read dup 0 > do + while BUFFER_CAP buffer fd @64 read dup 0 > do buffer puts end drop end if argc 2 < do - fd stdin .64 + stdin fd !64 cat_fd else 1 while dup argc < do @@ -25,9 +25,9 @@ else "\n" eputs drop else - fd swap .64 + fd !64 cat_fd - fd ,64 close drop + fd @64 close drop end 1 + diff --git a/examples/cat.txt b/examples/cat.txt index b773db37..f7577b5f 100644 --- a/examples/cat.txt +++ b/examples/cat.txt @@ -8,7 +8,7 @@ foo :b stdin 0 :i returncode 0 -:b stdout 590 +:b stdout 585 include "std.porth" macro BUFFER_CAP 1024 end @@ -18,13 +18,13 @@ macro fd mem end macro buffer fd 8 + end macro cat_fd - while BUFFER_CAP buffer fd ,64 read dup 0 > do + while BUFFER_CAP buffer fd @64 read dup 0 > do buffer puts end drop end if argc 2 < do - fd stdin .64 + stdin fd !64 cat_fd else 1 while dup argc < do @@ -36,9 +36,9 @@ else "\n" eputs drop else - fd swap .64 + fd !64 cat_fd - fd ,64 close drop + fd @64 close drop end 1 + diff --git a/examples/gol.porth b/examples/gol.porth index badc6c54..631ee52e 100644 --- a/examples/gol.porth +++ b/examples/gol.porth @@ -33,15 +33,15 @@ macro putd end macro board_current - board_base board_current_index ,64 BOARD_SIZE * + + board_base board_current_index @64 BOARD_SIZE * + end macro board_next - board_base 1 board_current_index ,64 - BOARD_SIZE * + + board_base 1 board_current_index @64 - BOARD_SIZE * + end macro swap_boards - board_current_index 1 board_current_index ,64 - .64 + 1 board_current_index @64 - board_current_index !64 end macro display_row @@ -75,9 +75,9 @@ macro get_current_cell end macro set_next_cell - value swap .64 + value !64 swap COLS * + board_next + - value ,64 + value @64 . end @@ -89,7 +89,7 @@ macro in_bounds end macro count_current_nbors - nbors 0 .64 + 0 nbors !64 if 2dup 1 - swap 1 - swap 2dup in_bounds rot rot swap COLS * + board_current + , 1 = @@ -124,7 +124,7 @@ macro count_current_nbors and do nbors inc64 end 2drop - nbors ,64 + nbors @64 end macro compute_next_board @@ -164,7 +164,7 @@ macro put_glider end macro main - delta_time 8 + 100000000 .64 + 100000000 delta_time 8 + !64 board_current put_glider diff --git a/examples/reverse-linked-list.porth b/examples/reverse-linked-list.porth index e21dcf57..570548c5 100644 --- a/examples/reverse-linked-list.porth +++ b/examples/reverse-linked-list.porth @@ -1,7 +1,9 @@ include "./std.porth" -macro ,node/value ,64 end -macro ,node/prev 8 + ,64 end +// TODO: get rid of .64 macro in examples/reverse-linked-list.porth + +macro ,node/value @64 end +macro ,node/prev 8 + @64 end macro .node/value .64 end macro .node/prev swap 8 + swap .64 end macro sizeof(node) 16 end @@ -13,18 +15,18 @@ macro nodes_count list_b 8 + end macro nodes nodes_count 8 + end macro alloc_node - nodes_count ,64 sizeof(node) * nodes + - nodes_count dup ,64 1 + .64 + nodes_count @64 sizeof(node) * nodes + + nodes_count dup @64 1 + .64 end macro push_node - over alloc_node 2dup swap ,64 .node/prev .64 - swap ,64 cast(ptr) swap .node/value + over alloc_node 2dup swap @64 .node/prev .64 + swap @64 cast(ptr) swap .node/value end macro pop_node - dup ,64 cast(ptr) ,node/value swap - dup ,64 cast(ptr) ,node/prev .64 + dup @64 cast(ptr) ,node/value swap + dup @64 cast(ptr) ,node/prev .64 end // initialize list_a @@ -34,11 +36,11 @@ end end drop // reverse list_a into list_b -while list_a ,64 0 != do +while list_a @64 0 != do list_a pop_node list_b swap push_node end // print list_b -while list_b ,64 0 != do +while list_b @64 0 != do list_b pop_node print end diff --git a/examples/seq.porth b/examples/seq.porth index ba953111..e63188cc 100644 --- a/examples/seq.porth +++ b/examples/seq.porth @@ -17,15 +17,15 @@ while dup , 0 != do 1 exit end - limit ,64 10 * + limit @64 10 * over , '0' - + - limit swap .64 + limit !64 1 + end drop -0 while dup limit ,64 < do +0 while dup limit @64 < do dup print 1 + end drop diff --git a/porth.py b/porth.py index c5f7b2c8..e14b5304 100755 --- a/porth.py +++ b/porth.py @@ -63,8 +63,6 @@ class Intrinsic(Enum): FORTH_STORE=auto() LOAD64=auto() STORE64=auto() - FORTH_LOAD64=auto() - FORTH_STORE64=auto() CAST_PTR=auto() ARGC=auto() ARGV=auto() @@ -215,7 +213,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): else: ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 41, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" + assert len(Intrinsic) == 39, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" if op.operand == Intrinsic.PLUS: a = stack.pop() b = stack.pop() @@ -356,21 +354,6 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): stack.append(int.from_bytes(_bytes, byteorder="little")) ip += 1 elif op.operand == Intrinsic.STORE64: - store_value = stack.pop() - store_value64 = store_value.to_bytes(length=8, byteorder="little", signed=(store_value < 0)); - store_addr64 = stack.pop(); - for byte in store_value64: - mem[store_addr64] = byte; - store_addr64 += 1; - ip += 1 - elif op.operand == Intrinsic.FORTH_LOAD64: - addr = stack.pop() - _bytes = bytearray(8) - for offset in range(0,8): - _bytes[offset] = mem[addr + offset] - stack.append(int.from_bytes(_bytes, byteorder="little")) - ip += 1 - elif op.operand == Intrinsic.FORTH_STORE64: store_addr64 = stack.pop(); store_value = stack.pop() store_value64 = store_value.to_bytes(length=8, byteorder="little", signed=(store_value < 0)); @@ -564,7 +547,7 @@ def type_check_program(program: Program): ctx.stack.append((DataType.PTR, op.token)) ctx.ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 41, "Exhaustive intrinsic handling in type_check_program()" + assert len(Intrinsic) == 39, "Exhaustive intrinsic handling in type_check_program()" assert isinstance(op.operand, Intrinsic), "This could be a bug in compilation step" if op.operand == Intrinsic.PLUS: assert len(DataType) == 3, "Exhaustive type handling in PLUS intrinsic" @@ -901,32 +884,6 @@ def type_check_program(program: Program): a_type, a_loc = ctx.stack.pop() b_type, b_loc = ctx.stack.pop() - if (a_type == DataType.INT or a_type == DataType.PTR) and b_type == DataType.PTR: - pass - else: - compiler_error_with_expansion_stack(op.token, "invalid argument type for STORE64 intrinsic: %s" % [b_type, a_type]) - exit(1) - elif op.operand == Intrinsic.FORTH_LOAD64: - assert len(DataType) == 3, "Exhaustive type handling in LOAD64 intrinsic" - if len(ctx.stack) < 1: - not_enough_arguments(op) - exit(1) - a_type, a_loc = ctx.stack.pop() - - if a_type == DataType.PTR: - ctx.stack.append((DataType.INT, op.token)) - else: - compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD64 intrinsic") - exit(1) - elif op.operand == Intrinsic.FORTH_STORE64: - assert len(DataType) == 3, "Exhaustive type handling in STORE64 intrinsic" - if len(ctx.stack) < 2: - not_enough_arguments(op) - exit(1) - - a_type, a_loc = ctx.stack.pop() - b_type, b_loc = ctx.stack.pop() - if (b_type == DataType.INT or b_type == DataType.PTR) and a_type == DataType.PTR: pass else: @@ -1138,7 +1095,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): assert isinstance(op.operand, int), "This could be a bug in the parsing step" out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 41, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" + assert len(Intrinsic) == 39, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: out.write(" ;; -- plus --\n") out.write(" pop rax\n") @@ -1325,23 +1282,12 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" push str_%d\n" % len(strs)) strs.append(value) elif op.operand == Intrinsic.LOAD64: - out.write(" ;; -- load --\n") - out.write(" pop rax\n") - out.write(" xor rbx, rbx\n") - out.write(" mov rbx, [rax]\n") - out.write(" push rbx\n") - elif op.operand == Intrinsic.STORE64: - out.write(" ;; -- store --\n") - out.write(" pop rbx\n"); - out.write(" pop rax\n"); - out.write(" mov [rax], rbx\n"); - elif op.operand == Intrinsic.FORTH_LOAD64: out.write(" ;; -- forth load64 --\n") out.write(" pop rax\n") out.write(" xor rbx, rbx\n") out.write(" mov rbx, [rax]\n") out.write(" push rbx\n") - elif op.operand == Intrinsic.FORTH_STORE64: + elif op.operand == Intrinsic.STORE64: out.write(" ;; -- forth store64 --\n") out.write(" pop rax\n"); out.write(" pop rbx\n"); @@ -1432,7 +1378,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'include': Keyword.INCLUDE, } -assert len(Intrinsic) == 41, "Exhaustive INTRINSIC_BY_NAMES definition" +assert len(Intrinsic) == 39, "Exhaustive INTRINSIC_BY_NAMES definition" INTRINSIC_BY_NAMES = { '+': Intrinsic.PLUS, '-': Intrinsic.MINUS, @@ -1460,10 +1406,8 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): ',': Intrinsic.LOAD, '!': Intrinsic.FORTH_STORE, '@': Intrinsic.FORTH_LOAD, - '.64': Intrinsic.STORE64, - ',64': Intrinsic.LOAD64, - '!64': Intrinsic.FORTH_STORE64, - '@64': Intrinsic.FORTH_LOAD64, + '!64': Intrinsic.STORE64, + '@64': Intrinsic.LOAD64, 'cast(ptr)': Intrinsic.CAST_PTR, 'argc': Intrinsic.ARGC, 'argv': Intrinsic.ARGV, diff --git a/std/std.porth b/std/std.porth index 5d7a195d..8177656b 100644 --- a/std/std.porth +++ b/std/std.porth @@ -352,16 +352,20 @@ macro % divmod swap drop end macro mod % end macro div / end +// Old Style Memory Access (Deprecated) +macro .64 swap !64 end +macro ,64 @64 end + macro nth_argv - 8 * argv + ,64 cast(ptr) + 8 * argv + @64 cast(ptr) end macro inc64 - dup ,64 1 + .64 + dup @64 1 + swap !64 end macro dec64 - dup ,64 1 - .64 + dup @64 1 - swap !64 end macro cstrlen diff --git a/tests/memory.porth b/tests/memory.porth index 5a39365f..d91f85d5 100644 --- a/tests/memory.porth +++ b/tests/memory.porth @@ -18,5 +18,5 @@ mem 2 + dup , 1 + . 4 mem stdout write print // print UINT64_MAX (Largest 64 bit word) -mem 18446744073709551615 .64 -mem ,64 print \ No newline at end of file +18446744073709551615 mem !64 +mem @64 print From 1a993f18ee03b375de826ad06b4970058eb76aed Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 15 Oct 2021 09:44:15 +0700 Subject: [PATCH 016/145] Deprecate old style 8 bit memory access intrinsics --- README.md | 4 --- examples/gol.porth | 40 ++++++++++++++-------------- examples/name.porth | 2 +- examples/rot13.porth | 6 ++--- examples/rule110.porth | 18 ++++++------- examples/seq.porth | 6 ++--- porth.py | 60 ++++-------------------------------------- std/std.porth | 12 +++++---- tests/memory.porth | 14 +++++----- 9 files changed, 55 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 79773c98..ad0828a4 100644 --- a/README.md +++ b/README.md @@ -236,12 +236,8 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter | Name | Signature | Description | | --- | --- | --- | | `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | -| `.` | `[place: ptr] [byte: int] --` | store a given byte at the address on the stack. | -| `,` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. | | `!` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. Same as `.` but the arguments swapped. | | `@` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. Synonym to `,`. | -| `.64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. | -| `,64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. | | `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. | | `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. Synonym to `,64`. | diff --git a/examples/gol.porth b/examples/gol.porth index 631ee52e..c31fd180 100644 --- a/examples/gol.porth +++ b/examples/gol.porth @@ -23,7 +23,7 @@ macro putd while over 0 > do 1 - dup rot 10 divmod - rot swap '0' + . swap + rot swap '0' + swap ! swap end dup @@ -46,14 +46,14 @@ end macro display_row 0 while dup COLS < do - if 2dup + , 0 = do - display over + '.' . + if 2dup + @ 0 = do + display over + '.' swap ! else - display over + '#' . + display over + '#' swap ! end 1 + end drop - COLS display + '\n' . + COLS display + '\n' swap ! COLS 1 + display puts drop end @@ -71,14 +71,14 @@ macro display_current_board end macro get_current_cell - swap COLS * + board_current + , + swap COLS * + board_current + @ end macro set_next_cell value !64 swap COLS * + board_next + value @64 - . + swap ! end macro in_bounds @@ -92,35 +92,35 @@ macro count_current_nbors 0 nbors !64 if 2dup 1 - swap 1 - swap 2dup in_bounds - rot rot swap COLS * + board_current + , 1 = + rot rot swap COLS * + board_current + @ 1 = and do nbors inc64 end if 2dup 1 - 2dup in_bounds - rot rot swap COLS * + board_current + , 1 = + rot rot swap COLS * + board_current + @ 1 = and do nbors inc64 end if 2dup 1 - swap 1 + swap 2dup in_bounds - rot rot swap COLS * + board_current + , 1 = + rot rot swap COLS * + board_current + @ 1 = and do nbors inc64 end if 2dup swap 1 - swap 2dup in_bounds - rot rot swap COLS * + board_current + , 1 = + rot rot swap COLS * + board_current + @ 1 = and do nbors inc64 end if 2dup swap 1 + swap 2dup in_bounds - rot rot swap COLS * + board_current + , 1 = + rot rot swap COLS * + board_current + @ 1 = and do nbors inc64 end if 2dup 1 + swap 1 - swap 2dup in_bounds - rot rot swap COLS * + board_current + , 1 = + rot rot swap COLS * + board_current + @ 1 = and do nbors inc64 end if 2dup 1 + 2dup in_bounds - rot rot swap COLS * + board_current + , 1 = + rot rot swap COLS * + board_current + @ 1 = and do nbors inc64 end if 2dup 1 + swap 1 + swap 2dup in_bounds - rot rot swap COLS * + board_current + , 1 = + rot rot swap COLS * + board_current + @ 1 = and do nbors inc64 end 2drop @@ -155,11 +155,11 @@ end // ..* // *** macro put_glider - dup 0 COLS * 1 + + 1 . - dup 1 COLS * 2 + + 1 . - dup 2 COLS * 0 + + 1 . - dup 2 COLS * 1 + + 1 . - dup 2 COLS * 2 + + 1 . + dup 0 COLS * 1 + + 1 swap ! + dup 1 COLS * 2 + + 1 swap ! + dup 2 COLS * 0 + + 1 swap ! + dup 2 COLS * 1 + + 1 swap ! + dup 2 COLS * 2 + + 1 swap ! drop end diff --git a/examples/name.porth b/examples/name.porth index d7c16b26..93691daf 100644 --- a/examples/name.porth +++ b/examples/name.porth @@ -11,7 +11,7 @@ if dup 0 <= do 1 exit end -if name over + 1 - , '\n' = do +if name over + 1 - @ '\n' = do 1 - end diff --git a/examples/rot13.porth b/examples/rot13.porth index d3e1e837..843457e9 100644 --- a/examples/rot13.porth +++ b/examples/rot13.porth @@ -5,16 +5,16 @@ macro buffer mem end while BUFFER_CAP buffer stdin read dup 0 > do 0 while 2dup > do - dup buffer + , + dup buffer + @ if dup 'a' >= over 'z' <= and do 2dup 'a' - 13 + 26 mod 'a' + - swap buffer + swap . + swap buffer + ! end if dup 'A' >= over 'Z' <= and do 2dup 'A' - 13 + 26 mod 'A' + - swap buffer + swap . + swap buffer + ! end drop diff --git a/examples/rule110.porth b/examples/rule110.porth index b1e1eb5d..c3985390 100644 --- a/examples/rule110.porth +++ b/examples/rule110.porth @@ -7,30 +7,30 @@ macro N 100 end macro row mem end macro display row N + end -row N 2 - + 1 . -display N + 10 . +row N 2 - + 1 swap ! +display N + 10 swap ! 0 while dup N 2 - < do 0 while dup N < do - if dup row + , 1 = do - dup display + '*' . + if dup row + @ 1 = do + dup display + '*' swap ! else - dup display + ' ' . + dup display + ' ' swap ! end 1 + end drop N 1 + display puts - row , 1 shl - row 1 + , + row @ 1 shl + row 1 + @ or 1 while dup N 2 - < do swap 1 shl 7 and - over row + 1 + , or + over row + 1 + @ or 2dup 110 swap shr 1 and - swap row + swap . + swap row + ! swap 1 + diff --git a/examples/seq.porth b/examples/seq.porth index e63188cc..d1d22e4a 100644 --- a/examples/seq.porth +++ b/examples/seq.porth @@ -9,8 +9,8 @@ if argc 2 < do end 1 nth_argv -while dup , 0 != do - if dup , '0' < over , '9' > or do +while dup @ 0 != do + if dup @ '0' < over @ '9' > or do "ERROR: `" eputs 1 nth_argv cstrlen 1 nth_argv eputs "` is not a correct integer\n" eputs @@ -18,7 +18,7 @@ while dup , 0 != do end limit @64 10 * - over , '0' - + over @ '0' - + limit !64 diff --git a/porth.py b/porth.py index e14b5304..f5ff69d6 100755 --- a/porth.py +++ b/porth.py @@ -57,8 +57,6 @@ class Intrinsic(Enum): OVER=auto() ROT=auto() MEM=auto() - LOAD=auto() - STORE=auto() FORTH_LOAD=auto() FORTH_STORE=auto() LOAD64=auto() @@ -213,7 +211,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): else: ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 39, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" + assert len(Intrinsic) == 37, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" if op.operand == Intrinsic.PLUS: a = stack.pop() b = stack.pop() @@ -326,16 +324,6 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): elif op.operand == Intrinsic.MEM: stack.append(mem_buf_ptr) ip += 1 - elif op.operand == Intrinsic.LOAD: - addr = stack.pop() - byte = mem[addr] - stack.append(byte) - ip += 1 - elif op.operand == Intrinsic.STORE: - store_value = stack.pop() - store_addr = stack.pop() - mem[store_addr] = store_value & 0xFF - ip += 1 elif op.operand == Intrinsic.FORTH_LOAD: addr = stack.pop() byte = mem[addr] @@ -547,7 +535,7 @@ def type_check_program(program: Program): ctx.stack.append((DataType.PTR, op.token)) ctx.ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 39, "Exhaustive intrinsic handling in type_check_program()" + assert len(Intrinsic) == 37, "Exhaustive intrinsic handling in type_check_program()" assert isinstance(op.operand, Intrinsic), "This could be a bug in compilation step" if op.operand == Intrinsic.PLUS: assert len(DataType) == 3, "Exhaustive type handling in PLUS intrinsic" @@ -811,32 +799,6 @@ def type_check_program(program: Program): ctx.stack.append(c) elif op.operand == Intrinsic.MEM: ctx.stack.append((DataType.PTR, op.token)) - elif op.operand == Intrinsic.LOAD: - assert len(DataType) == 3, "Exhaustive type handling in LOAD intrinsic" - if len(ctx.stack) < 1: - not_enough_arguments(op) - exit(1) - a_type, a_loc = ctx.stack.pop() - - if a_type == DataType.PTR: - ctx.stack.append((DataType.INT, op.token)) - else: - compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD intrinsic: %s" % a_type) - exit(1) - elif op.operand == Intrinsic.STORE: - assert len(DataType) == 3, "Exhaustive type handling in STORE intrinsic" - if len(ctx.stack) < 2: - not_enough_arguments(op) - exit(1) - - a_type, a_loc = ctx.stack.pop() - b_type, b_loc = ctx.stack.pop() - - if a_type == DataType.INT and b_type == DataType.PTR: - pass - else: - compiler_error_with_expansion_stack(op.token, "invalid argument type for STORE intrinsic") - exit(1) elif op.operand == Intrinsic.FORTH_LOAD: assert len(DataType) == 3, "Exhaustive type handling in LOAD intrinsic" if len(ctx.stack) < 1: @@ -1095,7 +1057,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): assert isinstance(op.operand, int), "This could be a bug in the parsing step" out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 39, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" + assert len(Intrinsic) == 37, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: out.write(" ;; -- plus --\n") out.write(" pop rax\n") @@ -1241,17 +1203,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): elif op.operand == Intrinsic.MEM: out.write(" ;; -- mem --\n") out.write(" push mem\n") - elif op.operand == Intrinsic.LOAD: - out.write(" ;; -- load --\n") - out.write(" pop rax\n") - out.write(" xor rbx, rbx\n") - out.write(" mov bl, [rax]\n") - out.write(" push rbx\n") - elif op.operand == Intrinsic.STORE: - out.write(" ;; -- store --\n") - out.write(" pop rbx\n"); - out.write(" pop rax\n"); - out.write(" mov [rax], bl\n"); elif op.operand == Intrinsic.FORTH_LOAD: out.write(" ;; -- forth load --\n") out.write(" pop rax\n") @@ -1378,7 +1329,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'include': Keyword.INCLUDE, } -assert len(Intrinsic) == 39, "Exhaustive INTRINSIC_BY_NAMES definition" +assert len(Intrinsic) == 37, "Exhaustive INTRINSIC_BY_NAMES definition" INTRINSIC_BY_NAMES = { '+': Intrinsic.PLUS, '-': Intrinsic.MINUS, @@ -1402,8 +1353,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'over': Intrinsic.OVER, 'rot': Intrinsic.ROT, 'mem': Intrinsic.MEM, - '.': Intrinsic.STORE, - ',': Intrinsic.LOAD, + # TODO: rename ! and @ to !8 and @8 '!': Intrinsic.FORTH_STORE, '@': Intrinsic.FORTH_LOAD, '!64': Intrinsic.STORE64, diff --git a/std/std.porth b/std/std.porth index 8177656b..0995bd3c 100644 --- a/std/std.porth +++ b/std/std.porth @@ -355,6 +355,8 @@ macro div / end // Old Style Memory Access (Deprecated) macro .64 swap !64 end macro ,64 @64 end +macro . swap ! end +macro , @ end macro nth_argv 8 * argv + @64 cast(ptr) @@ -370,22 +372,22 @@ end macro cstrlen dup - while dup , 0 != do 1 + end + while dup @ 0 != do 1 + end swap - end macro cstreq while - if over , 0 != over , 0 != and do - over , over , = + if over @ 0 != over @ 0 != and do + over @ over @ = else false end do 1 + swap 1 + end - , 0 = - swap , 0 = + @ 0 = + swap @ 0 = and end diff --git a/tests/memory.porth b/tests/memory.porth index d91f85d5..b3c3ff6a 100644 --- a/tests/memory.porth +++ b/tests/memory.porth @@ -1,18 +1,18 @@ include "std.porth" // write "abc" into the memory -mem 0 + 97 . -mem 1 + 98 . -mem 2 + 99 . -mem 3 + 10 . +97 mem 0 + ! +98 mem 1 + ! +99 mem 2 + ! +10 mem 3 + ! // print "abc" to stdout 4 mem stdout write print // increament each character by 1 making it "bcd" -mem 0 + dup , 1 + . -mem 1 + dup , 1 + . -mem 2 + dup , 1 + . +mem 0 + dup @ 1 + swap ! +mem 1 + dup @ 1 + swap ! +mem 2 + dup @ 1 + swap ! // print "bcd" to stdout 4 mem stdout write print From 2e15628ebef8f7daf8a83a5e20ff91b3ca232970 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 15 Oct 2021 09:52:45 +0700 Subject: [PATCH 017/145] Make 8 bit memory access intrinsics more consistent with 64 bit ones --- README.md | 4 ++-- euler/problem08.porth | 2 +- examples/gol.porth | 40 +++++++++++++++++----------------- examples/name.porth | 2 +- examples/rot13.porth | 6 ++--- examples/rule110.porth | 18 +++++++-------- examples/seq.porth | 6 ++--- porth.py | 4 ++-- std/std.porth | 12 +++++----- tests/memory-forth-style.porth | 23 ------------------- tests/memory-forth-style.txt | 13 ----------- tests/memory.porth | 14 ++++++------ 12 files changed, 55 insertions(+), 89 deletions(-) delete mode 100644 tests/memory-forth-style.porth delete mode 100644 tests/memory-forth-style.txt diff --git a/README.md b/README.md index ad0828a4..987d6deb 100644 --- a/README.md +++ b/README.md @@ -236,8 +236,8 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter | Name | Signature | Description | | --- | --- | --- | | `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | -| `!` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. Same as `.` but the arguments swapped. | -| `@` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. Synonym to `,`. | +| `!8` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. Same as `.` but the arguments swapped. | +| `@8` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. Synonym to `,`. | | `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. | | `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. Synonym to `,64`. | diff --git a/euler/problem08.porth b/euler/problem08.porth index be78a07a..ebba7fe4 100644 --- a/euler/problem08.porth +++ b/euler/problem08.porth @@ -18,7 +18,7 @@ len !64 1 acc !64 0 while dup N < do - 2dup + str @64 cast(ptr) + @ '0' - + 2dup + str @64 cast(ptr) + @8 '0' - acc @64 * acc !64 1 + end drop diff --git a/examples/gol.porth b/examples/gol.porth index c31fd180..ed593ab2 100644 --- a/examples/gol.porth +++ b/examples/gol.porth @@ -23,7 +23,7 @@ macro putd while over 0 > do 1 - dup rot 10 divmod - rot swap '0' + swap ! swap + rot swap '0' + swap !8 swap end dup @@ -46,14 +46,14 @@ end macro display_row 0 while dup COLS < do - if 2dup + @ 0 = do - display over + '.' swap ! + if 2dup + @8 0 = do + display over + '.' swap !8 else - display over + '#' swap ! + display over + '#' swap !8 end 1 + end drop - COLS display + '\n' swap ! + COLS display + '\n' swap !8 COLS 1 + display puts drop end @@ -71,14 +71,14 @@ macro display_current_board end macro get_current_cell - swap COLS * + board_current + @ + swap COLS * + board_current + @8 end macro set_next_cell value !64 swap COLS * + board_next + value @64 - swap ! + swap !8 end macro in_bounds @@ -92,35 +92,35 @@ macro count_current_nbors 0 nbors !64 if 2dup 1 - swap 1 - swap 2dup in_bounds - rot rot swap COLS * + board_current + @ 1 = + rot rot swap COLS * + board_current + @8 1 = and do nbors inc64 end if 2dup 1 - 2dup in_bounds - rot rot swap COLS * + board_current + @ 1 = + rot rot swap COLS * + board_current + @8 1 = and do nbors inc64 end if 2dup 1 - swap 1 + swap 2dup in_bounds - rot rot swap COLS * + board_current + @ 1 = + rot rot swap COLS * + board_current + @8 1 = and do nbors inc64 end if 2dup swap 1 - swap 2dup in_bounds - rot rot swap COLS * + board_current + @ 1 = + rot rot swap COLS * + board_current + @8 1 = and do nbors inc64 end if 2dup swap 1 + swap 2dup in_bounds - rot rot swap COLS * + board_current + @ 1 = + rot rot swap COLS * + board_current + @8 1 = and do nbors inc64 end if 2dup 1 + swap 1 - swap 2dup in_bounds - rot rot swap COLS * + board_current + @ 1 = + rot rot swap COLS * + board_current + @8 1 = and do nbors inc64 end if 2dup 1 + 2dup in_bounds - rot rot swap COLS * + board_current + @ 1 = + rot rot swap COLS * + board_current + @8 1 = and do nbors inc64 end if 2dup 1 + swap 1 + swap 2dup in_bounds - rot rot swap COLS * + board_current + @ 1 = + rot rot swap COLS * + board_current + @8 1 = and do nbors inc64 end 2drop @@ -155,11 +155,11 @@ end // ..* // *** macro put_glider - dup 0 COLS * 1 + + 1 swap ! - dup 1 COLS * 2 + + 1 swap ! - dup 2 COLS * 0 + + 1 swap ! - dup 2 COLS * 1 + + 1 swap ! - dup 2 COLS * 2 + + 1 swap ! + dup 0 COLS * 1 + + 1 swap !8 + dup 1 COLS * 2 + + 1 swap !8 + dup 2 COLS * 0 + + 1 swap !8 + dup 2 COLS * 1 + + 1 swap !8 + dup 2 COLS * 2 + + 1 swap !8 drop end diff --git a/examples/name.porth b/examples/name.porth index 93691daf..a362b990 100644 --- a/examples/name.porth +++ b/examples/name.porth @@ -11,7 +11,7 @@ if dup 0 <= do 1 exit end -if name over + 1 - @ '\n' = do +if name over + 1 - @8 '\n' = do 1 - end diff --git a/examples/rot13.porth b/examples/rot13.porth index 843457e9..842a76b6 100644 --- a/examples/rot13.porth +++ b/examples/rot13.porth @@ -5,16 +5,16 @@ macro buffer mem end while BUFFER_CAP buffer stdin read dup 0 > do 0 while 2dup > do - dup buffer + @ + dup buffer + @8 if dup 'a' >= over 'z' <= and do 2dup 'a' - 13 + 26 mod 'a' + - swap buffer + ! + swap buffer + !8 end if dup 'A' >= over 'Z' <= and do 2dup 'A' - 13 + 26 mod 'A' + - swap buffer + ! + swap buffer + !8 end drop diff --git a/examples/rule110.porth b/examples/rule110.porth index c3985390..9efd25f4 100644 --- a/examples/rule110.porth +++ b/examples/rule110.porth @@ -7,30 +7,30 @@ macro N 100 end macro row mem end macro display row N + end -row N 2 - + 1 swap ! -display N + 10 swap ! +row N 2 - + 1 swap !8 +display N + 10 swap !8 0 while dup N 2 - < do 0 while dup N < do - if dup row + @ 1 = do - dup display + '*' swap ! + if dup row + @8 1 = do + dup display + '*' swap !8 else - dup display + ' ' swap ! + dup display + ' ' swap !8 end 1 + end drop N 1 + display puts - row @ 1 shl - row 1 + @ + row @8 1 shl + row 1 + @8 or 1 while dup N 2 - < do swap 1 shl 7 and - over row + 1 + @ or + over row + 1 + @8 or 2dup 110 swap shr 1 and - swap row + ! + swap row + !8 swap 1 + diff --git a/examples/seq.porth b/examples/seq.porth index d1d22e4a..d5a54c1d 100644 --- a/examples/seq.porth +++ b/examples/seq.porth @@ -9,8 +9,8 @@ if argc 2 < do end 1 nth_argv -while dup @ 0 != do - if dup @ '0' < over @ '9' > or do +while dup @8 0 != do + if dup @8 '0' < over @8 '9' > or do "ERROR: `" eputs 1 nth_argv cstrlen 1 nth_argv eputs "` is not a correct integer\n" eputs @@ -18,7 +18,7 @@ while dup @ 0 != do end limit @64 10 * - over @ '0' - + over @8 '0' - + limit !64 diff --git a/porth.py b/porth.py index f5ff69d6..9ba91540 100755 --- a/porth.py +++ b/porth.py @@ -1354,8 +1354,8 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'rot': Intrinsic.ROT, 'mem': Intrinsic.MEM, # TODO: rename ! and @ to !8 and @8 - '!': Intrinsic.FORTH_STORE, - '@': Intrinsic.FORTH_LOAD, + '!8': Intrinsic.FORTH_STORE, + '@8': Intrinsic.FORTH_LOAD, '!64': Intrinsic.STORE64, '@64': Intrinsic.LOAD64, 'cast(ptr)': Intrinsic.CAST_PTR, diff --git a/std/std.porth b/std/std.porth index 0995bd3c..7fbf7fe4 100644 --- a/std/std.porth +++ b/std/std.porth @@ -355,6 +355,8 @@ macro div / end // Old Style Memory Access (Deprecated) macro .64 swap !64 end macro ,64 @64 end +macro ! !8 end +macro @ @8 end macro . swap ! end macro , @ end @@ -372,22 +374,22 @@ end macro cstrlen dup - while dup @ 0 != do 1 + end + while dup @8 0 != do 1 + end swap - end macro cstreq while - if over @ 0 != over @ 0 != and do - over @ over @ = + if over @8 0 != over @8 0 != and do + over @8 over @8 = else false end do 1 + swap 1 + end - @ 0 = - swap @ 0 = + @8 0 = + swap @8 0 = and end diff --git a/tests/memory-forth-style.porth b/tests/memory-forth-style.porth deleted file mode 100644 index d21bfd28..00000000 --- a/tests/memory-forth-style.porth +++ /dev/null @@ -1,23 +0,0 @@ -// Forth-style memory access -include "std.porth" - -// write "abc" into the memory -97 mem 0 + ! -98 mem 1 + ! -99 mem 2 + ! -10 mem 3 + ! - -// print "abc" to stdout -4 mem stdout write print - -// increament each character by 1 making it "bcd" -mem 0 + dup @ 1 + swap ! -mem 1 + dup @ 1 + swap ! -mem 2 + dup @ 1 + swap ! - -// print "bcd" to stdout -4 mem stdout write print - -// print UINT64_MAX (Largest 64 bit word) -18446744073709551615 mem !64 -mem @64 print diff --git a/tests/memory-forth-style.txt b/tests/memory-forth-style.txt deleted file mode 100644 index a4a9a422..00000000 --- a/tests/memory-forth-style.txt +++ /dev/null @@ -1,13 +0,0 @@ -:i argc 0 -:b stdin 0 - -:i returncode 0 -:b stdout 33 -abc -4 -bcd -4 -18446744073709551615 - -:b stderr 0 - diff --git a/tests/memory.porth b/tests/memory.porth index b3c3ff6a..dde1be0e 100644 --- a/tests/memory.porth +++ b/tests/memory.porth @@ -1,18 +1,18 @@ include "std.porth" // write "abc" into the memory -97 mem 0 + ! -98 mem 1 + ! -99 mem 2 + ! -10 mem 3 + ! +97 mem 0 + !8 +98 mem 1 + !8 +99 mem 2 + !8 +10 mem 3 + !8 // print "abc" to stdout 4 mem stdout write print // increament each character by 1 making it "bcd" -mem 0 + dup @ 1 + swap ! -mem 1 + dup @ 1 + swap ! -mem 2 + dup @ 1 + swap ! +mem 0 + dup @8 1 + swap !8 +mem 1 + dup @8 1 + swap !8 +mem 2 + dup @8 1 + swap !8 // print "bcd" to stdout 4 mem stdout write print From 5abd5dcaf82162d80cb62c3bb0106f8d9abd1f8f Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 15 Oct 2021 10:03:00 +0700 Subject: [PATCH 018/145] Tweak wording --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 987d6deb..e7d551a8 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Porth is planned to be - [x] Native - [x] Stack-based (just like Forth) - [x] [Turing-complete](./examples/rule110.porth) -- [x] Statically typed (the type checking is probably gonna be similar to the [WASM validation](https://binji.github.io/posts/webassembly-type-checking/)) +- [x] Statically typed (the type checking is similar to [WASM validation](https://binji.github.io/posts/webassembly-type-checking/)) - [ ] Self-hosted (Python is used only as an initial bootstrap, once the language is mature enough we gonna rewrite it in itself) (these are not the selling points, but rather milestones of the development) From 5a2c804bdebf2ecc61e4c7387899ed2d31447119 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 15 Oct 2021 11:13:26 +0700 Subject: [PATCH 019/145] Remove the FORTH_* prefix from 8 bit memory access intrinsics --- porth.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/porth.py b/porth.py index 9ba91540..6ba8e454 100755 --- a/porth.py +++ b/porth.py @@ -57,8 +57,8 @@ class Intrinsic(Enum): OVER=auto() ROT=auto() MEM=auto() - FORTH_LOAD=auto() - FORTH_STORE=auto() + LOAD=auto() + STORE=auto() LOAD64=auto() STORE64=auto() CAST_PTR=auto() @@ -324,12 +324,12 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): elif op.operand == Intrinsic.MEM: stack.append(mem_buf_ptr) ip += 1 - elif op.operand == Intrinsic.FORTH_LOAD: + elif op.operand == Intrinsic.LOAD: addr = stack.pop() byte = mem[addr] stack.append(byte) ip += 1 - elif op.operand == Intrinsic.FORTH_STORE: + elif op.operand == Intrinsic.STORE: store_addr = stack.pop() store_value = stack.pop() mem[store_addr] = store_value & 0xFF @@ -799,7 +799,7 @@ def type_check_program(program: Program): ctx.stack.append(c) elif op.operand == Intrinsic.MEM: ctx.stack.append((DataType.PTR, op.token)) - elif op.operand == Intrinsic.FORTH_LOAD: + elif op.operand == Intrinsic.LOAD: assert len(DataType) == 3, "Exhaustive type handling in LOAD intrinsic" if len(ctx.stack) < 1: not_enough_arguments(op) @@ -811,7 +811,7 @@ def type_check_program(program: Program): else: compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD intrinsic: %s" % a_type) exit(1) - elif op.operand == Intrinsic.FORTH_STORE: + elif op.operand == Intrinsic.STORE: assert len(DataType) == 3, "Exhaustive type handling in STORE intrinsic" if len(ctx.stack) < 2: not_enough_arguments(op) @@ -1203,13 +1203,13 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): elif op.operand == Intrinsic.MEM: out.write(" ;; -- mem --\n") out.write(" push mem\n") - elif op.operand == Intrinsic.FORTH_LOAD: + elif op.operand == Intrinsic.LOAD: out.write(" ;; -- forth load --\n") out.write(" pop rax\n") out.write(" xor rbx, rbx\n") out.write(" mov bl, [rax]\n") out.write(" push rbx\n") - elif op.operand == Intrinsic.FORTH_STORE: + elif op.operand == Intrinsic.STORE: out.write(" ;; -- store --\n") out.write(" pop rax\n"); out.write(" pop rbx\n"); @@ -1353,9 +1353,8 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'over': Intrinsic.OVER, 'rot': Intrinsic.ROT, 'mem': Intrinsic.MEM, - # TODO: rename ! and @ to !8 and @8 - '!8': Intrinsic.FORTH_STORE, - '@8': Intrinsic.FORTH_LOAD, + '!8': Intrinsic.STORE, + '@8': Intrinsic.LOAD, '!64': Intrinsic.STORE64, '@64': Intrinsic.LOAD64, 'cast(ptr)': Intrinsic.CAST_PTR, From ed0e54d0b85e639e30e3009614b091e771992cb3 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 15 Oct 2021 23:13:35 +0700 Subject: [PATCH 020/145] test --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e7d551a8..43e6d87f 100644 --- a/README.md +++ b/README.md @@ -293,3 +293,5 @@ include "file.porth" TBD + + From f271d14fece8b1bae2ad394140e2d7a9d7cfc152 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 04:40:19 +0700 Subject: [PATCH 021/145] Add memory-map-file.porth --- memory-map-file.porth | 93 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 memory-map-file.porth diff --git a/memory-map-file.porth b/memory-map-file.porth new file mode 100644 index 00000000..1e9d0334 --- /dev/null +++ b/memory-map-file.porth @@ -0,0 +1,93 @@ +include "std.porth" + +macro fd mem end +macro statbuf fd 8 + end +macro content statbuf sizeof(stat) + end +macro line content sizeof(Str) + end +macro word line sizeof(Str) + end +macro a word sizeof(Str) + end +macro b a sizeof(Str) + end + +macro str-copy // count data dst + @Str rot !Str +end + +macro streq // s1 s2 + if + 2dup Str.count @64 + swap Str.count @64 + = + do + // count data1 data2 *data2 = *data1 + dup Str.count @64 + rot Str.data @64 cast(ptr) + rot Str.data @64 cast(ptr) + rot + while + if dup 0 > do + rot rot + 2dup + @8 + swap @8 + = + else false end + do + 1 + + swap 1 + + rot 1 - + end + dup 0 = + swap drop + swap drop + else drop drop false end +end + +if argc 2 < do + "Usage: " eputs 0 nth_argv cstr-to-pstr eputs " \n" eputs + "ERROR: no input file is provided\n" eputs + 1 exit +end + +"File name is " puts 1 nth_argv cstr-to-pstr puts "\n" puts + +O_RDONLY // flags +1 nth_argv // pathname +AT_FDCWD // dirfd +openat + +if dup 0 < do + "ERROR: could not open file " eputs 1 nth_argv cstr-to-pstr eputs "\n" eputs + 1 exit +end + +fd !64 + +if statbuf fd @64 fstat 0 < do + "ERROR: could not determine the size of file " eputs 1 nth_argv cstr-to-pstr eputs "\n" eputs + 1 exit +end + +statbuf stat.st_size @64 content Str.count !64 + +0 // offset +fd @64 // fd +MAP_PRIVATE // flags +PROT_READ // prot +content Str.count @64 // length +NULL // addr +mmap +content Str.data !64 + +if content Str.data @64 0 < do + "ERROR: could not memory map file " eputs 1 nth_argv cstr-to-pstr eputs "\n" eputs + 1 exit +end + +while content Str.count @64 0 > do + line content str-chop-line + while line Str.count @64 0 > do + line str-trim-left + word line str-chop-word + "|" puts word @Str puts "|\n" puts + end +end From 2cebbf0d55749d4ea66a7468b5e5c4eb4fe4f6ad Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 04:48:57 +0700 Subject: [PATCH 022/145] Add more stuff to std.porth --- porth.py | 3 ++ std/std.porth | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/porth.py b/porth.py index 6ba8e454..2d4b4bc2 100755 --- a/porth.py +++ b/porth.py @@ -522,6 +522,7 @@ def type_check_program(program: Program): contexts: List[Context] = [Context(stack=[], ip=0)] while len(contexts) > 0: ctx = contexts[-1]; + # TODO: type checking fails on empty programs op = program[ctx.ip] assert len(OpType) == 10, "Exhaustive ops handling in type_check_program()" if op.typ == OpType.PUSH_INT: @@ -948,6 +949,8 @@ def type_check_program(program: Program): compiler_error_with_expansion_stack(op.token, 'Loops are not allowed to alter types and amount of elements on the stack.') compiler_note(op.token.loc, 'Expected elements: %s' % expected_types) compiler_note(op.token.loc, 'Actual elements: %s' % actual_types) + for t, token in ctx.stack: + compiler_note(token.loc, '...') exit(1) contexts.pop() if len(contexts) > 0: diff --git a/std/std.porth b/std/std.porth index 7fbf7fe4..e3818781 100644 --- a/std/std.porth +++ b/std/std.porth @@ -336,12 +336,45 @@ macro O_RDONLY 0 end macro CLOCK_MONOTONIC 1 end macro TIMER_ABSTIME 1 end +macro MAP_PRIVATE 2 end +macro PROT_READ 1 end + +macro sizeof(stat) 144 end +macro stat.st_dev 0 + end +macro stat.st_ino 8 + end +macro stat.st_mode 24 + end +macro stat.st_nlink 16 + end +macro stat.st_uid 28 + end +macro stat.st_gid 32 + end +macro stat.st_rdev 40 + end +macro stat.st_size 48 + end +macro stat.st_blksize 56 + end +macro stat.st_blocks 64 + end +macro stat.st_atim 72 + end +macro stat.st_mtim 88 + end +macro stat.st_ctim 104 + end +macro sizeof(stat.st_dev) 8 end +macro sizeof(stat.st_ino) 8 end +macro sizeof(stat.st_mode) 4 end +macro sizeof(stat.st_nlink) 8 end +macro sizeof(stat.st_uid) 4 end +macro sizeof(stat.st_gid) 4 end +macro sizeof(stat.st_rdev) 8 end +macro sizeof(stat.st_size) 8 end +macro sizeof(stat.st_blksize) 8 end +macro sizeof(stat.st_blocks) 8 end +macro sizeof(stat.st_atim) 16 end +macro sizeof(stat.st_mtim) 16 end +macro sizeof(stat.st_ctim) 16 end + // Wrappers for common syscalls macro write SYS_write syscall3 end macro read SYS_read syscall3 end macro openat SYS_openat syscall3 end +macro fstat SYS_fstat syscall2 end macro close SYS_close syscall1 end macro exit SYS_exit syscall1 drop end +macro mmap SYS_mmap syscall6 end macro clock_nanosleep SYS_clock_nanosleep syscall4 end macro 2dup over over end @@ -408,3 +441,75 @@ end macro eputs stderr fputs end + +macro sizeof(Str) 16 end +macro Str.count 0 + end +macro Str.data 8 + end + +macro @Str + dup Str.count @64 + swap Str.data @64 +end + +macro !Str // count data dst + dup Str.data rot swap !64 + Str.count !64 +end + +macro str-chop-left + dup Str.count dec64 + dup Str.data inc64 + drop +end + +macro str-trim-left // input -- + while + if dup Str.count @64 0 > do + dup Str.data @64 cast(ptr) @8 ' ' = + else + false + end + do + dup str-chop-left + end + drop +end + +macro str-chop-line // line input -- + 2dup Str.data @64 swap Str.data !64 + over Str.count 0 swap !64 + while + if dup Str.count @64 0 > do + dup Str.data @64 cast(ptr) @8 '\n' != + else + false + end + do + dup str-chop-left + swap dup Str.count inc64 swap + end + if dup Str.count @64 0 > do + dup str-chop-left + end + 2drop +end + +// TODO: merge str-chop-word and str-chop-line into a single macro +macro str-chop-word // line input -- + 2dup Str.data @64 swap Str.data !64 + over Str.count 0 swap !64 + while + if dup Str.count @64 0 > do + dup Str.data @64 cast(ptr) @8 ' ' != + else + false + end + do + dup str-chop-left + swap dup Str.count inc64 swap + end + if dup Str.count @64 0 > do + dup str-chop-left + end + 2drop +end From f4b7293282f5bb23a8698911f47f27148549aa50 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 05:20:56 +0700 Subject: [PATCH 023/145] Remove debug logging --- porth.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/porth.py b/porth.py index 2d4b4bc2..7d2c0955 100755 --- a/porth.py +++ b/porth.py @@ -949,8 +949,6 @@ def type_check_program(program: Program): compiler_error_with_expansion_stack(op.token, 'Loops are not allowed to alter types and amount of elements on the stack.') compiler_note(op.token.loc, 'Expected elements: %s' % expected_types) compiler_note(op.token.loc, 'Actual elements: %s' % actual_types) - for t, token in ctx.stack: - compiler_note(token.loc, '...') exit(1) contexts.pop() if len(contexts) > 0: From 53e935fc85b0fde4a1d59d10453bbef05e058c8f Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 05:23:38 +0700 Subject: [PATCH 024/145] Add words analyzer to examples --- examples/.gitignore | 1 + memory-map-file.porth => examples/words.porth | 38 ------------------- 2 files changed, 1 insertion(+), 38 deletions(-) rename memory-map-file.porth => examples/words.porth (65%) diff --git a/examples/.gitignore b/examples/.gitignore index 314b2462..4bcc0058 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -8,3 +8,4 @@ seq fib gol fizz-buzz +words \ No newline at end of file diff --git a/memory-map-file.porth b/examples/words.porth similarity index 65% rename from memory-map-file.porth rename to examples/words.porth index 1e9d0334..ac0a500b 100644 --- a/memory-map-file.porth +++ b/examples/words.porth @@ -5,42 +5,6 @@ macro statbuf fd 8 + end macro content statbuf sizeof(stat) + end macro line content sizeof(Str) + end macro word line sizeof(Str) + end -macro a word sizeof(Str) + end -macro b a sizeof(Str) + end - -macro str-copy // count data dst - @Str rot !Str -end - -macro streq // s1 s2 - if - 2dup Str.count @64 - swap Str.count @64 - = - do - // count data1 data2 *data2 = *data1 - dup Str.count @64 - rot Str.data @64 cast(ptr) - rot Str.data @64 cast(ptr) - rot - while - if dup 0 > do - rot rot - 2dup - @8 - swap @8 - = - else false end - do - 1 + - swap 1 + - rot 1 - - end - dup 0 = - swap drop - swap drop - else drop drop false end -end if argc 2 < do "Usage: " eputs 0 nth_argv cstr-to-pstr eputs " \n" eputs @@ -48,8 +12,6 @@ if argc 2 < do 1 exit end -"File name is " puts 1 nth_argv cstr-to-pstr puts "\n" puts - O_RDONLY // flags 1 nth_argv // pathname AT_FDCWD // dirfd From 0b94b000f6f37ce1ef00b04ece5827654cd27064 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 06:15:57 +0700 Subject: [PATCH 025/145] Add the rest of the cast intrinsics --- README.md | 17 ++++++++++------- porth.py | 49 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 43e6d87f..33928478 100644 --- a/README.md +++ b/README.md @@ -233,13 +233,16 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter #### Memory -| Name | Signature | Description | -| --- | --- | --- | -| `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | -| `!8` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. Same as `.` but the arguments swapped. | -| `@8` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. Synonym to `,`. | -| `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. | -| `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. Synonym to `,64`. | +| Name | Signature | Description | +| --- | --- | --- | +| `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | +| `!8` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. Same as `.` but the arguments swapped. | +| `@8` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. Synonym to `,`. | +| `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. | +| `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. Synonym to `,64`. | +| `cast(int)` | `[a: any] -- [a: int]` | cast the element on top of the stack to `int` | +| `cast(bool)` | `[a: any] -- [a: bool]` | cast the element on top of the stack to `bool` | +| `cast(ptr)` | `[a: any] -- [a: ptr]` | cast the element on top of the stack to `ptr` | #### System diff --git a/porth.py b/porth.py index 7d2c0955..ca70652c 100755 --- a/porth.py +++ b/porth.py @@ -34,6 +34,12 @@ class Keyword(Enum): MACRO=auto() INCLUDE=auto() +class DataType(IntEnum): + INT=auto() + BOOL=auto() + PTR=auto() + +assert len(DataType) == 3, 'Exhaustive casts for all data types' class Intrinsic(Enum): PLUS=auto() MINUS=auto() @@ -62,6 +68,8 @@ class Intrinsic(Enum): LOAD64=auto() STORE64=auto() CAST_PTR=auto() + CAST_INT=auto() + CAST_BOOL=auto() ARGC=auto() ARGV=auto() HERE=auto() @@ -211,7 +219,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): else: ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 37, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" + assert len(Intrinsic) == 39, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" if op.operand == Intrinsic.PLUS: a = stack.pop() b = stack.pop() @@ -370,6 +378,12 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): elif op.operand == Intrinsic.CAST_PTR: # Ignore the type casting. It's only useful for type_check_program() phase ip += 1 + elif op.operand == Intrinsic.CAST_BOOL: + # Ignore the type casting. It's only useful for type_check_program() phase + ip += 1 + elif op.operand == Intrinsic.CAST_INT: + # Ignore the type casting. It's only useful for type_check_program() phase + ip += 1 elif op.operand == Intrinsic.SYSCALL0: syscall_number = stack.pop(); if syscall_number == 39: # SYS_getpid @@ -470,11 +484,6 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): print("[INFO] Memory dump") print(mem[:20]) -class DataType(IntEnum): - INT=auto() - BOOL=auto() - PTR=auto() - def compiler_diagnostic(loc: Loc, tag: str, message: str): print("%s:%d:%d: %s: %s" % (loc + (tag, message)), file=sys.stderr) @@ -536,7 +545,7 @@ def type_check_program(program: Program): ctx.stack.append((DataType.PTR, op.token)) ctx.ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 37, "Exhaustive intrinsic handling in type_check_program()" + assert len(Intrinsic) == 39, "Exhaustive intrinsic handling in type_check_program()" assert isinstance(op.operand, Intrinsic), "This could be a bug in compilation step" if op.operand == Intrinsic.PLUS: assert len(DataType) == 3, "Exhaustive type handling in PLUS intrinsic" @@ -860,6 +869,22 @@ def type_check_program(program: Program): a_type, a_token = ctx.stack.pop() ctx.stack.append((DataType.PTR, a_token)) + elif op.operand == Intrinsic.CAST_INT: + if len(ctx.stack) < 1: + not_enough_arguments(op) + exit(1) + + a_type, a_token = ctx.stack.pop() + + ctx.stack.append((DataType.INT, a_token)) + elif op.operand == Intrinsic.CAST_BOOL: + if len(ctx.stack) < 1: + not_enough_arguments(op) + exit(1) + + a_type, a_token = ctx.stack.pop() + + ctx.stack.append((DataType.BOOL, a_token)) elif op.operand == Intrinsic.ARGC: ctx.stack.append((DataType.INT, op.token)) elif op.operand == Intrinsic.ARGV: @@ -1058,7 +1083,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): assert isinstance(op.operand, int), "This could be a bug in the parsing step" out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 37, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" + assert len(Intrinsic) == 39, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: out.write(" ;; -- plus --\n") out.write(" pop rax\n") @@ -1246,6 +1271,10 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" mov [rax], rbx\n"); elif op.operand == Intrinsic.CAST_PTR: out.write(" ;; -- cast(ptr) --\n") + elif op.operand == Intrinsic.CAST_INT: + out.write(" ;; -- cast(int) --\n") + elif op.operand == Intrinsic.CAST_BOOL: + out.write(" ;; -- cast(bool) --\n") elif op.operand == Intrinsic.SYSCALL0: out.write(" ;; -- syscall0 --\n") out.write(" pop rax\n") @@ -1330,7 +1359,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'include': Keyword.INCLUDE, } -assert len(Intrinsic) == 37, "Exhaustive INTRINSIC_BY_NAMES definition" +assert len(Intrinsic) == 39, "Exhaustive INTRINSIC_BY_NAMES definition" INTRINSIC_BY_NAMES = { '+': Intrinsic.PLUS, '-': Intrinsic.MINUS, @@ -1359,6 +1388,8 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): '!64': Intrinsic.STORE64, '@64': Intrinsic.LOAD64, 'cast(ptr)': Intrinsic.CAST_PTR, + 'cast(int)': Intrinsic.CAST_INT, + 'cast(bool)': Intrinsic.CAST_BOOL, 'argc': Intrinsic.ARGC, 'argv': Intrinsic.ARGV, 'here': Intrinsic.HERE, From 178b37c8f5783b399b15517887a5a20d604bf7bb Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 06:18:02 +0700 Subject: [PATCH 026/145] Revert "test" This reverts commit ed0e54d0b85e639e30e3009614b091e771992cb3. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 33928478..a57a2dff 100644 --- a/README.md +++ b/README.md @@ -296,5 +296,3 @@ include "file.porth" TBD - - From 324e3b3baa8cad6790c72af3bd7c9a126222a3a6 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 19:03:23 +0700 Subject: [PATCH 027/145] Remove mentioning of `.` and `.64` intrinsics from the reference --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a57a2dff..6a7d19de 100644 --- a/README.md +++ b/README.md @@ -236,9 +236,9 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter | Name | Signature | Description | | --- | --- | --- | | `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | -| `!8` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. Same as `.` but the arguments swapped. | +| `!8` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. | | `@8` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. Synonym to `,`. | -| `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. Same as `.64` but the arguments swapped. | +| `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. | | `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. Synonym to `,64`. | | `cast(int)` | `[a: any] -- [a: int]` | cast the element on top of the stack to `int` | | `cast(bool)` | `[a: any] -- [a: bool]` | cast the element on top of the stack to `bool` | From 30e94eee67097c8e14760bb3d3348fa1fec63a25 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 19:37:54 +0700 Subject: [PATCH 028/145] Refactor reversed-linked-list example --- examples/reverse-linked-list.porth | 26 +++++++++++++------------- porth.py | 2 ++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/examples/reverse-linked-list.porth b/examples/reverse-linked-list.porth index 570548c5..fb8802d6 100644 --- a/examples/reverse-linked-list.porth +++ b/examples/reverse-linked-list.porth @@ -1,12 +1,12 @@ include "./std.porth" -// TODO: get rid of .64 macro in examples/reverse-linked-list.porth - -macro ,node/value @64 end -macro ,node/prev 8 + @64 end -macro .node/value .64 end -macro .node/prev swap 8 + swap .64 end -macro sizeof(node) 16 end +macro sizeof(Node) 16 end +macro Node.value 0 + end +macro Node.prev 8 + end +macro @Node.value Node.value @64 end +macro @Node.prev Node.prev @64 end +macro !Node.value Node.value !64 end +macro !Node.prev Node.prev !64 end macro list_a mem end macro list_b list_a 8 + end @@ -15,18 +15,18 @@ macro nodes_count list_b 8 + end macro nodes nodes_count 8 + end macro alloc_node - nodes_count @64 sizeof(node) * nodes + - nodes_count dup @64 1 + .64 + nodes_count @64 sizeof(Node) * nodes + + nodes_count dup @64 1 + swap !64 end macro push_node - over alloc_node 2dup swap @64 .node/prev .64 - swap @64 cast(ptr) swap .node/value + over alloc_node 2dup swap @64 swap !Node.prev swap !64 + swap @64 cast(ptr) !Node.value end macro pop_node - dup @64 cast(ptr) ,node/value swap - dup @64 cast(ptr) ,node/prev .64 + dup @64 cast(ptr) @Node.value swap + dup @64 cast(ptr) @Node.prev swap !64 end // initialize list_a diff --git a/porth.py b/porth.py index ca70652c..e60274ba 100755 --- a/porth.py +++ b/porth.py @@ -526,6 +526,8 @@ class Context: stack: DataStack ip: OpAddr +# TODO: better error reporting on type checking errors of intrinsics +# Reported expected and actual types with the location that introduced the actual type def type_check_program(program: Program): visited_dos: Dict[OpAddr, DataStack] = {} contexts: List[Context] = [Context(stack=[], ip=0)] From 1f1e258b359050d79528ce22c6269c52496e3ee1 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 19:44:21 +0700 Subject: [PATCH 029/145] Use struct accessor style from reversed-linked-list example in Porth --- porth.porth | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/porth.porth b/porth.porth index 3bd18a5f..b1f6169d 100644 --- a/porth.porth +++ b/porth.porth @@ -8,13 +8,13 @@ macro OP_PUSH_INT 0 end macro OP_PLUS 1 end macro OP_PRINT 2 end -// struct Op { -// type: u64, -// operand: u64, -// } -macro Op.type nop end -macro Op.operand 8 + end -macro sizeof(Op) 16 end +macro sizeof(Op) 16 end +macro Op.type 0 + end +macro Op.operand 8 + end +macro @Op.type Op.type @64 end +macro !Op.type Op.type !64 end +macro @Op.operand Op.operand @64 end +macro !Op.operand Op.operand !64 end // Memory Layout macro putd-buffer mem end @@ -59,7 +59,7 @@ end macro push-op // type operand -- ops-count @64 sizeof(Op) * ops + dup Op.operand rot swap !64 - Op.type !64 + !Op.type ops-count inc64 end @@ -67,8 +67,8 @@ macro dump-ops // -- 0 while dup ops-count @64 < do // ptr ptr dup sizeof(Op) * ops + - "Type: " puts dup Op.type @64 print - "Operand: " puts Op.operand @64 print + "Type: " puts dup @Op.type print + "Operand: " puts @Op.operand print "----------\n" puts 1 + end @@ -119,17 +119,17 @@ macro compile-ops // -- 0 while dup ops-count @64 < do dup sizeof(Op) * ops + - if dup Op.type @64 OP_PUSH_INT = do - " ;; -- push int " puts dup Op.operand @64 putd " --\n" puts - " mov rax, " puts dup Op.operand @64 putd "\n" puts + if dup @Op.type OP_PUSH_INT = do + " ;; -- push int " puts dup @Op.operand putd " --\n" puts + " mov rax, " puts dup @Op.operand putd "\n" puts " push rax\n" puts - elif dup Op.type @64 OP_PLUS = do + elif dup @Op.type OP_PLUS = do " ;; -- plus --\n" puts " pop rax\n" puts " pop rbx\n" puts " add rax, rbx\n" puts " push rax\n" puts - elif dup Op.type @64 OP_PRINT = do + elif dup @Op.type OP_PRINT = do " ;; -- print --\n" puts " pop rdi\n" puts " call print\n" puts @@ -155,14 +155,14 @@ macro simulate-ops // -- 0 while dup ops-count @64 < do dup sizeof(Op) * ops + - if dup Op.type @64 OP_PUSH_INT = do - dup Op.operand @64 sim-stack-push - elif dup Op.type @64 OP_PLUS = do + if dup @Op.type OP_PUSH_INT = do + dup @Op.operand sim-stack-push + elif dup @Op.type OP_PLUS = do sim-stack-pop sim-stack-pop + sim-stack-push - elif dup Op.type @64 OP_PRINT = do + elif dup @Op.type OP_PRINT = do sim-stack-pop print else here eputs ": unreachable\n" eputs 1 exit From d8627c582f377afd5ad6a2d6be59e65c18f0addd Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 19:49:02 +0700 Subject: [PATCH 030/145] Use !Op.operand in porth.porth --- porth.porth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/porth.porth b/porth.porth index b1f6169d..8c29afb0 100644 --- a/porth.porth +++ b/porth.porth @@ -58,7 +58,7 @@ end macro push-op // type operand -- ops-count @64 sizeof(Op) * ops + - dup Op.operand rot swap !64 + dup rot swap !Op.operand !Op.type ops-count inc64 end From 955189aed29a37ce42b78023ad2775f41eada766 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 20:28:49 +0700 Subject: [PATCH 031/145] Better Str struct accessors --- examples/words.porth | 20 +++++----- std/std.porth | 90 ++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/examples/words.porth b/examples/words.porth index ac0a500b..c0cbcaa2 100644 --- a/examples/words.porth +++ b/examples/words.porth @@ -7,7 +7,7 @@ macro line content sizeof(Str) + end macro word line sizeof(Str) + end if argc 2 < do - "Usage: " eputs 0 nth_argv cstr-to-pstr eputs " \n" eputs + "Usage: " eputs 0 nth_argv cstr-to-str eputs " \n" eputs "ERROR: no input file is provided\n" eputs 1 exit end @@ -18,36 +18,36 @@ AT_FDCWD // dirfd openat if dup 0 < do - "ERROR: could not open file " eputs 1 nth_argv cstr-to-pstr eputs "\n" eputs + "ERROR: could not open file " eputs 1 nth_argv cstr-to-str eputs "\n" eputs 1 exit end fd !64 if statbuf fd @64 fstat 0 < do - "ERROR: could not determine the size of file " eputs 1 nth_argv cstr-to-pstr eputs "\n" eputs + "ERROR: could not determine the size of file " eputs 1 nth_argv cstr-to-str eputs "\n" eputs 1 exit end -statbuf stat.st_size @64 content Str.count !64 +statbuf @stat.st_size content !Str.count 0 // offset fd @64 // fd MAP_PRIVATE // flags PROT_READ // prot -content Str.count @64 // length +content @Str.count // length NULL // addr mmap -content Str.data !64 +content !Str.data -if content Str.data @64 0 < do - "ERROR: could not memory map file " eputs 1 nth_argv cstr-to-pstr eputs "\n" eputs +if content @Str.data cast(int) 0 < do + "ERROR: could not memory map file " eputs 1 nth_argv cstr-to-str eputs "\n" eputs 1 exit end -while content Str.count @64 0 > do +while content @Str.count 0 > do line content str-chop-line - while line Str.count @64 0 > do + while line @Str.count 0 > do line str-trim-left word line str-chop-word "|" puts word @Str puts "|\n" puts diff --git a/std/std.porth b/std/std.porth index e3818781..9bd18c00 100644 --- a/std/std.porth +++ b/std/std.porth @@ -348,6 +348,7 @@ macro stat.st_uid 28 + end macro stat.st_gid 32 + end macro stat.st_rdev 40 + end macro stat.st_size 48 + end +macro @stat.st_size stat.st_size @64 end macro stat.st_blksize 56 + end macro stat.st_blocks 64 + end macro stat.st_atim 72 + end @@ -385,14 +386,6 @@ macro % divmod swap drop end macro mod % end macro div / end -// Old Style Memory Access (Deprecated) -macro .64 swap !64 end -macro ,64 @64 end -macro ! !8 end -macro @ @8 end -macro . swap ! end -macro , @ end - macro nth_argv 8 * argv + @64 cast(ptr) end @@ -426,7 +419,7 @@ macro cstreq and end -macro cstr-to-pstr +macro cstr-to-str dup cstrlen swap end @@ -443,73 +436,80 @@ macro eputs end macro sizeof(Str) 16 end -macro Str.count 0 + end -macro Str.data 8 + end +macro Str.count 0 + end +macro Str.data 8 + end +macro @Str.count Str.count @64 end +macro @Str.data Str.data @64 cast(ptr) end +macro !Str.count Str.count !64 end +macro !Str.data Str.data !64 end macro @Str - dup Str.count @64 - swap Str.data @64 + dup @Str.count + swap @Str.data end -macro !Str // count data dst - dup Str.data rot swap !64 - Str.count !64 +macro !Str // count data dst - + dup rot swap + !Str.data + !Str.count end -macro str-chop-left +macro str-chop-one-left dup Str.count dec64 - dup Str.data inc64 - drop + Str.data inc64 end macro str-trim-left // input -- while - if dup Str.count @64 0 > do - dup Str.data @64 cast(ptr) @8 ' ' = - else - false - end + if dup @Str.count 0 > do + dup @Str.data @8 ' ' = + else false end do - dup str-chop-left + dup str-chop-one-left end drop end macro str-chop-line // line input -- - 2dup Str.data @64 swap Str.data !64 - over Str.count 0 swap !64 + 2dup @Str.data swap !Str.data + over 0 swap !Str.count while - if dup Str.count @64 0 > do - dup Str.data @64 cast(ptr) @8 '\n' != - else - false - end + if dup @Str.count 0 > do + dup @Str.data @8 '\n' != + else false end do - dup str-chop-left + dup str-chop-one-left swap dup Str.count inc64 swap end - if dup Str.count @64 0 > do - dup str-chop-left + if dup @Str.count 0 > do + dup str-chop-one-left end 2drop end // TODO: merge str-chop-word and str-chop-line into a single macro macro str-chop-word // line input -- - 2dup Str.data @64 swap Str.data !64 - over Str.count 0 swap !64 + 2dup @Str.data swap !Str.data + over 0 swap !Str.count while - if dup Str.count @64 0 > do - dup Str.data @64 cast(ptr) @8 ' ' != - else - false - end + if dup @Str.count 0 > do + dup @Str.data @8 ' ' != + else false end do - dup str-chop-left + dup str-chop-one-left swap dup Str.count inc64 swap end - if dup Str.count @64 0 > do - dup str-chop-left + if dup @Str.count 0 > do + dup str-chop-one-left end 2drop end + +// Deprecated Words +macro .64 swap !64 end +macro ,64 @64 end +macro ! !8 end +macro @ @8 end +macro . swap ! end +macro , @ end +macro cstr-to-pstr cstr-to-str end From 277e8ddc12a69109d6b7d49fa61a9c9fa05efc81 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 20:32:06 +0700 Subject: [PATCH 032/145] Use type casting in the definition of `true` and `false` --- std/std.porth | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/std.porth b/std/std.porth index 9bd18c00..027bef24 100644 --- a/std/std.porth +++ b/std/std.porth @@ -2,8 +2,8 @@ macro NULL 0 end macro nop end -macro true 0 0 = end -macro false 0 0 != end +macro true 1 cast(bool) end +macro false 0 cast(bool) end /// Standard streams macro stdin 0 end From b19df02de77b68a379bbfa8b32940aeea34ce6a2 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 20:35:27 +0700 Subject: [PATCH 033/145] Stub conventions section --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 6a7d19de..e73ac914 100644 --- a/README.md +++ b/README.md @@ -296,3 +296,23 @@ include "file.porth" TBD + +### Conventions + +#### Structures + +TBD + + + +#### Arrays + +TBD + + + +#### Memory Layout + +TBD + + From d7fa5721fd86db9973604c504d4f7abc35bbe928 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 20:47:24 +0700 Subject: [PATCH 034/145] Fix crash on type checking empty program --- porth.py | 17 ++++++----------- tests/.gitignore | 3 ++- tests/empty.porth | 0 tests/empty.txt | 8 ++++++++ tests/if-else-fail.txt | 4 ++-- tests/while-fail.txt | 4 ++-- 6 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 tests/empty.porth create mode 100644 tests/empty.txt diff --git a/porth.py b/porth.py index e60274ba..82bfe458 100755 --- a/porth.py +++ b/porth.py @@ -533,7 +533,12 @@ def type_check_program(program: Program): contexts: List[Context] = [Context(stack=[], ip=0)] while len(contexts) > 0: ctx = contexts[-1]; - # TODO: type checking fails on empty programs + if ctx.ip >= len(program): + if len(ctx.stack) != 0: + compiler_error_with_expansion_stack(ctx.stack[-1][1], "unhandled data on the data stack: %s" % list(map(lambda x: x[0], ctx.stack))) + exit(1) + contexts.pop() + continue op = program[ctx.ip] assert len(OpType) == 10, "Exhaustive ops handling in type_check_program()" if op.typ == OpType.PUSH_INT: @@ -978,10 +983,6 @@ def type_check_program(program: Program): compiler_note(op.token.loc, 'Actual elements: %s' % actual_types) exit(1) contexts.pop() - if len(contexts) > 0: - ctx = contexts[-1] - else: - continue else: visited_dos[ctx.ip] = copy(ctx.stack) ctx.ip += 1 @@ -990,12 +991,6 @@ def type_check_program(program: Program): else: assert False, "unreachable" - if ctx.ip >= len(program): - if len(ctx.stack) != 0: - compiler_error_with_expansion_stack(ctx.stack[-1][1], "unhandled data on the ctx.stack: %s" % list(map(lambda x: x[0], ctx.stack))) - exit(1) - contexts.pop() - def generate_nasm_linux_x86_64(program: Program, out_file_path: str): strs: List[bytes] = [] with open(out_file_path, "w") as out: diff --git a/tests/.gitignore b/tests/.gitignore index a64dab01..dda9aed2 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -12,4 +12,5 @@ if-else-less while here memory-forth-style -cstr \ No newline at end of file +cstr +empty \ No newline at end of file diff --git a/tests/empty.porth b/tests/empty.porth new file mode 100644 index 00000000..e69de29b diff --git a/tests/empty.txt b/tests/empty.txt new file mode 100644 index 00000000..5cb432cf --- /dev/null +++ b/tests/empty.txt @@ -0,0 +1,8 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 0 + +:b stderr 0 + diff --git a/tests/if-else-fail.txt b/tests/if-else-fail.txt index 1c144e38..e86ea2d5 100644 --- a/tests/if-else-fail.txt +++ b/tests/if-else-fail.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 93 -./tests/if-else-fail.porth:1:34: ERROR: unhandled data on the ctx.stack: [] +:b stderr 94 +./tests/if-else-fail.porth:1:34: ERROR: unhandled data on the data stack: [] diff --git a/tests/while-fail.txt b/tests/while-fail.txt index 4b26ba50..848dd355 100644 --- a/tests/while-fail.txt +++ b/tests/while-fail.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 90 -./tests/while-fail.porth:1:1: ERROR: unhandled data on the ctx.stack: [] +:b stderr 91 +./tests/while-fail.porth:1:1: ERROR: unhandled data on the data stack: [] From af6e6ab0eae064743ed6f818b834d0f278d3700e Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 20:49:54 +0700 Subject: [PATCH 035/145] Add a note to porth.porth about what it is --- porth.porth | 2 ++ 1 file changed, 2 insertions(+) diff --git a/porth.porth b/porth.porth index 8c29afb0..dc16faf4 100644 --- a/porth.porth +++ b/porth.porth @@ -1,3 +1,5 @@ +// In progress rewrite of ./porth.py in Porth + include "std.porth" macro PUTD_BUFFER_CAP 32 end From 76101cc70debb695aa5310a6b8bf9796fee0dc0d Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 20:52:25 +0700 Subject: [PATCH 036/145] Add another milestone to the plan --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e73ac914..2fc8d78c 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Porth is planned to be - [x] [Turing-complete](./examples/rule110.porth) - [x] Statically typed (the type checking is similar to [WASM validation](https://binji.github.io/posts/webassembly-type-checking/)) - [ ] Self-hosted (Python is used only as an initial bootstrap, once the language is mature enough we gonna rewrite it in itself) +- [ ] Optimized (these are not the selling points, but rather milestones of the development) From d12097226525f519d73e2590be5bdede80e23ed5 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 20:54:33 +0700 Subject: [PATCH 037/145] Refer to the current progress in the "Self-hosted" milestone --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fc8d78c..71ed0bc1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Porth is planned to be - [x] Stack-based (just like Forth) - [x] [Turing-complete](./examples/rule110.porth) - [x] Statically typed (the type checking is similar to [WASM validation](https://binji.github.io/posts/webassembly-type-checking/)) -- [ ] Self-hosted (Python is used only as an initial bootstrap, once the language is mature enough we gonna rewrite it in itself) +- [ ] Self-hosted (See [./porth.porth](./porth.porth) for the current progress) - [ ] Optimized (these are not the selling points, but rather milestones of the development) From 3619e2d069ae93ac42fd9597d3d5dbb60fc1ae81 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 16 Oct 2021 21:42:17 +0700 Subject: [PATCH 038/145] Remove mentioning of `,` and `,64` intrinsics from README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 71ed0bc1..54c00a4a 100644 --- a/README.md +++ b/README.md @@ -238,9 +238,9 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter | --- | --- | --- | | `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | | `!8` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. | -| `@8` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. Synonym to `,`. | +| `@8` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. | | `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. | -| `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. Synonym to `,64`. | +| `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. | | `cast(int)` | `[a: any] -- [a: int]` | cast the element on top of the stack to `int` | | `cast(bool)` | `[a: any] -- [a: bool]` | cast the element on top of the stack to `bool` | | `cast(ptr)` | `[a: any] -- [a: ptr]` | cast the element on top of the stack to `ptr` | From c4e433d561645a6bd99ea15444f8bcb6052ba39d Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 17 Oct 2021 00:05:33 +0700 Subject: [PATCH 039/145] Add solution for Project Euler Problem 10 --- euler/.gitignore | 3 ++- euler/problem10.porth | 36 ++++++++++++++++++++++++++++++++++++ porth.py | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 euler/problem10.porth diff --git a/euler/.gitignore b/euler/.gitignore index db06909d..a46c8bea 100644 --- a/euler/.gitignore +++ b/euler/.gitignore @@ -6,4 +6,5 @@ problem05 problem06 problem07 problem08 -problem09 \ No newline at end of file +problem09 +problem10 \ No newline at end of file diff --git a/euler/problem10.porth b/euler/problem10.porth new file mode 100644 index 00000000..899692cb --- /dev/null +++ b/euler/problem10.porth @@ -0,0 +1,36 @@ +include "std.porth" + +// TODO: this solution requires more than 640kb of memory +// Add a test case when there is an opportunity to customize that + +macro sizeof(u64) 8 end +macro ans mem end +macro primes-count ans 8 + end +macro primes primes-count 8 + end + +macro push-prime // value -- + primes-count @64 sizeof(u64) * primes + !64 + primes-count inc64 +end + +macro is-prime // [value: int] -> [ret: bool] + 0 while + if 2dup sizeof(u64) * primes + @64 dup * >= do + 2dup sizeof(u64) * primes + @64 mod 0 != + else false end + do 1 + end + sizeof(u64) * primes + @64 dup * < +end + +2 push-prime +2 ans !64 + +3 while dup 2000000 < do + if dup is-prime do + dup push-prime + dup ans @64 + ans !64 + end + 1 + +end drop + +ans @64 print diff --git a/porth.py b/porth.py index 82bfe458..330ffe48 100755 --- a/porth.py +++ b/porth.py @@ -15,6 +15,7 @@ PORTH_EXT = '.porth' DEFAULT_EXPANSION_LIMIT=1000 EXPANSION_DIAGNOSTIC_LIMIT=10 +# TODO: customize memory capacity via a flag MEM_CAPACITY = 640_000 # should be enough for everyone SIM_NULL_POINTER_PADDING = 1 # just a little bit of a padding at the beginning of the memory to make 0 an invalid address SIM_STR_CAPACITY = 640_000 From 11ce39e88c304c86a8410d371b4cec5d4eaeffd5 Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 17 Oct 2021 01:44:56 +0700 Subject: [PATCH 040/145] Integrate self-hosted parsing into porth.porth --- examples/.gitignore | 1 - examples/words.porth | 55 ------------------- porth.porth | 127 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 109 insertions(+), 74 deletions(-) delete mode 100644 examples/words.porth diff --git a/examples/.gitignore b/examples/.gitignore index 4bcc0058..314b2462 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -8,4 +8,3 @@ seq fib gol fizz-buzz -words \ No newline at end of file diff --git a/examples/words.porth b/examples/words.porth deleted file mode 100644 index c0cbcaa2..00000000 --- a/examples/words.porth +++ /dev/null @@ -1,55 +0,0 @@ -include "std.porth" - -macro fd mem end -macro statbuf fd 8 + end -macro content statbuf sizeof(stat) + end -macro line content sizeof(Str) + end -macro word line sizeof(Str) + end - -if argc 2 < do - "Usage: " eputs 0 nth_argv cstr-to-str eputs " \n" eputs - "ERROR: no input file is provided\n" eputs - 1 exit -end - -O_RDONLY // flags -1 nth_argv // pathname -AT_FDCWD // dirfd -openat - -if dup 0 < do - "ERROR: could not open file " eputs 1 nth_argv cstr-to-str eputs "\n" eputs - 1 exit -end - -fd !64 - -if statbuf fd @64 fstat 0 < do - "ERROR: could not determine the size of file " eputs 1 nth_argv cstr-to-str eputs "\n" eputs - 1 exit -end - -statbuf @stat.st_size content !Str.count - -0 // offset -fd @64 // fd -MAP_PRIVATE // flags -PROT_READ // prot -content @Str.count // length -NULL // addr -mmap -content !Str.data - -if content @Str.data cast(int) 0 < do - "ERROR: could not memory map file " eputs 1 nth_argv cstr-to-str eputs "\n" eputs - 1 exit -end - -while content @Str.count 0 > do - line content str-chop-line - while line @Str.count 0 > do - line str-trim-left - word line str-chop-word - "|" puts word @Str puts "|\n" puts - end -end diff --git a/porth.porth b/porth.porth index dc16faf4..5b51e6c0 100644 --- a/porth.porth +++ b/porth.porth @@ -20,7 +20,19 @@ macro !Op.operand Op.operand !64 end // Memory Layout macro putd-buffer mem end -macro sim-stack-count putd-buffer PUTD_BUFFER_CAP + end + +macro file_path_cstr putd-buffer PUTD_BUFFER_CAP + end +macro fd file_path_cstr 8 + end +macro statbuf fd 8 + end + +macro content statbuf sizeof(stat) + end +macro line content sizeof(Str) + end +macro word line sizeof(Str) + end +macro streq_a word sizeof(Str) + end +macro streq_b streq_a sizeof(Str) + end +macro parse_int_a streq_b sizeof(Str) + end + +macro sim-stack-count parse_int_a sizeof(Str) + end macro sim-stack sim-stack-count 8 + end macro ops-count sim-stack SIM_STACK_CAP 8 * + end macro ops ops-count 8 + end @@ -58,6 +70,32 @@ macro putd // u64 -- drop end +macro parse_int // n1 s1 - ret + parse_int_a !Str + 0 0 while dup parse_int_a @Str.count < do + // TODO: parse_int does not check if the input is actually a number + dup parse_int_a @Str.data + @8 '0' - + rot 10 * + + swap + 1 + + end drop +end + +macro streq // n1 s1 n2 s2 + streq_a !Str + streq_b !Str + if streq_a @Str.count streq_b @Str.count = do + 0 while + if dup streq_a @Str.count < do + dup streq_a @Str.data + @8 + over streq_b @Str.data + @8 + = + else false end + do 1 + end + streq_a @Str.count >= + else false end +end + macro push-op // type operand -- ops-count @64 sizeof(Op) * ops + dup rot swap !Op.operand @@ -177,27 +215,62 @@ macro simulate-ops // -- drop end -macro program69 // -- - OP_PUSH_INT 34 push-op - OP_PUSH_INT 35 push-op - OP_PLUS 0 push-op - OP_PRINT 0 push-op -end +macro parse_file_path + O_RDONLY // flags + file_path_cstr @64 cast(ptr) // pathname + AT_FDCWD // dirfd + openat + + if dup 0 < do + "ERROR: could not open file " eputs file_path_cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs + 1 exit + end + + fd !64 + + if statbuf fd @64 fstat 0 < do + "ERROR: could not determine the size of file " eputs file_path_cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs + 1 exit + end -macro program123 // -- - OP_PUSH_INT 1 push-op - OP_PRINT 0 push-op - OP_PUSH_INT 2 push-op - OP_PRINT 0 push-op - OP_PUSH_INT 3 push-op - OP_PRINT 0 push-op + statbuf @stat.st_size content !Str.count + + 0 // offset + fd @64 // fd + MAP_PRIVATE // flags + PROT_READ // prot + content @Str.count // length + NULL // addr + mmap + content !Str.data + + if content @Str.data cast(int) 0 < do + "ERROR: could not memory map file " eputs file_path_cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs + 1 exit + end + + while content @Str.count 0 > do + line content str-chop-line + while line @Str.count 0 > do + line str-trim-left + word line str-chop-word + if word @Str "+" streq do + OP_PLUS 0 push-op + elif word @Str "print" streq do + OP_PRINT 0 push-op + else + OP_PUSH_INT word @Str parse_int push-op + end + end + end + // TODO: parse_file_path does not clean up resources after itself end macro usage // -- dup "Usage: porth \n" rot fputs dup " SUBCOMMANDS:\n" rot fputs - dup " sim Simulate the program.\n" rot fputs - dup " com Compile the program\n" rot fputs + dup " sim Simulate the program.\n" rot fputs + dup " com Compile the program\n" rot fputs dup " dump Dump the ops of the program\n" rot fputs dup " help Print this help to stdout and exit with 0 code\n" rot fputs drop @@ -210,14 +283,32 @@ macro main // -- 1 exit end - program123 - // TODO: parsing file is not implemented 1 nth_argv if dup "sim"c cstreq do + if argc 3 < do + stderr usage + "ERROR: no input file is provided for the `sim` subcommand\n" eputs + 1 exit + end + + 2 nth_argv file_path_cstr !64 + + parse_file_path + simulate-ops elif dup "com"c cstreq do + if argc 3 < do + stderr usage + "ERROR: no input file is provided for the `com` subcommand\n" eputs + 1 exit + end + + 2 nth_argv file_path_cstr !64 + + parse_file_path + compile-ops elif dup "help"c cstreq do stdout usage From c6288a70084c7670eedb33e3759f9124e27dd585 Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 17 Oct 2021 01:53:04 +0700 Subject: [PATCH 041/145] Unhardcode dump subcommand --- porth.porth | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/porth.porth b/porth.porth index 5b51e6c0..20013a59 100644 --- a/porth.porth +++ b/porth.porth @@ -103,11 +103,24 @@ macro push-op // type operand -- ops-count inc64 end +macro print-op-type + if dup OP_PUSH_INT = do + "OP_PUSH_INT" puts + elif dup OP_PLUS = do + "OP_PLUS" puts + elif dup OP_PRINT = do + "OP_PRINT" puts + else + here eputs ": Unknown op type\n" eputs 1 exit + end + drop +end + macro dump-ops // -- 0 while dup ops-count @64 < do // ptr ptr dup sizeof(Op) * ops + - "Type: " puts dup @Op.type print + "Type: " puts dup @Op.type print-op-type "\n" puts "Operand: " puts @Op.operand print "----------\n" puts 1 + @@ -271,7 +284,7 @@ macro usage // -- dup " SUBCOMMANDS:\n" rot fputs dup " sim Simulate the program.\n" rot fputs dup " com Compile the program\n" rot fputs - dup " dump Dump the ops of the program\n" rot fputs + dup " dump Dump the ops of the program\n" rot fputs dup " help Print this help to stdout and exit with 0 code\n" rot fputs drop end @@ -283,8 +296,6 @@ macro main // -- 1 exit end - // TODO: parsing file is not implemented - 1 nth_argv if dup "sim"c cstreq do if argc 3 < do @@ -314,6 +325,16 @@ macro main // -- stdout usage 0 exit elif dup "dump"c cstreq do + if argc 3 < do + stderr usage + "ERROR: no input file is provided for the `dump` subcommand\n" eputs + 1 exit + end + + 2 nth_argv file_path_cstr !64 + + parse_file_path + dump-ops else stderr usage From 77aa504ec03c91ed19edb7c4c96f356c43c42cd8 Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 17 Oct 2021 05:06:59 +0700 Subject: [PATCH 042/145] Update CONTRIBUTING.md --- CONTRIBUTING.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1414840a..1d1d86ca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,9 @@ No contributions are accepted right now since the language is not -stablized yet and I'm still actively experimenting with the design. +stablized yet and I'm actively experimenting with the +design. Contributions will be opened later when I feel that the +language is stable enough. Feel free to do whatever you want with the +source code itself according to the MIT license though. -Feel free to do whatever you want with the code itself according to -the MIT license though. +If you have any questions about the language or want to report bugs +(NO FEATURE REQUESTS) please send them to reximkut@gmail.com or +tsodingbiz@gmail.com From 7616e8be9abb8b605b6225db0aa14353ff332f72 Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 17 Oct 2021 03:35:25 +0700 Subject: [PATCH 043/145] Rename 8-bit memory access intrinsics --- porth.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/porth.py b/porth.py index 330ffe48..a1fc06a5 100755 --- a/porth.py +++ b/porth.py @@ -64,8 +64,8 @@ class Intrinsic(Enum): OVER=auto() ROT=auto() MEM=auto() - LOAD=auto() - STORE=auto() + LOAD8=auto() + STORE8=auto() LOAD64=auto() STORE64=auto() CAST_PTR=auto() @@ -333,12 +333,12 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): elif op.operand == Intrinsic.MEM: stack.append(mem_buf_ptr) ip += 1 - elif op.operand == Intrinsic.LOAD: + elif op.operand == Intrinsic.LOAD8: addr = stack.pop() byte = mem[addr] stack.append(byte) ip += 1 - elif op.operand == Intrinsic.STORE: + elif op.operand == Intrinsic.STORE8: store_addr = stack.pop() store_value = stack.pop() mem[store_addr] = store_value & 0xFF @@ -817,7 +817,7 @@ def type_check_program(program: Program): ctx.stack.append(c) elif op.operand == Intrinsic.MEM: ctx.stack.append((DataType.PTR, op.token)) - elif op.operand == Intrinsic.LOAD: + elif op.operand == Intrinsic.LOAD8: assert len(DataType) == 3, "Exhaustive type handling in LOAD intrinsic" if len(ctx.stack) < 1: not_enough_arguments(op) @@ -829,7 +829,7 @@ def type_check_program(program: Program): else: compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD intrinsic: %s" % a_type) exit(1) - elif op.operand == Intrinsic.STORE: + elif op.operand == Intrinsic.STORE8: assert len(DataType) == 3, "Exhaustive type handling in STORE intrinsic" if len(ctx.stack) < 2: not_enough_arguments(op) @@ -1227,13 +1227,13 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): elif op.operand == Intrinsic.MEM: out.write(" ;; -- mem --\n") out.write(" push mem\n") - elif op.operand == Intrinsic.LOAD: + elif op.operand == Intrinsic.LOAD8: out.write(" ;; -- forth load --\n") out.write(" pop rax\n") out.write(" xor rbx, rbx\n") out.write(" mov bl, [rax]\n") out.write(" push rbx\n") - elif op.operand == Intrinsic.STORE: + elif op.operand == Intrinsic.STORE8: out.write(" ;; -- store --\n") out.write(" pop rax\n"); out.write(" pop rbx\n"); @@ -1381,8 +1381,8 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'over': Intrinsic.OVER, 'rot': Intrinsic.ROT, 'mem': Intrinsic.MEM, - '!8': Intrinsic.STORE, - '@8': Intrinsic.LOAD, + '!8': Intrinsic.STORE8, + '@8': Intrinsic.LOAD8, '!64': Intrinsic.STORE64, '@64': Intrinsic.LOAD64, 'cast(ptr)': Intrinsic.CAST_PTR, From c39744b95dc85321bb42eb263bb616dca04a4557 Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 17 Oct 2021 09:02:29 +0700 Subject: [PATCH 044/145] Implement 32 bit memory access intrinsics --- README.md | 2 ++ porth.py | 99 ++++++++++++++++++++++++++++++++++++++++----------- std/std.porth | 11 ++++++ 3 files changed, 91 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 54c00a4a..12b6bbaa 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,8 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter | `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | | `!8` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. | | `@8` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. | +| `!32` | `[place: ptr] [byte: int] --` | store an 4-byte word at the address on the stack. | +| `@32` | `[place: ptr] -- [byte: int]` | load an 4-byte word from the address on the stack. | | `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. | | `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. | | `cast(int)` | `[a: any] -- [a: int]` | cast the element on top of the stack to `int` | diff --git a/porth.py b/porth.py index a1fc06a5..c78fdaee 100755 --- a/porth.py +++ b/porth.py @@ -66,6 +66,8 @@ class Intrinsic(Enum): MEM=auto() LOAD8=auto() STORE8=auto() + LOAD32=auto() + STORE32=auto() LOAD64=auto() STORE64=auto() CAST_PTR=auto() @@ -220,7 +222,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): else: ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 39, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" + assert len(Intrinsic) == 41, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" if op.operand == Intrinsic.PLUS: a = stack.pop() b = stack.pop() @@ -343,6 +345,22 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): store_value = stack.pop() mem[store_addr] = store_value & 0xFF ip += 1 + # TODO: get rid of loops from memory access intrinsics in simulation mode + elif op.operand == Intrinsic.LOAD32: + addr = stack.pop() + _bytes = bytearray(4) + for offset in range(0,4): + _bytes[offset] = mem[addr + offset] + stack.append(int.from_bytes(_bytes, byteorder="little")) + ip += 1 + elif op.operand == Intrinsic.STORE32: + store_addr32 = stack.pop(); + store_value = stack.pop() + store_value32 = store_value.to_bytes(length=4, byteorder="little", signed=(store_value < 0)); + for byte in store_value32: + mem[store_addr32] = byte; + store_addr32 += 1; + ip += 1 elif op.operand == Intrinsic.LOAD64: addr = stack.pop() _bytes = bytearray(8) @@ -553,7 +571,7 @@ def type_check_program(program: Program): ctx.stack.append((DataType.PTR, op.token)) ctx.ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 39, "Exhaustive intrinsic handling in type_check_program()" + assert len(Intrinsic) == 41, "Exhaustive intrinsic handling in type_check_program()" assert isinstance(op.operand, Intrinsic), "This could be a bug in compilation step" if op.operand == Intrinsic.PLUS: assert len(DataType) == 3, "Exhaustive type handling in PLUS intrinsic" @@ -818,7 +836,7 @@ def type_check_program(program: Program): elif op.operand == Intrinsic.MEM: ctx.stack.append((DataType.PTR, op.token)) elif op.operand == Intrinsic.LOAD8: - assert len(DataType) == 3, "Exhaustive type handling in LOAD intrinsic" + assert len(DataType) == 3, "Exhaustive type handling in LOAD8 intrinsic" if len(ctx.stack) < 1: not_enough_arguments(op) exit(1) @@ -827,10 +845,10 @@ def type_check_program(program: Program): if a_type == DataType.PTR: ctx.stack.append((DataType.INT, op.token)) else: - compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD intrinsic: %s" % a_type) + compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD8 intrinsic: %s" % a_type) exit(1) elif op.operand == Intrinsic.STORE8: - assert len(DataType) == 3, "Exhaustive type handling in STORE intrinsic" + assert len(DataType) == 3, "Exhaustive type handling in STORE8 intrinsic" if len(ctx.stack) < 2: not_enough_arguments(op) exit(1) @@ -841,7 +859,33 @@ def type_check_program(program: Program): if a_type == DataType.PTR and b_type == DataType.INT: pass else: - compiler_error_with_expansion_stack(op.token, "invalid argument type for STORE intrinsic") + compiler_error_with_expansion_stack(op.token, "invalid argument type for STORE8 intrinsic") + exit(1) + elif op.operand == Intrinsic.LOAD32: + assert len(DataType) == 3, "Exhaustive type handling in LOAD32 intrinsic" + if len(ctx.stack) < 1: + not_enough_arguments(op) + exit(1) + a_type, a_loc = ctx.stack.pop() + + if a_type == DataType.PTR: + ctx.stack.append((DataType.INT, op.token)) + else: + compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD32 intrinsic: %s" % a_type) + exit(1) + elif op.operand == Intrinsic.STORE32: + assert len(DataType) == 3, "Exhaustive type handling in STORE32 intrinsic" + if len(ctx.stack) < 2: + not_enough_arguments(op) + exit(1) + + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() + + if a_type == DataType.PTR and b_type == DataType.INT: + pass + else: + compiler_error_with_expansion_stack(op.token, "invalid argument type for STORE32 intrinsic") exit(1) elif op.operand == Intrinsic.LOAD64: assert len(DataType) == 3, "Exhaustive type handling in LOAD64 intrinsic" @@ -1081,7 +1125,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): assert isinstance(op.operand, int), "This could be a bug in the parsing step" out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 39, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" + assert len(Intrinsic) == 41, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: out.write(" ;; -- plus --\n") out.write(" pop rax\n") @@ -1228,16 +1272,38 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" ;; -- mem --\n") out.write(" push mem\n") elif op.operand == Intrinsic.LOAD8: - out.write(" ;; -- forth load --\n") + out.write(" ;; -- @8 --\n") out.write(" pop rax\n") out.write(" xor rbx, rbx\n") out.write(" mov bl, [rax]\n") out.write(" push rbx\n") elif op.operand == Intrinsic.STORE8: - out.write(" ;; -- store --\n") + out.write(" ;; -- !8 --\n") out.write(" pop rax\n"); out.write(" pop rbx\n"); out.write(" mov [rax], bl\n"); + elif op.operand == Intrinsic.LOAD32: + out.write(" ;; -- @32 --\n") + out.write(" pop rax\n") + out.write(" xor rbx, rbx\n") + out.write(" mov ebx, [rax]\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.STORE32: + out.write(" ;; -- !32 --\n") + out.write(" pop rax\n"); + out.write(" pop rbx\n"); + out.write(" mov [rax], ebx\n"); + elif op.operand == Intrinsic.LOAD64: + out.write(" ;; -- @64 --\n") + out.write(" pop rax\n") + out.write(" xor rbx, rbx\n") + out.write(" mov rbx, [rax]\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.STORE64: + out.write(" ;; -- !64 --\n") + out.write(" pop rax\n"); + out.write(" pop rbx\n"); + out.write(" mov [rax], rbx\n"); elif op.operand == Intrinsic.ARGC: out.write(" ;; -- argc --\n") out.write(" mov rax, [args_ptr]\n") @@ -1256,17 +1322,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" push rax\n") out.write(" push str_%d\n" % len(strs)) strs.append(value) - elif op.operand == Intrinsic.LOAD64: - out.write(" ;; -- forth load64 --\n") - out.write(" pop rax\n") - out.write(" xor rbx, rbx\n") - out.write(" mov rbx, [rax]\n") - out.write(" push rbx\n") - elif op.operand == Intrinsic.STORE64: - out.write(" ;; -- forth store64 --\n") - out.write(" pop rax\n"); - out.write(" pop rbx\n"); - out.write(" mov [rax], rbx\n"); elif op.operand == Intrinsic.CAST_PTR: out.write(" ;; -- cast(ptr) --\n") elif op.operand == Intrinsic.CAST_INT: @@ -1357,7 +1412,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'include': Keyword.INCLUDE, } -assert len(Intrinsic) == 39, "Exhaustive INTRINSIC_BY_NAMES definition" +assert len(Intrinsic) == 41, "Exhaustive INTRINSIC_BY_NAMES definition" INTRINSIC_BY_NAMES = { '+': Intrinsic.PLUS, '-': Intrinsic.MINUS, @@ -1383,6 +1438,8 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'mem': Intrinsic.MEM, '!8': Intrinsic.STORE8, '@8': Intrinsic.LOAD8, + '!32': Intrinsic.STORE32, + '@32': Intrinsic.LOAD32, '!64': Intrinsic.STORE64, '@64': Intrinsic.LOAD64, 'cast(ptr)': Intrinsic.CAST_PTR, diff --git a/std/std.porth b/std/std.porth index 027bef24..3b7975d2 100644 --- a/std/std.porth +++ b/std/std.porth @@ -398,6 +398,17 @@ macro dec64 dup @64 1 - swap !64 end +macro inc32 + dup @32 1 + swap !32 +end + +macro dec32 + dup @32 1 - swap !32 +end + +macro sizeof(u64) 8 end +macro sizeof(u32) 4 end + macro cstrlen dup while dup @8 0 != do 1 + end From 9ba3bc1a6db8e709063195f0c05981ad8ecf12c4 Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 17 Oct 2021 09:02:40 +0700 Subject: [PATCH 045/145] Use 32 bit numbers in Project Euler Problem 10 to reduce memory usage --- euler/problem10.porth | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/euler/problem10.porth b/euler/problem10.porth index 899692cb..35ee542c 100644 --- a/euler/problem10.porth +++ b/euler/problem10.porth @@ -1,25 +1,23 @@ include "std.porth" -// TODO: this solution requires more than 640kb of memory -// Add a test case when there is an opportunity to customize that +// TODO: this solution is too slow for simulation mode even with pypy -macro sizeof(u64) 8 end macro ans mem end -macro primes-count ans 8 + end -macro primes primes-count 8 + end +macro primes-count ans sizeof(u64) + end +macro primes primes-count sizeof(u32) + end macro push-prime // value -- - primes-count @64 sizeof(u64) * primes + !64 - primes-count inc64 + primes-count @32 sizeof(u32) * primes + !32 + primes-count inc32 end macro is-prime // [value: int] -> [ret: bool] 0 while - if 2dup sizeof(u64) * primes + @64 dup * >= do - 2dup sizeof(u64) * primes + @64 mod 0 != + if 2dup sizeof(u32) * primes + @32 dup * >= do + 2dup sizeof(u32) * primes + @32 mod 0 != else false end do 1 + end - sizeof(u64) * primes + @64 dup * < + sizeof(u32) * primes + @32 dup * < end 2 push-prime From 233ab176b88393b4058846a119cf698353bf033d Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 17 Oct 2021 20:33:47 +0700 Subject: [PATCH 046/145] Get rid of loops from memory access intrinsics in simulation mode --- porth.py | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/porth.py b/porth.py index c78fdaee..c31ce789 100755 --- a/porth.py +++ b/porth.py @@ -345,36 +345,23 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): store_value = stack.pop() mem[store_addr] = store_value & 0xFF ip += 1 - # TODO: get rid of loops from memory access intrinsics in simulation mode elif op.operand == Intrinsic.LOAD32: - addr = stack.pop() - _bytes = bytearray(4) - for offset in range(0,4): - _bytes[offset] = mem[addr + offset] - stack.append(int.from_bytes(_bytes, byteorder="little")) + load_addr = stack.pop() + stack.append(int.from_bytes(mem[load_addr:load_addr+4], byteorder="little")) ip += 1 elif op.operand == Intrinsic.STORE32: - store_addr32 = stack.pop(); + store_addr = stack.pop(); store_value = stack.pop() - store_value32 = store_value.to_bytes(length=4, byteorder="little", signed=(store_value < 0)); - for byte in store_value32: - mem[store_addr32] = byte; - store_addr32 += 1; + mem[store_addr:store_addr+4] = store_value.to_bytes(length=4, byteorder="little", signed=(store_value < 0)); ip += 1 elif op.operand == Intrinsic.LOAD64: - addr = stack.pop() - _bytes = bytearray(8) - for offset in range(0,8): - _bytes[offset] = mem[addr + offset] - stack.append(int.from_bytes(_bytes, byteorder="little")) + load_addr = stack.pop() + stack.append(int.from_bytes(mem[load_addr:load_addr+8], byteorder="little")) ip += 1 elif op.operand == Intrinsic.STORE64: - store_addr64 = stack.pop(); + store_addr = stack.pop(); store_value = stack.pop() - store_value64 = store_value.to_bytes(length=8, byteorder="little", signed=(store_value < 0)); - for byte in store_value64: - mem[store_addr64] = byte; - store_addr64 += 1; + mem[store_addr:store_addr+8] = store_value.to_bytes(length=8, byteorder="little", signed=(store_value < 0)); ip += 1 elif op.operand == Intrinsic.ARGC: stack.append(argc) From d02b7d640217738cb56836623b68bf35a79477d6 Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 17 Oct 2021 20:38:23 +0700 Subject: [PATCH 047/145] Add more details about Project Euler Problem 10 performance --- euler/problem10.porth | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/euler/problem10.porth b/euler/problem10.porth index 35ee542c..3cf210d2 100644 --- a/euler/problem10.porth +++ b/euler/problem10.porth @@ -1,6 +1,10 @@ include "std.porth" // TODO: this solution is too slow for simulation mode even with pypy +// It takes ~1min with `pypy ./test.py run ./euler/problem10.porth` on my machine +// It's actually relatively slow even in the compilation mode (~1sec). +// Maybe we could come up with a faster method of computer prime numbers? +// We could precompute them and save the to a file I guess. macro ans mem end macro primes-count ans sizeof(u64) + end From 9390b9ed835b4a13c545c3b74fb634b54f972eeb Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 18 Oct 2021 00:31:38 +0700 Subject: [PATCH 048/145] Add support for `-` intrinsic to porth.porth --- porth.porth | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/porth.porth b/porth.porth index 20013a59..4364b868 100644 --- a/porth.porth +++ b/porth.porth @@ -8,7 +8,9 @@ macro SIM_STACK_CAP 1024 end macro OP_PUSH_INT 0 end macro OP_PLUS 1 end -macro OP_PRINT 2 end +macro OP_MINUS 2 end +macro OP_PRINT 3 end +macro COUNT_OPS 4 end macro sizeof(Op) 16 end macro Op.type 0 + end @@ -104,10 +106,17 @@ macro push-op // type operand -- end macro print-op-type + if COUNT_OPS 4 != do + here eputs ": Assertion Failed: Exhaustive handling of Op types in print-op-type\n" eputs + 1 exit + end + if dup OP_PUSH_INT = do "OP_PUSH_INT" puts elif dup OP_PLUS = do "OP_PLUS" puts + elif dup OP_MINUS = do + "OP_MINUS" puts elif dup OP_PRINT = do "OP_PRINT" puts else @@ -172,6 +181,12 @@ macro compile-ops // -- 0 while dup ops-count @64 < do dup sizeof(Op) * ops + + // TODO: compile time assertion + if COUNT_OPS 4 != do + here eputs ": Assertion Failed: Exhaustive handling of Op types in compile-ops\n" eputs + 1 exit + end + if dup @Op.type OP_PUSH_INT = do " ;; -- push int " puts dup @Op.operand putd " --\n" puts " mov rax, " puts dup @Op.operand putd "\n" puts @@ -182,6 +197,12 @@ macro compile-ops // -- " pop rbx\n" puts " add rax, rbx\n" puts " push rax\n" puts + elif dup @Op.type OP_MINUS = do + " ;; -- minus --\n" puts + " pop rax\n" puts + " pop rbx\n" puts + " sub rbx, rax\n" puts + " push rbx\n" puts elif dup @Op.type OP_PRINT = do " ;; -- print --\n" puts " pop rdi\n" puts @@ -208,6 +229,11 @@ macro simulate-ops // -- 0 while dup ops-count @64 < do dup sizeof(Op) * ops + + if COUNT_OPS 4 != do + here eputs ": Assertion Failed: Exhaustive handling of Op types in simulate-ops\n" eputs + 1 exit + end + if dup @Op.type OP_PUSH_INT = do dup @Op.operand sim-stack-push elif dup @Op.type OP_PLUS = do @@ -215,6 +241,12 @@ macro simulate-ops // -- sim-stack-pop + sim-stack-push + elif dup @Op.type OP_MINUS = do + sim-stack-pop + sim-stack-pop + swap + - + sim-stack-push elif dup @Op.type OP_PRINT = do sim-stack-pop print else @@ -229,9 +261,9 @@ macro simulate-ops // -- end macro parse_file_path - O_RDONLY // flags + O_RDONLY // flags file_path_cstr @64 cast(ptr) // pathname - AT_FDCWD // dirfd + AT_FDCWD // dirfd openat if dup 0 < do @@ -267,8 +299,16 @@ macro parse_file_path while line @Str.count 0 > do line str-trim-left word line str-chop-word + + if COUNT_OPS 4 != do + here eputs ": Assertion Failed: Exhaustive handling of Op types in parse-file-path\n" eputs + 1 exit + end + if word @Str "+" streq do OP_PLUS 0 push-op + elif word @Str "-" streq do + OP_MINUS 0 push-op elif word @Str "print" streq do OP_PRINT 0 push-op else From 4f156000dfb224e6c631a7ce04bfa00eefac2346 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 18 Oct 2021 03:13:33 +0700 Subject: [PATCH 049/145] Add logical not to std.porth --- std/std.porth | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/std/std.porth b/std/std.porth index 3b7975d2..bf40d66d 100644 --- a/std/std.porth +++ b/std/std.porth @@ -516,6 +516,12 @@ macro str-chop-word // line input -- 2drop end +// Custom logical not, since the intrinsic `not` is the bitwise one and does not allow +// to properly invert a boolean. +macro lnot + cast(int) 1 - cast(bool) +end + // Deprecated Words macro .64 swap !64 end macro ,64 @64 end From c4e5f4cfa265f9e2e8f957a1f5187cda3249a467 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 18 Oct 2021 03:13:40 +0700 Subject: [PATCH 050/145] Add isdigit to std.porth --- std/std.porth | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/std/std.porth b/std/std.porth index bf40d66d..dd5da9f7 100644 --- a/std/std.porth +++ b/std/std.porth @@ -516,6 +516,12 @@ macro str-chop-word // line input -- 2drop end +macro isdigit + dup '0' >= + swap '9' <= + and +end + // Custom logical not, since the intrinsic `not` is the bitwise one and does not allow // to properly invert a boolean. macro lnot From 9f4b0be1866d0058ec016de96abee41cbf2e588f Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 18 Oct 2021 03:34:13 +0700 Subject: [PATCH 051/145] Report location of unknown words in porth.porth --- porth.porth | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/porth.porth b/porth.porth index 4364b868..c4afdd6d 100644 --- a/porth.porth +++ b/porth.porth @@ -30,11 +30,12 @@ macro statbuf fd 8 + end macro content statbuf sizeof(stat) + end macro line content sizeof(Str) + end macro word line sizeof(Str) + end -macro streq_a word sizeof(Str) + end +macro line_number word sizeof(Str) + end +macro line_start line_number 8 + end +macro streq_a line_start 8 + end macro streq_b streq_a sizeof(Str) + end -macro parse_int_a streq_b sizeof(Str) + end -macro sim-stack-count parse_int_a sizeof(Str) + end +macro sim-stack-count streq_b sizeof(Str) + end macro sim-stack sim-stack-count 8 + end macro ops-count sim-stack SIM_STACK_CAP 8 * + end macro ops ops-count 8 + end @@ -72,11 +73,19 @@ macro putd // u64 -- drop end -macro parse_int // n1 s1 - ret - parse_int_a !Str - 0 0 while dup parse_int_a @Str.count < do - // TODO: parse_int does not check if the input is actually a number - dup parse_int_a @Str.data + @8 '0' - +macro try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret + 0 0 while dup word @Str.count < do + dup word @Str.data + @8 + + if dup isdigit lnot do + file_path_cstr @64 cast(ptr) cstr-to-str eputs + ":" puts line_number @64 putd + ":" puts word @Str.data cast(int) line_start @64 - 1 + putd + ": ERROR: `" eputs word @Str eputs "` is unknown word\n" eputs + 1 exit + end + + '0' - rot 10 * + swap 1 + @@ -258,9 +267,9 @@ macro simulate-ops // -- 1 + end drop -end +end -macro parse_file_path +macro parse_file_path_cstr_into_ops O_RDONLY // flags file_path_cstr @64 cast(ptr) // pathname AT_FDCWD // dirfd @@ -294,8 +303,10 @@ macro parse_file_path 1 exit end + 1 line_number !64 while content @Str.count 0 > do line content str-chop-line + line @Str.data line_start !64 while line @Str.count 0 > do line str-trim-left word line str-chop-word @@ -312,9 +323,10 @@ macro parse_file_path elif word @Str "print" streq do OP_PRINT 0 push-op else - OP_PUSH_INT word @Str parse_int push-op + OP_PUSH_INT try_to_parse_word_as_int_or_fail_as_unknown_word push-op end end + line_number inc64 end // TODO: parse_file_path does not clean up resources after itself end @@ -346,7 +358,7 @@ macro main // -- 2 nth_argv file_path_cstr !64 - parse_file_path + parse_file_path_cstr_into_ops simulate-ops elif dup "com"c cstreq do @@ -358,7 +370,7 @@ macro main // -- 2 nth_argv file_path_cstr !64 - parse_file_path + parse_file_path_cstr_into_ops compile-ops elif dup "help"c cstreq do @@ -373,7 +385,7 @@ macro main // -- 2 nth_argv file_path_cstr !64 - parse_file_path + parse_file_path_cstr_into_ops dump-ops else From 12cf6379d634b3f398c60c8fb2d3ddb2a2bf6346 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 18 Oct 2021 03:42:53 +0700 Subject: [PATCH 052/145] Implement `dup` and `drop` for porth.porth --- porth.porth | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/porth.porth b/porth.porth index c4afdd6d..ed427e26 100644 --- a/porth.porth +++ b/porth.porth @@ -10,7 +10,9 @@ macro OP_PUSH_INT 0 end macro OP_PLUS 1 end macro OP_MINUS 2 end macro OP_PRINT 3 end -macro COUNT_OPS 4 end +macro OP_DUP 4 end +macro OP_DROP 5 end +macro COUNT_OPS 6 end macro sizeof(Op) 16 end macro Op.type 0 + end @@ -115,7 +117,7 @@ macro push-op // type operand -- end macro print-op-type - if COUNT_OPS 4 != do + if COUNT_OPS 6 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in print-op-type\n" eputs 1 exit end @@ -126,6 +128,10 @@ macro print-op-type "OP_PLUS" puts elif dup OP_MINUS = do "OP_MINUS" puts + elif dup OP_DUP = do + "OP_DUP" puts + elif dup OP_DROP = do + "OP_DROP" puts elif dup OP_PRINT = do "OP_PRINT" puts else @@ -191,7 +197,7 @@ macro compile-ops // -- dup sizeof(Op) * ops + // TODO: compile time assertion - if COUNT_OPS 4 != do + if COUNT_OPS 6 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in compile-ops\n" eputs 1 exit end @@ -216,6 +222,14 @@ macro compile-ops // -- " ;; -- print --\n" puts " pop rdi\n" puts " call print\n" puts + elif dup @Op.type OP_DUP = do + " ;; -- dup --\n" puts + " pop rax\n" puts + " push rax\n" puts + " push rax\n" puts + elif dup @Op.type OP_DROP = do + " ;; -- drop --\n" puts + " pop rax\n" puts else here eputs ": unreachable\n" eputs 1 exit end @@ -238,7 +252,7 @@ macro simulate-ops // -- 0 while dup ops-count @64 < do dup sizeof(Op) * ops + - if COUNT_OPS 4 != do + if COUNT_OPS 6 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in simulate-ops\n" eputs 1 exit end @@ -258,6 +272,14 @@ macro simulate-ops // -- sim-stack-push elif dup @Op.type OP_PRINT = do sim-stack-pop print + elif dup @Op.type OP_DUP = do + sim-stack-pop + dup + sim-stack-push + sim-stack-push + elif dup @Op.type OP_DROP = do + sim-stack-pop + drop else here eputs ": unreachable\n" eputs 1 exit end @@ -311,7 +333,7 @@ macro parse_file_path_cstr_into_ops line str-trim-left word line str-chop-word - if COUNT_OPS 4 != do + if COUNT_OPS 6 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in parse-file-path\n" eputs 1 exit end @@ -322,6 +344,10 @@ macro parse_file_path_cstr_into_ops OP_MINUS 0 push-op elif word @Str "print" streq do OP_PRINT 0 push-op + elif word @Str "dup" streq do + OP_DUP 0 push-op + elif word @Str "drop" streq do + OP_DROP 0 push-op else OP_PUSH_INT try_to_parse_word_as_int_or_fail_as_unknown_word push-op end From ae0d74072cf75cd8f14b5d3edbb581d3fbdafbfd Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 18 Oct 2021 05:19:57 +0700 Subject: [PATCH 053/145] Implement 16 bit memory access intrinsics --- README.md | 2 ++ porth.py | 58 ++++++++++++++++++++++++++++++++++++++++++---- tests/memory.porth | 4 ++++ tests/memory.txt | 3 ++- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 12b6bbaa..9242ec04 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,8 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter | `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | | `!8` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. | | `@8` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. | +| `!16` | `[place: ptr] [byte: int] --` | store an 2-byte word at the address on the stack. | +| `@16` | `[place: ptr] -- [byte: int]` | load an 2-byte word from the address on the stack. | | `!32` | `[place: ptr] [byte: int] --` | store an 4-byte word at the address on the stack. | | `@32` | `[place: ptr] -- [byte: int]` | load an 4-byte word from the address on the stack. | | `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. | diff --git a/porth.py b/porth.py index c31ce789..0b859650 100755 --- a/porth.py +++ b/porth.py @@ -66,6 +66,8 @@ class Intrinsic(Enum): MEM=auto() LOAD8=auto() STORE8=auto() + LOAD16=auto() + STORE16=auto() LOAD32=auto() STORE32=auto() LOAD64=auto() @@ -222,7 +224,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): else: ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 41, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" + assert len(Intrinsic) == 43, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" if op.operand == Intrinsic.PLUS: a = stack.pop() b = stack.pop() @@ -345,6 +347,15 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): store_value = stack.pop() mem[store_addr] = store_value & 0xFF ip += 1 + elif op.operand == Intrinsic.LOAD16: + load_addr = stack.pop() + stack.append(int.from_bytes(mem[load_addr:load_addr+2], byteorder="little")) + ip += 1 + elif op.operand == Intrinsic.STORE16: + store_addr = stack.pop(); + store_value = stack.pop() + mem[store_addr:store_addr+2] = store_value.to_bytes(length=2, byteorder="little", signed=(store_value < 0)); + ip += 1 elif op.operand == Intrinsic.LOAD32: load_addr = stack.pop() stack.append(int.from_bytes(mem[load_addr:load_addr+4], byteorder="little")) @@ -558,7 +569,7 @@ def type_check_program(program: Program): ctx.stack.append((DataType.PTR, op.token)) ctx.ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 41, "Exhaustive intrinsic handling in type_check_program()" + assert len(Intrinsic) == 43, "Exhaustive intrinsic handling in type_check_program()" assert isinstance(op.operand, Intrinsic), "This could be a bug in compilation step" if op.operand == Intrinsic.PLUS: assert len(DataType) == 3, "Exhaustive type handling in PLUS intrinsic" @@ -848,6 +859,32 @@ def type_check_program(program: Program): else: compiler_error_with_expansion_stack(op.token, "invalid argument type for STORE8 intrinsic") exit(1) + elif op.operand == Intrinsic.LOAD16: + assert len(DataType) == 3, "Exhaustive type handling in LOAD16 intrinsic" + if len(ctx.stack) < 1: + not_enough_arguments(op) + exit(1) + a_type, a_loc = ctx.stack.pop() + + if a_type == DataType.PTR: + ctx.stack.append((DataType.INT, op.token)) + else: + compiler_error_with_expansion_stack(op.token, "invalid argument type for LOAD16 intrinsic: %s" % a_type) + exit(1) + elif op.operand == Intrinsic.STORE16: + assert len(DataType) == 3, "Exhaustive type handling in STORE16 intrinsic" + if len(ctx.stack) < 2: + not_enough_arguments(op) + exit(1) + + a_type, a_loc = ctx.stack.pop() + b_type, b_loc = ctx.stack.pop() + + if a_type == DataType.PTR and b_type == DataType.INT: + pass + else: + compiler_error_with_expansion_stack(op.token, "invalid argument type for STORE16 intrinsic") + exit(1) elif op.operand == Intrinsic.LOAD32: assert len(DataType) == 3, "Exhaustive type handling in LOAD32 intrinsic" if len(ctx.stack) < 1: @@ -1112,7 +1149,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): assert isinstance(op.operand, int), "This could be a bug in the parsing step" out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 41, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" + assert len(Intrinsic) == 43, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: out.write(" ;; -- plus --\n") out.write(" pop rax\n") @@ -1269,6 +1306,17 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" pop rax\n"); out.write(" pop rbx\n"); out.write(" mov [rax], bl\n"); + elif op.operand == Intrinsic.LOAD16: + out.write(" ;; -- @16 --\n") + out.write(" pop rax\n") + out.write(" xor rbx, rbx\n") + out.write(" mov bx, [rax]\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.STORE16: + out.write(" ;; -- !16 --\n") + out.write(" pop rax\n"); + out.write(" pop rbx\n"); + out.write(" mov [rax], bx\n"); elif op.operand == Intrinsic.LOAD32: out.write(" ;; -- @32 --\n") out.write(" pop rax\n") @@ -1399,7 +1447,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'include': Keyword.INCLUDE, } -assert len(Intrinsic) == 41, "Exhaustive INTRINSIC_BY_NAMES definition" +assert len(Intrinsic) == 43, "Exhaustive INTRINSIC_BY_NAMES definition" INTRINSIC_BY_NAMES = { '+': Intrinsic.PLUS, '-': Intrinsic.MINUS, @@ -1425,6 +1473,8 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'mem': Intrinsic.MEM, '!8': Intrinsic.STORE8, '@8': Intrinsic.LOAD8, + '!16': Intrinsic.STORE16, + '@16': Intrinsic.LOAD16, '!32': Intrinsic.STORE32, '@32': Intrinsic.LOAD32, '!64': Intrinsic.STORE64, diff --git a/tests/memory.porth b/tests/memory.porth index dde1be0e..6ca7d580 100644 --- a/tests/memory.porth +++ b/tests/memory.porth @@ -20,3 +20,7 @@ mem 2 + dup @8 1 + swap !8 // print UINT64_MAX (Largest 64 bit word) 18446744073709551615 mem !64 mem @64 print + +255 mem !8 +255 mem 1 + !8 +mem @16 print diff --git a/tests/memory.txt b/tests/memory.txt index a4a9a422..1f12ef65 100644 --- a/tests/memory.txt +++ b/tests/memory.txt @@ -2,12 +2,13 @@ :b stdin 0 :i returncode 0 -:b stdout 33 +:b stdout 39 abc 4 bcd 4 18446744073709551615 +65535 :b stderr 0 From 05d7895abd2a062bd388ac075da18d67912bf89f Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 00:37:45 +0700 Subject: [PATCH 054/145] Fix error in the documentation --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9242ec04..6b525046 100644 --- a/README.md +++ b/README.md @@ -239,11 +239,11 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter | `mem` | `-- [mem: ptr]` | pushes the address of the beginning of the memory where you can read and write onto the stack. | | `!8` | `[byte: int] [place: ptr] -- ` | store a given byte at the address on the stack. | | `@8` | `[place: ptr] -- [byte: int]` | load a byte from the address on the stack. | -| `!16` | `[place: ptr] [byte: int] --` | store an 2-byte word at the address on the stack. | +| `!16` | `[byte: int] [place: ptr] --` | store an 2-byte word at the address on the stack. | | `@16` | `[place: ptr] -- [byte: int]` | load an 2-byte word from the address on the stack. | -| `!32` | `[place: ptr] [byte: int] --` | store an 4-byte word at the address on the stack. | +| `!32` | `[byte: int] [place: ptr] --` | store an 4-byte word at the address on the stack. | | `@32` | `[place: ptr] -- [byte: int]` | load an 4-byte word from the address on the stack. | -| `!64` | `[place: ptr] [byte: int] --` | store an 8-byte word at the address on the stack. | +| `!64` | `[byte: int] [place: ptr] --` | store an 8-byte word at the address on the stack. | | `@64` | `[place: ptr] -- [byte: int]` | load an 8-byte word from the address on the stack. | | `cast(int)` | `[a: any] -- [a: int]` | cast the element on top of the stack to `int` | | `cast(bool)` | `[a: any] -- [a: bool]` | cast the element on top of the stack to `bool` | From e83bb4f644362035b7022534d13c6a6147630ec8 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 00:38:03 +0700 Subject: [PATCH 055/145] Implement memory region definition --- editor/porth-mode.el | 2 +- porth.py | 229 +++++++++++++++++++++++++++---------------- std/std.porth | 1 + 3 files changed, 146 insertions(+), 86 deletions(-) diff --git a/editor/porth-mode.el b/editor/porth-mode.el index 558c3b4a..8d101a8f 100644 --- a/editor/porth-mode.el +++ b/editor/porth-mode.el @@ -42,7 +42,7 @@ (eval-and-compile (defconst porth-keywords - '("if" "elif" "else" "end" "while" "do" "macro" "include"))) + '("if" "elif" "else" "end" "while" "do" "macro" "include" "memory"))) (defconst porth-highlights `((,(regexp-opt porth-keywords 'symbols) . font-lock-keyword-face))) diff --git a/porth.py b/porth.py index 0b859650..c2ae316f 100755 --- a/porth.py +++ b/porth.py @@ -16,7 +16,6 @@ DEFAULT_EXPANSION_LIMIT=1000 EXPANSION_DIAGNOSTIC_LIMIT=10 # TODO: customize memory capacity via a flag -MEM_CAPACITY = 640_000 # should be enough for everyone SIM_NULL_POINTER_PADDING = 1 # just a little bit of a padding at the beginning of the memory to make 0 an invalid address SIM_STR_CAPACITY = 640_000 SIM_ARGV_CAPACITY = 640_000 @@ -34,6 +33,7 @@ class Keyword(Enum): DO=auto() MACRO=auto() INCLUDE=auto() + MEMORY=auto() class DataType(IntEnum): INT=auto() @@ -63,7 +63,6 @@ class Intrinsic(Enum): DROP=auto() OVER=auto() ROT=auto() - MEM=auto() LOAD8=auto() STORE8=auto() LOAD16=auto() @@ -90,6 +89,7 @@ class OpType(Enum): PUSH_INT=auto() PUSH_STR=auto() PUSH_CSTR=auto() + PUSH_MEM=auto() INTRINSIC=auto() IF=auto() ELIF=auto() @@ -118,6 +118,7 @@ class Token: expanded_count: int = 0 OpAddr=int +MemAddr=int @dataclass class Op: @@ -125,8 +126,10 @@ class Op: token: Token operand: Optional[Union[int, str, Intrinsic, OpAddr]] = None -Program=List[Op] - +@dataclass +class Program: + ops: List[Op] + memory_capacity: int def get_cstr_from_mem(mem: bytearray, ptr: int) -> bytes: end = ptr @@ -141,7 +144,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): CLOCK_MONOTONIC=1 stack: List[int] = [] - mem = bytearray(SIM_NULL_POINTER_PADDING + SIM_STR_CAPACITY + SIM_ARGV_CAPACITY + MEM_CAPACITY) + mem = bytearray(SIM_NULL_POINTER_PADDING + SIM_STR_CAPACITY + SIM_ARGV_CAPACITY + program.memory_capacity) str_buf_ptr = SIM_NULL_POINTER_PADDING str_ptrs: Dict[int, int] = {} @@ -170,9 +173,9 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): assert argc*8 <= SIM_ARGV_CAPACITY, "Argv buffer, overflow" ip = 0 - while ip < len(program): - assert len(OpType) == 10, "Exhaustive op handling in simulate_little_endian_linux" - op = program[ip] + while ip < len(program.ops): + assert len(OpType) == 11, "Exhaustive op handling in simulate_little_endian_linux" + op = program.ops[ip] try: if op.typ == OpType.PUSH_INT: assert isinstance(op.operand, int), "This could be a bug in the parsing step" @@ -203,6 +206,10 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): assert str_size <= SIM_STR_CAPACITY, "String buffer overflow" stack.append(str_ptrs[ip]) ip += 1 + elif op.typ == OpType.PUSH_MEM: + assert isinstance(op.operand, MemAddr), "This could be a bug in the parsing step" + stack.append(mem_buf_ptr + op.operand) + ip += 1 elif op.typ == OpType.IF: ip += 1 elif op.typ == OpType.WHILE: @@ -224,7 +231,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): else: ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 43, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" + assert len(Intrinsic) == 42, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" if op.operand == Intrinsic.PLUS: a = stack.pop() b = stack.pop() @@ -334,9 +341,6 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): stack.append(a) stack.append(c) ip += 1 - elif op.operand == Intrinsic.MEM: - stack.append(mem_buf_ptr) - ip += 1 elif op.operand == Intrinsic.LOAD8: addr = stack.pop() byte = mem[addr] @@ -550,14 +554,14 @@ def type_check_program(program: Program): contexts: List[Context] = [Context(stack=[], ip=0)] while len(contexts) > 0: ctx = contexts[-1]; - if ctx.ip >= len(program): + if ctx.ip >= len(program.ops): if len(ctx.stack) != 0: compiler_error_with_expansion_stack(ctx.stack[-1][1], "unhandled data on the data stack: %s" % list(map(lambda x: x[0], ctx.stack))) exit(1) contexts.pop() continue - op = program[ctx.ip] - assert len(OpType) == 10, "Exhaustive ops handling in type_check_program()" + op = program.ops[ctx.ip] + assert len(OpType) == 11, "Exhaustive ops handling in type_check_program()" if op.typ == OpType.PUSH_INT: ctx.stack.append((DataType.INT, op.token)) ctx.ip += 1 @@ -568,8 +572,11 @@ def type_check_program(program: Program): elif op.typ == OpType.PUSH_CSTR: ctx.stack.append((DataType.PTR, op.token)) ctx.ip += 1 + elif op.typ == OpType.PUSH_MEM: + ctx.stack.append((DataType.PTR, op.token)) + ctx.ip += 1 elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 43, "Exhaustive intrinsic handling in type_check_program()" + assert len(Intrinsic) == 42, "Exhaustive intrinsic handling in type_check_program()" assert isinstance(op.operand, Intrinsic), "This could be a bug in compilation step" if op.operand == Intrinsic.PLUS: assert len(DataType) == 3, "Exhaustive type handling in PLUS intrinsic" @@ -831,8 +838,6 @@ def type_check_program(program: Program): ctx.stack.append(b) ctx.stack.append(a) ctx.stack.append(c) - elif op.operand == Intrinsic.MEM: - ctx.stack.append((DataType.PTR, op.token)) elif op.operand == Intrinsic.LOAD8: assert len(DataType) == 3, "Exhaustive type handling in LOAD8 intrinsic" if len(ctx.stack) < 1: @@ -1101,9 +1106,9 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("global _start\n") out.write("_start:\n") out.write(" mov [args_ptr], rsp\n") - for ip in range(len(program)): - op = program[ip] - assert len(OpType) == 10, "Exhaustive ops handling in generate_nasm_linux_x86_64" + for ip in range(len(program.ops)): + op = program.ops[ip] + assert len(OpType) == 11, "Exhaustive ops handling in generate_nasm_linux_x86_64" out.write("addr_%d:\n" % ip) if op.typ == OpType.PUSH_INT: assert isinstance(op.operand, int), "This could be a bug in the parsing step" @@ -1125,6 +1130,12 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" ;; -- push str --\n") out.write(" push str_%d\n" % len(strs)) strs.append(value) + elif op.typ == OpType.PUSH_MEM: + assert isinstance(op.operand, MemAddr), "This could be a bug in the parsing step" + out.write(" ;; -- push mem --\n") + out.write(" mov rax, mem\n") + out.write(" add rax, %d\n" % op.operand) + out.write(" push rax\n") elif op.typ == OpType.IF: out.write(" ;; -- if --\n") elif op.typ == OpType.WHILE: @@ -1149,7 +1160,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): assert isinstance(op.operand, int), "This could be a bug in the parsing step" out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 43, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" + assert len(Intrinsic) == 42, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: out.write(" ;; -- plus --\n") out.write(" pop rax\n") @@ -1292,9 +1303,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" push rbx\n") out.write(" push rax\n") out.write(" push rcx\n") - elif op.operand == Intrinsic.MEM: - out.write(" ;; -- mem --\n") - out.write(" push mem\n") elif op.operand == Intrinsic.LOAD8: out.write(" ;; -- @8 --\n") out.write(" pop rax\n") @@ -1424,7 +1432,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): else: assert False, "unreachable" - out.write("addr_%d:\n" % len(program)) + out.write("addr_%d:\n" % len(program.ops)) out.write(" mov rax, 60\n") out.write(" mov rdi, 0\n") out.write(" syscall\n") @@ -1433,9 +1441,9 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("str_%d: db %s\n" % (index, ','.join(map(hex, list(s))))) out.write("segment .bss\n") out.write("args_ptr: resq 1\n") - out.write("mem: resb %d\n" % MEM_CAPACITY) + out.write("mem: resb %d\n" % program.memory_capacity) -assert len(Keyword) == 8, "Exhaustive KEYWORD_NAMES definition." +assert len(Keyword) == 9, "Exhaustive KEYWORD_NAMES definition." KEYWORD_NAMES = { 'if': Keyword.IF, 'elif': Keyword.ELIF, @@ -1445,9 +1453,10 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'do': Keyword.DO, 'macro': Keyword.MACRO, 'include': Keyword.INCLUDE, + 'memory': Keyword.MEMORY, } -assert len(Intrinsic) == 43, "Exhaustive INTRINSIC_BY_NAMES definition" +assert len(Intrinsic) == 42, "Exhaustive INTRINSIC_BY_NAMES definition" INTRINSIC_BY_NAMES = { '+': Intrinsic.PLUS, '-': Intrinsic.MINUS, @@ -1470,7 +1479,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'drop': Intrinsic.DROP, 'over': Intrinsic.OVER, 'rot': Intrinsic.ROT, - 'mem': Intrinsic.MEM, '!8': Intrinsic.STORE8, '@8': Intrinsic.LOAD8, '!16': Intrinsic.STORE16, @@ -1527,9 +1535,10 @@ def expand_macro(macro: Macro, expanded_from: Token) -> List[Token]: def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], expansion_limit: int) -> Program: stack: List[OpAddr] = [] - program: List[Op] = [] + program: Program = Program(ops=[], memory_capacity=0) rtokens: List[Token] = list(reversed(tokens)) macros: Dict[str, Macro] = {} + memories: Dict[str, MemAddr] = {} ip: OpAddr = 0; while len(rtokens) > 0: token = rtokens.pop() @@ -1537,120 +1546,123 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp if token.typ == TokenType.WORD: assert isinstance(token.value, str), "This could be a bug in the lexer" if token.value in INTRINSIC_BY_NAMES: - program.append(Op(typ=OpType.INTRINSIC, token=token, operand=INTRINSIC_BY_NAMES[token.value])) + program.ops.append(Op(typ=OpType.INTRINSIC, token=token, operand=INTRINSIC_BY_NAMES[token.value])) ip += 1 elif token.value in macros: if token.expanded_count >= expansion_limit: compiler_error_with_expansion_stack(token, "the macro exceeded the expansion limit (it expanded %d times)" % token.expanded_count) exit(1) rtokens += reversed(expand_macro(macros[token.value], token)) + elif token.value in memories: + program.ops.append(Op(typ=OpType.PUSH_MEM, token=token, operand=memories[token.value])) + ip += 1 else: compiler_error_with_expansion_stack(token, "unknown word `%s`" % token.value) exit(1) elif token.typ == TokenType.INT: assert isinstance(token.value, int), "This could be a bug in the lexer" - program.append(Op(typ=OpType.PUSH_INT, operand=token.value, token=token)) + program.ops.append(Op(typ=OpType.PUSH_INT, operand=token.value, token=token)) ip += 1 elif token.typ == TokenType.STR: assert isinstance(token.value, str), "This could be a bug in the lexer" - program.append(Op(typ=OpType.PUSH_STR, operand=token.value, token=token)); + program.ops.append(Op(typ=OpType.PUSH_STR, operand=token.value, token=token)); ip += 1 elif token.typ == TokenType.CSTR: assert isinstance(token.value, str), "This could be a bug in the lexer" - program.append(Op(typ=OpType.PUSH_CSTR, operand=token.value, token=token)); + program.ops.append(Op(typ=OpType.PUSH_CSTR, operand=token.value, token=token)); ip += 1 elif token.typ == TokenType.CHAR: assert isinstance(token.value, int) - program.append(Op(typ=OpType.PUSH_INT, operand=token.value, token=token)); + program.ops.append(Op(typ=OpType.PUSH_INT, operand=token.value, token=token)); ip += 1 elif token.typ == TokenType.KEYWORD: - assert len(Keyword) == 8, "Exhaustive keywords handling in parse_program_from_tokens()" + assert len(Keyword) == 9, "Exhaustive keywords handling in parse_program_from_tokens()" if token.value == Keyword.IF: - program.append(Op(typ=OpType.IF, token=token)) + program.ops.append(Op(typ=OpType.IF, token=token)) stack.append(ip) ip += 1 elif token.value == Keyword.ELIF: - program.append(Op(typ=OpType.ELIF, token=token)) + program.ops.append(Op(typ=OpType.ELIF, token=token)) do_ip = stack.pop() - if program[do_ip].typ != OpType.DO: - compiler_error_with_expansion_stack(program[do_ip].token, '`elif` can only close `do`-blocks') + if program.ops[do_ip].typ != OpType.DO: + compiler_error_with_expansion_stack(program.ops[do_ip].token, '`elif` can only close `do`-blocks') exit(1) - pre_do_ip = program[do_ip].operand + pre_do_ip = program.ops[do_ip].operand assert isinstance(pre_do_ip, OpAddr) - if program[pre_do_ip].typ == OpType.IF: - program[do_ip].operand = ip + 1 + if program.ops[pre_do_ip].typ == OpType.IF: + program.ops[do_ip].operand = ip + 1 stack.append(ip) ip += 1 - elif program[pre_do_ip].typ == OpType.ELIF: - program[pre_do_ip].operand = ip - program[do_ip].operand = ip + 1 + elif program.ops[pre_do_ip].typ == OpType.ELIF: + program.ops[pre_do_ip].operand = ip + program.ops[do_ip].operand = ip + 1 stack.append(ip) ip += 1 else: - compiler_error_with_expansion_stack(program[pre_do_ip].token, '`elif` can only close `do`-blocks that are preceded by `if` or another `elif`') + compiler_error_with_expansion_stack(program.ops[pre_do_ip].token, '`elif` can only close `do`-blocks that are preceded by `if` or another `elif`') exit(1) elif token.value == Keyword.ELSE: - program.append(Op(typ=OpType.ELSE, token=token)) + program.ops.append(Op(typ=OpType.ELSE, token=token)) do_ip = stack.pop() - if program[do_ip].typ != OpType.DO: - compiler_error_with_expansion_stack(program[do_ip].token, '`else` can only be used in `do` blocks') + if program.ops[do_ip].typ != OpType.DO: + compiler_error_with_expansion_stack(program.ops[do_ip].token, '`else` can only be used in `do` blocks') exit(1) - pre_do_ip = program[do_ip].operand + pre_do_ip = program.ops[do_ip].operand assert isinstance(pre_do_ip, OpAddr) - if program[pre_do_ip].typ == OpType.IF: - program[do_ip].operand = ip + 1 + if program.ops[pre_do_ip].typ == OpType.IF: + program.ops[do_ip].operand = ip + 1 stack.append(ip) ip += 1 - elif program[pre_do_ip].typ == OpType.ELIF: - program[pre_do_ip].operand = ip - program[do_ip].operand = ip + 1 + elif program.ops[pre_do_ip].typ == OpType.ELIF: + program.ops[pre_do_ip].operand = ip + program.ops[do_ip].operand = ip + 1 stack.append(ip) ip += 1 else: - compiler_error_with_expansion_stack(program[pre_do_ip].token, '`else` can only close `do`-blocks that are preceded by `if` or `elif`') + compiler_error_with_expansion_stack(program.ops[pre_do_ip].token, '`else` can only close `do`-blocks that are preceded by `if` or `elif`') exit(1) elif token.value == Keyword.END: - program.append(Op(typ=OpType.END, token=token)) + program.ops.append(Op(typ=OpType.END, token=token)) block_ip = stack.pop() - if program[block_ip].typ == OpType.ELSE: - program[block_ip].operand = ip - program[ip].operand = ip + 1 - elif program[block_ip].typ == OpType.DO: - assert program[block_ip].operand is not None - pre_do_ip = program[block_ip].operand + if program.ops[block_ip].typ == OpType.ELSE: + program.ops[block_ip].operand = ip + program.ops[ip].operand = ip + 1 + elif program.ops[block_ip].typ == OpType.DO: + assert program.ops[block_ip].operand is not None + pre_do_ip = program.ops[block_ip].operand assert isinstance(pre_do_ip, OpAddr) - if program[pre_do_ip].typ == OpType.WHILE: - program[ip].operand = pre_do_ip - program[block_ip].operand = ip + 1 - elif program[pre_do_ip].typ == OpType.IF: - program[ip].operand = ip + 1 - program[block_ip].operand = ip + 1 - elif program[pre_do_ip].typ == OpType.ELIF: - program[pre_do_ip].operand = ip - program[ip].operand = ip + 1 - program[block_ip].operand = ip + 1 + if program.ops[pre_do_ip].typ == OpType.WHILE: + program.ops[ip].operand = pre_do_ip + program.ops[block_ip].operand = ip + 1 + elif program.ops[pre_do_ip].typ == OpType.IF: + program.ops[ip].operand = ip + 1 + program.ops[block_ip].operand = ip + 1 + elif program.ops[pre_do_ip].typ == OpType.ELIF: + program.ops[pre_do_ip].operand = ip + program.ops[ip].operand = ip + 1 + program.ops[block_ip].operand = ip + 1 else: - compiler_error_with_expansion_stack(program[pre_do_ip].token, '`end` can only close `do` blocks that are preceded by `if`, `while` or `elif`') + compiler_error_with_expansion_stack(program.ops[pre_do_ip].token, '`end` can only close `do` blocks that are preceded by `if`, `while` or `elif`') exit(1) else: - compiler_error_with_expansion_stack(program[block_ip].token, '`end` can only close `else`, `do` or `macro` blocks for now') + compiler_error_with_expansion_stack(program.ops[block_ip].token, '`end` can only close `else`, `do` or `macro` blocks for now') exit(1) ip += 1 elif token.value == Keyword.WHILE: - program.append(Op(typ=OpType.WHILE, token=token)) + program.ops.append(Op(typ=OpType.WHILE, token=token)) stack.append(ip) ip += 1 elif token.value == Keyword.DO: - program.append(Op(typ=OpType.DO, token=token)) + program.ops.append(Op(typ=OpType.DO, token=token)) if len(stack) == 0: compiler_error_with_expansion_stack(token, "`do` is not preceded by `if`, `while` or `elif`") exit(1) pre_do_ip = stack.pop() - if program[pre_do_ip].typ != OpType.WHILE and program[pre_do_ip].typ != OpType.IF and program[pre_do_ip].typ != OpType.ELIF: + if program.ops[pre_do_ip].typ != OpType.WHILE and program.ops[pre_do_ip].typ != OpType.IF and program.ops[pre_do_ip].typ != OpType.ELIF: compiler_error_with_expansion_stack(token, "`do` is not preceded by `if`, `while` or `elif`") exit(1) - program[ip].operand = pre_do_ip + program.ops[ip].operand = pre_do_ip stack.append(ip) ip += 1 elif token.value == Keyword.INCLUDE: @@ -1676,6 +1688,51 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp if not file_included: compiler_error_with_expansion_stack(token, "file `%s` not found" % token.value) exit(1) + elif token.value == Keyword.MEMORY: + if len(rtokens) == 0: + compiler_error_with_expansion_stack(token, "expected memory name but found nothing") + exit(1) + token = rtokens.pop() + if token.typ != TokenType.WORD: + compiler_error_with_expansion_stack(token, "expected memory name to be %s but found %s" % (human(TokenType.WORD), human(token.typ))) + exit(1) + assert isinstance(token.value, str), "This is probably a bug in the lexer" + memory_name = token.value + if memory_name in memories: + assert False, "TODO: redefinition of a memory region" + if memory_name in INTRINSIC_BY_NAMES: + assert False, "TODO: redefinition of an intrinsic" + if memory_name in macros: + assert False, "TODO: redefinition of a macro" + mem_size_stack: List[int] = [] + while len(rtokens) > 0: + token = rtokens.pop() + if token.typ == TokenType.KEYWORD: + if token.value == Keyword.END: + break + else: + assert False, "TODO: unsupported keyword in memory definition" + elif token.typ == TokenType.INT: + assert isinstance(token.value, int) + mem_size_stack.append(token.value) + elif token.typ == TokenType.WORD: + if token.value == INTRINSIC_NAMES[Intrinsic.PLUS]: + a = mem_size_stack.pop() + b = mem_size_stack.pop() + mem_size_stack.append(a + b) + elif token.value == INTRINSIC_NAMES[Intrinsic.MUL]: + a = mem_size_stack.pop() + b = mem_size_stack.pop() + mem_size_stack.append(a * b) + else: + assert False, "TODO: unsupported word in memory definition" + else: + assert False, "TODO: unsupported token in memory definition" + if len(mem_size_stack) != 1: + assert False, "TODO: memory definition expects only one integer" + memory_size = mem_size_stack.pop() + memories[memory_name] = program.memory_capacity + program.memory_capacity += memory_size elif token.value == Keyword.MACRO: if len(rtokens) == 0: compiler_error_with_expansion_stack(token, "expected macro name but found nothing") @@ -1692,6 +1749,8 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp if token.value in INTRINSIC_BY_NAMES: compiler_error_with_expansion_stack(token, "redefinition of an intrinsic word `%s`. Please choose a different name for your macro." % (token.value, )) exit(1) + if token.value in memories: + assert False, "TODO: redefinition of a memory" macro = Macro(token.loc, []) macros[token.value] = macro nesting_depth = 0 @@ -1715,7 +1774,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp assert False, 'unreachable' if len(stack) > 0: - compiler_error_with_expansion_stack(program[stack.pop()].token, 'unclosed block') + compiler_error_with_expansion_stack(program.ops[stack.pop()].token, 'unclosed block') exit(1) return program @@ -1827,8 +1886,8 @@ def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): with open(dot_path, "w") as f: f.write("digraph Program {\n") assert len(OpType) == 10, "Exhaustive handling of OpType in generate_control_flow_graph_as_dot_file()" - for ip in range(len(program)): - op = program[ip] + for ip in range(len(program.ops)): + op = program.ops[ip] if op.typ == OpType.INTRINSIC: assert isinstance(op.operand, Intrinsic) f.write(f" Node_{ip} [label={repr(repr(INTRINSIC_NAMES[op.operand]))}];\n") @@ -1870,7 +1929,7 @@ def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): f.write(f" Node_{ip} -> Node_{op.operand};\n") else: assert False, f"unimplemented operation {op.typ}" - f.write(f" Node_{len(program)} [label=halt];\n") + f.write(f" Node_{len(program.ops)} [label=halt];\n") f.write("}\n") def usage(compiler_name: str): diff --git a/std/std.porth b/std/std.porth index dd5da9f7..53fddaad 100644 --- a/std/std.porth +++ b/std/std.porth @@ -536,3 +536,4 @@ macro @ @8 end macro . swap ! end macro , @ end macro cstr-to-pstr cstr-to-str end +memory mem 640000 end From 6d83a238647a701e641f699df30fb0baac69a131 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 00:54:36 +0700 Subject: [PATCH 056/145] Use memory sections in porth.porth --- porth.porth | 65 ++++++++++++--------------------------------------- porth.py | 7 +++++- std/std.porth | 37 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/porth.porth b/porth.porth index ed427e26..d72735b9 100644 --- a/porth.porth +++ b/porth.porth @@ -2,7 +2,7 @@ include "std.porth" -macro PUTD_BUFFER_CAP 32 end + macro MEM_CAPACITY 640000 end macro SIM_STACK_CAP 1024 end @@ -14,6 +14,7 @@ macro OP_DUP 4 end macro OP_DROP 5 end macro COUNT_OPS 6 end +macro OPS_CAP 1024 end macro sizeof(Op) 16 end macro Op.type 0 + end macro Op.operand 8 + end @@ -22,25 +23,19 @@ macro !Op.type Op.type !64 end macro @Op.operand Op.operand @64 end macro !Op.operand Op.operand !64 end -// Memory Layout -macro putd-buffer mem end - -macro file_path_cstr putd-buffer PUTD_BUFFER_CAP + end -macro fd file_path_cstr 8 + end -macro statbuf fd 8 + end - -macro content statbuf sizeof(stat) + end -macro line content sizeof(Str) + end -macro word line sizeof(Str) + end -macro line_number word sizeof(Str) + end -macro line_start line_number 8 + end -macro streq_a line_start 8 + end -macro streq_b streq_a sizeof(Str) + end -macro sim-stack-count streq_b sizeof(Str) + end -macro sim-stack sim-stack-count 8 + end -macro ops-count sim-stack SIM_STACK_CAP 8 * + end -macro ops ops-count 8 + end +memory file_path_cstr sizeof(ptr) end +memory fd sizeof(u64) end +memory statbuf sizeof(stat) end +memory content sizeof(Str) end +memory line sizeof(Str) end +memory word sizeof(Str) end +memory line_number sizeof(u64) end +memory line_start sizeof(ptr) end +memory sim-stack-count sizeof(u64) end +memory sim-stack sizeof(u64) SIM_STACK_CAP * end +memory ops-count sizeof(u64) end +memory ops sizeof(Op) OPS_CAP * end macro sim-stack-push // u64 -- if sim-stack-count @64 SIM_STACK_CAP >= do @@ -58,23 +53,6 @@ macro sim-stack-pop // -- u64 sim-stack sim-stack-count @64 8 * + @64 end -macro putd // u64 -- - if dup 0 = do - "0" puts - else - putd-buffer PUTD_BUFFER_CAP + - while over 0 > do - 1 - dup rot - 10 divmod - rot swap '0' + . swap - end - - dup - putd-buffer PUTD_BUFFER_CAP + swap - swap puts - end - drop -end - macro try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret 0 0 while dup word @Str.count < do dup word @Str.data + @8 @@ -94,22 +72,9 @@ macro try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret end drop end -macro streq // n1 s1 n2 s2 - streq_a !Str - streq_b !Str - if streq_a @Str.count streq_b @Str.count = do - 0 while - if dup streq_a @Str.count < do - dup streq_a @Str.data + @8 - over streq_b @Str.data + @8 - = - else false end - do 1 + end - streq_a @Str.count >= - else false end -end macro push-op // type operand -- + // TODO: assert OPS_CAP ops-count @64 sizeof(Op) * ops + dup rot swap !Op.operand !Op.type diff --git a/porth.py b/porth.py index c2ae316f..0abb2fbd 100755 --- a/porth.py +++ b/porth.py @@ -1724,8 +1724,13 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp a = mem_size_stack.pop() b = mem_size_stack.pop() mem_size_stack.append(a * b) + elif token.value in macros: + if token.expanded_count >= expansion_limit: + compiler_error_with_expansion_stack(token, "the macro exceeded the expansion limit (it expanded %d times)" % token.expanded_count) + exit(1) + rtokens += reversed(expand_macro(macros[token.value], token)) else: - assert False, "TODO: unsupported word in memory definition" + assert False, f"TODO: unsupported word in memory definition {token.value}" else: assert False, "TODO: unsupported token in memory definition" if len(mem_size_stack) != 1: diff --git a/std/std.porth b/std/std.porth index 53fddaad..0906b59f 100644 --- a/std/std.porth +++ b/std/std.porth @@ -408,6 +408,7 @@ end macro sizeof(u64) 8 end macro sizeof(u32) 4 end +macro sizeof(ptr) sizeof(u64) end macro cstrlen dup @@ -516,6 +517,23 @@ macro str-chop-word // line input -- 2drop end +memory streq_a sizeof(Str) end +memory streq_b sizeof(Str) end +macro streq // n1 s1 n2 s2 + streq_a !Str + streq_b !Str + if streq_a @Str.count streq_b @Str.count = do + 0 while + if dup streq_a @Str.count < do + dup streq_a @Str.data + @8 + over streq_b @Str.data + @8 + = + else false end + do 1 + end + streq_a @Str.count >= + else false end +end + macro isdigit dup '0' >= swap '9' <= @@ -528,6 +546,25 @@ macro lnot cast(int) 1 - cast(bool) end +macro PUTD_BUFFER_CAP 32 end +memory putd-buffer PUTD_BUFFER_CAP end +macro putd // u64 -- + if dup 0 = do + "0" puts + else + putd-buffer PUTD_BUFFER_CAP + + while over 0 > do + 1 - dup rot + 10 divmod + rot swap '0' + . swap + end + + dup + putd-buffer PUTD_BUFFER_CAP + swap - swap puts + end + drop +end + // Deprecated Words macro .64 swap !64 end macro ,64 @64 end From 1df87d00b375b2ae728121e74b9e4f05f837bf93 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 00:59:07 +0700 Subject: [PATCH 057/145] Fix compilation of GoL example --- examples/gol.porth | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/examples/gol.porth b/examples/gol.porth index ed593ab2..ac358a51 100644 --- a/examples/gol.porth +++ b/examples/gol.porth @@ -4,33 +4,16 @@ include "std.porth" macro ROWS 10 end macro COLS 20 end macro BOARD_SIZE ROWS COLS * end -macro PUTD_BUFFER_CAP 32 end -// memory layout -macro putd_buffer mem end -macro delta_time putd_buffer PUTD_BUFFER_CAP + end -macro board_current_index delta_time 16 + end -macro nbors board_current_index 8 + end -macro value nbors 8 + end -macro board_base value 8 + end -macro display BOARD_SIZE 2 * board_base + end - -macro putd - if dup 0 = do - "0" puts - else - putd_buffer PUTD_BUFFER_CAP + - while over 0 > do - 1 - dup rot - 10 divmod - rot swap '0' + swap !8 swap - end +macro sizeof(timespec) 16 end - dup - putd_buffer PUTD_BUFFER_CAP + swap - swap puts - end - drop -end +// memory layout +memory delta_time sizeof(timespec) end +memory board_current_index sizeof(u64) end +memory nbors sizeof(u64) end +memory value sizeof(u64) end +memory board_base BOARD_SIZE 2 * end +memory display COLS 1 + end macro board_current board_base board_current_index @64 BOARD_SIZE * + From 6d68f146b7e63ee3acf41ce17af8887dce6c667a Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 01:01:05 +0700 Subject: [PATCH 058/145] TODO: check of stack underflows in memory definition --- porth.py | 1 + 1 file changed, 1 insertion(+) diff --git a/porth.py b/porth.py index 0abb2fbd..d146eeb7 100755 --- a/porth.py +++ b/porth.py @@ -1716,6 +1716,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp assert isinstance(token.value, int) mem_size_stack.append(token.value) elif token.typ == TokenType.WORD: + # TODO: check of stack underflows in memory definition if token.value == INTRINSIC_NAMES[Intrinsic.PLUS]: a = mem_size_stack.pop() b = mem_size_stack.pop() From 35f03900e86544b0a4d0e4b3b926f0f99e21b2a1 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 01:08:06 +0700 Subject: [PATCH 059/145] Fix bug while parsing macro containing memory block --- porth.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/porth.py b/porth.py index d146eeb7..2e115af3 100755 --- a/porth.py +++ b/porth.py @@ -1767,7 +1767,8 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp else: macro.tokens.append(token) if token.typ == TokenType.KEYWORD: - if token.value in [Keyword.IF, Keyword.WHILE, Keyword.MACRO]: + assert len(Keyword) == 9, "Exhaustive handling of keywords in parsing macro body" + if token.value in [Keyword.IF, Keyword.WHILE, Keyword.MACRO, Keyword.MEMORY]: nesting_depth += 1 elif token.value == Keyword.END: nesting_depth -= 1 From ef87796c3f0f479da24398272cd6e10c355487c6 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 04:04:09 +0700 Subject: [PATCH 060/145] Don't use mem in Project Euler solutions --- euler/problem02.porth | 2 +- euler/problem04.porth | 2 +- euler/problem05.porth | 7 +++---- euler/problem07.porth | 4 ++-- euler/problem08.porth | 16 ++++++---------- euler/problem09.porth | 6 +++--- euler/problem10.porth | 6 +++--- 7 files changed, 19 insertions(+), 24 deletions(-) diff --git a/euler/problem02.porth b/euler/problem02.porth index 8ab15ff4..336217e4 100644 --- a/euler/problem02.porth +++ b/euler/problem02.porth @@ -1,6 +1,6 @@ include "std.porth" -macro acc mem end +memory acc sizeof(u64) end 1 2 while over 4000000 < do if over 2 mod 0 = do diff --git a/euler/problem04.porth b/euler/problem04.porth index 60684363..315b6642 100644 --- a/euler/problem04.porth +++ b/euler/problem04.porth @@ -1,7 +1,7 @@ // WARNING! This problem is extremely slow in Simulation Mode with CPython! Use PyPy or Compilation Mode! include "std.porth" -macro ans mem end +memory ans sizeof(u64) end 100 while dup 1000 < do 100 while dup 1000 < do diff --git a/euler/problem05.porth b/euler/problem05.porth index 6e2540f9..506f8827 100644 --- a/euler/problem05.porth +++ b/euler/problem05.porth @@ -2,10 +2,9 @@ include "std.porth" macro N 20 end -// |acc |tmp |ans| -macro acc mem end -macro tmp mem 8 N * + end -macro ans tmp 8 N * + end +memory acc sizeof(u64) N * end +memory tmp sizeof(u64) N * end +memory ans sizeof(u64) end 2 while dup N <= do diff --git a/euler/problem07.porth b/euler/problem07.porth index b4e7a960..62551892 100644 --- a/euler/problem07.porth +++ b/euler/problem07.porth @@ -2,8 +2,8 @@ include "std.porth" macro N 10001 end -macro primes-count mem end -macro primes primes-count 8 + end +memory primes-count sizeof(u64) end +memory primes sizeof(u64) N * 10 + end macro is-prime 0 diff --git a/euler/problem08.porth b/euler/problem08.porth index ebba7fe4..d5034285 100644 --- a/euler/problem08.porth +++ b/euler/problem08.porth @@ -2,23 +2,19 @@ include "std.porth" macro N 13 end -macro str mem end -macro len str 8 + end -macro acc len 8 + end -macro ans acc 8 + end +memory str sizeof(Str) end +memory acc sizeof(u64) end +memory ans sizeof(u64) end -"7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450" - -str !64 -len !64 +"7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450" str !Str 0 ans !64 -0 while dup len @64 N - 1 + < do +0 while dup str @Str.count N - 1 + < do 1 acc !64 0 while dup N < do - 2dup + str @64 cast(ptr) + @8 '0' - + 2dup + str @Str.data cast(ptr) + @8 '0' - acc @64 * acc !64 1 + end drop diff --git a/euler/problem09.porth b/euler/problem09.porth index c76109ea..5979cb84 100644 --- a/euler/problem09.porth +++ b/euler/problem09.porth @@ -1,8 +1,8 @@ include "std.porth" -macro a mem end -macro b a 8 + end -macro c b 8 + end +memory a sizeof(u64) end +memory b sizeof(u64) end +memory c sizeof(u64) end 1 while dup 1000 < do dup a !64 diff --git a/euler/problem10.porth b/euler/problem10.porth index 3cf210d2..75e24bdb 100644 --- a/euler/problem10.porth +++ b/euler/problem10.porth @@ -6,9 +6,9 @@ include "std.porth" // Maybe we could come up with a faster method of computer prime numbers? // We could precompute them and save the to a file I guess. -macro ans mem end -macro primes-count ans sizeof(u64) + end -macro primes primes-count sizeof(u32) + end +memory ans sizeof(u64) end +memory primes-count sizeof(u64) end +memory primes sizeof(u32) 1000000 * end macro push-prime // value -- primes-count @32 sizeof(u32) * primes + !32 From f5511d3336e160788d19813ba325d2bfeab2e88e Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 04:04:30 +0700 Subject: [PATCH 061/145] Don't use mem in unit tests --- tests/memory.porth | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/memory.porth b/tests/memory.porth index 6ca7d580..74583558 100644 --- a/tests/memory.porth +++ b/tests/memory.porth @@ -1,26 +1,28 @@ include "std.porth" +memory abc 8 end + // write "abc" into the memory -97 mem 0 + !8 -98 mem 1 + !8 -99 mem 2 + !8 -10 mem 3 + !8 +97 abc 0 + !8 +98 abc 1 + !8 +99 abc 2 + !8 +10 abc 3 + !8 // print "abc" to stdout -4 mem stdout write print +4 abc stdout write print // increament each character by 1 making it "bcd" -mem 0 + dup @8 1 + swap !8 -mem 1 + dup @8 1 + swap !8 -mem 2 + dup @8 1 + swap !8 +abc 0 + dup @8 1 + swap !8 +abc 1 + dup @8 1 + swap !8 +abc 2 + dup @8 1 + swap !8 // print "bcd" to stdout -4 mem stdout write print +4 abc stdout write print // print UINT64_MAX (Largest 64 bit word) -18446744073709551615 mem !64 -mem @64 print +18446744073709551615 abc !64 +abc @64 print -255 mem !8 -255 mem 1 + !8 -mem @16 print +255 abc !8 +255 abc 1 + !8 +abc @16 print From 10d4d15702dc609c9cba212a22596cd84ff1d3fd Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 04:09:38 +0700 Subject: [PATCH 062/145] Don't use mem in examples --- examples/cat.porth | 5 ++--- examples/cat.txt | 7 +++---- examples/name.porth | 2 +- examples/reverse-linked-list.porth | 9 +++++---- examples/rot13.porth | 2 +- examples/rule110.porth | 4 ++-- examples/seq.porth | 2 +- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/examples/cat.porth b/examples/cat.porth index a8d73b33..175b2ce5 100644 --- a/examples/cat.porth +++ b/examples/cat.porth @@ -2,9 +2,8 @@ include "std.porth" macro BUFFER_CAP 1024 end -// memory layout -macro fd mem end -macro buffer fd 8 + end +memory fd sizeof(u64) end +memory buffer BUFFER_CAP end macro cat_fd while BUFFER_CAP buffer fd @64 read dup 0 > do diff --git a/examples/cat.txt b/examples/cat.txt index f7577b5f..8b1f7450 100644 --- a/examples/cat.txt +++ b/examples/cat.txt @@ -8,14 +8,13 @@ foo :b stdin 0 :i returncode 0 -:b stdout 585 +:b stdout 582 include "std.porth" macro BUFFER_CAP 1024 end -// memory layout -macro fd mem end -macro buffer fd 8 + end +memory fd sizeof(u64) end +memory buffer BUFFER_CAP end macro cat_fd while BUFFER_CAP buffer fd @64 read dup 0 > do diff --git a/examples/name.porth b/examples/name.porth index a362b990..8d2aa674 100644 --- a/examples/name.porth +++ b/examples/name.porth @@ -1,7 +1,7 @@ include "std.porth" macro NAME_CAPACITY 256 end -macro name mem end +memory name NAME_CAPACITY end "What is your name? " puts diff --git a/examples/reverse-linked-list.porth b/examples/reverse-linked-list.porth index fb8802d6..db5b2de5 100644 --- a/examples/reverse-linked-list.porth +++ b/examples/reverse-linked-list.porth @@ -8,11 +8,12 @@ macro @Node.prev Node.prev @64 end macro !Node.value Node.value !64 end macro !Node.prev Node.prev !64 end -macro list_a mem end -macro list_b list_a 8 + end +memory list_a sizeof(ptr) end +memory list_b sizeof(ptr) end -macro nodes_count list_b 8 + end -macro nodes nodes_count 8 + end +macro NODES_CAP 1024 end +memory nodes_count sizeof(u64) end +memory nodes sizeof(Node) NODES_CAP * end macro alloc_node nodes_count @64 sizeof(Node) * nodes + diff --git a/examples/rot13.porth b/examples/rot13.porth index 842a76b6..48ffae7a 100644 --- a/examples/rot13.porth +++ b/examples/rot13.porth @@ -1,7 +1,7 @@ include "std.porth" macro BUFFER_CAP 1024 end -macro buffer mem end +memory buffer BUFFER_CAP end while BUFFER_CAP buffer stdin read dup 0 > do 0 while 2dup > do diff --git a/examples/rule110.porth b/examples/rule110.porth index 9efd25f4..0c60d6e0 100644 --- a/examples/rule110.porth +++ b/examples/rule110.porth @@ -4,8 +4,8 @@ include "std.porth" macro N 100 end // Memory layout -macro row mem end -macro display row N + end +memory row N end +memory display N 1 + end row N 2 - + 1 swap !8 display N + 10 swap !8 diff --git a/examples/seq.porth b/examples/seq.porth index d5a54c1d..e3dae40a 100644 --- a/examples/seq.porth +++ b/examples/seq.porth @@ -1,6 +1,6 @@ include "std.porth" -macro limit mem end +memory limit sizeof(u64) end if argc 2 < do "Usage: seq \n" eputs From 844acdf746df2590c89f973ff1d4732e094aa8d0 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 04:11:33 +0700 Subject: [PATCH 063/145] Fix mypy remarks --- porth.py | 1 + 1 file changed, 1 insertion(+) diff --git a/porth.py b/porth.py index 2e115af3..32465607 100755 --- a/porth.py +++ b/porth.py @@ -1716,6 +1716,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp assert isinstance(token.value, int) mem_size_stack.append(token.value) elif token.typ == TokenType.WORD: + assert isinstance(token.value, str) # TODO: check of stack underflows in memory definition if token.value == INTRINSIC_NAMES[Intrinsic.PLUS]: a = mem_size_stack.pop() From 85273a3b585e684fc833f6032a2e9ad0602f7305 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 07:30:47 +0700 Subject: [PATCH 064/145] Implement simple PPM generator --- .gitignore | 1 + examples/.gitignore | 1 + examples/cat.porth | 20 ++++++++---- examples/cat.txt | 22 +++++++++---- examples/checker.porth | 72 ++++++++++++++++++++++++++++++++++++++++++ porth.py | 33 ++++++++++--------- std/std.porth | 18 ++++++++--- 7 files changed, 135 insertions(+), 32 deletions(-) create mode 100644 examples/checker.porth diff --git a/.gitignore b/.gitignore index 6d1ee607..c5cdce22 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ __pycache__/ *.o *.dot *.svg +*.ppm porth \ No newline at end of file diff --git a/examples/.gitignore b/examples/.gitignore index 314b2462..b0c42d1b 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -8,3 +8,4 @@ seq fib gol fizz-buzz +checker \ No newline at end of file diff --git a/examples/cat.porth b/examples/cat.porth index 175b2ce5..df5a4ada 100644 --- a/examples/cat.porth +++ b/examples/cat.porth @@ -1,26 +1,34 @@ include "std.porth" -macro BUFFER_CAP 1024 end - memory fd sizeof(u64) end +macro @fd fd @64 end +macro !fd fd !64 end + +macro BUFFER_CAP 1024 end memory buffer BUFFER_CAP end +memory file_path_cstr sizeof(ptr) end +macro @file_path_cstr file_path_cstr @64 cast(ptr) end +macro !file_path_cstr file_path_cstr !64 end + macro cat_fd - while BUFFER_CAP buffer fd @64 read dup 0 > do + while BUFFER_CAP buffer @fd read dup 0 > do buffer puts end drop end if argc 2 < do - stdin fd !64 + stdin !fd cat_fd else 1 while dup argc < do - O_RDONLY over nth_argv AT_FDCWD openat + dup nth_argv !file_path_cstr + + 0 O_RDONLY @file_path_cstr AT_FDCWD openat if dup 0 < do "ERROR: could not open file " eputs - over nth_argv dup cstrlen swap eputs + @file_path_cstr cstr-to-str eputs "\n" eputs drop else diff --git a/examples/cat.txt b/examples/cat.txt index 8b1f7450..d9771f06 100644 --- a/examples/cat.txt +++ b/examples/cat.txt @@ -8,30 +8,38 @@ foo :b stdin 0 :i returncode 0 -:b stdout 582 +:b stdout 792 include "std.porth" -macro BUFFER_CAP 1024 end - memory fd sizeof(u64) end +macro @fd fd @64 end +macro !fd fd !64 end + +macro BUFFER_CAP 1024 end memory buffer BUFFER_CAP end +memory file_path_cstr sizeof(ptr) end +macro @file_path_cstr file_path_cstr @64 cast(ptr) end +macro !file_path_cstr file_path_cstr !64 end + macro cat_fd - while BUFFER_CAP buffer fd @64 read dup 0 > do + while BUFFER_CAP buffer @fd read dup 0 > do buffer puts end drop end if argc 2 < do - stdin fd !64 + stdin !fd cat_fd else 1 while dup argc < do - O_RDONLY over nth_argv AT_FDCWD openat + dup nth_argv !file_path_cstr + + 0 O_RDONLY @file_path_cstr AT_FDCWD openat if dup 0 < do "ERROR: could not open file " eputs - over nth_argv dup cstrlen swap eputs + @file_path_cstr cstr-to-str eputs "\n" eputs drop else diff --git a/examples/checker.porth b/examples/checker.porth new file mode 100644 index 00000000..ee462c9b --- /dev/null +++ b/examples/checker.porth @@ -0,0 +1,72 @@ +// $ ./porth.py com ./checker.porth +// $ ./checker output.ppm +// $ feh ./output.ppm + +include "std.porth" + +if argc 2 < do + "Usage: ./checker \n" eputs + "[ERROR] no output file path is provided\n" eputs + 1 exit +end + +macro WIDTH 512 end +macro HEIGHT 512 end +macro CELL_WIDTH 64 end +macro CELL_HEIGHT 64 end +macro sizeof(pixel) 3 end + +memory canvas sizeof(pixel) WIDTH * HEIGHT * end + +0 while dup HEIGHT < do + 0 while dup WIDTH < do + 2dup CELL_WIDTH / + swap CELL_HEIGHT / + + + if 2 % 0 = do + 2dup swap WIDTH * + sizeof(pixel) * canvas + + dup 255 swap !8 1 + + dup 0 swap !8 1 + + dup 255 swap !8 drop + else + 2dup swap WIDTH * + sizeof(pixel) * canvas + + dup 0 swap !8 1 + + dup 0 swap !8 1 + + dup 0 swap !8 drop + end + 1 + + end drop + 1 + +end drop + +memory file_path_cstr sizeof(ptr) end +macro @file_path_cstr file_path_cstr @64 cast(ptr) end +macro @file_path @file_path_cstr cstr-to-str end +1 nth_argv file_path_cstr !64 + +memory fd sizeof(u64) end +macro @fd fd @64 end +macro !fd fd !64 end + +"[INFO] Generating " puts @file_path puts "\n" puts + +420 +O_CREAT O_WRONLY or +@file_path_cstr +AT_FDCWD +openat +!fd + +if @fd 0 < do + "[ERROR] could not open file `" eputs + @file_path eputs + "`\n" eputs + 1 exit +end + +"P6\n" @fd fputs +WIDTH @fd fputd +" " @fd fputs +HEIGHT @fd fputd +" 255\n" @fd fputs +WIDTH HEIGHT * sizeof(pixel) * canvas @fd fputs diff --git a/porth.py b/porth.py index 32465607..d97d9967 100755 --- a/porth.py +++ b/porth.py @@ -448,21 +448,6 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): fds[fd].write(mem[buf:buf+count]) fds[fd].flush() stack.append(count) - elif syscall_number == 257: # SYS_openat - dirfd = arg1 - pathname_ptr = arg2 - flags = arg3 - if dirfd != AT_FDCWD: - assert False, "openat: unsupported dirfd" - if flags != O_RDONLY: - assert False, "openat: unsupported flags" - pathname = get_cstr_from_mem(mem, pathname_ptr).decode('utf-8') - fd = len(fds) - try: - fds.append(open(pathname, 'rb')) - stack.append(fd) - except FileNotFoundError: - stack.append(-ENOENT) else: assert False, "unknown syscall number %d" % syscall_number ip += 1 @@ -486,6 +471,24 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): nano_seconds = int.from_bytes(mem[request_ptr+8:request_ptr+8+8], byteorder='little') sleep(float(seconds)+float(nano_seconds)*1e-09) stack.append(0) + elif syscall_number == 257: # SYS_openat + dirfd = arg1 + pathname_ptr = arg2 + flags = arg3 + mode = arg4 + if dirfd != AT_FDCWD: + assert False, f"openat: unsupported dirfd: {dirfd}" + if flags != O_RDONLY: + assert False, f"openat: unsupported flags: {flags}" + if mode != 0: + assert False, f"openat: unsupported mode: {mode}" + pathname = get_cstr_from_mem(mem, pathname_ptr).decode('utf-8') + fd = len(fds) + try: + fds.append(open(pathname, 'rb')) + stack.append(fd) + except FileNotFoundError: + stack.append(-ENOENT) else: assert False, "unknown syscall number %d" % syscall_number ip += 1 diff --git a/std/std.porth b/std/std.porth index 0906b59f..1b6f52d3 100644 --- a/std/std.porth +++ b/std/std.porth @@ -332,6 +332,9 @@ macro SYS_finit_module 313 end macro AT_FDCWD -100 end macro O_RDONLY 0 end +macro O_WRONLY 1 end +macro O_RDWR 2 end +macro O_CREAT 64 end macro CLOCK_MONOTONIC 1 end macro TIMER_ABSTIME 1 end @@ -371,7 +374,7 @@ macro sizeof(stat.st_ctim) 16 end // Wrappers for common syscalls macro write SYS_write syscall3 end macro read SYS_read syscall3 end -macro openat SYS_openat syscall3 end +macro openat SYS_openat syscall4 end macro fstat SYS_fstat syscall2 end macro close SYS_close syscall1 end macro exit SYS_exit syscall1 drop end @@ -435,6 +438,7 @@ macro cstr-to-str dup cstrlen swap end +// TODO: fputs should crash the app if write fails macro fputs write drop end @@ -548,9 +552,13 @@ end macro PUTD_BUFFER_CAP 32 end memory putd-buffer PUTD_BUFFER_CAP end -macro putd // u64 -- +memory putd-fd sizeof(u64) end +// TODO: fputd should fail if write call fails +macro fputd // value fd -- + putd-fd !64 + if dup 0 = do - "0" puts + "0" putd-fd @64 fputs else putd-buffer PUTD_BUFFER_CAP + while over 0 > do @@ -560,11 +568,13 @@ macro putd // u64 -- end dup - putd-buffer PUTD_BUFFER_CAP + swap - swap puts + putd-buffer PUTD_BUFFER_CAP + swap - swap putd-fd @64 fputs end drop end +macro putd stdout fputd end + // Deprecated Words macro .64 swap !64 end macro ,64 @64 end From 94921cf98eda6ca85d751cbf46a7b825f9ca84da Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 07:35:58 +0700 Subject: [PATCH 065/145] Fix porth.porth --- porth.porth | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/porth.porth b/porth.porth index d72735b9..a2a3ab4b 100644 --- a/porth.porth +++ b/porth.porth @@ -2,7 +2,6 @@ include "std.porth" - macro MEM_CAPACITY 640000 end macro SIM_STACK_CAP 1024 end @@ -110,7 +109,7 @@ macro dump-ops // -- // ptr ptr dup sizeof(Op) * ops + "Type: " puts dup @Op.type print-op-type "\n" puts - "Operand: " puts @Op.operand print + "Operand: " puts @Op.operand putd "\n" puts "----------\n" puts 1 + end @@ -257,6 +256,7 @@ macro simulate-ops // -- end macro parse_file_path_cstr_into_ops + 0 // mode O_RDONLY // flags file_path_cstr @64 cast(ptr) // pathname AT_FDCWD // dirfd From 67e06a5c24aceb3aa8bab579a4241ef0de900825 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 07:41:50 +0700 Subject: [PATCH 066/145] No need to have a flag to customize memory capacity --- porth.py | 1 - 1 file changed, 1 deletion(-) diff --git a/porth.py b/porth.py index d97d9967..473ed62c 100755 --- a/porth.py +++ b/porth.py @@ -15,7 +15,6 @@ PORTH_EXT = '.porth' DEFAULT_EXPANSION_LIMIT=1000 EXPANSION_DIAGNOSTIC_LIMIT=10 -# TODO: customize memory capacity via a flag SIM_NULL_POINTER_PADDING = 1 # just a little bit of a padding at the beginning of the memory to make 0 an invalid address SIM_STR_CAPACITY = 640_000 SIM_ARGV_CAPACITY = 640_000 From 9b16e56602595b374b573a995a4079654cda4e6f Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 07:42:53 +0700 Subject: [PATCH 067/145] Fix example in README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6b525046..b1069ad2 100644 --- a/README.md +++ b/README.md @@ -158,10 +158,10 @@ It's like a regular string but it does not push its size on the stack and implic ``` include "std.porth" -O_RDONLY "input.txt"c AT_FDCWD openat -// ^ -// | -// postfix that indicates a C-style string +0 O_RDONLY "input.txt"c AT_FDCWD openat +// ^ +// | +// postfix that indicates a C-style string if dup 0 < do "ERROR: could not open the file\n" eputs From ff42c7f12a8953f94773739581cd24a0ef23de9d Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 09:08:52 +0700 Subject: [PATCH 068/145] Implement MUL intrinsic for porth.porth --- porth.porth | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/porth.porth b/porth.porth index a2a3ab4b..7135eee1 100644 --- a/porth.porth +++ b/porth.porth @@ -8,10 +8,11 @@ macro SIM_STACK_CAP 1024 end macro OP_PUSH_INT 0 end macro OP_PLUS 1 end macro OP_MINUS 2 end -macro OP_PRINT 3 end -macro OP_DUP 4 end -macro OP_DROP 5 end -macro COUNT_OPS 6 end +macro OP_MUL 3 end +macro OP_PRINT 4 end +macro OP_DUP 5 end +macro OP_DROP 6 end +macro COUNT_OPS 7 end macro OPS_CAP 1024 end macro sizeof(Op) 16 end @@ -81,7 +82,7 @@ macro push-op // type operand -- end macro print-op-type - if COUNT_OPS 6 != do + if COUNT_OPS 7 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in print-op-type\n" eputs 1 exit end @@ -92,6 +93,8 @@ macro print-op-type "OP_PLUS" puts elif dup OP_MINUS = do "OP_MINUS" puts + elif dup OP_MUL = do + "OP_MUL" puts elif dup OP_DUP = do "OP_DUP" puts elif dup OP_DROP = do @@ -161,7 +164,7 @@ macro compile-ops // -- dup sizeof(Op) * ops + // TODO: compile time assertion - if COUNT_OPS 6 != do + if COUNT_OPS 7 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in compile-ops\n" eputs 1 exit end @@ -182,6 +185,12 @@ macro compile-ops // -- " pop rbx\n" puts " sub rbx, rax\n" puts " push rbx\n" puts + elif dup @Op.type OP_MUL = do + " ;; -- mul --\n" puts + " pop rax\n" puts + " pop rbx\n" puts + " mul rbx\n" puts + " push rax\n" puts elif dup @Op.type OP_PRINT = do " ;; -- print --\n" puts " pop rdi\n" puts @@ -216,7 +225,7 @@ macro simulate-ops // -- 0 while dup ops-count @64 < do dup sizeof(Op) * ops + - if COUNT_OPS 6 != do + if COUNT_OPS 7 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in simulate-ops\n" eputs 1 exit end @@ -234,6 +243,11 @@ macro simulate-ops // -- swap - sim-stack-push + elif dup @Op.type OP_MUL = do + sim-stack-pop + sim-stack-pop + * + sim-stack-push elif dup @Op.type OP_PRINT = do sim-stack-pop print elif dup @Op.type OP_DUP = do @@ -298,7 +312,7 @@ macro parse_file_path_cstr_into_ops line str-trim-left word line str-chop-word - if COUNT_OPS 6 != do + if COUNT_OPS 7 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in parse-file-path\n" eputs 1 exit end @@ -307,6 +321,8 @@ macro parse_file_path_cstr_into_ops OP_PLUS 0 push-op elif word @Str "-" streq do OP_MINUS 0 push-op + elif word @Str "*" streq do + OP_MUL 0 push-op elif word @Str "print" streq do OP_PRINT 0 push-op elif word @Str "dup" streq do From d39e8b414819ce96df8a02ac7345aa52f1a69932 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 09:53:38 +0700 Subject: [PATCH 069/145] Move a bunch of things to std.porth --- examples/gol.porth | 2 -- std/std.porth | 31 ++++++++++++++++++------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/examples/gol.porth b/examples/gol.porth index ac358a51..52d31ad4 100644 --- a/examples/gol.porth +++ b/examples/gol.porth @@ -5,8 +5,6 @@ macro ROWS 10 end macro COLS 20 end macro BOARD_SIZE ROWS COLS * end -macro sizeof(timespec) 16 end - // memory layout memory delta_time sizeof(timespec) end memory board_current_index sizeof(u64) end diff --git a/std/std.porth b/std/std.porth index 1b6f52d3..6871d576 100644 --- a/std/std.porth +++ b/std/std.porth @@ -342,6 +342,8 @@ macro TIMER_ABSTIME 1 end macro MAP_PRIVATE 2 end macro PROT_READ 1 end +macro sizeof(timespec) 16 end + macro sizeof(stat) 144 end macro stat.st_dev 0 + end macro stat.st_ino 8 + end @@ -357,19 +359,19 @@ macro stat.st_blocks 64 + end macro stat.st_atim 72 + end macro stat.st_mtim 88 + end macro stat.st_ctim 104 + end -macro sizeof(stat.st_dev) 8 end -macro sizeof(stat.st_ino) 8 end -macro sizeof(stat.st_mode) 4 end -macro sizeof(stat.st_nlink) 8 end -macro sizeof(stat.st_uid) 4 end -macro sizeof(stat.st_gid) 4 end -macro sizeof(stat.st_rdev) 8 end -macro sizeof(stat.st_size) 8 end -macro sizeof(stat.st_blksize) 8 end -macro sizeof(stat.st_blocks) 8 end -macro sizeof(stat.st_atim) 16 end -macro sizeof(stat.st_mtim) 16 end -macro sizeof(stat.st_ctim) 16 end +macro sizeof(stat.st_dev) sizeof(u64) end +macro sizeof(stat.st_ino) sizeof(u64) end +macro sizeof(stat.st_mode) sizeof(u32) end +macro sizeof(stat.st_nlink) sizeof(u64) end +macro sizeof(stat.st_uid) sizeof(u32) end +macro sizeof(stat.st_gid) sizeof(u32) end +macro sizeof(stat.st_rdev) sizeof(u64) end +macro sizeof(stat.st_size) sizeof(u64) end +macro sizeof(stat.st_blksize) sizeof(u64) end +macro sizeof(stat.st_blocks) sizeof(u64) end +macro sizeof(stat.st_atim) sizeof(timespec) end +macro sizeof(stat.st_mtim) sizeof(timespec) end +macro sizeof(stat.st_ctim) sizeof(timespec) end // Wrappers for common syscalls macro write SYS_write syscall3 end @@ -380,6 +382,9 @@ macro close SYS_close syscall1 end macro exit SYS_exit syscall1 drop end macro mmap SYS_mmap syscall6 end macro clock_nanosleep SYS_clock_nanosleep syscall4 end +macro fork SYS_fork syscall0 end +macro getpid SYS_getpid syscall0 end +macro execve SYS_execve syscall3 end macro 2dup over over end macro 2drop drop drop end From 6dc2c48268e72227072ae2e426de44da952a3813 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 10:51:16 +0700 Subject: [PATCH 070/145] Make porth.porth run nasm and ld as child processes --- porth.porth | 220 +++++++++++++++++++++++++++++++++----------------- porth.py | 4 +- std/std.porth | 1 + 3 files changed, 151 insertions(+), 74 deletions(-) diff --git a/porth.porth b/porth.porth index 7135eee1..0be2479f 100644 --- a/porth.porth +++ b/porth.porth @@ -37,6 +37,61 @@ memory sim-stack sizeof(u64) SIM_STACK_CAP * end memory ops-count sizeof(u64) end memory ops sizeof(Op) OPS_CAP * end +memory out_fd sizeof(u64) end +macro @out_fd out_fd @64 end +macro !out_fd out_fd !64 end + +memory empty_envp sizeof(ptr) end +memory nasm_argv sizeof(ptr) 4 * end +// TODO: search for external utilities in $PATH +"/usr/bin/nasm"c nasm_argv 0 8 * + !64 +"-felf64"c nasm_argv 1 8 * + !64 +"output.asm"c nasm_argv 2 8 * + !64 + +memory ld_argv sizeof(ptr) 5 * end +"/usr/bin/ld"c ld_argv 0 8 * + !64 +"-o"c ld_argv 1 8 * + !64 +"output"c ld_argv 2 8 * + !64 +"output.o"c ld_argv 3 8 * + !64 + +memory output_argv sizeof(ptr) 2 * end +"./output"c output_argv 0 8 * + !64 + +memory wstatus sizeof(u64) end +macro cmd_echoed // argv + "[CMD]" puts + dup while dup @64 0 != do + " " puts + // TODO: properly escape the logged CMD + dup @64 cast(ptr) cstr-to-str puts + 8 + + end drop + "\n" puts + + fork + + if dup 0 = do + drop + dup @64 cast(ptr) empty_envp + rot rot + execve + if dup 0 < do + "[ERROR] could not exec external program\n" eputs + 1 exit + end + elif dup 0 > do + drop + // TODO: handle the result of wait4 + NULL 0 wstatus -1 wait4 drop + else + drop + "[ERROR] could not fork a child\n" eputs + 1 exit + end + + drop +end + macro sim-stack-push // u64 -- if sim-stack-count @64 SIM_STACK_CAP >= do here eputs ": ERROR: data stack overflow in simulation mode\n" eputs 1 exit @@ -119,46 +174,60 @@ macro dump-ops // -- drop end -// TODO: porth.porth does not run nasm and ld as external commands to finish off the process of compilation macro compile-ops // -- - "BITS 64\n" puts - "segment .text\n" puts - "print:\n" puts - " mov r9, -3689348814741910323\n" puts - " sub rsp, 40\n" puts - " mov BYTE [rsp+31], 10\n" puts - " lea rcx, [rsp+30]\n" puts - ".L2:\n" puts - " mov rax, rdi\n" puts - " lea r8, [rsp+32]\n" puts - " mul r9\n" puts - " mov rax, rdi\n" puts - " sub r8, rcx\n" puts - " shr rdx, 3\n" puts - " lea rsi, [rdx+rdx*4]\n" puts - " add rsi, rsi\n" puts - " sub rax, rsi\n" puts - " add eax, 48\n" puts - " mov BYTE [rcx], al\n" puts - " mov rax, rdi\n" puts - " mov rdi, rdx\n" puts - " mov rdx, rcx\n" puts - " sub rcx, 1\n" puts - " cmp rax, 9\n" puts - " ja .L2\n" puts - " lea rax, [rsp+32]\n" puts - " mov edi, 1\n" puts - " sub rdx, rax\n" puts - " xor eax, eax\n" puts - " lea rsi, [rsp+32+rdx]\n" puts - " mov rdx, r8\n" puts - " mov rax, 1\n" puts - " syscall\n" puts - " add rsp, 40\n" puts - " ret\n" puts - "global _start\n" puts - "_start:\n" puts - " mov [args_ptr], rsp\n" puts + "[INFO] Generating output.asm\n" puts + + 420 // mode + O_CREAT O_WRONLY or // flags + // TODO: the output file path should be based on the input file path + "output.asm"c // pathname + AT_FDCWD + openat + !out_fd + + if @out_fd 0 < do + "[ERROR] could not open `output.asm`\n" eputs + 1 exit + end + + "BITS 64\n" @out_fd fputs + "segment .text\n" @out_fd fputs + "print:\n" @out_fd fputs + " mov r9, -3689348814741910323\n" @out_fd fputs + " sub rsp, 40\n" @out_fd fputs + " mov BYTE [rsp+31], 10\n" @out_fd fputs + " lea rcx, [rsp+30]\n" @out_fd fputs + ".L2:\n" @out_fd fputs + " mov rax, rdi\n" @out_fd fputs + " lea r8, [rsp+32]\n" @out_fd fputs + " mul r9\n" @out_fd fputs + " mov rax, rdi\n" @out_fd fputs + " sub r8, rcx\n" @out_fd fputs + " shr rdx, 3\n" @out_fd fputs + " lea rsi, [rdx+rdx*4]\n" @out_fd fputs + " add rsi, rsi\n" @out_fd fputs + " sub rax, rsi\n" @out_fd fputs + " add eax, 48\n" @out_fd fputs + " mov BYTE [rcx], al\n" @out_fd fputs + " mov rax, rdi\n" @out_fd fputs + " mov rdi, rdx\n" @out_fd fputs + " mov rdx, rcx\n" @out_fd fputs + " sub rcx, 1\n" @out_fd fputs + " cmp rax, 9\n" @out_fd fputs + " ja .L2\n" @out_fd fputs + " lea rax, [rsp+32]\n" @out_fd fputs + " mov edi, 1\n" @out_fd fputs + " sub rdx, rax\n" @out_fd fputs + " xor eax, eax\n" @out_fd fputs + " lea rsi, [rsp+32+rdx]\n" @out_fd fputs + " mov rdx, r8\n" @out_fd fputs + " mov rax, 1\n" @out_fd fputs + " syscall\n" @out_fd fputs + " add rsp, 40\n" @out_fd fputs + " ret\n" @out_fd fputs + "global _start\n" @out_fd fputs + "_start:\n" @out_fd fputs + " mov [args_ptr], rsp\n" @out_fd fputs 0 while dup ops-count @64 < do dup sizeof(Op) * ops + @@ -170,39 +239,39 @@ macro compile-ops // -- end if dup @Op.type OP_PUSH_INT = do - " ;; -- push int " puts dup @Op.operand putd " --\n" puts - " mov rax, " puts dup @Op.operand putd "\n" puts - " push rax\n" puts + " ;; -- push int " @out_fd fputs dup @Op.operand @out_fd fputd " --\n" @out_fd fputs + " mov rax, " @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs + " push rax\n" @out_fd fputs elif dup @Op.type OP_PLUS = do - " ;; -- plus --\n" puts - " pop rax\n" puts - " pop rbx\n" puts - " add rax, rbx\n" puts - " push rax\n" puts + " ;; -- plus --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " add rax, rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs elif dup @Op.type OP_MINUS = do - " ;; -- minus --\n" puts - " pop rax\n" puts - " pop rbx\n" puts - " sub rbx, rax\n" puts - " push rbx\n" puts + " ;; -- minus --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " sub rbx, rax\n" @out_fd fputs + " push rbx\n" @out_fd fputs elif dup @Op.type OP_MUL = do - " ;; -- mul --\n" puts - " pop rax\n" puts - " pop rbx\n" puts - " mul rbx\n" puts - " push rax\n" puts + " ;; -- mul --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " mul rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs elif dup @Op.type OP_PRINT = do - " ;; -- print --\n" puts - " pop rdi\n" puts - " call print\n" puts + " ;; -- print --\n" @out_fd fputs + " pop rdi\n" @out_fd fputs + " call print\n" @out_fd fputs elif dup @Op.type OP_DUP = do - " ;; -- dup --\n" puts - " pop rax\n" puts - " push rax\n" puts - " push rax\n" puts + " ;; -- dup --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " push rax\n" @out_fd fputs + " push rax\n" @out_fd fputs elif dup @Op.type OP_DROP = do - " ;; -- drop --\n" puts - " pop rax\n" puts + " ;; -- drop --\n" @out_fd fputs + " pop rax\n" @out_fd fputs else here eputs ": unreachable\n" eputs 1 exit end @@ -213,12 +282,18 @@ macro compile-ops // -- end drop - " mov rax, 60\n" puts - " mov rdi, 0\n" puts - " syscall\n" puts - "segment .bss\n" puts - "args_ptr: resq 1\n" puts - "mem: resb " puts MEM_CAPACITY putd "\n" puts + " mov rax, 60\n" @out_fd fputs + " mov rdi, 0\n" @out_fd fputs + " syscall\n" @out_fd fputs + "segment .bss\n" @out_fd fputs + "args_ptr: resq 1\n" @out_fd fputs + "mem: resb " @out_fd fputs MEM_CAPACITY @out_fd fputd "\n" @out_fd fputs + + @out_fd close drop + + nasm_argv cmd_echoed + ld_argv cmd_echoed + output_argv cmd_echoed end macro simulate-ops // -- @@ -342,6 +417,7 @@ macro usage // -- dup "Usage: porth \n" rot fputs dup " SUBCOMMANDS:\n" rot fputs dup " sim Simulate the program.\n" rot fputs + // TODO: -r flag for com subcommand dup " com Compile the program\n" rot fputs dup " dump Dump the ops of the program\n" rot fputs dup " help Print this help to stdout and exit with 0 code\n" rot fputs diff --git a/porth.py b/porth.py index 473ed62c..61ab6d2f 100755 --- a/porth.py +++ b/porth.py @@ -647,10 +647,10 @@ def type_check_program(program: Program): a_type, a_loc = ctx.stack.pop() b_type, b_loc = ctx.stack.pop() - if a_type == b_type and a_type == DataType.INT: + if a_type == b_type: ctx.stack.append((DataType.BOOL, op.token)) else: - compiler_error_with_expansion_stack(op.token, "invalid argument types fo EQ intrinsic. Expected INT.") + compiler_error_with_expansion_stack(op.token, "invalid argument types fo EQ intrinsic.") exit(1) elif op.operand == Intrinsic.GT: assert len(DataType) == 3, "Exhaustive type handling in GT intrinsic" diff --git a/std/std.porth b/std/std.porth index 6871d576..cdce9cba 100644 --- a/std/std.porth +++ b/std/std.porth @@ -385,6 +385,7 @@ macro clock_nanosleep SYS_clock_nanosleep syscall4 end macro fork SYS_fork syscall0 end macro getpid SYS_getpid syscall0 end macro execve SYS_execve syscall3 end +macro wait4 SYS_wait4 syscall4 end macro 2dup over over end macro 2drop drop drop end From 14713bcc633703d241e822ba3538f51f201d6822 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 20:22:10 +0700 Subject: [PATCH 071/145] Fix "not enough argument" error reporting for do blocks --- porth.porth | 1 - porth.py | 6 +++--- tests/not-enough-args-for-do.porth | 5 +++++ tests/not-enough-args-for-do.txt | 9 +++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 tests/not-enough-args-for-do.porth create mode 100644 tests/not-enough-args-for-do.txt diff --git a/porth.porth b/porth.porth index 0be2479f..4d201968 100644 --- a/porth.porth +++ b/porth.porth @@ -23,7 +23,6 @@ macro !Op.type Op.type !64 end macro @Op.operand Op.operand @64 end macro !Op.operand Op.operand !64 end - memory file_path_cstr sizeof(ptr) end memory fd sizeof(u64) end memory statbuf sizeof(stat) end diff --git a/porth.py b/porth.py index 61ab6d2f..a17165e0 100755 --- a/porth.py +++ b/porth.py @@ -533,12 +533,12 @@ def compiler_note(loc: Loc, message: str): compiler_diagnostic(loc, 'NOTE', message) def not_enough_arguments(op: Op): + assert len(OpType) == 11, f"Exhaustive handling of Op types in not_enough_arguments() (expected {len(OpType)}). Keep in mind that not all of the ops should be handled in here. Only those that consume elements from the stack." if op.typ == OpType.INTRINSIC: assert isinstance(op.operand, Intrinsic) compiler_error_with_expansion_stack(op.token, "not enough arguments for the `%s` intrinsic" % INTRINSIC_NAMES[op.operand]) - # TODO: why don't we add while-do here too? - elif op.typ == OpType.IF: - compiler_error_with_expansion_stack(op.token, "not enough arguments for the if-block") + elif op.typ == OpType.DO: + compiler_error_with_expansion_stack(op.token, "not enough arguments for the do-block") else: assert False, "unsupported type of operation" diff --git a/tests/not-enough-args-for-do.porth b/tests/not-enough-args-for-do.porth new file mode 100644 index 00000000..67ce4d0f --- /dev/null +++ b/tests/not-enough-args-for-do.porth @@ -0,0 +1,5 @@ +include "std.porth" + +if do + "test\n" puts +end diff --git a/tests/not-enough-args-for-do.txt b/tests/not-enough-args-for-do.txt new file mode 100644 index 00000000..f5e5fef4 --- /dev/null +++ b/tests/not-enough-args-for-do.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 87 +./tests/not-enough-args-for-do.porth:3:4: ERROR: not enough arguments for the do-block + From 828243db34d2a0b0be3c15bbdab76f7cdf74a4f3 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 21:49:00 +0700 Subject: [PATCH 072/145] Add full subcommand to test.py --- test.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test.py b/test.py index f8154690..caef8413 100755 --- a/test.py +++ b/test.py @@ -186,6 +186,9 @@ def usage(exe_name: str): print(" file or folder with *.porth files. The default [TARGET] is") print(" './tests/'") print() + print(" full") + print(" Test and type check everything. (Should be run on CI)") + print() print(" help") print(" Print this message to stdout and exit with 0 code.") @@ -237,6 +240,12 @@ def usage(exe_name: str): run_test_for_file(target) else: assert False, 'unreachable' + elif subcommand == 'full': + cmd_run_echoed(['mypy', './porth.py']) + cmd_run_echoed(['mypy', './test.py']) + run_test_for_folder('./tests/') + run_test_for_folder('./examples/') + run_test_for_folder('./euler/') elif subcommand == 'help': usage(exe_name) else: From cc06f09ad3495196bc15f5202c6b17dcd30a1cee Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 21:50:31 +0700 Subject: [PATCH 073/145] TODO about bug in test.py --- test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test.py b/test.py index caef8413..6dceee17 100755 --- a/test.py +++ b/test.py @@ -239,6 +239,7 @@ def usage(exe_name: str): elif path.isfile(target): run_test_for_file(target) else: + # TODO: `./test.py run non-existing-file` fails with 'unreachable' assert False, 'unreachable' elif subcommand == 'full': cmd_run_echoed(['mypy', './porth.py']) From 181584295e7978c8ec92e388a7896f90f978df47 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 21:59:05 +0700 Subject: [PATCH 074/145] Proper error reporting for memory redefinition --- porth.py | 16 ++++++++++++---- tests/memory-redefinition.porth | 2 ++ tests/memory-redefinition.txt | 10 ++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 tests/memory-redefinition.porth create mode 100644 tests/memory-redefinition.txt diff --git a/porth.py b/porth.py index a17165e0..6a9b96d6 100755 --- a/porth.py +++ b/porth.py @@ -1535,12 +1535,17 @@ def expand_macro(macro: Macro, expanded_from: Token) -> List[Token]: token.expanded_count = expanded_from.expanded_count + 1 return result +@dataclass +class Memory: + offset: MemAddr + loc: Loc + def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], expansion_limit: int) -> Program: stack: List[OpAddr] = [] program: Program = Program(ops=[], memory_capacity=0) rtokens: List[Token] = list(reversed(tokens)) macros: Dict[str, Macro] = {} - memories: Dict[str, MemAddr] = {} + memories: Dict[str, Memory] = {} ip: OpAddr = 0; while len(rtokens) > 0: token = rtokens.pop() @@ -1556,7 +1561,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp exit(1) rtokens += reversed(expand_macro(macros[token.value], token)) elif token.value in memories: - program.ops.append(Op(typ=OpType.PUSH_MEM, token=token, operand=memories[token.value])) + program.ops.append(Op(typ=OpType.PUSH_MEM, token=token, operand=memories[token.value].offset)) ip += 1 else: compiler_error_with_expansion_stack(token, "unknown word `%s`" % token.value) @@ -1700,8 +1705,11 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp exit(1) assert isinstance(token.value, str), "This is probably a bug in the lexer" memory_name = token.value + memory_loc = token.loc if memory_name in memories: - assert False, "TODO: redefinition of a memory region" + compiler_error_with_expansion_stack(token, "redefinition of already existing memory `%s`" % memory_name) + compiler_note(memories[memory_name].loc, "the first definition is located here") + exit(1) if memory_name in INTRINSIC_BY_NAMES: assert False, "TODO: redefinition of an intrinsic" if memory_name in macros: @@ -1740,7 +1748,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp if len(mem_size_stack) != 1: assert False, "TODO: memory definition expects only one integer" memory_size = mem_size_stack.pop() - memories[memory_name] = program.memory_capacity + memories[memory_name] = Memory(offset=program.memory_capacity, loc=memory_loc) program.memory_capacity += memory_size elif token.value == Keyword.MACRO: if len(rtokens) == 0: diff --git a/tests/memory-redefinition.porth b/tests/memory-redefinition.porth new file mode 100644 index 00000000..44ab5a2f --- /dev/null +++ b/tests/memory-redefinition.porth @@ -0,0 +1,2 @@ +memory xs 69 end +memory xs 420 end diff --git a/tests/memory-redefinition.txt b/tests/memory-redefinition.txt new file mode 100644 index 00000000..bc9cae90 --- /dev/null +++ b/tests/memory-redefinition.txt @@ -0,0 +1,10 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 173 +./tests/memory-redefinition.porth:2:8: ERROR: redefinition of already existing memory `xs` +./tests/memory-redefinition.porth:1:8: NOTE: the first definition is located here + From 298a316585cc7c5a426d87d09c818807a62ae0a5 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 22:31:45 +0700 Subject: [PATCH 075/145] Proper error reporting on memory redefining intrinsic --- porth.py | 3 ++- test.py | 3 +-- tests/memory-redefinition-of-intrinsic.porth | 1 + tests/memory-redefinition-of-intrinsic.txt | 9 +++++++++ 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 tests/memory-redefinition-of-intrinsic.porth create mode 100644 tests/memory-redefinition-of-intrinsic.txt diff --git a/porth.py b/porth.py index 6a9b96d6..c98b82f9 100755 --- a/porth.py +++ b/porth.py @@ -1711,7 +1711,8 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp compiler_note(memories[memory_name].loc, "the first definition is located here") exit(1) if memory_name in INTRINSIC_BY_NAMES: - assert False, "TODO: redefinition of an intrinsic" + compiler_error_with_expansion_stack(token, "redefinition of an intrinsic word `%s`. Please choose a different name for your memory region." % (memory_name, )) + exit(1) if memory_name in macros: assert False, "TODO: redefinition of a macro" mem_size_stack: List[int] = [] diff --git a/test.py b/test.py index 6dceee17..c894dc90 100755 --- a/test.py +++ b/test.py @@ -242,8 +242,7 @@ def usage(exe_name: str): # TODO: `./test.py run non-existing-file` fails with 'unreachable' assert False, 'unreachable' elif subcommand == 'full': - cmd_run_echoed(['mypy', './porth.py']) - cmd_run_echoed(['mypy', './test.py']) + cmd_run_echoed(['mypy', './porth.py', './test.py']) run_test_for_folder('./tests/') run_test_for_folder('./examples/') run_test_for_folder('./euler/') diff --git a/tests/memory-redefinition-of-intrinsic.porth b/tests/memory-redefinition-of-intrinsic.porth new file mode 100644 index 00000000..0b558870 --- /dev/null +++ b/tests/memory-redefinition-of-intrinsic.porth @@ -0,0 +1 @@ +memory dup 69 end diff --git a/tests/memory-redefinition-of-intrinsic.txt b/tests/memory-redefinition-of-intrinsic.txt new file mode 100644 index 00000000..f7b65e72 --- /dev/null +++ b/tests/memory-redefinition-of-intrinsic.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 155 +./tests/memory-redefinition-of-intrinsic.porth:1:8: ERROR: redefinition of an intrinsic word `dup`. Please choose a different name for your memory region. + From 9c510a80ca14b8875a5c5948fd045f2458676403 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 22:50:08 +0700 Subject: [PATCH 076/145] Proper error reporting on memory redefinining macro --- porth.py | 4 +++- tests/memory-redefinition-of-macro.porth | 2 ++ tests/memory-redefinition-of-macro.txt | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/memory-redefinition-of-macro.porth create mode 100644 tests/memory-redefinition-of-macro.txt diff --git a/porth.py b/porth.py index c98b82f9..a4f5e80e 100755 --- a/porth.py +++ b/porth.py @@ -1714,7 +1714,9 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp compiler_error_with_expansion_stack(token, "redefinition of an intrinsic word `%s`. Please choose a different name for your memory region." % (memory_name, )) exit(1) if memory_name in macros: - assert False, "TODO: redefinition of a macro" + compiler_error_with_expansion_stack(token, "redefinition of a macro `%s`" % (memory_name, )) + compiler_note(macros[memory_name].loc, "the original definition is located here") + exit(1) mem_size_stack: List[int] = [] while len(rtokens) > 0: token = rtokens.pop() diff --git a/tests/memory-redefinition-of-macro.porth b/tests/memory-redefinition-of-macro.porth new file mode 100644 index 00000000..534814a7 --- /dev/null +++ b/tests/memory-redefinition-of-macro.porth @@ -0,0 +1,2 @@ +macro hello 69 end +memory hello 69 end diff --git a/tests/memory-redefinition-of-macro.txt b/tests/memory-redefinition-of-macro.txt new file mode 100644 index 00000000..e00d3350 --- /dev/null +++ b/tests/memory-redefinition-of-macro.txt @@ -0,0 +1,10 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 181 +./tests/memory-redefinition-of-macro.porth:2:8: ERROR: redefinition of a macro `hello` +./tests/memory-redefinition-of-macro.porth:1:7: NOTE: the original definition is located here + From 0bdd26ac52c3c04993c6f30b78c5ddd00296eb4e Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 22:59:39 +0700 Subject: [PATCH 077/145] Factor out check_word_redefinition() --- porth.py | 38 ++++++++++------------ tests/intrinsic-redefinition-error.txt | 4 +-- tests/macro-redefinition-error.txt | 6 ++-- tests/memory-redefinition-of-intrinsic.txt | 4 +-- tests/memory-redefinition.txt | 6 ++-- 5 files changed, 28 insertions(+), 30 deletions(-) diff --git a/porth.py b/porth.py index a4f5e80e..fb9b1b46 100755 --- a/porth.py +++ b/porth.py @@ -1540,6 +1540,22 @@ class Memory: offset: MemAddr loc: Loc +def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: Dict[str, Macro]): + assert token.typ == TokenType.WORD + assert isinstance(token.value, str) + name: str = token.value + if name in memories: + compiler_error_with_expansion_stack(token, "redefinition of a memory region `%s`" % name) + compiler_note(memories[name].loc, "the original definition is located here") + exit(1) + if name in INTRINSIC_BY_NAMES: + compiler_error_with_expansion_stack(token, "redefinition of an intrinsic word `%s`" % (name, )) + exit(1) + if name in macros: + compiler_error_with_expansion_stack(token, "redefinition of a macro `%s`" % (name, )) + compiler_note(macros[name].loc, "the original definition is located here") + exit(1) + def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], expansion_limit: int) -> Program: stack: List[OpAddr] = [] program: Program = Program(ops=[], memory_capacity=0) @@ -1706,17 +1722,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp assert isinstance(token.value, str), "This is probably a bug in the lexer" memory_name = token.value memory_loc = token.loc - if memory_name in memories: - compiler_error_with_expansion_stack(token, "redefinition of already existing memory `%s`" % memory_name) - compiler_note(memories[memory_name].loc, "the first definition is located here") - exit(1) - if memory_name in INTRINSIC_BY_NAMES: - compiler_error_with_expansion_stack(token, "redefinition of an intrinsic word `%s`. Please choose a different name for your memory region." % (memory_name, )) - exit(1) - if memory_name in macros: - compiler_error_with_expansion_stack(token, "redefinition of a macro `%s`" % (memory_name, )) - compiler_note(macros[memory_name].loc, "the original definition is located here") - exit(1) + check_word_redefinition(token, memories, macros) mem_size_stack: List[int] = [] while len(rtokens) > 0: token = rtokens.pop() @@ -1762,15 +1768,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp compiler_error_with_expansion_stack(token, "expected macro name to be %s but found %s" % (human(TokenType.WORD), human(token.typ))) exit(1) assert isinstance(token.value, str), "This is probably a bug in the lexer" - if token.value in macros: - compiler_error_with_expansion_stack(token, "redefinition of already existing macro `%s`" % token.value) - compiler_note(macros[token.value].loc, "the first definition is located here") - exit(1) - if token.value in INTRINSIC_BY_NAMES: - compiler_error_with_expansion_stack(token, "redefinition of an intrinsic word `%s`. Please choose a different name for your macro." % (token.value, )) - exit(1) - if token.value in memories: - assert False, "TODO: redefinition of a memory" + check_word_redefinition(token, memories, macros) macro = Macro(token.loc, []) macros[token.value] = macro nesting_depth = 0 diff --git a/tests/intrinsic-redefinition-error.txt b/tests/intrinsic-redefinition-error.txt index a7392cca..88323c8d 100644 --- a/tests/intrinsic-redefinition-error.txt +++ b/tests/intrinsic-redefinition-error.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 141 -./tests/intrinsic-redefinition-error.porth:1:7: ERROR: redefinition of an intrinsic word `+`. Please choose a different name for your macro. +:b stderr 93 +./tests/intrinsic-redefinition-error.porth:1:7: ERROR: redefinition of an intrinsic word `+` diff --git a/tests/macro-redefinition-error.txt b/tests/macro-redefinition-error.txt index 21451dcd..6c6958f1 100644 --- a/tests/macro-redefinition-error.txt +++ b/tests/macro-redefinition-error.txt @@ -4,7 +4,7 @@ :i returncode 1 :b stdout 0 -:b stderr 184 -./tests/macro-redefinition-error.porth:2:7: ERROR: redefinition of already existing macro `test` -./tests/macro-redefinition-error.porth:1:7: NOTE: the first definition is located here +:b stderr 172 +./tests/macro-redefinition-error.porth:2:7: ERROR: redefinition of a macro `test` +./tests/macro-redefinition-error.porth:1:7: NOTE: the original definition is located here diff --git a/tests/memory-redefinition-of-intrinsic.txt b/tests/memory-redefinition-of-intrinsic.txt index f7b65e72..6dc1fb4b 100644 --- a/tests/memory-redefinition-of-intrinsic.txt +++ b/tests/memory-redefinition-of-intrinsic.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 155 -./tests/memory-redefinition-of-intrinsic.porth:1:8: ERROR: redefinition of an intrinsic word `dup`. Please choose a different name for your memory region. +:b stderr 99 +./tests/memory-redefinition-of-intrinsic.porth:1:8: ERROR: redefinition of an intrinsic word `dup` diff --git a/tests/memory-redefinition.txt b/tests/memory-redefinition.txt index bc9cae90..24454cf6 100644 --- a/tests/memory-redefinition.txt +++ b/tests/memory-redefinition.txt @@ -4,7 +4,7 @@ :i returncode 1 :b stdout 0 -:b stderr 173 -./tests/memory-redefinition.porth:2:8: ERROR: redefinition of already existing memory `xs` -./tests/memory-redefinition.porth:1:8: NOTE: the first definition is located here +:b stderr 168 +./tests/memory-redefinition.porth:2:8: ERROR: redefinition of a memory region `xs` +./tests/memory-redefinition.porth:1:8: NOTE: the original definition is located here From 400c21a603f791a6206b3b33090bd8f4737c954d Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 23:34:46 +0700 Subject: [PATCH 078/145] Report unsupported keyword in memory definition properly --- porth.py | 17 ++++++++++------- .../memory-definition-unsupported-keyword.porth | 7 +++++++ tests/memory-definition-unsupported-keyword.txt | 9 +++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 tests/memory-definition-unsupported-keyword.porth create mode 100644 tests/memory-definition-unsupported-keyword.txt diff --git a/porth.py b/porth.py index fb9b1b46..9f3f0f3e 100755 --- a/porth.py +++ b/porth.py @@ -1446,7 +1446,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("mem: resb %d\n" % program.memory_capacity) assert len(Keyword) == 9, "Exhaustive KEYWORD_NAMES definition." -KEYWORD_NAMES = { +KEYWORD_BY_NAMES: Dict[str, Keyword] = { 'if': Keyword.IF, 'elif': Keyword.ELIF, 'else': Keyword.ELSE, @@ -1457,9 +1457,10 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'include': Keyword.INCLUDE, 'memory': Keyword.MEMORY, } +KEYWORD_NAMES: Dict[Keyword, str] = {v: k for k, v in KEYWORD_BY_NAMES.items()} assert len(Intrinsic) == 42, "Exhaustive INTRINSIC_BY_NAMES definition" -INTRINSIC_BY_NAMES = { +INTRINSIC_BY_NAMES: Dict[str, Intrinsic] = { '+': Intrinsic.PLUS, '-': Intrinsic.MINUS, '*': Intrinsic.MUL, @@ -1503,7 +1504,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'syscall5': Intrinsic.SYSCALL5, 'syscall6': Intrinsic.SYSCALL6, } -INTRINSIC_NAMES = {v: k for k, v in INTRINSIC_BY_NAMES.items()} +INTRINSIC_NAMES: Dict[Intrinsic, str] = {v: k for k, v in INTRINSIC_BY_NAMES.items()} @dataclass class Macro: @@ -1727,16 +1728,18 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp while len(rtokens) > 0: token = rtokens.pop() if token.typ == TokenType.KEYWORD: + assert isinstance(token.value, Keyword) if token.value == Keyword.END: break else: - assert False, "TODO: unsupported keyword in memory definition" + compiler_error_with_expansion_stack(token, f"unsupported keyword `{KEYWORD_NAMES[token.value]}` in memory definition") + exit(1) elif token.typ == TokenType.INT: assert isinstance(token.value, int) mem_size_stack.append(token.value) elif token.typ == TokenType.WORD: assert isinstance(token.value, str) - # TODO: check of stack underflows in memory definition + # TODO: check if stack underflows in memory definition if token.value == INTRINSIC_NAMES[Intrinsic.PLUS]: a = mem_size_stack.pop() b = mem_size_stack.pop() @@ -1875,8 +1878,8 @@ def lex_lines(file_path: str, lines: List[str]) -> Generator[Token, None, None]: try: yield Token(TokenType.INT, text_of_token, loc, int(text_of_token)) except ValueError: - if text_of_token in KEYWORD_NAMES: - yield Token(TokenType.KEYWORD, text_of_token, loc, KEYWORD_NAMES[text_of_token]) + if text_of_token in KEYWORD_BY_NAMES: + yield Token(TokenType.KEYWORD, text_of_token, loc, KEYWORD_BY_NAMES[text_of_token]) else: if text_of_token.startswith("//"): break diff --git a/tests/memory-definition-unsupported-keyword.porth b/tests/memory-definition-unsupported-keyword.porth new file mode 100644 index 00000000..25b20c54 --- /dev/null +++ b/tests/memory-definition-unsupported-keyword.porth @@ -0,0 +1,7 @@ +memory hello + if true do + 69 + else + 420 + end +end diff --git a/tests/memory-definition-unsupported-keyword.txt b/tests/memory-definition-unsupported-keyword.txt new file mode 100644 index 00000000..79ac0269 --- /dev/null +++ b/tests/memory-definition-unsupported-keyword.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 110 +./tests/memory-definition-unsupported-keyword.porth:2:3: ERROR: unsupported keyword `if` in memory definition + From ab1c46401a1bb336077eb996cd0a29b9c403d53b Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 23:40:52 +0700 Subject: [PATCH 079/145] Report stack underflow in memory definition properly --- porth.py | 7 ++++++- tests/memory-definition-stack-underflow.porth | 1 + tests/memory-definition-stack-underflow.txt | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/memory-definition-stack-underflow.porth create mode 100644 tests/memory-definition-stack-underflow.txt diff --git a/porth.py b/porth.py index 9f3f0f3e..cc0c705e 100755 --- a/porth.py +++ b/porth.py @@ -1739,12 +1739,17 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp mem_size_stack.append(token.value) elif token.typ == TokenType.WORD: assert isinstance(token.value, str) - # TODO: check if stack underflows in memory definition if token.value == INTRINSIC_NAMES[Intrinsic.PLUS]: + if len(mem_size_stack) < 2: + compiler_error_with_expansion_stack(token, f"not enough arguments for `{INTRINSIC_NAMES[Intrinsic.PLUS]}` intrinsic in memory definition") + exit(1) a = mem_size_stack.pop() b = mem_size_stack.pop() mem_size_stack.append(a + b) elif token.value == INTRINSIC_NAMES[Intrinsic.MUL]: + if len(mem_size_stack) < 2: + compiler_error_with_expansion_stack(token, f"not enough arguments for `{INTRINSIC_NAMES[Intrinsic.PLUS]}` intrinsic in memory definition") + exit(1) a = mem_size_stack.pop() b = mem_size_stack.pop() mem_size_stack.append(a * b) diff --git a/tests/memory-definition-stack-underflow.porth b/tests/memory-definition-stack-underflow.porth new file mode 100644 index 00000000..1669638d --- /dev/null +++ b/tests/memory-definition-stack-underflow.porth @@ -0,0 +1 @@ +memory xs + end diff --git a/tests/memory-definition-stack-underflow.txt b/tests/memory-definition-stack-underflow.txt new file mode 100644 index 00000000..8dd9ab58 --- /dev/null +++ b/tests/memory-definition-stack-underflow.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 121 +./tests/memory-definition-stack-underflow.porth:1:11: ERROR: not enough arguments for `+` intrinsic in memory definition + From c4f7053d2da0ae6ca7274b866e8e77fd67feba8f Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 19 Oct 2021 23:55:28 +0700 Subject: [PATCH 080/145] Improve error reporting in memory definition --- porth.py | 62 ++++++++++++++++------- tests/memory-single-number.porth | 1 + tests/memory-single-number.txt | 9 ++++ tests/memory-unsupported-token-type.porth | 1 + tests/memory-unsupported-token-type.txt | 9 ++++ tests/memory-unsupported-word.porth | 1 + tests/memory-unsupported-word.txt | 9 ++++ 7 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 tests/memory-single-number.porth create mode 100644 tests/memory-single-number.txt create mode 100644 tests/memory-unsupported-token-type.porth create mode 100644 tests/memory-unsupported-token-type.txt create mode 100644 tests/memory-unsupported-word.porth create mode 100644 tests/memory-unsupported-word.txt diff --git a/porth.py b/porth.py index cc0c705e..f0724725 100755 --- a/porth.py +++ b/porth.py @@ -1511,21 +1511,45 @@ class Macro: loc: Loc tokens: List[Token] -def human(obj: Union[TokenType, Op, Intrinsic]) -> str: +class HumanNumber(Enum): + Singular=auto() + Plural=auto() + +def human(obj: TokenType, number: HumanNumber = HumanNumber.Singular) -> str: '''Human readable representation of an object that can be used in error messages''' - assert len(TokenType) == 6, "Exhaustive handling of token types in human()" - if obj == TokenType.WORD: - return "a word" - elif obj == TokenType.INT: - return "an integer" - elif obj == TokenType.STR: - return "a string" - elif obj == TokenType.CSTR: - return "a C-style string" - elif obj == TokenType.CHAR: - return "a character" - elif obj == TokenType.KEYWORD: - return "a keyword" + assert len(HumanNumber) == 2, "Exhaustive handling of number category in human()" + if number == HumanNumber.Singular: + assert len(TokenType) == 6, "Exhaustive handling of token types in human()" + if obj == TokenType.WORD: + return "a word" + elif obj == TokenType.INT: + return "an integer" + elif obj == TokenType.STR: + return "a string" + elif obj == TokenType.CSTR: + return "a C-style string" + elif obj == TokenType.CHAR: + return "a character" + elif obj == TokenType.KEYWORD: + return "a keyword" + else: + assert False, "unreachable" + elif number == HumanNumber.Plural: + assert len(TokenType) == 6, "Exhaustive handling of token types in human()" + if obj == TokenType.WORD: + return "words" + elif obj == TokenType.INT: + return "integers" + elif obj == TokenType.STR: + return "strings" + elif obj == TokenType.CSTR: + return "C-style strings" + elif obj == TokenType.CHAR: + return "characters" + elif obj == TokenType.KEYWORD: + return "keywords" + else: + assert False, "unreachable" else: assert False, "unreachable" @@ -1560,6 +1584,7 @@ def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: D def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], expansion_limit: int) -> Program: stack: List[OpAddr] = [] program: Program = Program(ops=[], memory_capacity=0) + # TODO: use deque for rtokens rtokens: List[Token] = list(reversed(tokens)) macros: Dict[str, Macro] = {} memories: Dict[str, Memory] = {} @@ -1759,11 +1784,14 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp exit(1) rtokens += reversed(expand_macro(macros[token.value], token)) else: - assert False, f"TODO: unsupported word in memory definition {token.value}" + compiler_error_with_expansion_stack(token, f"unsupported word in memory definition {token.value}") + exit(1) else: - assert False, "TODO: unsupported token in memory definition" + compiler_error_with_expansion_stack(token, f"{human(token.typ, HumanNumber.Plural)} are not supported in memory definition") + exit(1) if len(mem_size_stack) != 1: - assert False, "TODO: memory definition expects only one integer" + compiler_error_with_expansion_stack(token, "The result of expression in the memory definition must be a single number") + exit(1) memory_size = mem_size_stack.pop() memories[memory_name] = Memory(offset=program.memory_capacity, loc=memory_loc) program.memory_capacity += memory_size diff --git a/tests/memory-single-number.porth b/tests/memory-single-number.porth new file mode 100644 index 00000000..ffabd320 --- /dev/null +++ b/tests/memory-single-number.porth @@ -0,0 +1 @@ +memory hello end diff --git a/tests/memory-single-number.txt b/tests/memory-single-number.txt new file mode 100644 index 00000000..d185fa41 --- /dev/null +++ b/tests/memory-single-number.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 122 +./tests/memory-single-number.porth:1:14: ERROR: The result of expression in the memory definition must be a single number + diff --git a/tests/memory-unsupported-token-type.porth b/tests/memory-unsupported-token-type.porth new file mode 100644 index 00000000..abdbbb8c --- /dev/null +++ b/tests/memory-unsupported-token-type.porth @@ -0,0 +1 @@ +memory hello "world" end diff --git a/tests/memory-unsupported-token-type.txt b/tests/memory-unsupported-token-type.txt new file mode 100644 index 00000000..2a2f0f49 --- /dev/null +++ b/tests/memory-unsupported-token-type.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 104 +./tests/memory-unsupported-token-type.porth:1:14: ERROR: strings are not supported in memory definition + diff --git a/tests/memory-unsupported-word.porth b/tests/memory-unsupported-word.porth new file mode 100644 index 00000000..72cf6e3d --- /dev/null +++ b/tests/memory-unsupported-word.porth @@ -0,0 +1 @@ +memory hello word end diff --git a/tests/memory-unsupported-word.txt b/tests/memory-unsupported-word.txt new file mode 100644 index 00000000..b31b5fdc --- /dev/null +++ b/tests/memory-unsupported-word.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 94 +./tests/memory-unsupported-word.porth:1:14: ERROR: unsupported word in memory definition word + From 7be8b3bf22c929ef1db34a55f7cc4667e2b2b14a Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 20 Oct 2021 02:10:28 +0700 Subject: [PATCH 081/145] Remove obsolete TODO I don't think it matters that much. After Porth is rewritten in itself simulation is going to be pretty trivial --- porth.py | 1 - 1 file changed, 1 deletion(-) diff --git a/porth.py b/porth.py index f0724725..63b7bdd6 100755 --- a/porth.py +++ b/porth.py @@ -436,7 +436,6 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): # NOTE: trying to behave like a POSIX tty in canonical mode by making the data available # on each newline # https://en.wikipedia.org/wiki/POSIX_terminal_interface#Canonical_mode_processing - # TODO: maybe this behavior should be customizable data = fds[fd].readline(count) mem[buf:buf+len(data)] = data stack.append(len(data)) From 177b00626ddc3c54a0b6789f98474957e1b4d419 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 20 Oct 2021 02:42:45 +0700 Subject: [PATCH 082/145] Remove obsolete TODO I don't think it matters that much. Parsing is far from slowest part of the compilation process. --- porth.py | 1 - 1 file changed, 1 deletion(-) diff --git a/porth.py b/porth.py index 63b7bdd6..0da8f07a 100755 --- a/porth.py +++ b/porth.py @@ -1583,7 +1583,6 @@ def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: D def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], expansion_limit: int) -> Program: stack: List[OpAddr] = [] program: Program = Program(ops=[], memory_capacity=0) - # TODO: use deque for rtokens rtokens: List[Token] = list(reversed(tokens)) macros: Dict[str, Macro] = {} memories: Dict[str, Memory] = {} From ca2f74f902c604e5620930c398d2991e060968c5 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 20 Oct 2021 02:51:09 +0700 Subject: [PATCH 083/145] Merge str-chop-word and str-chop-line into a single macro --- porth.porth | 4 ++-- std/std.porth | 24 ++++-------------------- test.py | 1 + 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/porth.porth b/porth.porth index 4d201968..772b3379 100644 --- a/porth.porth +++ b/porth.porth @@ -380,11 +380,11 @@ macro parse_file_path_cstr_into_ops 1 line_number !64 while content @Str.count 0 > do - line content str-chop-line + '\n' line content str-chop-by-delim line @Str.data line_start !64 while line @Str.count 0 > do line str-trim-left - word line str-chop-word + ' ' word line str-chop-by-delim if COUNT_OPS 7 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in parse-file-path\n" eputs diff --git a/std/std.porth b/std/std.porth index cdce9cba..76dcc083 100644 --- a/std/std.porth +++ b/std/std.porth @@ -492,30 +492,14 @@ macro str-trim-left // input -- drop end -macro str-chop-line // line input -- +memory str-chop-by-delim-tmp sizeof(u64) end +macro str-chop-by-delim // delim line input + rot str-chop-by-delim-tmp !64 2dup @Str.data swap !Str.data over 0 swap !Str.count while if dup @Str.count 0 > do - dup @Str.data @8 '\n' != - else false end - do - dup str-chop-one-left - swap dup Str.count inc64 swap - end - if dup @Str.count 0 > do - dup str-chop-one-left - end - 2drop -end - -// TODO: merge str-chop-word and str-chop-line into a single macro -macro str-chop-word // line input -- - 2dup @Str.data swap !Str.data - over 0 swap !Str.count - while - if dup @Str.count 0 > do - dup @Str.data @8 ' ' != + dup @Str.data @8 str-chop-by-delim-tmp @64 != else false end do dup str-chop-one-left diff --git a/test.py b/test.py index c894dc90..d144e29b 100755 --- a/test.py +++ b/test.py @@ -246,6 +246,7 @@ def usage(exe_name: str): run_test_for_folder('./tests/') run_test_for_folder('./examples/') run_test_for_folder('./euler/') + cmd_run_echoed([sys.executable, './porth.py', 'com', './porth.porth']) elif subcommand == 'help': usage(exe_name) else: From 47329a1b6683399833837b4b07632ac2d6facfa7 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 21 Oct 2021 01:18:26 +0700 Subject: [PATCH 084/145] Implement procedures for compilation mode without type checking --- editor/porth-mode.el | 2 +- editor/porth.vim | 2 +- porth.porth | 24 +++++----- porth.py | 104 +++++++++++++++++++++++++++++++++++++++---- std/std.porth | 38 ++++++++-------- 5 files changed, 128 insertions(+), 42 deletions(-) diff --git a/editor/porth-mode.el b/editor/porth-mode.el index 8d101a8f..b4310bab 100644 --- a/editor/porth-mode.el +++ b/editor/porth-mode.el @@ -42,7 +42,7 @@ (eval-and-compile (defconst porth-keywords - '("if" "elif" "else" "end" "while" "do" "macro" "include" "memory"))) + '("if" "elif" "else" "end" "while" "do" "macro" "include" "memory" "proc"))) (defconst porth-highlights `((,(regexp-opt porth-keywords 'symbols) . font-lock-keyword-face))) diff --git a/editor/porth.vim b/editor/porth.vim index 201c08dd..4c6617e9 100644 --- a/editor/porth.vim +++ b/editor/porth.vim @@ -13,7 +13,7 @@ endif syntax keyword porthTodos TODO XXX FIXME NOTE " Language keywords -syntax keyword porthKeywords if elif else end while do macro include +syntax keyword porthKeywords if elif else end while do macro include memory proc " Comments syntax region porthCommentLine start="//" end="$" contains=porthTodos diff --git a/porth.porth b/porth.porth index 772b3379..fd95fa24 100644 --- a/porth.porth +++ b/porth.porth @@ -57,7 +57,7 @@ memory output_argv sizeof(ptr) 2 * end "./output"c output_argv 0 8 * + !64 memory wstatus sizeof(u64) end -macro cmd_echoed // argv +proc cmd_echoed // argv "[CMD]" puts dup while dup @64 0 != do " " puts @@ -91,7 +91,7 @@ macro cmd_echoed // argv drop end -macro sim-stack-push // u64 -- +proc sim-stack-push // u64 -- if sim-stack-count @64 SIM_STACK_CAP >= do here eputs ": ERROR: data stack overflow in simulation mode\n" eputs 1 exit end @@ -99,7 +99,7 @@ macro sim-stack-push // u64 -- sim-stack-count inc64 end -macro sim-stack-pop // -- u64 +proc sim-stack-pop // -- u64 if sim-stack-count @64 0 = do here eputs ": ERROR: data stack underflow in simulation mode\n" eputs 1 exit end @@ -107,7 +107,7 @@ macro sim-stack-pop // -- u64 sim-stack sim-stack-count @64 8 * + @64 end -macro try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret +proc try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret 0 0 while dup word @Str.count < do dup word @Str.data + @8 @@ -127,7 +127,7 @@ macro try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret end -macro push-op // type operand -- +proc push-op // type operand -- // TODO: assert OPS_CAP ops-count @64 sizeof(Op) * ops + dup rot swap !Op.operand @@ -135,7 +135,7 @@ macro push-op // type operand -- ops-count inc64 end -macro print-op-type +proc print-op-type if COUNT_OPS 7 != do here eputs ": Assertion Failed: Exhaustive handling of Op types in print-op-type\n" eputs 1 exit @@ -161,7 +161,7 @@ macro print-op-type drop end -macro dump-ops // -- +proc dump-ops // -- 0 while dup ops-count @64 < do // ptr ptr dup sizeof(Op) * ops + @@ -173,7 +173,7 @@ macro dump-ops // -- drop end -macro compile-ops // -- +proc compile-ops // -- "[INFO] Generating output.asm\n" puts 420 // mode @@ -295,7 +295,7 @@ macro compile-ops // -- output_argv cmd_echoed end -macro simulate-ops // -- +proc simulate-ops // -- 0 while dup ops-count @64 < do dup sizeof(Op) * ops + @@ -343,7 +343,7 @@ macro simulate-ops // -- drop end -macro parse_file_path_cstr_into_ops +proc parse_file_path_cstr_into_ops 0 // mode O_RDONLY // flags file_path_cstr @64 cast(ptr) // pathname @@ -412,7 +412,7 @@ macro parse_file_path_cstr_into_ops // TODO: parse_file_path does not clean up resources after itself end -macro usage // -- +proc usage // -- dup "Usage: porth \n" rot fputs dup " SUBCOMMANDS:\n" rot fputs dup " sim Simulate the program.\n" rot fputs @@ -423,7 +423,7 @@ macro usage // -- drop end -macro main // -- +proc main // -- if argc 2 < do stderr usage "ERROR: subcommand is not provided\n" eputs diff --git a/porth.py b/porth.py index 0da8f07a..f81cc94a 100755 --- a/porth.py +++ b/porth.py @@ -15,6 +15,7 @@ PORTH_EXT = '.porth' DEFAULT_EXPANSION_LIMIT=1000 EXPANSION_DIAGNOSTIC_LIMIT=10 +X86_64_RET_STACK_CAP=4096 SIM_NULL_POINTER_PADDING = 1 # just a little bit of a padding at the beginning of the memory to make 0 an invalid address SIM_STR_CAPACITY = 640_000 SIM_ARGV_CAPACITY = 640_000 @@ -33,6 +34,7 @@ class Keyword(Enum): MACRO=auto() INCLUDE=auto() MEMORY=auto() + PROC=auto() class DataType(IntEnum): INT=auto() @@ -96,6 +98,10 @@ class OpType(Enum): END=auto() WHILE=auto() DO=auto() + SKIP_PROC=auto() + PREP_PROC=auto() + RET=auto() + CALL=auto() class TokenType(Enum): WORD=auto() @@ -143,6 +149,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): CLOCK_MONOTONIC=1 stack: List[int] = [] + ret_stack: List[OpAddr] = [] mem = bytearray(SIM_NULL_POINTER_PADDING + SIM_STR_CAPACITY + SIM_ARGV_CAPACITY + program.memory_capacity) str_buf_ptr = SIM_NULL_POINTER_PADDING @@ -173,7 +180,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): ip = 0 while ip < len(program.ops): - assert len(OpType) == 11, "Exhaustive op handling in simulate_little_endian_linux" + assert len(OpType) == 14, "Exhaustive op handling in simulate_little_endian_linux" op = program.ops[ip] try: if op.typ == OpType.PUSH_INT: @@ -229,6 +236,15 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): ip = op.operand else: ip += 1 + elif op.typ == OpType.PROC: + assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" + ip = op.operand + elif op.typ == OpType.RET: + ip = ret_stack.pop() + elif op.typ == OpType.CALL: + assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" + ret_stack.append(ip + 1) + ip = op.operand elif op.typ == OpType.INTRINSIC: assert len(Intrinsic) == 42, "Exhaustive handling of intrinsic in simulate_little_endian_linux()" if op.operand == Intrinsic.PLUS: @@ -1107,9 +1123,11 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("global _start\n") out.write("_start:\n") out.write(" mov [args_ptr], rsp\n") + out.write(" mov rax, ret_stack_end\n") + out.write(" mov [ret_stack_rsp], rax\n") for ip in range(len(program.ops)): op = program.ops[ip] - assert len(OpType) == 11, "Exhaustive ops handling in generate_nasm_linux_x86_64" + assert len(OpType) == 15, "Exhaustive ops handling in generate_nasm_linux_x86_64" out.write("addr_%d:\n" % ip) if op.typ == OpType.PUSH_INT: assert isinstance(op.operand, int), "This could be a bug in the parsing step" @@ -1160,6 +1178,24 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" test rax, rax\n") assert isinstance(op.operand, int), "This could be a bug in the parsing step" out.write(" jz addr_%d\n" % op.operand) + elif op.typ == OpType.SKIP_PROC: + out.write(" ;; -- skip proc --\n") + assert isinstance(op.operand, OpAddr), f"This could be a bug in the parsing step: {op.operand}" + out.write(" jmp addr_%d\n" % op.operand) + elif op.typ == OpType.PREP_PROC: + out.write(" ;; -- prep proc -- \n") + out.write(" mov [ret_stack_rsp], rsp\n") + out.write(" mov rsp, rax\n") + elif op.typ == OpType.CALL: + out.write(" mov rax, rsp\n") + out.write(" mov rsp, [ret_stack_rsp]\n") + out.write(" call addr_%d\n" % op.operand) + out.write(" mov [ret_stack_rsp], rsp\n") + out.write(" mov rsp, rax\n") + elif op.typ == OpType.RET: + out.write(" mov rax, rsp\n") + out.write(" mov rsp, [ret_stack_rsp]\n") + out.write(" ret\n") elif op.typ == OpType.INTRINSIC: assert len(Intrinsic) == 42, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: @@ -1442,9 +1478,12 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("str_%d: db %s\n" % (index, ','.join(map(hex, list(s))))) out.write("segment .bss\n") out.write("args_ptr: resq 1\n") + out.write("ret_stack_rsp: resq 1\n") + out.write("ret_stack: resb %d\n" % X86_64_RET_STACK_CAP) + out.write("ret_stack_end: resq 1\n") out.write("mem: resb %d\n" % program.memory_capacity) -assert len(Keyword) == 9, "Exhaustive KEYWORD_NAMES definition." +assert len(Keyword) == 10, "Exhaustive KEYWORD_NAMES definition." KEYWORD_BY_NAMES: Dict[str, Keyword] = { 'if': Keyword.IF, 'elif': Keyword.ELIF, @@ -1455,6 +1494,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'macro': Keyword.MACRO, 'include': Keyword.INCLUDE, 'memory': Keyword.MEMORY, + 'proc': Keyword.PROC, } KEYWORD_NAMES: Dict[Keyword, str] = {v: k for k, v in KEYWORD_BY_NAMES.items()} @@ -1586,6 +1626,8 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp rtokens: List[Token] = list(reversed(tokens)) macros: Dict[str, Macro] = {} memories: Dict[str, Memory] = {} + procs: Dict[str, OpAddr] = {} + current_proc: Option[OpAddr] = None ip: OpAddr = 0; while len(rtokens) > 0: token = rtokens.pop() @@ -1603,6 +1645,9 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp elif token.value in memories: program.ops.append(Op(typ=OpType.PUSH_MEM, token=token, operand=memories[token.value].offset)) ip += 1 + elif token.value in procs: + program.ops.append(Op(typ=OpType.CALL, token=token, operand=procs[token.value])) + ip += 1 else: compiler_error_with_expansion_stack(token, "unknown word `%s`" % token.value) exit(1) @@ -1623,7 +1668,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops.append(Op(typ=OpType.PUSH_INT, operand=token.value, token=token)); ip += 1 elif token.typ == TokenType.KEYWORD: - assert len(Keyword) == 9, "Exhaustive keywords handling in parse_program_from_tokens()" + assert len(Keyword) == 10, "Exhaustive keywords handling in parse_program_from_tokens()" if token.value == Keyword.IF: program.ops.append(Op(typ=OpType.IF, token=token)) stack.append(ip) @@ -1669,12 +1714,14 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp compiler_error_with_expansion_stack(program.ops[pre_do_ip].token, '`else` can only close `do`-blocks that are preceded by `if` or `elif`') exit(1) elif token.value == Keyword.END: - program.ops.append(Op(typ=OpType.END, token=token)) block_ip = stack.pop() + if program.ops[block_ip].typ == OpType.ELSE: + program.ops.append(Op(typ=OpType.END, token=token)) program.ops[block_ip].operand = ip program.ops[ip].operand = ip + 1 elif program.ops[block_ip].typ == OpType.DO: + program.ops.append(Op(typ=OpType.END, token=token)) assert program.ops[block_ip].operand is not None pre_do_ip = program.ops[block_ip].operand @@ -1692,8 +1739,13 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp else: compiler_error_with_expansion_stack(program.ops[pre_do_ip].token, '`end` can only close `do` blocks that are preceded by `if`, `while` or `elif`') exit(1) + elif program.ops[block_ip].typ == OpType.SKIP_PROC: + program.ops.append(Op(typ=OpType.RET, token=token)) + program.ops[block_ip].operand = ip + 1 + current_proc = None else: - compiler_error_with_expansion_stack(program.ops[block_ip].token, '`end` can only close `else`, `do` or `macro` blocks for now') + # NOTE: the closing of `macro` blocks is handled in its own separate place, not here + compiler_error_with_expansion_stack(program.ops[block_ip].token, '`end` can only close `else`, `do`, `macro` or `proc` blocks for now') exit(1) ip += 1 elif token.value == Keyword.WHILE: @@ -1813,14 +1865,37 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp else: macro.tokens.append(token) if token.typ == TokenType.KEYWORD: - assert len(Keyword) == 9, "Exhaustive handling of keywords in parsing macro body" - if token.value in [Keyword.IF, Keyword.WHILE, Keyword.MACRO, Keyword.MEMORY]: + assert len(Keyword) == 10, "Exhaustive handling of keywords in parsing macro body" + if token.value in [Keyword.IF, Keyword.WHILE, Keyword.MACRO, Keyword.MEMORY, Keyword.PROC]: nesting_depth += 1 elif token.value == Keyword.END: nesting_depth -= 1 if token.typ != TokenType.KEYWORD or token.value != Keyword.END: compiler_error_with_expansion_stack(token, "expected `end` at the end of the macro definition but got `%s`" % (token.value, )) exit(1) + elif token.value == Keyword.PROC: + if current_proc is None: + program.ops.append(Op(typ=OpType.SKIP_PROC, token=token)) + current_proc = ip + stack.append(ip) + ip += 1 + + program.ops.append(Op(typ=OpType.PREP_PROC, token=token)) + ip += 1 + + if len(rtokens) == 0: + compiler_error_with_expansion_stack(token, "expected procedure name but found nothing") + exit(1) + token = rtokens.pop() + if token.typ != TokenType.WORD: + compiler_error_with_expansion_stack(token, "expected procedure name to be %s but found %s" % (human(TokenType.WORD), human(token.typ))) + exit(1) + assert isinstance(token.value, str), "This is probably a bug in the lexer" + proc_name = token.value + procs[proc_name] = current_proc + 1 + else: + compiler_error_with_expansion_stack(token, "defining procedures inside of procedures is not allowed") + compiler_note(program.ops[current_proc].token.loc, "the current procedure starts here") else: assert False, 'unreachable'; else: @@ -1938,7 +2013,7 @@ def cmd_call_echoed(cmd: List[str], silent: bool) -> int: def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): with open(dot_path, "w") as f: f.write("digraph Program {\n") - assert len(OpType) == 10, "Exhaustive handling of OpType in generate_control_flow_graph_as_dot_file()" + assert len(OpType) == 14, f"Exhaustive handling of OpType in generate_control_flow_graph_as_dot_file(), {len(OpType)}" for ip in range(len(program.ops)): op = program.ops[ip] if op.typ == OpType.INTRINSIC: @@ -1980,6 +2055,17 @@ def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): assert isinstance(op.operand, OpAddr) f.write(f" Node_{ip} [shape=record label=end];\n") f.write(f" Node_{ip} -> Node_{op.operand};\n") + elif op.typ == OpType.PROC: + assert isinstance(op.operand, OpAddr) + f.write(f" Node_{ip} [shape=record label=proc];\n") + f.write(f" Node_{ip} -> Node_{op.operand};\n") + elif op.typ == OpType.RET: + f.write(f" Node_{ip} [shape=record label=ret];\n") + elif op.typ == OpType.CALL: + assert isinstance(op.operand, OpAddr) + f.write(f" Node_{ip} [shape=record label=call];\n") + f.write(f" Node_{ip} -> Node_{op.operand};\n") + f.write(f" Node_{ip} -> Node_{ip + 1};\n") else: assert False, f"unimplemented operation {op.typ}" f.write(f" Node_{len(program.ops)} [label=halt];\n") diff --git a/std/std.porth b/std/std.porth index 76dcc083..b66127bc 100644 --- a/std/std.porth +++ b/std/std.porth @@ -1,3 +1,13 @@ +// Deprecated Words +macro .64 swap !64 end +macro ,64 @64 end +macro ! !8 end +macro @ @8 end +macro . swap ! end +macro , @ end +macro cstr-to-pstr cstr-to-str end +memory mem 640000 end + macro NULL 0 end macro nop end @@ -419,13 +429,13 @@ macro sizeof(u64) 8 end macro sizeof(u32) 4 end macro sizeof(ptr) sizeof(u64) end -macro cstrlen +proc cstrlen dup while dup @8 0 != do 1 + end swap - end -macro cstreq +proc cstreq while if over @8 0 != over @8 0 != and do over @8 over @8 = @@ -470,18 +480,18 @@ macro @Str swap @Str.data end -macro !Str // count data dst - +proc !Str // count data dst - dup rot swap !Str.data !Str.count end -macro str-chop-one-left +proc str-chop-one-left dup Str.count dec64 Str.data inc64 end -macro str-trim-left // input -- +proc str-trim-left // input -- while if dup @Str.count 0 > do dup @Str.data @8 ' ' = @@ -493,7 +503,7 @@ macro str-trim-left // input -- end memory str-chop-by-delim-tmp sizeof(u64) end -macro str-chop-by-delim // delim line input +proc str-chop-by-delim // delim line input rot str-chop-by-delim-tmp !64 2dup @Str.data swap !Str.data over 0 swap !Str.count @@ -513,7 +523,7 @@ end memory streq_a sizeof(Str) end memory streq_b sizeof(Str) end -macro streq // n1 s1 n2 s2 +proc streq // n1 s1 n2 s2 streq_a !Str streq_b !Str if streq_a @Str.count streq_b @Str.count = do @@ -528,7 +538,7 @@ macro streq // n1 s1 n2 s2 else false end end -macro isdigit +proc isdigit dup '0' >= swap '9' <= and @@ -544,7 +554,7 @@ macro PUTD_BUFFER_CAP 32 end memory putd-buffer PUTD_BUFFER_CAP end memory putd-fd sizeof(u64) end // TODO: fputd should fail if write call fails -macro fputd // value fd -- +proc fputd // value fd -- putd-fd !64 if dup 0 = do @@ -564,13 +574,3 @@ macro fputd // value fd -- end macro putd stdout fputd end - -// Deprecated Words -macro .64 swap !64 end -macro ,64 @64 end -macro ! !8 end -macro @ @8 end -macro . swap ! end -macro , @ end -macro cstr-to-pstr cstr-to-str end -memory mem 640000 end From 488fc9ccfa8883cb5c0a3ad0fe09eb0ed77e6833 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 21 Oct 2021 01:24:03 +0700 Subject: [PATCH 085/145] Fix proc redefinition error --- porth.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/porth.py b/porth.py index f81cc94a..6c73e2ce 100755 --- a/porth.py +++ b/porth.py @@ -236,7 +236,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): ip = op.operand else: ip += 1 - elif op.typ == OpType.PROC: + elif op.typ == OpType.SKIP_PROC: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" ip = op.operand elif op.typ == OpType.RET: @@ -1187,6 +1187,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" mov [ret_stack_rsp], rsp\n") out.write(" mov rsp, rax\n") elif op.typ == OpType.CALL: + assert isinstance(op.operand, OpAddr), f"This could be a bug in the parsing step: {op.operand}" out.write(" mov rax, rsp\n") out.write(" mov rsp, [ret_stack_rsp]\n") out.write(" call addr_%d\n" % op.operand) @@ -1480,7 +1481,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("args_ptr: resq 1\n") out.write("ret_stack_rsp: resq 1\n") out.write("ret_stack: resb %d\n" % X86_64_RET_STACK_CAP) - out.write("ret_stack_end: resq 1\n") + out.write("ret_stack_end:\n") out.write("mem: resb %d\n" % program.memory_capacity) assert len(Keyword) == 10, "Exhaustive KEYWORD_NAMES definition." @@ -1604,7 +1605,7 @@ class Memory: offset: MemAddr loc: Loc -def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: Dict[str, Macro]): +def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: Dict[str, Macro], procs: Dict[str, OpAddr]): assert token.typ == TokenType.WORD assert isinstance(token.value, str) name: str = token.value @@ -1619,6 +1620,11 @@ def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: D compiler_error_with_expansion_stack(token, "redefinition of a macro `%s`" % (name, )) compiler_note(macros[name].loc, "the original definition is located here") exit(1) + if name in procs: + compiler_error_with_expansion_stack(token, "redefinition of a proc `%s`" % (name, )) + # TODO: report the location of the original proc definition + # compiler_note(procs[name].loc, "the original definition is located here") + exit(1) def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], expansion_limit: int) -> Program: stack: List[OpAddr] = [] @@ -1627,7 +1633,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp macros: Dict[str, Macro] = {} memories: Dict[str, Memory] = {} procs: Dict[str, OpAddr] = {} - current_proc: Option[OpAddr] = None + current_proc: Optional[OpAddr] = None ip: OpAddr = 0; while len(rtokens) > 0: token = rtokens.pop() @@ -1798,7 +1804,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp assert isinstance(token.value, str), "This is probably a bug in the lexer" memory_name = token.value memory_loc = token.loc - check_word_redefinition(token, memories, macros) + check_word_redefinition(token, memories, macros, procs) mem_size_stack: List[int] = [] while len(rtokens) > 0: token = rtokens.pop() @@ -1854,7 +1860,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp compiler_error_with_expansion_stack(token, "expected macro name to be %s but found %s" % (human(TokenType.WORD), human(token.typ))) exit(1) assert isinstance(token.value, str), "This is probably a bug in the lexer" - check_word_redefinition(token, memories, macros) + check_word_redefinition(token, memories, macros, procs) macro = Macro(token.loc, []) macros[token.value] = macro nesting_depth = 0 @@ -1892,6 +1898,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp exit(1) assert isinstance(token.value, str), "This is probably a bug in the lexer" proc_name = token.value + check_word_redefinition(token, memories, macros, procs) procs[proc_name] = current_proc + 1 else: compiler_error_with_expansion_stack(token, "defining procedures inside of procedures is not allowed") @@ -2055,7 +2062,7 @@ def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): assert isinstance(op.operand, OpAddr) f.write(f" Node_{ip} [shape=record label=end];\n") f.write(f" Node_{ip} -> Node_{op.operand};\n") - elif op.typ == OpType.PROC: + elif op.typ == OpType.SKIP_PROC: assert isinstance(op.operand, OpAddr) f.write(f" Node_{ip} [shape=record label=proc];\n") f.write(f" Node_{ip} -> Node_{op.operand};\n") From 321c007ce601ea0b3eb9f0fe3a82e1e4fc646d88 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 21 Oct 2021 02:35:16 +0700 Subject: [PATCH 086/145] Fix simulation mode --- porth.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/porth.py b/porth.py index 6c73e2ce..9acd69ad 100755 --- a/porth.py +++ b/porth.py @@ -180,7 +180,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): ip = 0 while ip < len(program.ops): - assert len(OpType) == 14, "Exhaustive op handling in simulate_little_endian_linux" + assert len(OpType) == 15, "Exhaustive op handling in simulate_little_endian_linux" op = program.ops[ip] try: if op.typ == OpType.PUSH_INT: @@ -239,6 +239,8 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): elif op.typ == OpType.SKIP_PROC: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" ip = op.operand + elif op.typ == OpType.PREP_PROC: + ip += 1 elif op.typ == OpType.RET: ip = ret_stack.pop() elif op.typ == OpType.CALL: From 324eddb6b213d44df310c3c87965bd26f3cd05f5 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 21 Oct 2021 04:08:40 +0700 Subject: [PATCH 087/145] Rough implementation of the typecheck of procs --- porth.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/porth.py b/porth.py index 9acd69ad..321cae17 100755 --- a/porth.py +++ b/porth.py @@ -564,13 +564,14 @@ def not_enough_arguments(op: Op): @dataclass class Context: stack: DataStack + ret_stack: List[OpAddr] ip: OpAddr # TODO: better error reporting on type checking errors of intrinsics # Reported expected and actual types with the location that introduced the actual type def type_check_program(program: Program): visited_dos: Dict[OpAddr, DataStack] = {} - contexts: List[Context] = [Context(stack=[], ip=0)] + contexts: List[Context] = [Context(stack=[], ip=0, ret_stack=[])] while len(contexts) > 0: ctx = contexts[-1]; if ctx.ip >= len(program.ops): @@ -580,7 +581,7 @@ def type_check_program(program: Program): contexts.pop() continue op = program.ops[ctx.ip] - assert len(OpType) == 11, "Exhaustive ops handling in type_check_program()" + assert len(OpType) == 15, "Exhaustive ops handling in type_check_program()" if op.typ == OpType.PUSH_INT: ctx.stack.append((DataType.INT, op.token)) ctx.ip += 1 @@ -594,6 +595,17 @@ def type_check_program(program: Program): elif op.typ == OpType.PUSH_MEM: ctx.stack.append((DataType.PTR, op.token)) ctx.ip += 1 + elif op.typ == OpType.SKIP_PROC: + assert isinstance(op.operand, OpAddr) + ctx.ip = op.operand + elif op.typ == OpType.PREP_PROC: + ctx.ip += 1 + elif op.typ == OpType.CALL: + ctx.ret_stack.append(ctx.ip + 1) + assert isinstance(op.operand, OpAddr) + ctx.ip = op.operand + elif op.typ == OpType.RET: + ctx.ip = ctx.ret_stack.pop() elif op.typ == OpType.INTRINSIC: assert len(Intrinsic) == 42, "Exhaustive intrinsic handling in type_check_program()" assert isinstance(op.operand, Intrinsic), "This could be a bug in compilation step" @@ -1079,7 +1091,7 @@ def type_check_program(program: Program): else: visited_dos[ctx.ip] = copy(ctx.stack) ctx.ip += 1 - contexts.append(Context(stack=copy(ctx.stack), ip=op.operand)) + contexts.append(Context(stack=copy(ctx.stack), ip=op.operand, ret_stack=copy(ctx.ret_stack))) ctx = contexts[-1] else: assert False, "unreachable" From b6916d25c29006cdf9c0e995dd9e516dbeaa3b6c Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 21 Oct 2021 04:15:34 +0700 Subject: [PATCH 088/145] Fix the tests --- examples/gol.porth | 26 +++++++++++++------------- porth.py | 2 +- tests/end-cant-close-error.txt | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/gol.porth b/examples/gol.porth index 52d31ad4..7d4c653c 100644 --- a/examples/gol.porth +++ b/examples/gol.porth @@ -13,19 +13,19 @@ memory value sizeof(u64) end memory board_base BOARD_SIZE 2 * end memory display COLS 1 + end -macro board_current +proc board_current board_base board_current_index @64 BOARD_SIZE * + end -macro board_next +proc board_next board_base 1 board_current_index @64 - BOARD_SIZE * + end -macro swap_boards +proc swap_boards 1 board_current_index @64 - board_current_index !64 end -macro display_row +proc display_row 0 while dup COLS < do if 2dup + @8 0 = do display over + '.' swap !8 @@ -39,7 +39,7 @@ macro display_row drop end -macro display_board +proc display_board 0 while dup ROWS < do 2dup COLS * + display_row 1 + @@ -47,29 +47,29 @@ macro display_board drop end -macro display_current_board +proc display_current_board board_current display_board end -macro get_current_cell +proc get_current_cell swap COLS * + board_current + @8 end -macro set_next_cell +proc set_next_cell value !64 swap COLS * + board_next + value @64 swap !8 end -macro in_bounds +proc in_bounds dup 0 >= swap COLS < and swap dup 0 >= swap ROWS < and and end -macro count_current_nbors +proc count_current_nbors 0 nbors !64 if 2dup 1 - swap 1 - swap 2dup in_bounds @@ -108,7 +108,7 @@ macro count_current_nbors nbors @64 end -macro compute_next_board +proc compute_next_board 0 while dup ROWS < do 0 while dup COLS < do if 2dup get_current_cell 1 = do @@ -135,7 +135,7 @@ end // .*. // ..* // *** -macro put_glider +proc put_glider dup 0 COLS * 1 + + 1 swap !8 dup 1 COLS * 2 + + 1 swap !8 dup 2 COLS * 0 + + 1 swap !8 @@ -144,7 +144,7 @@ macro put_glider drop end -macro main +proc main 100000000 delta_time 8 + !64 board_current put_glider diff --git a/porth.py b/porth.py index 321cae17..a46338a2 100755 --- a/porth.py +++ b/porth.py @@ -550,7 +550,7 @@ def compiler_note(loc: Loc, message: str): compiler_diagnostic(loc, 'NOTE', message) def not_enough_arguments(op: Op): - assert len(OpType) == 11, f"Exhaustive handling of Op types in not_enough_arguments() (expected {len(OpType)}). Keep in mind that not all of the ops should be handled in here. Only those that consume elements from the stack." + assert len(OpType) == 15, f"Exhaustive handling of Op types in not_enough_arguments() (expected {len(OpType)}). Keep in mind that not all of the ops should be handled in here. Only those that consume elements from the stack." if op.typ == OpType.INTRINSIC: assert isinstance(op.operand, Intrinsic) compiler_error_with_expansion_stack(op.token, "not enough arguments for the `%s` intrinsic" % INTRINSIC_NAMES[op.operand]) diff --git a/tests/end-cant-close-error.txt b/tests/end-cant-close-error.txt index f5255f43..3ade8f5d 100644 --- a/tests/end-cant-close-error.txt +++ b/tests/end-cant-close-error.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 107 -./tests/end-cant-close-error.porth:1:1: ERROR: `end` can only close `else`, `do` or `macro` blocks for now +:b stderr 115 +./tests/end-cant-close-error.porth:1:1: ERROR: `end` can only close `else`, `do`, `macro` or `proc` blocks for now From f4c9d017690368a3ab09b9b46980da18d25e580b Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 21 Oct 2021 08:47:49 +0700 Subject: [PATCH 089/145] Disable type checking for now --- porth.py | 1 + 1 file changed, 1 insertion(+) diff --git a/porth.py b/porth.py index a46338a2..dc7af47a 100755 --- a/porth.py +++ b/porth.py @@ -570,6 +570,7 @@ class Context: # TODO: better error reporting on type checking errors of intrinsics # Reported expected and actual types with the location that introduced the actual type def type_check_program(program: Program): + assert False, "TODO: the type checking is broken at the moment. Run with `-unsafe` flag." visited_dos: Dict[OpAddr, DataStack] = {} contexts: List[Context] = [Context(stack=[], ip=0, ret_stack=[])] while len(contexts) > 0: From 978f3a28e16b5d58de0ba2a82e8cd5efec907919 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 21 Oct 2021 08:49:10 +0700 Subject: [PATCH 090/145] Elaborate on how to use `-unsafe` --- porth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/porth.py b/porth.py index dc7af47a..ce4560c6 100755 --- a/porth.py +++ b/porth.py @@ -570,7 +570,7 @@ class Context: # TODO: better error reporting on type checking errors of intrinsics # Reported expected and actual types with the location that introduced the actual type def type_check_program(program: Program): - assert False, "TODO: the type checking is broken at the moment. Run with `-unsafe` flag." + assert False, "TODO: the type checking is broken at the moment. Run with `-unsafe` flag before subcommand. See `./porth.py help` for more info." visited_dos: Dict[OpAddr, DataStack] = {} contexts: List[Context] = [Context(stack=[], ip=0, ret_stack=[])] while len(contexts) > 0: From 34b3d70ac4232037f8606f7dd1c8e906f405f644 Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 21 Oct 2021 20:34:37 +0700 Subject: [PATCH 091/145] Don't disable type checking in debug mode --- porth.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/porth.py b/porth.py index ce4560c6..43213e0f 100755 --- a/porth.py +++ b/porth.py @@ -570,7 +570,8 @@ class Context: # TODO: better error reporting on type checking errors of intrinsics # Reported expected and actual types with the location that introduced the actual type def type_check_program(program: Program): - assert False, "TODO: the type checking is broken at the moment. Run with `-unsafe` flag before subcommand. See `./porth.py help` for more info." + if not debug: + assert False, "TODO: the type checking is broken at the moment. Run with `-unsafe` flag before subcommand. See `./porth.py help` for more info." visited_dos: Dict[OpAddr, DataStack] = {} contexts: List[Context] = [Context(stack=[], ip=0, ret_stack=[])] while len(contexts) > 0: From 997dcfd5c799ad9eaf54b44fea3d55140fae4d7b Mon Sep 17 00:00:00 2001 From: rexim Date: Thu, 21 Oct 2021 23:56:47 +0700 Subject: [PATCH 092/145] elif -> orelse --- editor/porth-mode.el | 2 +- editor/porth.vim | 2 +- euler/problem01.porth | 7 +- euler/problem02.porth | 2 +- euler/problem03.porth | 2 +- euler/problem04.porth | 4 +- euler/problem05.porth | 9 +- euler/problem07.porth | 4 +- euler/problem08.porth | 2 +- euler/problem09.porth | 2 +- euler/problem10.porth | 4 +- examples/cat.porth | 4 +- examples/cat.txt | 6 +- examples/checker.porth | 7 +- examples/fizz-buzz.porth | 6 +- examples/gol.porth | 56 +++++----- examples/name.porth | 4 +- examples/rot13.porth | 4 +- examples/rule110.porth | 2 +- examples/seq.porth | 4 +- porth.porth | 98 ++++++++--------- porth.py | 162 +++++++++++++++-------------- std/std.porth | 17 +-- tests/else-not-inside-if-error.txt | 4 +- tests/end-cant-close-error.txt | 4 +- tests/if-else-fail.porth | 2 +- tests/if-else-fail.txt | 2 +- tests/if-else-less-fail.porth | 2 +- tests/if-else-less-fail.txt | 2 +- tests/if-else-less.porth | 4 +- tests/if-else.porth | 4 +- tests/macros.porth | 4 +- tests/not-enough-args-for-do.txt | 4 +- 33 files changed, 226 insertions(+), 216 deletions(-) diff --git a/editor/porth-mode.el b/editor/porth-mode.el index b4310bab..3d29da22 100644 --- a/editor/porth-mode.el +++ b/editor/porth-mode.el @@ -42,7 +42,7 @@ (eval-and-compile (defconst porth-keywords - '("if" "elif" "else" "end" "while" "do" "macro" "include" "memory" "proc"))) + '("if" "orelse" "else" "while" "do" "macro" "include" "memory" "proc" "end"))) (defconst porth-highlights `((,(regexp-opt porth-keywords 'symbols) . font-lock-keyword-face))) diff --git a/editor/porth.vim b/editor/porth.vim index 4c6617e9..9532906a 100644 --- a/editor/porth.vim +++ b/editor/porth.vim @@ -13,7 +13,7 @@ endif syntax keyword porthTodos TODO XXX FIXME NOTE " Language keywords -syntax keyword porthKeywords if elif else end while do macro include memory proc +syntax keyword porthKeywords if orelse else while do macro include memory proc end " Comments syntax region porthCommentLine start="//" end="$" contains=porthTodos diff --git a/euler/problem01.porth b/euler/problem01.porth index 9d6c6e1c..1a2a4d80 100644 --- a/euler/problem01.porth +++ b/euler/problem01.porth @@ -1,10 +1,9 @@ include "std.porth" 0 3 while dup 1000 < do - if dup 3 mod 0 = - over 5 mod 0 = - or - do + dup 3 mod 0 = + over 5 mod 0 = + or if swap over + swap end diff --git a/euler/problem02.porth b/euler/problem02.porth index 336217e4..e1df4b37 100644 --- a/euler/problem02.porth +++ b/euler/problem02.porth @@ -3,7 +3,7 @@ include "std.porth" memory acc sizeof(u64) end 1 2 while over 4000000 < do - if over 2 mod 0 = do + over 2 mod 0 = if over acc @64 + acc !64 end diff --git a/euler/problem03.porth b/euler/problem03.porth index 63370070..76da647f 100644 --- a/euler/problem03.porth +++ b/euler/problem03.porth @@ -1,7 +1,7 @@ include "std.porth" 600851475143 2 while over 1 > do - if 2dup mod 0 = do + 2dup mod 0 = if swap over / swap else 1 + diff --git a/euler/problem04.porth b/euler/problem04.porth index 315b6642..064fd327 100644 --- a/euler/problem04.porth +++ b/euler/problem04.porth @@ -16,8 +16,8 @@ memory ans sizeof(u64) end swap drop // a b - if 2dup = do - if dup ans @64 > do + 2dup = if + dup ans @64 > if ans over swap !64 end end diff --git a/euler/problem05.porth b/euler/problem05.porth index 506f8827..9ecbf831 100644 --- a/euler/problem05.porth +++ b/euler/problem05.porth @@ -16,7 +16,7 @@ memory ans sizeof(u64) end // factorize dup 2 while over 1 > do - if 2dup mod 0 = do + 2dup mod 0 = if dup 8 * tmp + inc64 swap over / swap else @@ -25,10 +25,9 @@ memory ans sizeof(u64) end end 2drop 0 while dup N < do - if dup 8 * acc + @64 - over 8 * tmp + @64 - < - do + dup 8 * acc + @64 + over 8 * tmp + @64 + < if dup 8 * acc + over 8 * tmp + @64 swap !64 diff --git a/euler/problem07.porth b/euler/problem07.porth index 62551892..00c0d27c 100644 --- a/euler/problem07.porth +++ b/euler/problem07.porth @@ -8,7 +8,7 @@ memory primes sizeof(u64) N * 10 + end macro is-prime 0 while - if 2dup 8 * primes + @64 dup * >= do + 2dup 8 * primes + @64 dup * >= if 2dup 8 * primes + @64 mod 0 != else false @@ -25,7 +25,7 @@ end 2 add-prime 3 while primes-count @64 N < do - if dup is-prime do + dup is-prime if dup add-prime end 1 + diff --git a/euler/problem08.porth b/euler/problem08.porth index d5034285..ec835cf8 100644 --- a/euler/problem08.porth +++ b/euler/problem08.porth @@ -19,7 +19,7 @@ memory ans sizeof(u64) end 1 + end drop - if acc @64 ans @64 > do + acc @64 ans @64 > if acc @64 ans !64 end diff --git a/euler/problem09.porth b/euler/problem09.porth index 5979cb84..570e2c46 100644 --- a/euler/problem09.porth +++ b/euler/problem09.porth @@ -10,7 +10,7 @@ memory c sizeof(u64) end dup b !64 1000 a @64 - b @64 - c !64 - if a @64 dup * b @64 dup * + c @64 dup * = do + a @64 dup * b @64 dup * + c @64 dup * = if a @64 b @64 * c @64 * print 0 exit end diff --git a/euler/problem10.porth b/euler/problem10.porth index 75e24bdb..ed4a9391 100644 --- a/euler/problem10.porth +++ b/euler/problem10.porth @@ -17,7 +17,7 @@ end macro is-prime // [value: int] -> [ret: bool] 0 while - if 2dup sizeof(u32) * primes + @32 dup * >= do + 2dup sizeof(u32) * primes + @32 dup * >= if 2dup sizeof(u32) * primes + @32 mod 0 != else false end do 1 + end @@ -28,7 +28,7 @@ end 2 ans !64 3 while dup 2000000 < do - if dup is-prime do + dup is-prime if dup push-prime dup ans @64 + ans !64 end diff --git a/examples/cat.porth b/examples/cat.porth index df5a4ada..50d21d5c 100644 --- a/examples/cat.porth +++ b/examples/cat.porth @@ -17,7 +17,7 @@ macro cat_fd end drop end -if argc 2 < do +argc 2 < if stdin !fd cat_fd else @@ -26,7 +26,7 @@ else 0 O_RDONLY @file_path_cstr AT_FDCWD openat - if dup 0 < do + dup 0 < if "ERROR: could not open file " eputs @file_path_cstr cstr-to-str eputs "\n" eputs diff --git a/examples/cat.txt b/examples/cat.txt index d9771f06..dcdad2a6 100644 --- a/examples/cat.txt +++ b/examples/cat.txt @@ -8,7 +8,7 @@ foo :b stdin 0 :i returncode 0 -:b stdout 792 +:b stdout 786 include "std.porth" memory fd sizeof(u64) end @@ -28,7 +28,7 @@ macro cat_fd end drop end -if argc 2 < do +argc 2 < if stdin !fd cat_fd else @@ -37,7 +37,7 @@ else 0 O_RDONLY @file_path_cstr AT_FDCWD openat - if dup 0 < do + dup 0 < if "ERROR: could not open file " eputs @file_path_cstr cstr-to-str eputs "\n" eputs diff --git a/examples/checker.porth b/examples/checker.porth index ee462c9b..6284fc8c 100644 --- a/examples/checker.porth +++ b/examples/checker.porth @@ -4,7 +4,7 @@ include "std.porth" -if argc 2 < do +argc 2 < if "Usage: ./checker \n" eputs "[ERROR] no output file path is provided\n" eputs 1 exit @@ -23,7 +23,8 @@ memory canvas sizeof(pixel) WIDTH * HEIGHT * end 2dup CELL_WIDTH / swap CELL_HEIGHT / + - if 2 % 0 = do + 2 % + 0 = if 2dup swap WIDTH * + sizeof(pixel) * canvas + dup 255 swap !8 1 + dup 0 swap !8 1 + @@ -57,7 +58,7 @@ AT_FDCWD openat !fd -if @fd 0 < do +@fd 0 < if "[ERROR] could not open file `" eputs @file_path eputs "`\n" eputs diff --git a/examples/fizz-buzz.porth b/examples/fizz-buzz.porth index 96e9d07d..281d9f6f 100644 --- a/examples/fizz-buzz.porth +++ b/examples/fizz-buzz.porth @@ -1,11 +1,11 @@ include "std.porth" 1 while dup 100 < do - if dup 15 mod 0 = do + dup 15 mod 0 = if "FizzBuzz\n" puts - elif dup 3 mod 0 = do + orelse dup 3 mod 0 = if "Fizz\n" puts - elif dup 5 mod 0 = do + orelse dup 5 mod 0 = if "Buzz\n" puts else dup print diff --git a/examples/gol.porth b/examples/gol.porth index 7d4c653c..164ee60d 100644 --- a/examples/gol.porth +++ b/examples/gol.porth @@ -27,7 +27,7 @@ end proc display_row 0 while dup COLS < do - if 2dup + @8 0 = do + 2dup + @8 0 = if display over + '.' swap !8 else display over + '#' swap !8 @@ -72,37 +72,37 @@ end proc count_current_nbors 0 nbors !64 - if 2dup 1 - swap 1 - swap 2dup in_bounds - rot rot swap COLS * + board_current + @8 1 = - and do nbors inc64 end + 2dup 1 - swap 1 - swap 2dup in_bounds + rot rot swap COLS * + board_current + @8 1 = + and if nbors inc64 end - if 2dup 1 - 2dup in_bounds - rot rot swap COLS * + board_current + @8 1 = - and do nbors inc64 end + 2dup 1 - 2dup in_bounds + rot rot swap COLS * + board_current + @8 1 = + and if nbors inc64 end - if 2dup 1 - swap 1 + swap 2dup in_bounds - rot rot swap COLS * + board_current + @8 1 = - and do nbors inc64 end + 2dup 1 - swap 1 + swap 2dup in_bounds + rot rot swap COLS * + board_current + @8 1 = + and if nbors inc64 end - if 2dup swap 1 - swap 2dup in_bounds - rot rot swap COLS * + board_current + @8 1 = - and do nbors inc64 end + 2dup swap 1 - swap 2dup in_bounds + rot rot swap COLS * + board_current + @8 1 = + and if nbors inc64 end - if 2dup swap 1 + swap 2dup in_bounds - rot rot swap COLS * + board_current + @8 1 = - and do nbors inc64 end + 2dup swap 1 + swap 2dup in_bounds + rot rot swap COLS * + board_current + @8 1 = + and if nbors inc64 end - if 2dup 1 + swap 1 - swap 2dup in_bounds - rot rot swap COLS * + board_current + @8 1 = - and do nbors inc64 end + 2dup 1 + swap 1 - swap 2dup in_bounds + rot rot swap COLS * + board_current + @8 1 = + and if nbors inc64 end - if 2dup 1 + 2dup in_bounds - rot rot swap COLS * + board_current + @8 1 = - and do nbors inc64 end + 2dup 1 + 2dup in_bounds + rot rot swap COLS * + board_current + @8 1 = + and if nbors inc64 end - if 2dup 1 + swap 1 + swap 2dup in_bounds - rot rot swap COLS * + board_current + @8 1 = - and do nbors inc64 end + 2dup 1 + swap 1 + swap 2dup in_bounds + rot rot swap COLS * + board_current + @8 1 = + and if nbors inc64 end 2drop nbors @64 @@ -111,15 +111,15 @@ end proc compute_next_board 0 while dup ROWS < do 0 while dup COLS < do - if 2dup get_current_cell 1 = do + 2dup get_current_cell 1 = if 2dup count_current_nbors - if dup 2 = swap 3 = or do + dup 2 = swap 3 = or if 2dup 1 set_next_cell else 2dup 0 set_next_cell end else - if 2dup count_current_nbors 3 = do + 2dup count_current_nbors 3 = if 2dup 1 set_next_cell else 2dup 0 set_next_cell diff --git a/examples/name.porth b/examples/name.porth index 8d2aa674..6f9b9a3b 100644 --- a/examples/name.porth +++ b/examples/name.porth @@ -6,12 +6,12 @@ memory name NAME_CAPACITY end "What is your name? " puts NAME_CAPACITY name stdin read -if dup 0 <= do +dup 0 <= if "ERROR: could not read your name, sorry ( ._.)\n" eputs 1 exit end -if name over + 1 - @8 '\n' = do +name over + 1 - @8 '\n' = if 1 - end diff --git a/examples/rot13.porth b/examples/rot13.porth index 48ffae7a..1edde239 100644 --- a/examples/rot13.porth +++ b/examples/rot13.porth @@ -7,12 +7,12 @@ while BUFFER_CAP buffer stdin read dup 0 > do 0 while 2dup > do dup buffer + @8 - if dup 'a' >= over 'z' <= and do + dup 'a' >= over 'z' <= and if 2dup 'a' - 13 + 26 mod 'a' + swap buffer + !8 end - if dup 'A' >= over 'Z' <= and do + dup 'A' >= over 'Z' <= and if 2dup 'A' - 13 + 26 mod 'A' + swap buffer + !8 end diff --git a/examples/rule110.porth b/examples/rule110.porth index 0c60d6e0..6034a946 100644 --- a/examples/rule110.porth +++ b/examples/rule110.porth @@ -12,7 +12,7 @@ display N + 10 swap !8 0 while dup N 2 - < do 0 while dup N < do - if dup row + @8 1 = do + dup row + @8 1 = if dup display + '*' swap !8 else dup display + ' ' swap !8 diff --git a/examples/seq.porth b/examples/seq.porth index e3dae40a..9ae27e58 100644 --- a/examples/seq.porth +++ b/examples/seq.porth @@ -2,7 +2,7 @@ include "std.porth" memory limit sizeof(u64) end -if argc 2 < do +argc 2 < if "Usage: seq \n" eputs "ERROR: no limit is provided\n" eputs 1 exit @@ -10,7 +10,7 @@ end 1 nth_argv while dup @8 0 != do - if dup @8 '0' < over @8 '9' > or do + dup @8 '0' < over @8 '9' > or if "ERROR: `" eputs 1 nth_argv cstrlen 1 nth_argv eputs "` is not a correct integer\n" eputs diff --git a/porth.porth b/porth.porth index fd95fa24..c68f073c 100644 --- a/porth.porth +++ b/porth.porth @@ -69,16 +69,16 @@ proc cmd_echoed // argv fork - if dup 0 = do + dup 0 = if drop dup @64 cast(ptr) empty_envp rot rot execve - if dup 0 < do + dup 0 < if "[ERROR] could not exec external program\n" eputs 1 exit end - elif dup 0 > do + orelse dup 0 > if drop // TODO: handle the result of wait4 NULL 0 wstatus -1 wait4 drop @@ -92,7 +92,7 @@ proc cmd_echoed // argv end proc sim-stack-push // u64 -- - if sim-stack-count @64 SIM_STACK_CAP >= do + sim-stack-count @64 SIM_STACK_CAP >= if here eputs ": ERROR: data stack overflow in simulation mode\n" eputs 1 exit end sim-stack sim-stack-count @64 8 * + !64 @@ -100,7 +100,7 @@ proc sim-stack-push // u64 -- end proc sim-stack-pop // -- u64 - if sim-stack-count @64 0 = do + sim-stack-count @64 0 = if here eputs ": ERROR: data stack underflow in simulation mode\n" eputs 1 exit end sim-stack-count dec64 @@ -111,7 +111,7 @@ proc try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret 0 0 while dup word @Str.count < do dup word @Str.data + @8 - if dup isdigit lnot do + dup isdigit lnot if file_path_cstr @64 cast(ptr) cstr-to-str eputs ":" puts line_number @64 putd ":" puts word @Str.data cast(int) line_start @64 - 1 + putd @@ -136,24 +136,24 @@ proc push-op // type operand -- end proc print-op-type - if COUNT_OPS 7 != do + COUNT_OPS 7 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in print-op-type\n" eputs 1 exit end - if dup OP_PUSH_INT = do + dup OP_PUSH_INT = if "OP_PUSH_INT" puts - elif dup OP_PLUS = do + orelse dup OP_PLUS = if "OP_PLUS" puts - elif dup OP_MINUS = do + orelse dup OP_MINUS = if "OP_MINUS" puts - elif dup OP_MUL = do + orelse dup OP_MUL = if "OP_MUL" puts - elif dup OP_DUP = do + orelse dup OP_DUP = if "OP_DUP" puts - elif dup OP_DROP = do + orelse dup OP_DROP = if "OP_DROP" puts - elif dup OP_PRINT = do + orelse dup OP_PRINT = if "OP_PRINT" puts else here eputs ": Unknown op type\n" eputs 1 exit @@ -184,7 +184,7 @@ proc compile-ops // -- openat !out_fd - if @out_fd 0 < do + @out_fd 0 < if "[ERROR] could not open `output.asm`\n" eputs 1 exit end @@ -232,43 +232,43 @@ proc compile-ops // -- dup sizeof(Op) * ops + // TODO: compile time assertion - if COUNT_OPS 7 != do + COUNT_OPS 7 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in compile-ops\n" eputs 1 exit end - if dup @Op.type OP_PUSH_INT = do + dup @Op.type OP_PUSH_INT = if " ;; -- push int " @out_fd fputs dup @Op.operand @out_fd fputd " --\n" @out_fd fputs " mov rax, " @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs " push rax\n" @out_fd fputs - elif dup @Op.type OP_PLUS = do + orelse dup @Op.type OP_PLUS = if " ;; -- plus --\n" @out_fd fputs " pop rax\n" @out_fd fputs " pop rbx\n" @out_fd fputs " add rax, rbx\n" @out_fd fputs " push rax\n" @out_fd fputs - elif dup @Op.type OP_MINUS = do + orelse dup @Op.type OP_MINUS = if " ;; -- minus --\n" @out_fd fputs " pop rax\n" @out_fd fputs " pop rbx\n" @out_fd fputs " sub rbx, rax\n" @out_fd fputs " push rbx\n" @out_fd fputs - elif dup @Op.type OP_MUL = do + orelse dup @Op.type OP_MUL = if " ;; -- mul --\n" @out_fd fputs " pop rax\n" @out_fd fputs " pop rbx\n" @out_fd fputs " mul rbx\n" @out_fd fputs " push rax\n" @out_fd fputs - elif dup @Op.type OP_PRINT = do + orelse dup @Op.type OP_PRINT = if " ;; -- print --\n" @out_fd fputs " pop rdi\n" @out_fd fputs " call print\n" @out_fd fputs - elif dup @Op.type OP_DUP = do + orelse dup @Op.type OP_DUP = if " ;; -- dup --\n" @out_fd fputs " pop rax\n" @out_fd fputs " push rax\n" @out_fd fputs " push rax\n" @out_fd fputs - elif dup @Op.type OP_DROP = do + orelse dup @Op.type OP_DROP = if " ;; -- drop --\n" @out_fd fputs " pop rax\n" @out_fd fputs else @@ -299,37 +299,37 @@ proc simulate-ops // -- 0 while dup ops-count @64 < do dup sizeof(Op) * ops + - if COUNT_OPS 7 != do + COUNT_OPS 7 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in simulate-ops\n" eputs 1 exit end - if dup @Op.type OP_PUSH_INT = do + dup @Op.type OP_PUSH_INT = if dup @Op.operand sim-stack-push - elif dup @Op.type OP_PLUS = do + orelse dup @Op.type OP_PLUS = if sim-stack-pop sim-stack-pop + sim-stack-push - elif dup @Op.type OP_MINUS = do + orelse dup @Op.type OP_MINUS = if sim-stack-pop sim-stack-pop swap - sim-stack-push - elif dup @Op.type OP_MUL = do + orelse dup @Op.type OP_MUL = if sim-stack-pop sim-stack-pop * sim-stack-push - elif dup @Op.type OP_PRINT = do + orelse dup @Op.type OP_PRINT = if sim-stack-pop print - elif dup @Op.type OP_DUP = do + orelse dup @Op.type OP_DUP = if sim-stack-pop dup sim-stack-push sim-stack-push - elif dup @Op.type OP_DROP = do + orelse dup @Op.type OP_DROP = if sim-stack-pop drop else @@ -350,14 +350,14 @@ proc parse_file_path_cstr_into_ops AT_FDCWD // dirfd openat - if dup 0 < do + dup 0 < if "ERROR: could not open file " eputs file_path_cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs 1 exit end fd !64 - if statbuf fd @64 fstat 0 < do + statbuf fd @64 fstat 0 < if "ERROR: could not determine the size of file " eputs file_path_cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs 1 exit end @@ -373,7 +373,7 @@ proc parse_file_path_cstr_into_ops mmap content !Str.data - if content @Str.data cast(int) 0 < do + content @Str.data cast(int) 0 < if "ERROR: could not memory map file " eputs file_path_cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs 1 exit end @@ -386,22 +386,22 @@ proc parse_file_path_cstr_into_ops line str-trim-left ' ' word line str-chop-by-delim - if COUNT_OPS 7 != do + COUNT_OPS 7 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in parse-file-path\n" eputs 1 exit end - if word @Str "+" streq do + word @Str "+" streq if OP_PLUS 0 push-op - elif word @Str "-" streq do + orelse word @Str "-" streq if OP_MINUS 0 push-op - elif word @Str "*" streq do + orelse word @Str "*" streq if OP_MUL 0 push-op - elif word @Str "print" streq do + orelse word @Str "print" streq if OP_PRINT 0 push-op - elif word @Str "dup" streq do + orelse word @Str "dup" streq if OP_DUP 0 push-op - elif word @Str "drop" streq do + orelse word @Str "drop" streq if OP_DROP 0 push-op else OP_PUSH_INT try_to_parse_word_as_int_or_fail_as_unknown_word push-op @@ -424,15 +424,15 @@ proc usage // -- end proc main // -- - if argc 2 < do + argc 2 < if stderr usage "ERROR: subcommand is not provided\n" eputs 1 exit end 1 nth_argv - if dup "sim"c cstreq do - if argc 3 < do + dup "sim"c cstreq if + argc 3 < if stderr usage "ERROR: no input file is provided for the `sim` subcommand\n" eputs 1 exit @@ -443,8 +443,8 @@ proc main // -- parse_file_path_cstr_into_ops simulate-ops - elif dup "com"c cstreq do - if argc 3 < do + orelse dup "com"c cstreq if + argc 3 < if stderr usage "ERROR: no input file is provided for the `com` subcommand\n" eputs 1 exit @@ -455,11 +455,11 @@ proc main // -- parse_file_path_cstr_into_ops compile-ops - elif dup "help"c cstreq do + orelse dup "help"c cstreq if stdout usage 0 exit - elif dup "dump"c cstreq do - if argc 3 < do + orelse dup "dump"c cstreq if + argc 3 < if stderr usage "ERROR: no input file is provided for the `dump` subcommand\n" eputs 1 exit diff --git a/porth.py b/porth.py index 43213e0f..8a4fb9cb 100755 --- a/porth.py +++ b/porth.py @@ -26,7 +26,7 @@ class Keyword(Enum): IF=auto() - ELIF=auto() + ORELSE=auto() ELSE=auto() END=auto() WHILE=auto() @@ -93,7 +93,7 @@ class OpType(Enum): PUSH_MEM=auto() INTRINSIC=auto() IF=auto() - ELIF=auto() + ORELSE=auto() ELSE=auto() END=auto() WHILE=auto() @@ -217,13 +217,18 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): stack.append(mem_buf_ptr + op.operand) ip += 1 elif op.typ == OpType.IF: - ip += 1 + a = stack.pop() + if a == 0: + assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" + ip = op.operand + else: + ip += 1 elif op.typ == OpType.WHILE: ip += 1 elif op.typ == OpType.ELSE: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" ip = op.operand - elif op.typ == OpType.ELIF: + elif op.typ == OpType.ORELSE: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" ip = op.operand elif op.typ == OpType.END: @@ -520,9 +525,6 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): compiler_error_with_expansion_stack(op.token, "Python Exception during simulation") traceback.print_exception(type(e), e, e.__traceback__) exit(1) - if debug: - print("[INFO] Memory dump") - print(mem[:20]) def compiler_diagnostic(loc: Loc, tag: str, message: str): print("%s:%d:%d: %s: %s" % (loc + (tag, message)), file=sys.stderr) @@ -1060,7 +1062,17 @@ def type_check_program(program: Program): assert False, "unreachable" ctx.ip += 1 elif op.typ == OpType.IF: + if len(ctx.stack) < 1: + not_enough_arguments(op) + exit(1) + a_type, a_token = ctx.stack.pop() + if a_type != DataType.BOOL: + compiler_error_with_expansion_stack(op.token, "Invalid argument for the if condition. Expected BOOL.") + exit(1) ctx.ip += 1 + assert isinstance(op.operand, OpAddr) + contexts.append(Context(stack=copy(ctx.stack), ip=op.operand, ret_stack=copy(ctx.ret_stack))) + ctx = contexts[-1] elif op.typ == OpType.WHILE: ctx.ip += 1 elif op.typ == OpType.END: @@ -1069,7 +1081,7 @@ def type_check_program(program: Program): elif op.typ == OpType.ELSE: assert isinstance(op.operand, OpAddr) ctx.ip = op.operand - elif op.typ == OpType.ELIF: + elif op.typ == OpType.ORELSE: assert isinstance(op.operand, OpAddr) ctx.ip = op.operand elif op.typ == OpType.DO: @@ -1173,13 +1185,17 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" push rax\n") elif op.typ == OpType.IF: out.write(" ;; -- if --\n") + out.write(" pop rax\n") + out.write(" test rax, rax\n") + assert isinstance(op.operand, OpAddr), f"This could be a bug in the parsing step {op.operand}" + out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.WHILE: out.write(" ;; -- while --\n") elif op.typ == OpType.ELSE: out.write(" ;; -- else --\n") assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" out.write(" jmp addr_%d\n" % op.operand) - elif op.typ == OpType.ELIF: + elif op.typ == OpType.ORELSE: out.write(" ;; -- elif --\n") assert isinstance(op.operand, OpAddr), f"This could be a bug in the parsing step: {op.operand}" out.write(" jmp addr_%d\n" % op.operand) @@ -1503,15 +1519,15 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): assert len(Keyword) == 10, "Exhaustive KEYWORD_NAMES definition." KEYWORD_BY_NAMES: Dict[str, Keyword] = { 'if': Keyword.IF, - 'elif': Keyword.ELIF, + 'orelse': Keyword.ORELSE, 'else': Keyword.ELSE, - 'end': Keyword.END, 'while': Keyword.WHILE, 'do': Keyword.DO, 'macro': Keyword.MACRO, 'include': Keyword.INCLUDE, 'memory': Keyword.MEMORY, 'proc': Keyword.PROC, + 'end': Keyword.END, } KEYWORD_NAMES: Dict[Keyword, str] = {v: k for k, v in KEYWORD_BY_NAMES.items()} @@ -1695,46 +1711,34 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops.append(Op(typ=OpType.IF, token=token)) stack.append(ip) ip += 1 - elif token.value == Keyword.ELIF: - program.ops.append(Op(typ=OpType.ELIF, token=token)) - do_ip = stack.pop() - if program.ops[do_ip].typ != OpType.DO: - compiler_error_with_expansion_stack(program.ops[do_ip].token, '`elif` can only close `do`-blocks') - exit(1) - pre_do_ip = program.ops[do_ip].operand - assert isinstance(pre_do_ip, OpAddr) - if program.ops[pre_do_ip].typ == OpType.IF: - program.ops[do_ip].operand = ip + 1 - stack.append(ip) - ip += 1 - elif program.ops[pre_do_ip].typ == OpType.ELIF: - program.ops[pre_do_ip].operand = ip - program.ops[do_ip].operand = ip + 1 - stack.append(ip) - ip += 1 - else: - compiler_error_with_expansion_stack(program.ops[pre_do_ip].token, '`elif` can only close `do`-blocks that are preceded by `if` or another `elif`') + elif token.value == Keyword.ORELSE: + program.ops.append(Op(typ=OpType.ORELSE, token=token)) + if_ip = stack.pop() + if program.ops[if_ip].typ != OpType.IF: + compiler_error_with_expansion_stack(program.ops[if_ip].token, '`orelse` can come after `if`') exit(1) + + if len(stack) > 0 and program.ops[stack[-1]].typ == OpType.ORELSE: + prev_orelse_ip = stack.pop() + program.ops[prev_orelse_ip].operand = ip + + program.ops[if_ip].operand = ip + 1 + stack.append(ip) + ip += 1 elif token.value == Keyword.ELSE: program.ops.append(Op(typ=OpType.ELSE, token=token)) - do_ip = stack.pop() - if program.ops[do_ip].typ != OpType.DO: - compiler_error_with_expansion_stack(program.ops[do_ip].token, '`else` can only be used in `do` blocks') - exit(1) - pre_do_ip = program.ops[do_ip].operand - assert isinstance(pre_do_ip, OpAddr) - if program.ops[pre_do_ip].typ == OpType.IF: - program.ops[do_ip].operand = ip + 1 - stack.append(ip) - ip += 1 - elif program.ops[pre_do_ip].typ == OpType.ELIF: - program.ops[pre_do_ip].operand = ip - program.ops[do_ip].operand = ip + 1 - stack.append(ip) - ip += 1 - else: - compiler_error_with_expansion_stack(program.ops[pre_do_ip].token, '`else` can only close `do`-blocks that are preceded by `if` or `elif`') + if_ip = stack.pop() + if program.ops[if_ip].typ != OpType.IF: + compiler_error_with_expansion_stack(program.ops[if_ip].token, '`else` can only come after `if`') exit(1) + + if len(stack) > 0 and program.ops[stack[-1]].typ == OpType.ORELSE: + orelse_ip = stack.pop() + program.ops[orelse_ip].operand = ip + + program.ops[if_ip].operand = ip + 1 + stack.append(ip) + ip += 1 elif token.value == Keyword.END: block_ip = stack.pop() @@ -1745,29 +1749,26 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp elif program.ops[block_ip].typ == OpType.DO: program.ops.append(Op(typ=OpType.END, token=token)) assert program.ops[block_ip].operand is not None - pre_do_ip = program.ops[block_ip].operand - - assert isinstance(pre_do_ip, OpAddr) - if program.ops[pre_do_ip].typ == OpType.WHILE: - program.ops[ip].operand = pre_do_ip - program.ops[block_ip].operand = ip + 1 - elif program.ops[pre_do_ip].typ == OpType.IF: - program.ops[ip].operand = ip + 1 - program.ops[block_ip].operand = ip + 1 - elif program.ops[pre_do_ip].typ == OpType.ELIF: - program.ops[pre_do_ip].operand = ip - program.ops[ip].operand = ip + 1 - program.ops[block_ip].operand = ip + 1 - else: - compiler_error_with_expansion_stack(program.ops[pre_do_ip].token, '`end` can only close `do` blocks that are preceded by `if`, `while` or `elif`') + while_ip = program.ops[block_ip].operand + assert isinstance(while_ip, OpAddr) + + if program.ops[while_ip].typ != OpType.WHILE: + compiler_error_with_expansion_stack(program.ops[while_ip].token, '`end` can only close `do` blocks that are preceded by `while`') exit(1) + + program.ops[ip].operand = while_ip + program.ops[block_ip].operand = ip + 1 elif program.ops[block_ip].typ == OpType.SKIP_PROC: program.ops.append(Op(typ=OpType.RET, token=token)) program.ops[block_ip].operand = ip + 1 current_proc = None + elif program.ops[block_ip].typ == OpType.IF: + program.ops.append(Op(typ=OpType.END, token=token)) + program.ops[block_ip].operand = ip + program.ops[ip].operand = ip + 1 else: # NOTE: the closing of `macro` blocks is handled in its own separate place, not here - compiler_error_with_expansion_stack(program.ops[block_ip].token, '`end` can only close `else`, `do`, `macro` or `proc` blocks for now') + compiler_error_with_expansion_stack(program.ops[block_ip].token, '`end` can only close `if`, `else`, `do`, `macro` or `proc` blocks for now') exit(1) ip += 1 elif token.value == Keyword.WHILE: @@ -1777,13 +1778,13 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp elif token.value == Keyword.DO: program.ops.append(Op(typ=OpType.DO, token=token)) if len(stack) == 0: - compiler_error_with_expansion_stack(token, "`do` is not preceded by `if`, `while` or `elif`") + compiler_error_with_expansion_stack(token, "`do` is not preceded by `while`") exit(1) - pre_do_ip = stack.pop() - if program.ops[pre_do_ip].typ != OpType.WHILE and program.ops[pre_do_ip].typ != OpType.IF and program.ops[pre_do_ip].typ != OpType.ELIF: - compiler_error_with_expansion_stack(token, "`do` is not preceded by `if`, `while` or `elif`") + while_ip = stack.pop() + if program.ops[while_ip].typ != OpType.WHILE: + compiler_error_with_expansion_stack(token, "`do` is not preceded by `while`") exit(1) - program.ops[ip].operand = pre_do_ip + program.ops[ip].operand = while_ip stack.append(ip) ip += 1 elif token.value == Keyword.INCLUDE: @@ -2036,7 +2037,7 @@ def cmd_call_echoed(cmd: List[str], silent: bool) -> int: def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): with open(dot_path, "w") as f: f.write("digraph Program {\n") - assert len(OpType) == 14, f"Exhaustive handling of OpType in generate_control_flow_graph_as_dot_file(), {len(OpType)}" + assert len(OpType) == 15, f"Exhaustive handling of OpType in generate_control_flow_graph_as_dot_file(), {len(OpType)}" for ip in range(len(program.ops)): op = program.ops[ip] if op.typ == OpType.INTRINSIC: @@ -2055,9 +2056,18 @@ def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): assert isinstance(op.operand, int) f.write(f" Node_{ip} [label={op.operand}]\n") f.write(f" Node_{ip} -> Node_{ip + 1};\n") + elif op.typ == OpType.PUSH_MEM: + assert isinstance(op.operand, int) + f.write(f" Node_{ip} [label=\"mem({op.operand})\"]\n") + f.write(f" Node_{ip} -> Node_{ip + 1};\n") elif op.typ == OpType.IF: + if op.operand is None: + compiler_note(op.token.loc, "sus") + exit(1) + assert isinstance(op.operand, OpAddr), f"{op.operand}" f.write(f" Node_{ip} [shape=record label=if];\n") - f.write(f" Node_{ip} -> Node_{ip + 1};\n") + f.write(f" Node_{ip} -> Node_{ip + 1} [label=true];\n") + f.write(f" Node_{ip} -> Node_{op.operand} [label=false style=dashed];\n") elif op.typ == OpType.WHILE: f.write(f" Node_{ip} [shape=record label=while];\n") f.write(f" Node_{ip} -> Node_{ip + 1};\n") @@ -2070,9 +2080,9 @@ def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): assert isinstance(op.operand, OpAddr) f.write(f" Node_{ip} [shape=record label=else];\n") f.write(f" Node_{ip} -> Node_{op.operand};\n") - elif op.typ == OpType.ELIF: + elif op.typ == OpType.ORELSE: assert isinstance(op.operand, OpAddr) - f.write(f" Node_{ip} [shape=record label=elif];\n") + f.write(f" Node_{ip} [shape=record label=orelse];\n") f.write(f" Node_{ip} -> Node_{op.operand};\n") elif op.typ == OpType.END: assert isinstance(op.operand, OpAddr) @@ -2080,8 +2090,11 @@ def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): f.write(f" Node_{ip} -> Node_{op.operand};\n") elif op.typ == OpType.SKIP_PROC: assert isinstance(op.operand, OpAddr) - f.write(f" Node_{ip} [shape=record label=proc];\n") + f.write(f" Node_{ip} [shape=record label=skip_proc];\n") f.write(f" Node_{ip} -> Node_{op.operand};\n") + elif op.typ == OpType.PREP_PROC: + f.write(f" Node_{ip} [shape=record label=prep_proc];\n") + f.write(f" Node_{ip} -> Node_{ip + 1};\n") elif op.typ == OpType.RET: f.write(f" Node_{ip} [shape=record label=ret];\n") elif op.typ == OpType.CALL: @@ -2146,9 +2159,6 @@ def usage(compiler_name: str): else: break - if debug: - print("[INFO] Debug mode is enabled") - if len(argv) < 1: usage(compiler_name) print("[ERROR] no subcommand is provided", file=sys.stderr) diff --git a/std/std.porth b/std/std.porth index b66127bc..b64f8661 100644 --- a/std/std.porth +++ b/std/std.porth @@ -437,7 +437,7 @@ end proc cstreq while - if over @8 0 != over @8 0 != and do + over @8 0 != over @8 0 != and if over @8 over @8 = else false @@ -493,8 +493,8 @@ end proc str-trim-left // input -- while - if dup @Str.count 0 > do - dup @Str.data @8 ' ' = + dup @Str.count 0 > if + dup @Str.data @8 ' ' = else false end do dup str-chop-one-left @@ -508,14 +508,15 @@ proc str-chop-by-delim // delim line input 2dup @Str.data swap !Str.data over 0 swap !Str.count while - if dup @Str.count 0 > do + dup @Str.count 0 > if dup @Str.data @8 str-chop-by-delim-tmp @64 != else false end do dup str-chop-one-left swap dup Str.count inc64 swap end - if dup @Str.count 0 > do + // TODO: sus + dup @Str.count 0 > if dup str-chop-one-left end 2drop @@ -526,9 +527,9 @@ memory streq_b sizeof(Str) end proc streq // n1 s1 n2 s2 streq_a !Str streq_b !Str - if streq_a @Str.count streq_b @Str.count = do + streq_a @Str.count streq_b @Str.count = if 0 while - if dup streq_a @Str.count < do + dup streq_a @Str.count < if dup streq_a @Str.data + @8 over streq_b @Str.data + @8 = @@ -557,7 +558,7 @@ memory putd-fd sizeof(u64) end proc fputd // value fd -- putd-fd !64 - if dup 0 = do + dup 0 = if "0" putd-fd @64 fputs else putd-buffer PUTD_BUFFER_CAP + diff --git a/tests/else-not-inside-if-error.txt b/tests/else-not-inside-if-error.txt index 38043676..265fc49a 100644 --- a/tests/else-not-inside-if-error.txt +++ b/tests/else-not-inside-if-error.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 90 -./tests/else-not-inside-if-error.porth:1:1: ERROR: `else` can only be used in `do` blocks +:b stderr 83 +./tests/else-not-inside-if-error.porth:2:1: ERROR: `else` can only come after `if` diff --git a/tests/end-cant-close-error.txt b/tests/end-cant-close-error.txt index 3ade8f5d..8ac71550 100644 --- a/tests/end-cant-close-error.txt +++ b/tests/end-cant-close-error.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 115 -./tests/end-cant-close-error.porth:1:1: ERROR: `end` can only close `else`, `do`, `macro` or `proc` blocks for now +:b stderr 121 +./tests/end-cant-close-error.porth:1:1: ERROR: `end` can only close `if`, `else`, `do`, `macro` or `proc` blocks for now diff --git a/tests/if-else-fail.porth b/tests/if-else-fail.porth index 8f012ca8..c2dda95c 100644 --- a/tests/if-else-fail.porth +++ b/tests/if-else-fail.porth @@ -1 +1 @@ -if 34 35 + 69 = do 69 print else 420 end +34 35 + 69 = if 69 print else 420 end diff --git a/tests/if-else-fail.txt b/tests/if-else-fail.txt index e86ea2d5..515ec535 100644 --- a/tests/if-else-fail.txt +++ b/tests/if-else-fail.txt @@ -5,5 +5,5 @@ :b stdout 0 :b stderr 94 -./tests/if-else-fail.porth:1:34: ERROR: unhandled data on the data stack: [] +./tests/if-else-fail.porth:1:31: ERROR: unhandled data on the data stack: [] diff --git a/tests/if-else-less-fail.porth b/tests/if-else-less-fail.porth index 807121d1..68b5e8c9 100644 --- a/tests/if-else-less-fail.porth +++ b/tests/if-else-less-fail.porth @@ -1 +1 @@ -if 34 35 + 69 = do 69 end print +34 35 + 69 = if 69 end print diff --git a/tests/if-else-less-fail.txt b/tests/if-else-less-fail.txt index 9caa9cd7..c4266ee4 100644 --- a/tests/if-else-less-fail.txt +++ b/tests/if-else-less-fail.txt @@ -5,5 +5,5 @@ :b stdout 0 :b stderr 92 -./tests/if-else-less-fail.porth:1:27: ERROR: not enough arguments for the `print` intrinsic +./tests/if-else-less-fail.porth:1:24: ERROR: not enough arguments for the `print` intrinsic diff --git a/tests/if-else-less.porth b/tests/if-else-less.porth index b2747ede..12ecd4b7 100644 --- a/tests/if-else-less.porth +++ b/tests/if-else-less.porth @@ -1,2 +1,2 @@ -if 34 35 + 70 = do 69 print end -if 34 35 + 69 = do 69 print end +34 35 + 70 = if 69 print end +34 35 + 69 = if 69 print end diff --git a/tests/if-else.porth b/tests/if-else.porth index b0ca95b6..49fa639c 100644 --- a/tests/if-else.porth +++ b/tests/if-else.porth @@ -1,2 +1,2 @@ -if 34 35 + 70 = do 69 else 420 end print -if 34 35 + 69 = do 69 else 420 end print +34 35 + 70 = if 69 else 420 end print +34 35 + 69 = if 69 else 420 end print diff --git a/tests/macros.porth b/tests/macros.porth index 1c4016b2..9316240c 100644 --- a/tests/macros.porth +++ b/tests/macros.porth @@ -1,7 +1,7 @@ include "std.porth" macro check_less - if < do "YES\n" else "NO\n" end puts + < if "YES\n" else "NO\n" end puts end 1 2 check_less @@ -9,7 +9,7 @@ end macro even_fibs 0 1 while over 1000000 < do - if over 2 mod 0 = do + over 2 mod 0 = if over print end swap over + diff --git a/tests/not-enough-args-for-do.txt b/tests/not-enough-args-for-do.txt index f5e5fef4..69d4e48b 100644 --- a/tests/not-enough-args-for-do.txt +++ b/tests/not-enough-args-for-do.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 87 -./tests/not-enough-args-for-do.porth:3:4: ERROR: not enough arguments for the do-block +:b stderr 81 +./tests/not-enough-args-for-do.porth:3:4: ERROR: `do` is not preceded by `while` From 1f772df9fdd61d08b4dc16fdc2b8cf8557ab6c9b Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 01:17:30 +0700 Subject: [PATCH 093/145] Add while-alter test case --- tests/while-alter.porth | 3 +++ tests/while-alter.txt | 11 +++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/while-alter.porth create mode 100644 tests/while-alter.txt diff --git a/tests/while-alter.porth b/tests/while-alter.porth new file mode 100644 index 00000000..e6948e8a --- /dev/null +++ b/tests/while-alter.porth @@ -0,0 +1,3 @@ +while 1 cast(bool) do + 1 +end diff --git a/tests/while-alter.txt b/tests/while-alter.txt new file mode 100644 index 00000000..e182bedd --- /dev/null +++ b/tests/while-alter.txt @@ -0,0 +1,11 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 248 +./tests/while-alter.porth:1:20: ERROR: Loops are not allowed to alter types and amount of elements on the stack. +./tests/while-alter.porth:1:20: NOTE: Expected elements: [] +./tests/while-alter.porth:1:20: NOTE: Actual elements: [] + From 02d37ab3752d8d0bed8e9b8a78751913bfbb99eb Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 01:20:52 +0700 Subject: [PATCH 094/145] Always in debug mode for now --- porth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/porth.py b/porth.py index 8a4fb9cb..86a13aa5 100755 --- a/porth.py +++ b/porth.py @@ -20,7 +20,7 @@ SIM_STR_CAPACITY = 640_000 SIM_ARGV_CAPACITY = 640_000 -debug=False +debug=True Loc=Tuple[str, int, int] From 12ba8232745851c291669cc776103bc60cefa280 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 01:24:01 +0700 Subject: [PATCH 095/145] Add while-procs test case --- tests/.gitignore | 3 ++- tests/while-procs.porth | 14 ++++++++++++++ tests/while-procs.txt | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/while-procs.porth create mode 100644 tests/while-procs.txt diff --git a/tests/.gitignore b/tests/.gitignore index dda9aed2..23276c41 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -13,4 +13,5 @@ while here memory-forth-style cstr -empty \ No newline at end of file +empty +while-procs \ No newline at end of file diff --git a/tests/while-procs.porth b/tests/while-procs.porth new file mode 100644 index 00000000..11c28fca --- /dev/null +++ b/tests/while-procs.porth @@ -0,0 +1,14 @@ +include "std.porth" + +proc fibs + 0 1 while over 100 < do + over print + 2dup + rot drop + end 2drop +end + +fibs +1 +"------------------------------\n" puts +fibs +drop diff --git a/tests/while-procs.txt b/tests/while-procs.txt new file mode 100644 index 00000000..3156e5a9 --- /dev/null +++ b/tests/while-procs.txt @@ -0,0 +1,33 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 89 +0 +1 +1 +2 +3 +5 +8 +13 +21 +34 +55 +89 +------------------------------ +0 +1 +1 +2 +3 +5 +8 +13 +21 +34 +55 +89 + +:b stderr 0 + From 321864068420c9945d8dfee3ce421205deeb51b0 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 01:25:33 +0700 Subject: [PATCH 096/145] Fix type checking loops inside of procs --- porth.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/porth.py b/porth.py index 86a13aa5..80c053df 100755 --- a/porth.py +++ b/porth.py @@ -20,7 +20,7 @@ SIM_STR_CAPACITY = 640_000 SIM_ARGV_CAPACITY = 640_000 -debug=True +debug=False Loc=Tuple[str, int, int] @@ -569,12 +569,12 @@ class Context: ret_stack: List[OpAddr] ip: OpAddr +CallPath=Tuple[OpAddr, ...] + # TODO: better error reporting on type checking errors of intrinsics # Reported expected and actual types with the location that introduced the actual type def type_check_program(program: Program): - if not debug: - assert False, "TODO: the type checking is broken at the moment. Run with `-unsafe` flag before subcommand. See `./porth.py help` for more info." - visited_dos: Dict[OpAddr, DataStack] = {} + visited_dos: Dict[CallPath, DataStack] = {} contexts: List[Context] = [Context(stack=[], ip=0, ret_stack=[])] while len(contexts) > 0: ctx = contexts[-1]; @@ -1093,8 +1093,9 @@ def type_check_program(program: Program): if a_type != DataType.BOOL: compiler_error_with_expansion_stack(op.token, "Invalid argument for the while-do condition. Expected BOOL.") exit(1) - if ctx.ip in visited_dos: - expected_types = list(map(lambda x: x[0], visited_dos[ctx.ip])) + call_path = tuple(ctx.ret_stack + [ctx.ip]) + if call_path in visited_dos: + expected_types = list(map(lambda x: x[0], visited_dos[call_path])) actual_types = list(map(lambda x: x[0], ctx.stack)) if expected_types != actual_types: compiler_error_with_expansion_stack(op.token, 'Loops are not allowed to alter types and amount of elements on the stack.') @@ -1103,7 +1104,7 @@ def type_check_program(program: Program): exit(1) contexts.pop() else: - visited_dos[ctx.ip] = copy(ctx.stack) + visited_dos[call_path] = copy(ctx.stack) ctx.ip += 1 contexts.append(Context(stack=copy(ctx.stack), ip=op.operand, ret_stack=copy(ctx.ret_stack))) ctx = contexts[-1] From afa1e7eba72c5f99a487b15976be49dc264911dd Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 01:26:12 +0700 Subject: [PATCH 097/145] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b1069ad2..19ff7cf2 100644 --- a/README.md +++ b/README.md @@ -275,7 +275,7 @@ here puts ": TODO: not implemented\n" puts 1 exit ### Control Flow -- `if do else end` - pops the element on top of the stack and if the element is not `0` executes the ``, otherwise ``. +- ` if else end` - pops the element on top of the stack and if the element is not `0` executes the ``, otherwise ``. - `while do end` - keeps executing both `` and `` until `` produces `0` at the top of the stack. Checking the result of the `` removes it from the stack. ### Macros From cf25d36549e546854bd8936c773379574c409e60 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 02:53:02 +0700 Subject: [PATCH 098/145] [porth.porth] Separate intrinsics from ops --- porth.porth | 185 +++++++++++++++++++++++++++----------------------- std/std.porth | 1 + 2 files changed, 100 insertions(+), 86 deletions(-) diff --git a/porth.porth b/porth.porth index c68f073c..8e559108 100644 --- a/porth.porth +++ b/porth.porth @@ -5,14 +5,17 @@ include "std.porth" macro MEM_CAPACITY 640000 end macro SIM_STACK_CAP 1024 end -macro OP_PUSH_INT 0 end -macro OP_PLUS 1 end -macro OP_MINUS 2 end -macro OP_MUL 3 end -macro OP_PRINT 4 end -macro OP_DUP 5 end -macro OP_DROP 6 end -macro COUNT_OPS 7 end +macro OP_PUSH_INT 0 end +macro OP_INTRINSIC 1 end +macro COUNT_OPS 2 end + +macro INTRINSIC_PLUS 0 end +macro INTRINSIC_MINUS 1 end +macro INTRINSIC_MUL 2 end +macro INTRINSIC_PRINT 3 end +macro INTRINSIC_DUP 4 end +macro INTRINSIC_DROP 5 end +macro COUNT_INTRINSICS 6 end macro OPS_CAP 1024 end macro sizeof(Op) 16 end @@ -136,25 +139,15 @@ proc push-op // type operand -- end proc print-op-type - COUNT_OPS 7 != if + COUNT_OPS 2 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in print-op-type\n" eputs 1 exit end dup OP_PUSH_INT = if "OP_PUSH_INT" puts - orelse dup OP_PLUS = if - "OP_PLUS" puts - orelse dup OP_MINUS = if - "OP_MINUS" puts - orelse dup OP_MUL = if - "OP_MUL" puts - orelse dup OP_DUP = if - "OP_DUP" puts - orelse dup OP_DROP = if - "OP_DROP" puts - orelse dup OP_PRINT = if - "OP_PRINT" puts + orelse dup OP_INTRINSIC = if + "OP_INTRINSIC" puts else here eputs ": Unknown op type\n" eputs 1 exit end @@ -232,7 +225,7 @@ proc compile-ops // -- dup sizeof(Op) * ops + // TODO: compile time assertion - COUNT_OPS 7 != if + COUNT_OPS 2 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in compile-ops\n" eputs 1 exit end @@ -241,38 +234,49 @@ proc compile-ops // -- " ;; -- push int " @out_fd fputs dup @Op.operand @out_fd fputd " --\n" @out_fd fputs " mov rax, " @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs " push rax\n" @out_fd fputs - orelse dup @Op.type OP_PLUS = if - " ;; -- plus --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " add rax, rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs - orelse dup @Op.type OP_MINUS = if - " ;; -- minus --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " sub rbx, rax\n" @out_fd fputs - " push rbx\n" @out_fd fputs - orelse dup @Op.type OP_MUL = if - " ;; -- mul --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " mul rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs - orelse dup @Op.type OP_PRINT = if - " ;; -- print --\n" @out_fd fputs - " pop rdi\n" @out_fd fputs - " call print\n" @out_fd fputs - orelse dup @Op.type OP_DUP = if - " ;; -- dup --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " push rax\n" @out_fd fputs - " push rax\n" @out_fd fputs - orelse dup @Op.type OP_DROP = if - " ;; -- drop --\n" @out_fd fputs - " pop rax\n" @out_fd fputs + orelse dup @Op.type OP_INTRINSIC = if + COUNT_INTRINSICS 6 != if + here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs + 1 exit + end + + dup @Op.operand INTRINSIC_PLUS = if + " ;; -- plus --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " add rax, rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_MINUS = if + " ;; -- minus --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " sub rbx, rax\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_MUL = if + " ;; -- mul --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " mul rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_PRINT = if + " ;; -- print --\n" @out_fd fputs + " pop rdi\n" @out_fd fputs + " call print\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_DUP = if + " ;; -- dup --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " push rax\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_DROP = if + " ;; -- drop --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + else + here eputs ": unreachable.\n" eputs + 1 exit + end else - here eputs ": unreachable\n" eputs 1 exit + here eputs ": unreachable.\n" eputs + 1 exit end drop @@ -306,32 +310,36 @@ proc simulate-ops // -- dup @Op.type OP_PUSH_INT = if dup @Op.operand sim-stack-push - orelse dup @Op.type OP_PLUS = if - sim-stack-pop - sim-stack-pop - + - sim-stack-push - orelse dup @Op.type OP_MINUS = if - sim-stack-pop - sim-stack-pop - swap - - - sim-stack-push - orelse dup @Op.type OP_MUL = if - sim-stack-pop - sim-stack-pop - * - sim-stack-push - orelse dup @Op.type OP_PRINT = if - sim-stack-pop print - orelse dup @Op.type OP_DUP = if - sim-stack-pop - dup - sim-stack-push - sim-stack-push - orelse dup @Op.type OP_DROP = if - sim-stack-pop - drop + orelse dup @Op.type OP_INTRINSIC = if + dup @Op.operand INTRINSIC_PLUS = if + sim-stack-pop + sim-stack-pop + + + sim-stack-push + orelse dup @Op.operand INTRINSIC_MINUS = if + sim-stack-pop + sim-stack-pop + swap + - + sim-stack-push + orelse dup @Op.operand INTRINSIC_MUL = if + sim-stack-pop + sim-stack-pop + * + sim-stack-push + orelse dup @Op.operand INTRINSIC_PRINT = if + sim-stack-pop print + orelse dup @Op.operand INTRINSIC_DUP = if + sim-stack-pop + dup + sim-stack-push + sim-stack-push + orelse dup @Op.operand INTRINSIC_DROP = if + sim-stack-pop + drop + else + here eputs ": unreachable\n" eputs 1 exit + end else here eputs ": unreachable\n" eputs 1 exit end @@ -386,23 +394,28 @@ proc parse_file_path_cstr_into_ops line str-trim-left ' ' word line str-chop-by-delim - COUNT_OPS 7 != if + COUNT_OPS 2 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in parse-file-path\n" eputs 1 exit end + COUNT_INTRINSICS 6 != if + here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in parse-file-path\n" eputs + 1 exit + end + word @Str "+" streq if - OP_PLUS 0 push-op + OP_INTRINSIC INTRINSIC_PLUS push-op orelse word @Str "-" streq if - OP_MINUS 0 push-op + OP_INTRINSIC INTRINSIC_MINUS push-op orelse word @Str "*" streq if - OP_MUL 0 push-op + OP_INTRINSIC INTRINSIC_MUL push-op orelse word @Str "print" streq if - OP_PRINT 0 push-op + OP_INTRINSIC INTRINSIC_PRINT push-op orelse word @Str "dup" streq if - OP_DUP 0 push-op + OP_INTRINSIC INTRINSIC_DUP push-op orelse word @Str "drop" streq if - OP_DROP 0 push-op + OP_INTRINSIC INTRINSIC_DROP push-op else OP_PUSH_INT try_to_parse_word_as_int_or_fail_as_unknown_word push-op end diff --git a/std/std.porth b/std/std.porth index b64f8661..a32e1061 100644 --- a/std/std.porth +++ b/std/std.porth @@ -575,3 +575,4 @@ proc fputd // value fd -- end macro putd stdout fputd end +macro eputd stderr fputd end From bf29e5aafe4b8fb3ebad9de67020c7688f9fd8bc Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 03:27:05 +0700 Subject: [PATCH 099/145] [porth.porth] draft --- porth.porth | 475 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 407 insertions(+), 68 deletions(-) diff --git a/porth.porth b/porth.porth index 8e559108..8565ac97 100644 --- a/porth.porth +++ b/porth.porth @@ -9,13 +9,49 @@ macro OP_PUSH_INT 0 end macro OP_INTRINSIC 1 end macro COUNT_OPS 2 end -macro INTRINSIC_PLUS 0 end -macro INTRINSIC_MINUS 1 end -macro INTRINSIC_MUL 2 end -macro INTRINSIC_PRINT 3 end -macro INTRINSIC_DUP 4 end -macro INTRINSIC_DROP 5 end -macro COUNT_INTRINSICS 6 end +macro INTRINSIC_PLUS 0 end +macro INTRINSIC_MINUS 1 end +macro INTRINSIC_MUL 2 end +macro INTRINSIC_DIVMOD 3 end +macro INTRINSIC_EQ 4 end +macro INTRINSIC_GT 5 end +macro INTRINSIC_LT 6 end +macro INTRINSIC_GE 7 end +macro INTRINSIC_LE 8 end +macro INTRINSIC_NE 9 end +macro INTRINSIC_SHR 10 end +macro INTRINSIC_SHL 11 end +macro INTRINSIC_OR 12 end +macro INTRINSIC_AND 13 end +macro INTRINSIC_NOT 14 end +macro INTRINSIC_PRINT 15 end +macro INTRINSIC_DUP 16 end +macro INTRINSIC_SWAP 17 end +macro INTRINSIC_DROP 18 end +macro INTRINSIC_OVER 19 end +macro INTRINSIC_ROT 20 end +macro INTRINSIC_LOAD8 21 end +macro INTRINSIC_STORE8 22 end +macro INTRINSIC_LOAD16 23 end +macro INTRINSIC_STORE16 24 end +macro INTRINSIC_LOAD32 25 end +macro INTRINSIC_STORE32 26 end +macro INTRINSIC_LOAD64 27 end +macro INTRINSIC_STORE64 28 end +macro INTRINSIC_CAST_PTR 29 end +macro INTRINSIC_CAST_INT 30 end +macro INTRINSIC_CAST_BOOL 31 end +macro INTRINSIC_ARGC 32 end +macro INTRINSIC_ARGV 33 end +macro INTRINSIC_HERE 34 end +macro INTRINSIC_SYSCALL0 35 end +macro INTRINSIC_SYSCALL1 36 end +macro INTRINSIC_SYSCALL2 37 end +macro INTRINSIC_SYSCALL3 38 end +macro INTRINSIC_SYSCALL4 39 end +macro INTRINSIC_SYSCALL5 40 end +macro INTRINSIC_SYSCALL6 41 end +macro COUNT_INTRINSICS 42 end macro OPS_CAP 1024 end macro sizeof(Op) 16 end @@ -71,7 +107,7 @@ proc cmd_echoed // argv "\n" puts fork - + dup 0 = if drop dup @64 cast(ptr) empty_envp @@ -90,7 +126,7 @@ proc cmd_echoed // argv "[ERROR] could not fork a child\n" eputs 1 exit end - + drop end @@ -130,7 +166,7 @@ proc try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret end -proc push-op // type operand -- +proc push-op // type operand -- // TODO: assert OPS_CAP ops-count @64 sizeof(Op) * ops + dup rot swap !Op.operand @@ -235,48 +271,278 @@ proc compile-ops // -- " mov rax, " @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs " push rax\n" @out_fd fputs orelse dup @Op.type OP_INTRINSIC = if - COUNT_INTRINSICS 6 != if + COUNT_INTRINSICS 42 != if here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs 1 exit end dup @Op.operand INTRINSIC_PLUS = if - " ;; -- plus --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " add rax, rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- plus --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " add rax, rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs orelse dup @Op.operand INTRINSIC_MINUS = if - " ;; -- minus --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " sub rbx, rax\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- minus --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " sub rbx, rax\n" @out_fd fputs + " push rbx\n" @out_fd fputs orelse dup @Op.operand INTRINSIC_MUL = if - " ;; -- mul --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " mul rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- mul --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " mul rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_DIVMOD = if + " ;; -- mod --\n" @out_fd fputs + " xor rdx, rdx\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " div rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs + " push rdx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SHR = if + " ;; -- shr --\n" @out_fd fputs + " pop rcx\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " shr rbx, cl\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SHL = if + " ;; -- shl --\n" @out_fd fputs + " pop rcx\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " shl rbx, cl\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_OR = if + " ;; -- bor --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " or rbx, rax\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_AND = if + " ;; -- band --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " and rbx, rax\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_NOT = if + " ;; -- not --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " not rax\n" @out_fd fputs + " push rax\n" @out_fd fputs orelse dup @Op.operand INTRINSIC_PRINT = if - " ;; -- print --\n" @out_fd fputs - " pop rdi\n" @out_fd fputs - " call print\n" @out_fd fputs + " ;; -- print --\n" @out_fd fputs + " pop rdi\n" @out_fd fputs + " call print\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_EQ = if + " ;; -- equal --\n" @out_fd fputs + " mov rcx, 0\n" @out_fd fputs + " mov rdx, 1\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " cmp rax, rbx\n" @out_fd fputs + " cmove rcx, rdx\n" @out_fd fputs + " push rcx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_GT = if + " ;; -- gt --\n" @out_fd fputs + " mov rcx, 0\n" @out_fd fputs + " mov rdx, 1\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " cmp rax, rbx\n" @out_fd fputs + " cmovg rcx, rdx\n" @out_fd fputs + " push rcx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_LT = if + " ;; -- gt --\n" @out_fd fputs + " mov rcx, 0\n" @out_fd fputs + " mov rdx, 1\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " cmp rax, rbx\n" @out_fd fputs + " cmovl rcx, rdx\n" @out_fd fputs + " push rcx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_GE = if + " ;; -- gt --\n" @out_fd fputs + " mov rcx, 0\n" @out_fd fputs + " mov rdx, 1\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " cmp rax, rbx\n" @out_fd fputs + " cmovge rcx, rdx\n" @out_fd fputs + " push rcx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_LE = if + " ;; -- gt --\n" @out_fd fputs + " mov rcx, 0\n" @out_fd fputs + " mov rdx, 1\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " cmp rax, rbx\n" @out_fd fputs + " cmovle rcx, rdx\n" @out_fd fputs + " push rcx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_NE = if + " ;; -- ne --\n" @out_fd fputs + " mov rcx, 0\n" @out_fd fputs + " mov rdx, 1\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " cmp rax, rbx\n" @out_fd fputs + " cmovne rcx, rdx\n" @out_fd fputs + " push rcx\n" @out_fd fputs orelse dup @Op.operand INTRINSIC_DUP = if - " ;; -- dup --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " push rax\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- dup --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " push rax\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SWAP = if + " ;; -- swap --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs + " push rbx\n" @out_fd fputs orelse dup @Op.operand INTRINSIC_DROP = if - " ;; -- drop --\n" @out_fd fputs - " pop rax\n" @out_fd fputs + " ;; -- drop --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_OVER = if + " ;; -- over --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " push rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_ROT = if + " ;; -- rot --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " pop rcx\n" @out_fd fputs + " push rbx\n" @out_fd fputs + " push rax\n" @out_fd fputs + " push rcx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_LOAD8 = if + " ;; -- @8 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " xor rbx, rbx\n" @out_fd fputs + " mov bl, [rax]\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_STORE8 = if + " ;; -- !8 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " mov [rax], bl\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_LOAD16 = if + " ;; -- @16 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " xor rbx, rbx\n" @out_fd fputs + " mov bx, [rax]\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_STORE16 = if + " ;; -- !16 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " mov [rax], bx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_LOAD32 = if + " ;; -- @32 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " xor rbx, rbx\n" @out_fd fputs + " mov ebx, [rax]\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_STORE32 = if + " ;; -- !32 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " mov [rax], ebx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_LOAD64 = if + " ;; -- @64 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " xor rbx, rbx\n" @out_fd fputs + " mov rbx, [rax]\n" @out_fd fputs + " push rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_STORE64 = if + " ;; -- !64 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rbx\n" @out_fd fputs + " mov [rax], rbx\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_ARGC = if + " ;; -- argc --\n" @out_fd fputs + " mov rax, [args_ptr]\n" @out_fd fputs + " mov rax, [rax]\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_ARGV = if + " ;; -- argv --\n" @out_fd fputs + " mov rax, [args_ptr]\n" @out_fd fputs + " add rax, 8\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_HERE = if + here eputs ": TODO: intrinsic `here` is not implemented yet" eputs + orelse dup @Op.operand INTRINSIC_CAST_PTR = if + " ;; -- cast(ptr) --\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_CAST_INT = if + " ;; -- cast(int) --\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_CAST_BOOL = if + " ;; -- cast(bool) --\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SYSCALL0 = if + " ;; -- syscall0 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " syscall\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SYSCALL1 = if + " ;; -- syscall1 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rdi\n" @out_fd fputs + " syscall\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SYSCALL2 = if + " ;; -- syscall2 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rdi\n" @out_fd fputs + " pop rsi\n" @out_fd fputs + " syscall\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SYSCALL3 = if + " ;; -- syscall3 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rdi\n" @out_fd fputs + " pop rsi\n" @out_fd fputs + " pop rdx\n" @out_fd fputs + " syscall\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SYSCALL4 = if + " ;; -- syscall4 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rdi\n" @out_fd fputs + " pop rsi\n" @out_fd fputs + " pop rdx\n" @out_fd fputs + " pop r10\n" @out_fd fputs + " syscall\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SYSCALL5 = if + " ;; -- syscall5 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rdi\n" @out_fd fputs + " pop rsi\n" @out_fd fputs + " pop rdx\n" @out_fd fputs + " pop r10\n" @out_fd fputs + " pop r8\n" @out_fd fputs + " syscall\n" @out_fd fputs + " push rax\n" @out_fd fputs + orelse dup @Op.operand INTRINSIC_SYSCALL6 = if + " ;; -- syscall6 --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " pop rdi\n" @out_fd fputs + " pop rsi\n" @out_fd fputs + " pop rdx\n" @out_fd fputs + " pop r10\n" @out_fd fputs + " pop r8\n" @out_fd fputs + " pop r9\n" @out_fd fputs + " syscall\n" @out_fd fputs + " push rax\n" @out_fd fputs else - here eputs ": unreachable.\n" eputs - 1 exit + here eputs ": unreachable.\n" eputs + 1 exit end else - here eputs ": unreachable.\n" eputs - 1 exit + here eputs ": unreachable.\n" eputs + 1 exit end drop @@ -297,7 +563,7 @@ proc compile-ops // -- nasm_argv cmd_echoed ld_argv cmd_echoed output_argv cmd_echoed -end +end proc simulate-ops // -- 0 while dup ops-count @64 < do @@ -391,34 +657,107 @@ proc parse_file_path_cstr_into_ops '\n' line content str-chop-by-delim line @Str.data line_start !64 while line @Str.count 0 > do - line str-trim-left - ' ' word line str-chop-by-delim - - COUNT_OPS 2 != if - here eputs ": Assertion Failed: Exhaustive handling of Op types in parse-file-path\n" eputs - 1 exit - end - - COUNT_INTRINSICS 6 != if - here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in parse-file-path\n" eputs - 1 exit - end - - word @Str "+" streq if - OP_INTRINSIC INTRINSIC_PLUS push-op - orelse word @Str "-" streq if - OP_INTRINSIC INTRINSIC_MINUS push-op - orelse word @Str "*" streq if - OP_INTRINSIC INTRINSIC_MUL push-op - orelse word @Str "print" streq if - OP_INTRINSIC INTRINSIC_PRINT push-op - orelse word @Str "dup" streq if - OP_INTRINSIC INTRINSIC_DUP push-op - orelse word @Str "drop" streq if - OP_INTRINSIC INTRINSIC_DROP push-op - else - OP_PUSH_INT try_to_parse_word_as_int_or_fail_as_unknown_word push-op - end + line str-trim-left + ' ' word line str-chop-by-delim + + COUNT_OPS 2 != if + here eputs ": Assertion Failed: Exhaustive handling of Op types in parse-file-path\n" eputs + 1 exit + end + + COUNT_INTRINSICS 42 != if + here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in parse-file-path\n" eputs + 1 exit + end + + word @Str "+" streq if + OP_INTRINSIC INTRINSIC_PLUS push-op + orelse word @Str "-" streq if + OP_INTRINSIC INTRINSIC_MINUS push-op + orelse word @Str "*" streq if + OP_INTRINSIC INTRINSIC_MUL push-op + orelse word @Str "divmod" streq if + OP_INTRINSIC INTRINSIC_DIVMOD push-op + orelse word @Str "print" streq if + OP_INTRINSIC INTRINSIC_PRINT push-op + orelse word @Str "=" streq if + OP_INTRINSIC INTRINSIC_EQ push-op + orelse word @Str ">" streq if + OP_INTRINSIC INTRINSIC_GT push-op + orelse word @Str "<" streq if + OP_INTRINSIC INTRINSIC_LT push-op + orelse word @Str ">=" streq if + OP_INTRINSIC INTRINSIC_GE push-op + orelse word @Str "<=" streq if + OP_INTRINSIC INTRINSIC_LE push-op + orelse word @Str "!=" streq if + OP_INTRINSIC INTRINSIC_NE push-op + orelse word @Str "shr" streq if + OP_INTRINSIC INTRINSIC_SHR push-op + orelse word @Str "shl" streq if + OP_INTRINSIC INTRINSIC_SHL push-op + orelse word @Str "or" streq if + OP_INTRINSIC INTRINSIC_OR push-op + orelse word @Str "and" streq if + OP_INTRINSIC INTRINSIC_AND push-op + orelse word @Str "not" streq if + OP_INTRINSIC INTRINSIC_NOT push-op + orelse word @Str "dup" streq if + OP_INTRINSIC INTRINSIC_DUP push-op + orelse word @Str "swap" streq if + OP_INTRINSIC INTRINSIC_SWAP push-op + orelse word @Str "drop" streq if + OP_INTRINSIC INTRINSIC_DROP push-op + orelse word @Str "over" streq if + OP_INTRINSIC INTRINSIC_OVER push-op + orelse word @Str "rot" streq if + OP_INTRINSIC INTRINSIC_ROT push-op + orelse word @Str "!8" streq if + OP_INTRINSIC INTRINSIC_STORE8 push-op + orelse word @Str "@8" streq if + OP_INTRINSIC INTRINSIC_LOAD8 push-op + orelse word @Str "!16" streq if + OP_INTRINSIC INTRINSIC_STORE16 push-op + orelse word @Str "@16" streq if + OP_INTRINSIC INTRINSIC_LOAD16 push-op + orelse word @Str "!32" streq if + OP_INTRINSIC INTRINSIC_STORE32 push-op + orelse word @Str "@32" streq if + OP_INTRINSIC INTRINSIC_LOAD32 push-op + orelse word @Str "!64" streq if + OP_INTRINSIC INTRINSIC_STORE64 push-op + orelse word @Str "@64" streq if + OP_INTRINSIC INTRINSIC_LOAD64 push-op + orelse word @Str "cast(ptr)" streq if + OP_INTRINSIC INTRINSIC_CAST_PTR push-op + orelse word @Str "cast(int)" streq if + OP_INTRINSIC INTRINSIC_CAST_INT push-op + orelse word @Str "cast(bool)" streq if + OP_INTRINSIC INTRINSIC_CAST_BOOL push-op + orelse word @Str "argc" streq if + OP_INTRINSIC INTRINSIC_ARGC push-op + orelse word @Str "argv" streq if + OP_INTRINSIC INTRINSIC_ARGV push-op + orelse word @Str "here" streq if + OP_INTRINSIC INTRINSIC_HERE push-op + orelse word @Str "syscall0" streq if + OP_INTRINSIC INTRINSIC_SYSCALL0 push-op + orelse word @Str "syscall1" streq if + OP_INTRINSIC INTRINSIC_SYSCALL1 push-op + orelse word @Str "syscall2" streq if + OP_INTRINSIC INTRINSIC_SYSCALL2 push-op + orelse word @Str "syscall3" streq if + OP_INTRINSIC INTRINSIC_SYSCALL3 push-op + orelse word @Str "syscall4" streq if + OP_INTRINSIC INTRINSIC_SYSCALL4 push-op + orelse word @Str "syscall5" streq if + OP_INTRINSIC INTRINSIC_SYSCALL5 push-op + orelse word @Str "syscall6" streq if + OP_INTRINSIC INTRINSIC_SYSCALL6 push-op + else + OP_PUSH_INT try_to_parse_word_as_int_or_fail_as_unknown_word push-op + end + end line_number inc64 end From d7fdd950fd1d04ab97b0f1215ad04c9f03c475c3 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 03:36:45 +0700 Subject: [PATCH 100/145] Fix unclosed orelse block bug --- porth.py | 4 ++++ tests/.gitignore | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/porth.py b/porth.py index 80c053df..c64a498c 100755 --- a/porth.py +++ b/porth.py @@ -1767,6 +1767,10 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops.append(Op(typ=OpType.END, token=token)) program.ops[block_ip].operand = ip program.ops[ip].operand = ip + 1 + + if len(stack) > 0 and program.ops[stack[-1]].typ == OpType.ORELSE: + orelse_ip = stack.pop() + program.ops[orelse_ip].operand = ip else: # NOTE: the closing of `macro` blocks is handled in its own separate place, not here compiler_error_with_expansion_stack(program.ops[block_ip].token, '`end` can only close `if`, `else`, `do`, `macro` or `proc` blocks for now') diff --git a/tests/.gitignore b/tests/.gitignore index 23276c41..318b67c7 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -14,4 +14,5 @@ here memory-forth-style cstr empty -while-procs \ No newline at end of file +while-procs +if-orelse \ No newline at end of file From 63b993fea100bc2bb21d7fad6d4e12099dafb74d Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 03:40:15 +0700 Subject: [PATCH 101/145] Add test case for unclosed orelse bug --- tests/if-orelse.porth | 7 +++++++ tests/if-orelse.txt | 9 +++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/if-orelse.porth create mode 100644 tests/if-orelse.txt diff --git a/tests/if-orelse.porth b/tests/if-orelse.porth new file mode 100644 index 00000000..f994f759 --- /dev/null +++ b/tests/if-orelse.porth @@ -0,0 +1,7 @@ +include "std.porth" + +true if + 69 print +orelse false if + 420 print +end diff --git a/tests/if-orelse.txt b/tests/if-orelse.txt new file mode 100644 index 00000000..e0327acb --- /dev/null +++ b/tests/if-orelse.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 3 +69 + +:b stderr 0 + From 4e3b7b6d3d0d665212a74b0671a3ba544026ed55 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 08:46:35 +0700 Subject: [PATCH 102/145] [porth.porth] translate as many intrinsics as possible --- porth.porth | 201 +++++++++++++++++++++++++++++++++++------ tests/.gitignore | 3 +- tests/intrinsics.porth | 27 ++++++ tests/intrinsics.txt | 41 +++++++++ 4 files changed, 241 insertions(+), 31 deletions(-) create mode 100644 tests/intrinsics.porth create mode 100644 tests/intrinsics.txt diff --git a/porth.porth b/porth.porth index 8565ac97..555441bc 100644 --- a/porth.porth +++ b/porth.porth @@ -131,6 +131,7 @@ proc cmd_echoed // argv end proc sim-stack-push // u64 -- + cast(int) sim-stack-count @64 SIM_STACK_CAP >= if here eputs ": ERROR: data stack overflow in simulation mode\n" eputs 1 exit end @@ -569,7 +570,7 @@ proc simulate-ops // -- 0 while dup ops-count @64 < do dup sizeof(Op) * ops + - COUNT_OPS 7 != if + COUNT_OPS 2 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in simulate-ops\n" eputs 1 exit end @@ -577,35 +578,175 @@ proc simulate-ops // -- dup @Op.type OP_PUSH_INT = if dup @Op.operand sim-stack-push orelse dup @Op.type OP_INTRINSIC = if - dup @Op.operand INTRINSIC_PLUS = if - sim-stack-pop - sim-stack-pop - + - sim-stack-push - orelse dup @Op.operand INTRINSIC_MINUS = if - sim-stack-pop - sim-stack-pop - swap - - - sim-stack-push - orelse dup @Op.operand INTRINSIC_MUL = if - sim-stack-pop - sim-stack-pop - * - sim-stack-push - orelse dup @Op.operand INTRINSIC_PRINT = if - sim-stack-pop print - orelse dup @Op.operand INTRINSIC_DUP = if - sim-stack-pop - dup - sim-stack-push - sim-stack-push - orelse dup @Op.operand INTRINSIC_DROP = if - sim-stack-pop - drop - else - here eputs ": unreachable\n" eputs 1 exit - end + COUNT_INTRINSICS 42 != if + here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs + 1 exit + end + + dup @Op.operand INTRINSIC_PLUS = if + sim-stack-pop + sim-stack-pop + + + sim-stack-push + orelse dup @Op.operand INTRINSIC_MINUS = if + sim-stack-pop + sim-stack-pop + swap + - + sim-stack-push + orelse dup @Op.operand INTRINSIC_MUL = if + sim-stack-pop + sim-stack-pop + * + sim-stack-push + orelse dup @Op.operand INTRINSIC_DIVMOD = if + sim-stack-pop + sim-stack-pop + swap + divmod + swap + sim-stack-push + sim-stack-push + orelse dup @Op.operand INTRINSIC_SHR = if + sim-stack-pop + sim-stack-pop + swap + shr + sim-stack-push + orelse dup @Op.operand INTRINSIC_SHL = if + sim-stack-pop + sim-stack-pop + swap + shl + sim-stack-push + orelse dup @Op.operand INTRINSIC_OR = if + sim-stack-pop + sim-stack-pop + or + sim-stack-push + orelse dup @Op.operand INTRINSIC_AND = if + sim-stack-pop + sim-stack-pop + and + sim-stack-push + orelse dup @Op.operand INTRINSIC_NOT = if + sim-stack-pop + not + sim-stack-push + orelse dup @Op.operand INTRINSIC_PRINT = if + sim-stack-pop + print + orelse dup @Op.operand INTRINSIC_EQ = if + sim-stack-pop + sim-stack-pop + = + sim-stack-push + orelse dup @Op.operand INTRINSIC_GT = if + sim-stack-pop + sim-stack-pop + swap + > + sim-stack-push + orelse dup @Op.operand INTRINSIC_LT = if + sim-stack-pop + sim-stack-pop + swap + < + sim-stack-push + orelse dup @Op.operand INTRINSIC_GE = if + sim-stack-pop + sim-stack-pop + swap + >= + sim-stack-push + orelse dup @Op.operand INTRINSIC_LE = if + sim-stack-pop + sim-stack-pop + swap + <= + sim-stack-push + orelse dup @Op.operand INTRINSIC_NE = if + sim-stack-pop + sim-stack-pop + != + sim-stack-push + orelse dup @Op.operand INTRINSIC_DUP = if + sim-stack-pop + dup + sim-stack-push + sim-stack-push + orelse dup @Op.operand INTRINSIC_SWAP = if + sim-stack-pop + sim-stack-pop + swap + sim-stack-push + sim-stack-push + orelse dup @Op.operand INTRINSIC_DROP = if + sim-stack-pop + drop + orelse dup @Op.operand INTRINSIC_OVER = if + sim-stack-pop + sim-stack-pop + dup + sim-stack-push + swap + sim-stack-push + sim-stack-push + orelse dup @Op.operand INTRINSIC_ROT = if + sim-stack-pop + sim-stack-pop + sim-stack-pop + swap + sim-stack-push + swap + sim-stack-push + sim-stack-push + orelse dup @Op.operand INTRINSIC_LOAD8 = if + here eputs ": TODO: `@8` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_STORE8 = if + here eputs ": TODO: `!8` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_LOAD16 = if + here eputs ": TODO: `@16` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_STORE16 = if + here eputs ": TODO: `!16` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_LOAD32 = if + here eputs ": TODO: `@32` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_STORE32 = if + here eputs ": TODO: `!32` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_LOAD64 = if + here eputs ": TODO: `@64` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_STORE64 = if + here eputs ": TODO: `!64` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_ARGC = if + here eputs ": TODO: `argc` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_ARGV = if + here eputs ": TODO: `argv` is not implemented yet" eputs 1 exit + orelse dup @Op.operand INTRINSIC_HERE = if + here eputs ": TODO: `here` is not implemented yet" eputs + orelse dup @Op.operand INTRINSIC_CAST_PTR = if + nop + orelse dup @Op.operand INTRINSIC_CAST_INT = if + nop + orelse dup @Op.operand INTRINSIC_CAST_BOOL = if + nop + orelse dup @Op.operand INTRINSIC_SYSCALL0 = if + here eputs ": TODO: `syscall0` is not implemented yet" eputs + orelse dup @Op.operand INTRINSIC_SYSCALL1 = if + here eputs ": TODO: `syscall1` is not implemented yet" eputs + orelse dup @Op.operand INTRINSIC_SYSCALL2 = if + here eputs ": TODO: `syscall2` is not implemented yet" eputs + orelse dup @Op.operand INTRINSIC_SYSCALL3 = if + here eputs ": TODO: `syscall3` is not implemented yet" eputs + orelse dup @Op.operand INTRINSIC_SYSCALL4 = if + here eputs ": TODO: `syscall4` is not implemented yet" eputs + orelse dup @Op.operand INTRINSIC_SYSCALL5 = if + here eputs ": TODO: `syscall5` is not implemented yet" eputs + orelse dup @Op.operand INTRINSIC_SYSCALL6 = if + here eputs ": TODO: `syscall6` is not implemented yet" eputs + else + here eputs ": unreachable.\n" eputs + 1 exit + end else here eputs ": unreachable\n" eputs 1 exit end diff --git a/tests/.gitignore b/tests/.gitignore index 23276c41..75886fdb 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -14,4 +14,5 @@ here memory-forth-style cstr empty -while-procs \ No newline at end of file +while-procs +intrinsics \ No newline at end of file diff --git a/tests/intrinsics.porth b/tests/intrinsics.porth new file mode 100644 index 00000000..4b709fb7 --- /dev/null +++ b/tests/intrinsics.porth @@ -0,0 +1,27 @@ +34 35 + print +35 34 - print +105 4 * print +269 100 divmod print print +1024 1 shr print +1 5 shl print +1 2 or 4 or print +5 2 and print +5 5 = print +5 6 = print +420 69 > print +69 420 > print +69 420 < print +420 69 < print +420 420 >= print +420 69 >= print +69 420 >= print +420 420 <= print +69 420 <= print +420 69 <= print +420 69 != print +69 69 != print +420 dup print print +69 420 swap print print +69 drop +10 20 over print print print +10 20 30 rot print print print diff --git a/tests/intrinsics.txt b/tests/intrinsics.txt new file mode 100644 index 00000000..c57fc33a --- /dev/null +++ b/tests/intrinsics.txt @@ -0,0 +1,41 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 86 +69 +1 +420 +69 +2 +512 +32 +7 +0 +1 +0 +1 +0 +1 +0 +1 +1 +0 +1 +1 +0 +1 +0 +420 +420 +69 +420 +10 +20 +10 +10 +30 +20 + +:b stderr 0 + From 1a264b387e0432691283fa908025df5564ca36d6 Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 09:53:11 +0700 Subject: [PATCH 103/145] [porth.porth] implement `if-end` --- porth.porth | 292 ++++++++++++++++++++++++++++++++------------------ std/std.porth | 14 +++ 2 files changed, 204 insertions(+), 102 deletions(-) diff --git a/porth.porth b/porth.porth index 555441bc..5fc38858 100644 --- a/porth.porth +++ b/porth.porth @@ -6,8 +6,10 @@ macro MEM_CAPACITY 640000 end macro SIM_STACK_CAP 1024 end macro OP_PUSH_INT 0 end -macro OP_INTRINSIC 1 end -macro COUNT_OPS 2 end +macro OP_IF 1 end +macro OP_END 2 end +macro OP_INTRINSIC 3 end +macro COUNT_OPS 4 end macro INTRINSIC_PLUS 0 end macro INTRINSIC_MINUS 1 end @@ -73,6 +75,7 @@ memory line_start sizeof(ptr) end memory sim-stack-count sizeof(u64) end memory sim-stack sizeof(u64) SIM_STACK_CAP * end memory ops-count sizeof(u64) end +macro @ops-count ops-count @64 end memory ops sizeof(Op) OPS_CAP * end memory out_fd sizeof(u64) end @@ -169,14 +172,14 @@ end proc push-op // type operand -- // TODO: assert OPS_CAP - ops-count @64 sizeof(Op) * ops + + @ops-count sizeof(Op) * ops + dup rot swap !Op.operand !Op.type ops-count inc64 end proc print-op-type - COUNT_OPS 2 != if + COUNT_OPS 4 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in print-op-type\n" eputs 1 exit end @@ -185,6 +188,10 @@ proc print-op-type "OP_PUSH_INT" puts orelse dup OP_INTRINSIC = if "OP_INTRINSIC" puts + orelse dup OP_IF = if + "OP_IF" puts + orelse dup OP_END = if + "OP_END" puts else here eputs ": Unknown op type\n" eputs 1 exit end @@ -192,9 +199,10 @@ proc print-op-type end proc dump-ops // -- - 0 while dup ops-count @64 < do + 0 while dup @ops-count < do // ptr ptr dup sizeof(Op) * ops + + "IP: " puts over putd "\n" puts "Type: " puts dup @Op.type print-op-type "\n" puts "Operand: " puts @Op.operand putd "\n" puts "----------\n" puts @@ -258,19 +266,31 @@ proc compile-ops // -- "_start:\n" @out_fd fputs " mov [args_ptr], rsp\n" @out_fd fputs - 0 while dup ops-count @64 < do + 0 while dup @ops-count < do dup sizeof(Op) * ops + // TODO: compile time assertion - COUNT_OPS 2 != if + COUNT_OPS 4 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in compile-ops\n" eputs 1 exit end + "addr_" @out_fd fputs + over @out_fd fputd + ":\n" @out_fd fputs + dup @Op.type OP_PUSH_INT = if " ;; -- push int " @out_fd fputs dup @Op.operand @out_fd fputd " --\n" @out_fd fputs " mov rax, " @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs " push rax\n" @out_fd fputs + orelse dup @Op.type OP_IF = if + " ;; -- if --\n" @out_fd fputs + " pop rax\n" @out_fd fputs + " test rax, rax\n" @out_fd fputs + " jz addr_" @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs + orelse dup @Op.type OP_END = if + " ;; -- end --\n" @out_fd fputs + " jmp addr_" @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs orelse dup @Op.type OP_INTRINSIC = if COUNT_INTRINSICS 42 != if here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs @@ -566,40 +586,58 @@ proc compile-ops // -- output_argv cmd_echoed end -proc simulate-ops // -- - 0 while dup ops-count @64 < do - dup sizeof(Op) * ops + +memory sim-ip sizeof(u64) end +macro @sim-ip sim-ip @64 end +macro !sim-ip sim-ip !64 end +memory sim-op sizeof(Op) end - COUNT_OPS 2 != if +proc simulate-ops // -- + 0 !sim-ip + while @sim-ip @ops-count < do + sizeof(Op) + @sim-ip sizeof(Op) * ops + + sim-op + memcpy + + COUNT_OPS 4 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in simulate-ops\n" eputs 1 exit end - dup @Op.type OP_PUSH_INT = if - dup @Op.operand sim-stack-push - orelse dup @Op.type OP_INTRINSIC = if + sim-op @Op.type OP_PUSH_INT = if + sim-op @Op.operand sim-stack-push + sim-ip inc64 + orelse sim-op @Op.type OP_IF = if + sim-stack-pop cast(bool) if + sim-ip inc64 + else + sim-op @Op.operand !sim-ip + end + orelse sim-op @Op.type OP_END = if + sim-op @Op.operand !sim-ip + orelse sim-op @Op.type OP_INTRINSIC = if COUNT_INTRINSICS 42 != if here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs 1 exit end - dup @Op.operand INTRINSIC_PLUS = if + sim-op @Op.operand INTRINSIC_PLUS = if sim-stack-pop sim-stack-pop + sim-stack-push - orelse dup @Op.operand INTRINSIC_MINUS = if + orelse sim-op @Op.operand INTRINSIC_MINUS = if sim-stack-pop sim-stack-pop swap - sim-stack-push - orelse dup @Op.operand INTRINSIC_MUL = if + orelse sim-op @Op.operand INTRINSIC_MUL = if sim-stack-pop sim-stack-pop * sim-stack-push - orelse dup @Op.operand INTRINSIC_DIVMOD = if + orelse sim-op @Op.operand INTRINSIC_DIVMOD = if sim-stack-pop sim-stack-pop swap @@ -607,84 +645,84 @@ proc simulate-ops // -- swap sim-stack-push sim-stack-push - orelse dup @Op.operand INTRINSIC_SHR = if + orelse sim-op @Op.operand INTRINSIC_SHR = if sim-stack-pop sim-stack-pop swap shr sim-stack-push - orelse dup @Op.operand INTRINSIC_SHL = if + orelse sim-op @Op.operand INTRINSIC_SHL = if sim-stack-pop sim-stack-pop swap shl sim-stack-push - orelse dup @Op.operand INTRINSIC_OR = if + orelse sim-op @Op.operand INTRINSIC_OR = if sim-stack-pop sim-stack-pop or sim-stack-push - orelse dup @Op.operand INTRINSIC_AND = if + orelse sim-op @Op.operand INTRINSIC_AND = if sim-stack-pop sim-stack-pop and sim-stack-push - orelse dup @Op.operand INTRINSIC_NOT = if + orelse sim-op @Op.operand INTRINSIC_NOT = if sim-stack-pop not sim-stack-push - orelse dup @Op.operand INTRINSIC_PRINT = if + orelse sim-op @Op.operand INTRINSIC_PRINT = if sim-stack-pop print - orelse dup @Op.operand INTRINSIC_EQ = if + orelse sim-op @Op.operand INTRINSIC_EQ = if sim-stack-pop sim-stack-pop = sim-stack-push - orelse dup @Op.operand INTRINSIC_GT = if + orelse sim-op @Op.operand INTRINSIC_GT = if sim-stack-pop sim-stack-pop swap > sim-stack-push - orelse dup @Op.operand INTRINSIC_LT = if + orelse sim-op @Op.operand INTRINSIC_LT = if sim-stack-pop sim-stack-pop swap < sim-stack-push - orelse dup @Op.operand INTRINSIC_GE = if + orelse sim-op @Op.operand INTRINSIC_GE = if sim-stack-pop sim-stack-pop swap >= sim-stack-push - orelse dup @Op.operand INTRINSIC_LE = if + orelse sim-op @Op.operand INTRINSIC_LE = if sim-stack-pop sim-stack-pop swap <= sim-stack-push - orelse dup @Op.operand INTRINSIC_NE = if + orelse sim-op @Op.operand INTRINSIC_NE = if sim-stack-pop sim-stack-pop != sim-stack-push - orelse dup @Op.operand INTRINSIC_DUP = if + orelse sim-op @Op.operand INTRINSIC_DUP = if sim-stack-pop dup sim-stack-push sim-stack-push - orelse dup @Op.operand INTRINSIC_SWAP = if + orelse sim-op @Op.operand INTRINSIC_SWAP = if sim-stack-pop sim-stack-pop swap sim-stack-push sim-stack-push - orelse dup @Op.operand INTRINSIC_DROP = if + orelse sim-op @Op.operand INTRINSIC_DROP = if sim-stack-pop drop - orelse dup @Op.operand INTRINSIC_OVER = if + orelse sim-op @Op.operand INTRINSIC_OVER = if sim-stack-pop sim-stack-pop dup @@ -692,7 +730,7 @@ proc simulate-ops // -- swap sim-stack-push sim-stack-push - orelse dup @Op.operand INTRINSIC_ROT = if + orelse sim-op @Op.operand INTRINSIC_ROT = if sim-stack-pop sim-stack-pop sim-stack-pop @@ -701,61 +739,79 @@ proc simulate-ops // -- swap sim-stack-push sim-stack-push - orelse dup @Op.operand INTRINSIC_LOAD8 = if + orelse sim-op @Op.operand INTRINSIC_LOAD8 = if here eputs ": TODO: `@8` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_STORE8 = if + orelse sim-op @Op.operand INTRINSIC_STORE8 = if here eputs ": TODO: `!8` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_LOAD16 = if + orelse sim-op @Op.operand INTRINSIC_LOAD16 = if here eputs ": TODO: `@16` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_STORE16 = if + orelse sim-op @Op.operand INTRINSIC_STORE16 = if here eputs ": TODO: `!16` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_LOAD32 = if + orelse sim-op @Op.operand INTRINSIC_LOAD32 = if here eputs ": TODO: `@32` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_STORE32 = if + orelse sim-op @Op.operand INTRINSIC_STORE32 = if here eputs ": TODO: `!32` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_LOAD64 = if + orelse sim-op @Op.operand INTRINSIC_LOAD64 = if here eputs ": TODO: `@64` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_STORE64 = if + orelse sim-op @Op.operand INTRINSIC_STORE64 = if here eputs ": TODO: `!64` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_ARGC = if + orelse sim-op @Op.operand INTRINSIC_ARGC = if here eputs ": TODO: `argc` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_ARGV = if + orelse sim-op @Op.operand INTRINSIC_ARGV = if here eputs ": TODO: `argv` is not implemented yet" eputs 1 exit - orelse dup @Op.operand INTRINSIC_HERE = if + orelse sim-op @Op.operand INTRINSIC_HERE = if here eputs ": TODO: `here` is not implemented yet" eputs - orelse dup @Op.operand INTRINSIC_CAST_PTR = if + orelse sim-op @Op.operand INTRINSIC_CAST_PTR = if nop - orelse dup @Op.operand INTRINSIC_CAST_INT = if + orelse sim-op @Op.operand INTRINSIC_CAST_INT = if nop - orelse dup @Op.operand INTRINSIC_CAST_BOOL = if + orelse sim-op @Op.operand INTRINSIC_CAST_BOOL = if nop - orelse dup @Op.operand INTRINSIC_SYSCALL0 = if + orelse sim-op @Op.operand INTRINSIC_SYSCALL0 = if here eputs ": TODO: `syscall0` is not implemented yet" eputs - orelse dup @Op.operand INTRINSIC_SYSCALL1 = if + orelse sim-op @Op.operand INTRINSIC_SYSCALL1 = if here eputs ": TODO: `syscall1` is not implemented yet" eputs - orelse dup @Op.operand INTRINSIC_SYSCALL2 = if + orelse sim-op @Op.operand INTRINSIC_SYSCALL2 = if here eputs ": TODO: `syscall2` is not implemented yet" eputs - orelse dup @Op.operand INTRINSIC_SYSCALL3 = if + orelse sim-op @Op.operand INTRINSIC_SYSCALL3 = if here eputs ": TODO: `syscall3` is not implemented yet" eputs - orelse dup @Op.operand INTRINSIC_SYSCALL4 = if + orelse sim-op @Op.operand INTRINSIC_SYSCALL4 = if here eputs ": TODO: `syscall4` is not implemented yet" eputs - orelse dup @Op.operand INTRINSIC_SYSCALL5 = if + orelse sim-op @Op.operand INTRINSIC_SYSCALL5 = if here eputs ": TODO: `syscall5` is not implemented yet" eputs - orelse dup @Op.operand INTRINSIC_SYSCALL6 = if + orelse sim-op @Op.operand INTRINSIC_SYSCALL6 = if here eputs ": TODO: `syscall6` is not implemented yet" eputs else here eputs ": unreachable.\n" eputs 1 exit end + + sim-ip inc64 else here eputs ": unreachable\n" eputs 1 exit end + end +end - drop +macro PARSE_BLOCK_STACK_CAP 1024 end +memory parse-block-stack-count sizeof(u64) end +macro @parse-block-stack-count parse-block-stack-count @64 end +memory parse-block-stack sizeof(u64) PARSE_BLOCK_STACK_CAP * end - 1 + +proc parse-block-stack-push + @parse-block-stack-count PARSE_BLOCK_STACK_CAP >= if + here eputs ": ERROR: parse block stack overflow\n" eputs 1 exit end - drop + parse-block-stack @parse-block-stack-count sizeof(u64) * + !64 + parse-block-stack-count inc64 +end + +proc parse-block-stack-pop + @parse-block-stack-count 0 = if + here eputs ": ERROR: parse block stack underflow\n" eputs 1 exit + end + parse-block-stack-count dec64 + parse-block-stack @parse-block-stack-count sizeof(u64) * + @64 end proc parse_file_path_cstr_into_ops @@ -801,7 +857,7 @@ proc parse_file_path_cstr_into_ops line str-trim-left ' ' word line str-chop-by-delim - COUNT_OPS 2 != if + COUNT_OPS 4 != if here eputs ": Assertion Failed: Exhaustive handling of Op types in parse-file-path\n" eputs 1 exit end @@ -811,90 +867,122 @@ proc parse_file_path_cstr_into_ops 1 exit end + // Intrinsics word @Str "+" streq if - OP_INTRINSIC INTRINSIC_PLUS push-op + OP_INTRINSIC INTRINSIC_PLUS push-op orelse word @Str "-" streq if - OP_INTRINSIC INTRINSIC_MINUS push-op + OP_INTRINSIC INTRINSIC_MINUS push-op orelse word @Str "*" streq if - OP_INTRINSIC INTRINSIC_MUL push-op + OP_INTRINSIC INTRINSIC_MUL push-op orelse word @Str "divmod" streq if - OP_INTRINSIC INTRINSIC_DIVMOD push-op + OP_INTRINSIC INTRINSIC_DIVMOD push-op orelse word @Str "print" streq if - OP_INTRINSIC INTRINSIC_PRINT push-op + OP_INTRINSIC INTRINSIC_PRINT push-op orelse word @Str "=" streq if - OP_INTRINSIC INTRINSIC_EQ push-op + OP_INTRINSIC INTRINSIC_EQ push-op orelse word @Str ">" streq if - OP_INTRINSIC INTRINSIC_GT push-op + OP_INTRINSIC INTRINSIC_GT push-op orelse word @Str "<" streq if - OP_INTRINSIC INTRINSIC_LT push-op + OP_INTRINSIC INTRINSIC_LT push-op orelse word @Str ">=" streq if - OP_INTRINSIC INTRINSIC_GE push-op + OP_INTRINSIC INTRINSIC_GE push-op orelse word @Str "<=" streq if - OP_INTRINSIC INTRINSIC_LE push-op + OP_INTRINSIC INTRINSIC_LE push-op orelse word @Str "!=" streq if - OP_INTRINSIC INTRINSIC_NE push-op + OP_INTRINSIC INTRINSIC_NE push-op orelse word @Str "shr" streq if - OP_INTRINSIC INTRINSIC_SHR push-op + OP_INTRINSIC INTRINSIC_SHR push-op orelse word @Str "shl" streq if - OP_INTRINSIC INTRINSIC_SHL push-op + OP_INTRINSIC INTRINSIC_SHL push-op orelse word @Str "or" streq if - OP_INTRINSIC INTRINSIC_OR push-op + OP_INTRINSIC INTRINSIC_OR push-op orelse word @Str "and" streq if - OP_INTRINSIC INTRINSIC_AND push-op + OP_INTRINSIC INTRINSIC_AND push-op orelse word @Str "not" streq if - OP_INTRINSIC INTRINSIC_NOT push-op + OP_INTRINSIC INTRINSIC_NOT push-op orelse word @Str "dup" streq if - OP_INTRINSIC INTRINSIC_DUP push-op + OP_INTRINSIC INTRINSIC_DUP push-op orelse word @Str "swap" streq if - OP_INTRINSIC INTRINSIC_SWAP push-op + OP_INTRINSIC INTRINSIC_SWAP push-op orelse word @Str "drop" streq if - OP_INTRINSIC INTRINSIC_DROP push-op + OP_INTRINSIC INTRINSIC_DROP push-op orelse word @Str "over" streq if - OP_INTRINSIC INTRINSIC_OVER push-op + OP_INTRINSIC INTRINSIC_OVER push-op orelse word @Str "rot" streq if - OP_INTRINSIC INTRINSIC_ROT push-op + OP_INTRINSIC INTRINSIC_ROT push-op orelse word @Str "!8" streq if - OP_INTRINSIC INTRINSIC_STORE8 push-op + OP_INTRINSIC INTRINSIC_STORE8 push-op orelse word @Str "@8" streq if - OP_INTRINSIC INTRINSIC_LOAD8 push-op + OP_INTRINSIC INTRINSIC_LOAD8 push-op orelse word @Str "!16" streq if - OP_INTRINSIC INTRINSIC_STORE16 push-op + OP_INTRINSIC INTRINSIC_STORE16 push-op orelse word @Str "@16" streq if - OP_INTRINSIC INTRINSIC_LOAD16 push-op + OP_INTRINSIC INTRINSIC_LOAD16 push-op orelse word @Str "!32" streq if - OP_INTRINSIC INTRINSIC_STORE32 push-op + OP_INTRINSIC INTRINSIC_STORE32 push-op orelse word @Str "@32" streq if - OP_INTRINSIC INTRINSIC_LOAD32 push-op + OP_INTRINSIC INTRINSIC_LOAD32 push-op orelse word @Str "!64" streq if - OP_INTRINSIC INTRINSIC_STORE64 push-op + OP_INTRINSIC INTRINSIC_STORE64 push-op orelse word @Str "@64" streq if - OP_INTRINSIC INTRINSIC_LOAD64 push-op + OP_INTRINSIC INTRINSIC_LOAD64 push-op orelse word @Str "cast(ptr)" streq if - OP_INTRINSIC INTRINSIC_CAST_PTR push-op + OP_INTRINSIC INTRINSIC_CAST_PTR push-op orelse word @Str "cast(int)" streq if - OP_INTRINSIC INTRINSIC_CAST_INT push-op + OP_INTRINSIC INTRINSIC_CAST_INT push-op orelse word @Str "cast(bool)" streq if - OP_INTRINSIC INTRINSIC_CAST_BOOL push-op + OP_INTRINSIC INTRINSIC_CAST_BOOL push-op orelse word @Str "argc" streq if - OP_INTRINSIC INTRINSIC_ARGC push-op + OP_INTRINSIC INTRINSIC_ARGC push-op orelse word @Str "argv" streq if - OP_INTRINSIC INTRINSIC_ARGV push-op + OP_INTRINSIC INTRINSIC_ARGV push-op orelse word @Str "here" streq if - OP_INTRINSIC INTRINSIC_HERE push-op + OP_INTRINSIC INTRINSIC_HERE push-op orelse word @Str "syscall0" streq if - OP_INTRINSIC INTRINSIC_SYSCALL0 push-op + OP_INTRINSIC INTRINSIC_SYSCALL0 push-op orelse word @Str "syscall1" streq if - OP_INTRINSIC INTRINSIC_SYSCALL1 push-op + OP_INTRINSIC INTRINSIC_SYSCALL1 push-op orelse word @Str "syscall2" streq if - OP_INTRINSIC INTRINSIC_SYSCALL2 push-op + OP_INTRINSIC INTRINSIC_SYSCALL2 push-op orelse word @Str "syscall3" streq if - OP_INTRINSIC INTRINSIC_SYSCALL3 push-op + OP_INTRINSIC INTRINSIC_SYSCALL3 push-op orelse word @Str "syscall4" streq if - OP_INTRINSIC INTRINSIC_SYSCALL4 push-op + OP_INTRINSIC INTRINSIC_SYSCALL4 push-op orelse word @Str "syscall5" streq if - OP_INTRINSIC INTRINSIC_SYSCALL5 push-op + OP_INTRINSIC INTRINSIC_SYSCALL5 push-op orelse word @Str "syscall6" streq if - OP_INTRINSIC INTRINSIC_SYSCALL6 push-op + OP_INTRINSIC INTRINSIC_SYSCALL6 push-op + + // Keywords + orelse word @Str "if" streq if + @ops-count parse-block-stack-push + OP_IF 0 push-op + orelse word @Str "end" streq if + @parse-block-stack-count 0 <= if + file_path_cstr @64 cast(ptr) cstr-to-str eputs + ":" puts line_number @64 putd + ":" puts word @Str.data cast(int) line_start @64 - 1 + putd + ": ERROR: `end` can only close `if` for now\n" eputs + 1 exit + end + + parse-block-stack-pop + + ops over sizeof(Op) * + + + dup @Op.type OP_IF != if + file_path_cstr @64 cast(ptr) cstr-to-str eputs + ":" puts line_number @64 putd + ":" puts word @Str.data cast(int) line_start @64 - 1 + putd + ": ERROR: `end` can only close `if` for now\n" eputs + 1 exit + end + + @ops-count swap !Op.operand + + drop // ip + + OP_END @ops-count 1 + push-op else OP_PUSH_INT try_to_parse_word_as_int_or_fail_as_unknown_word push-op end diff --git a/std/std.porth b/std/std.porth index a32e1061..71e5a639 100644 --- a/std/std.porth +++ b/std/std.porth @@ -576,3 +576,17 @@ end macro putd stdout fputd end macro eputd stderr fputd end + +memory memcpy-size sizeof(u64) end +memory memcpy-src sizeof(ptr) end +memory memcpy-dst sizeof(ptr) end +proc memcpy // size src dst + memcpy-dst !64 + memcpy-src !64 + memcpy-size !64 + 0 while dup memcpy-size @64 < do + dup memcpy-src @64 cast(ptr) + @8 + over memcpy-dst @64 cast(ptr) + !8 + 1 + + end drop +end From 7434dc7ffc75df06824cddea5553aeacf7bc1a8d Mon Sep 17 00:00:00 2001 From: rexim Date: Fri, 22 Oct 2021 18:06:51 +0700 Subject: [PATCH 104/145] [porth.porth] Check for ops overflow --- porth.porth | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/porth.porth b/porth.porth index 5fc38858..b438741d 100644 --- a/porth.porth +++ b/porth.porth @@ -169,9 +169,11 @@ proc try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret end drop end - proc push-op // type operand -- - // TODO: assert OPS_CAP + @ops-count OPS_CAP >= if + here eputs ": ERROR: ops overflow\n" eputs 1 exit + end + @ops-count sizeof(Op) * ops + dup rot swap !Op.operand !Op.type From 3949a5bfd8729a252164524eae1fd9ed6620198b Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 23 Oct 2021 04:59:02 +0700 Subject: [PATCH 105/145] [vim] Highlight keywords as keywords --- editor/porth.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/porth.vim b/editor/porth.vim index 9532906a..d4a51127 100644 --- a/editor/porth.vim +++ b/editor/porth.vim @@ -24,7 +24,7 @@ syntax region porthString start=/\v'/ skip=/\v\\./ end=/\v'/ " Set highlights highlight default link porthTodos Todo -highlight default link porthKeywords Identifier +highlight default link porthKeywords Keyword highlight default link porthCommentLine Comment highlight default link porthString String From cb154d0a235b8dac16a5be1e9e02a03b34da37f2 Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 23 Oct 2021 06:24:58 +0700 Subject: [PATCH 106/145] [porth.porth] add support for comments --- porth.porth | 40 +++++++++++++++++++++++++++++++++++++++- tests/intrinsics.porth | 1 + 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/porth.porth b/porth.porth index b438741d..f848d2a0 100644 --- a/porth.porth +++ b/porth.porth @@ -64,10 +64,13 @@ macro !Op.type Op.type !64 end macro @Op.operand Op.operand @64 end macro !Op.operand Op.operand !64 end +memory comment sizeof(Str) end "//" comment !Str + memory file_path_cstr sizeof(ptr) end memory fd sizeof(u64) end memory statbuf sizeof(stat) end memory content sizeof(Str) end +memory line-with-comment sizeof(Str) end memory line sizeof(Str) end memory word sizeof(Str) end memory line_number sizeof(u64) end @@ -816,6 +819,40 @@ proc parse-block-stack-pop parse-block-stack @parse-block-stack-count sizeof(u64) * + @64 end +proc str-starts-with // prefix-count prefix-data input-count input-data + memory ssw-prefix sizeof(Str) end + memory ssw-input sizeof(Str) end + ssw-input !Str + ssw-prefix !Str + + ssw-prefix @Str.count + ssw-input @Str.count + <= if + 0 while + dup ssw-prefix @Str.count < if + dup ssw-input @Str.data + @8 + over ssw-prefix @Str.data + @8 + = + else false end + do 1 + end + ssw-prefix @Str.count >= + else false end +end + +proc remove-comment // output input + over 0 swap !Str.count + 2dup @Str.data swap !Str.data + while + dup @Str.count 0 > if + dup comment @Str rot @Str str-starts-with lnot + else false end + do + dup str-chop-one-left + over Str.count inc64 + end + 2drop +end + proc parse_file_path_cstr_into_ops 0 // mode O_RDONLY // flags @@ -853,7 +890,8 @@ proc parse_file_path_cstr_into_ops 1 line_number !64 while content @Str.count 0 > do - '\n' line content str-chop-by-delim + '\n' line-with-comment content str-chop-by-delim + line line-with-comment remove-comment line @Str.data line_start !64 while line @Str.count 0 > do line str-trim-left diff --git a/tests/intrinsics.porth b/tests/intrinsics.porth index 4b709fb7..a1f95503 100644 --- a/tests/intrinsics.porth +++ b/tests/intrinsics.porth @@ -6,6 +6,7 @@ 1 5 shl print 1 2 or 4 or print 5 2 and print +// TODO: not intrinsics is not properly tested 5 5 = print 5 6 = print 420 69 > print From ddcb7ca2e1a1708d02fbcc793f307737a9fb0aee Mon Sep 17 00:00:00 2001 From: rexim Date: Sat, 23 Oct 2021 06:25:46 +0700 Subject: [PATCH 107/145] Fix TODO spelling --- tests/intrinsics.porth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/intrinsics.porth b/tests/intrinsics.porth index a1f95503..43ec5752 100644 --- a/tests/intrinsics.porth +++ b/tests/intrinsics.porth @@ -6,7 +6,7 @@ 1 5 shl print 1 2 or 4 or print 5 2 and print -// TODO: not intrinsics is not properly tested +// TODO: `not` intrinsic is not properly tested 5 5 = print 5 6 = print 420 69 > print From 3453a77116d6397751cb0ad1aba89492c626f9a2 Mon Sep 17 00:00:00 2001 From: rexim Date: Sun, 24 Oct 2021 20:48:35 +0700 Subject: [PATCH 108/145] Report the location of the original proc definition --- porth.py | 18 ++++++++++++------ tests/original-proc-def-error.porth | 11 +++++++++++ tests/original-proc-def-error.txt | 10 ++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 tests/original-proc-def-error.porth create mode 100644 tests/original-proc-def-error.txt diff --git a/porth.py b/porth.py index c64a498c..5687a21b 100755 --- a/porth.py +++ b/porth.py @@ -573,6 +573,8 @@ class Context: # TODO: better error reporting on type checking errors of intrinsics # Reported expected and actual types with the location that introduced the actual type +# TODO: better error reporting on type checking errors of procs +# Show the call path and stuff (like for macros) def type_check_program(program: Program): visited_dos: Dict[CallPath, DataStack] = {} contexts: List[Context] = [Context(stack=[], ip=0, ret_stack=[])] @@ -1638,7 +1640,12 @@ class Memory: offset: MemAddr loc: Loc -def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: Dict[str, Macro], procs: Dict[str, OpAddr]): +@dataclass +class Proc: + addr: OpAddr + loc: Loc + +def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: Dict[str, Macro], procs: Dict[str, Proc]): assert token.typ == TokenType.WORD assert isinstance(token.value, str) name: str = token.value @@ -1655,8 +1662,7 @@ def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: D exit(1) if name in procs: compiler_error_with_expansion_stack(token, "redefinition of a proc `%s`" % (name, )) - # TODO: report the location of the original proc definition - # compiler_note(procs[name].loc, "the original definition is located here") + compiler_note(procs[name].loc, "the original definition is located here") exit(1) def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], expansion_limit: int) -> Program: @@ -1665,7 +1671,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp rtokens: List[Token] = list(reversed(tokens)) macros: Dict[str, Macro] = {} memories: Dict[str, Memory] = {} - procs: Dict[str, OpAddr] = {} + procs: Dict[str, Proc] = {} current_proc: Optional[OpAddr] = None ip: OpAddr = 0; while len(rtokens) > 0: @@ -1685,7 +1691,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops.append(Op(typ=OpType.PUSH_MEM, token=token, operand=memories[token.value].offset)) ip += 1 elif token.value in procs: - program.ops.append(Op(typ=OpType.CALL, token=token, operand=procs[token.value])) + program.ops.append(Op(typ=OpType.CALL, token=token, operand=procs[token.value].addr)) ip += 1 else: compiler_error_with_expansion_stack(token, "unknown word `%s`" % token.value) @@ -1921,7 +1927,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp assert isinstance(token.value, str), "This is probably a bug in the lexer" proc_name = token.value check_word_redefinition(token, memories, macros, procs) - procs[proc_name] = current_proc + 1 + procs[proc_name] = Proc(addr=current_proc + 1, loc=token.loc) else: compiler_error_with_expansion_stack(token, "defining procedures inside of procedures is not allowed") compiler_note(program.ops[current_proc].token.loc, "the current procedure starts here") diff --git a/tests/original-proc-def-error.porth b/tests/original-proc-def-error.porth new file mode 100644 index 00000000..00e24a11 --- /dev/null +++ b/tests/original-proc-def-error.porth @@ -0,0 +1,11 @@ +include "std.porth" + +proc hello + "Hello, World" puts +end + +proc hello + "Foo, Bar" +end + +hello diff --git a/tests/original-proc-def-error.txt b/tests/original-proc-def-error.txt new file mode 100644 index 00000000..a211d3ff --- /dev/null +++ b/tests/original-proc-def-error.txt @@ -0,0 +1,10 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 170 +./tests/original-proc-def-error.porth:7:6: ERROR: redefinition of a proc `hello` +./tests/original-proc-def-error.porth:3:6: NOTE: the original definition is located here + From 0d9c095e437af5c6c7db1b434369978795a58f32 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 00:25:10 +0700 Subject: [PATCH 109/145] Implement constants --- editor/porth-mode.el | 2 +- editor/porth.vim | 2 +- porth.py | 141 +++++++++++------- tests/.gitignore | 1 + tests/consts.porth | 7 + tests/consts.txt | 11 ++ tests/memory-definition-stack-underflow.txt | 4 +- .../memory-definition-unsupported-keyword.txt | 4 +- tests/memory-single-number.txt | 4 +- tests/memory-unsupported-token-type.txt | 4 +- tests/memory-unsupported-word.txt | 4 +- tests/recursive-const.porth | 1 + tests/recursive-const.txt | 9 ++ 13 files changed, 130 insertions(+), 64 deletions(-) create mode 100644 tests/consts.porth create mode 100644 tests/consts.txt create mode 100644 tests/recursive-const.porth create mode 100644 tests/recursive-const.txt diff --git a/editor/porth-mode.el b/editor/porth-mode.el index 3d29da22..15574b81 100644 --- a/editor/porth-mode.el +++ b/editor/porth-mode.el @@ -42,7 +42,7 @@ (eval-and-compile (defconst porth-keywords - '("if" "orelse" "else" "while" "do" "macro" "include" "memory" "proc" "end"))) + '("if" "orelse" "else" "while" "do" "macro" "include" "memory" "proc" "const" "end"))) (defconst porth-highlights `((,(regexp-opt porth-keywords 'symbols) . font-lock-keyword-face))) diff --git a/editor/porth.vim b/editor/porth.vim index d4a51127..2a2b8143 100644 --- a/editor/porth.vim +++ b/editor/porth.vim @@ -13,7 +13,7 @@ endif syntax keyword porthTodos TODO XXX FIXME NOTE " Language keywords -syntax keyword porthKeywords if orelse else while do macro include memory proc end +syntax keyword porthKeywords if orelse else while do macro include memory proc const end " Comments syntax region porthCommentLine start="//" end="$" contains=porthTodos diff --git a/porth.py b/porth.py index 5687a21b..6f1b3467 100755 --- a/porth.py +++ b/porth.py @@ -35,6 +35,7 @@ class Keyword(Enum): INCLUDE=auto() MEMORY=auto() PROC=auto() + CONST=auto() class DataType(IntEnum): INT=auto() @@ -1519,7 +1520,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("ret_stack_end:\n") out.write("mem: resb %d\n" % program.memory_capacity) -assert len(Keyword) == 10, "Exhaustive KEYWORD_NAMES definition." +assert len(Keyword) == 11, "Exhaustive KEYWORD_NAMES definition." KEYWORD_BY_NAMES: Dict[str, Keyword] = { 'if': Keyword.IF, 'orelse': Keyword.ORELSE, @@ -1531,6 +1532,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'memory': Keyword.MEMORY, 'proc': Keyword.PROC, 'end': Keyword.END, + 'const': Keyword.CONST, } KEYWORD_NAMES: Dict[Keyword, str] = {v: k for k, v in KEYWORD_BY_NAMES.items()} @@ -1645,7 +1647,12 @@ class Proc: addr: OpAddr loc: Loc -def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: Dict[str, Macro], procs: Dict[str, Proc]): +@dataclass +class Const: + value: int + loc: Loc + +def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: Dict[str, Macro], procs: Dict[str, Proc], consts: Dict[str, Const]): assert token.typ == TokenType.WORD assert isinstance(token.value, str) name: str = token.value @@ -1664,6 +1671,58 @@ def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: D compiler_error_with_expansion_stack(token, "redefinition of a proc `%s`" % (name, )) compiler_note(procs[name].loc, "the original definition is located here") exit(1) + if name in consts: + compiler_error_with_expansion_stack(token, "redefinition of a constant `%s`" % (name, )) + compiler_note(consts[name].loc, "the original definition is located here") + exit(1) + +def eval_const_value(rtokens: List[Token], macros: Dict[str, Macro], consts: Dict[str, Const]) -> int: + stack: List[int] = [] + while len(rtokens) > 0: + token = rtokens.pop() + if token.typ == TokenType.KEYWORD: + assert isinstance(token.value, Keyword) + if token.value == Keyword.END: + break + else: + compiler_error_with_expansion_stack(token, f"unsupported keyword `{KEYWORD_NAMES[token.value]}` in compile time evaluation") + exit(1) + elif token.typ == TokenType.INT: + assert isinstance(token.value, int) + stack.append(token.value) + elif token.typ == TokenType.WORD: + assert isinstance(token.value, str) + if token.value == INTRINSIC_NAMES[Intrinsic.PLUS]: + if len(stack) < 2: + compiler_error_with_expansion_stack(token, f"not enough arguments for `{token.value}` intrinsic") + exit(1) + a = stack.pop() + b = stack.pop() + stack.append(a + b) + elif token.value == INTRINSIC_NAMES[Intrinsic.MUL]: + if len(stack) < 2: + compiler_error_with_expansion_stack(token, f"not enough arguments for `{token.value}` intrinsic") + exit(1) + a = stack.pop() + b = stack.pop() + stack.append(a * b) + elif token.value in macros: + if token.expanded_count >= expansion_limit: + compiler_error_with_expansion_stack(token, "the macro exceeded the expansion limit (it expanded %d times)" % token.expanded_count) + exit(1) + rtokens += reversed(expand_macro(macros[token.value], token)) + elif token.value in consts: + stack.append(consts[token.value].value) + else: + compiler_error_with_expansion_stack(token, f"unsupported word `{token.value}` in compile time evaluation") + exit(1) + else: + compiler_error_with_expansion_stack(token, f"{human(token.typ, HumanNumber.Plural)} are not supported in compile time evaluation") + exit(1) + if len(stack) != 1: + compiler_error_with_expansion_stack(token, "The result of expression in compile time evaluation must be a single number") + exit(1) + return stack.pop() def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], expansion_limit: int) -> Program: stack: List[OpAddr] = [] @@ -1672,7 +1731,9 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp macros: Dict[str, Macro] = {} memories: Dict[str, Memory] = {} procs: Dict[str, Proc] = {} + consts: Dict[str, Const] = {} current_proc: Optional[OpAddr] = None + # TODO: consider getting rid of the ip variable in parse_program_from_tokens() ip: OpAddr = 0; while len(rtokens) > 0: token = rtokens.pop() @@ -1693,6 +1754,9 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp elif token.value in procs: program.ops.append(Op(typ=OpType.CALL, token=token, operand=procs[token.value].addr)) ip += 1 + elif token.value in consts: + program.ops.append(Op(typ=OpType.PUSH_INT, token=token, operand=consts[token.value].value)) + ip += 1 else: compiler_error_with_expansion_stack(token, "unknown word `%s`" % token.value) exit(1) @@ -1713,7 +1777,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops.append(Op(typ=OpType.PUSH_INT, operand=token.value, token=token)); ip += 1 elif token.typ == TokenType.KEYWORD: - assert len(Keyword) == 10, "Exhaustive keywords handling in parse_program_from_tokens()" + assert len(Keyword) == 11, "Exhaustive keywords handling in parse_program_from_tokens()" if token.value == Keyword.IF: program.ops.append(Op(typ=OpType.IF, token=token)) stack.append(ip) @@ -1821,6 +1885,20 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp if not file_included: compiler_error_with_expansion_stack(token, "file `%s` not found" % token.value) exit(1) + elif token.value == Keyword.CONST: + if len(rtokens) == 0: + compiler_error_with_expansion_stack(token, "expected const name but found nothing") + exit(1) + token = rtokens.pop() + if token.typ != TokenType.WORD: + compiler_error_with_expansion_stack(token, "expected const name to be %s but found %s" % (human(TokenType.WORD), human(token.typ))) + exit(1) + assert isinstance(token.value, str), "This is probably a bug in the lexer" + const_name = token.value + const_loc = token.loc + check_word_redefinition(token, memories, macros, procs, consts) + const_value = eval_const_value(rtokens, macros, consts) + consts[const_name] = Const(value=const_value, loc=const_loc) elif token.value == Keyword.MEMORY: if len(rtokens) == 0: compiler_error_with_expansion_stack(token, "expected memory name but found nothing") @@ -1832,51 +1910,8 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp assert isinstance(token.value, str), "This is probably a bug in the lexer" memory_name = token.value memory_loc = token.loc - check_word_redefinition(token, memories, macros, procs) - mem_size_stack: List[int] = [] - while len(rtokens) > 0: - token = rtokens.pop() - if token.typ == TokenType.KEYWORD: - assert isinstance(token.value, Keyword) - if token.value == Keyword.END: - break - else: - compiler_error_with_expansion_stack(token, f"unsupported keyword `{KEYWORD_NAMES[token.value]}` in memory definition") - exit(1) - elif token.typ == TokenType.INT: - assert isinstance(token.value, int) - mem_size_stack.append(token.value) - elif token.typ == TokenType.WORD: - assert isinstance(token.value, str) - if token.value == INTRINSIC_NAMES[Intrinsic.PLUS]: - if len(mem_size_stack) < 2: - compiler_error_with_expansion_stack(token, f"not enough arguments for `{INTRINSIC_NAMES[Intrinsic.PLUS]}` intrinsic in memory definition") - exit(1) - a = mem_size_stack.pop() - b = mem_size_stack.pop() - mem_size_stack.append(a + b) - elif token.value == INTRINSIC_NAMES[Intrinsic.MUL]: - if len(mem_size_stack) < 2: - compiler_error_with_expansion_stack(token, f"not enough arguments for `{INTRINSIC_NAMES[Intrinsic.PLUS]}` intrinsic in memory definition") - exit(1) - a = mem_size_stack.pop() - b = mem_size_stack.pop() - mem_size_stack.append(a * b) - elif token.value in macros: - if token.expanded_count >= expansion_limit: - compiler_error_with_expansion_stack(token, "the macro exceeded the expansion limit (it expanded %d times)" % token.expanded_count) - exit(1) - rtokens += reversed(expand_macro(macros[token.value], token)) - else: - compiler_error_with_expansion_stack(token, f"unsupported word in memory definition {token.value}") - exit(1) - else: - compiler_error_with_expansion_stack(token, f"{human(token.typ, HumanNumber.Plural)} are not supported in memory definition") - exit(1) - if len(mem_size_stack) != 1: - compiler_error_with_expansion_stack(token, "The result of expression in the memory definition must be a single number") - exit(1) - memory_size = mem_size_stack.pop() + check_word_redefinition(token, memories, macros, procs, consts) + memory_size = eval_const_value(rtokens, macros, consts) memories[memory_name] = Memory(offset=program.memory_capacity, loc=memory_loc) program.memory_capacity += memory_size elif token.value == Keyword.MACRO: @@ -1888,7 +1923,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp compiler_error_with_expansion_stack(token, "expected macro name to be %s but found %s" % (human(TokenType.WORD), human(token.typ))) exit(1) assert isinstance(token.value, str), "This is probably a bug in the lexer" - check_word_redefinition(token, memories, macros, procs) + check_word_redefinition(token, memories, macros, procs, consts) macro = Macro(token.loc, []) macros[token.value] = macro nesting_depth = 0 @@ -1899,8 +1934,8 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp else: macro.tokens.append(token) if token.typ == TokenType.KEYWORD: - assert len(Keyword) == 10, "Exhaustive handling of keywords in parsing macro body" - if token.value in [Keyword.IF, Keyword.WHILE, Keyword.MACRO, Keyword.MEMORY, Keyword.PROC]: + assert len(Keyword) == 11, "Exhaustive handling of keywords in parsing macro body" + if token.value in [Keyword.IF, Keyword.WHILE, Keyword.MACRO, Keyword.MEMORY, Keyword.PROC, Keyword.CONST]: nesting_depth += 1 elif token.value == Keyword.END: nesting_depth -= 1 @@ -1926,7 +1961,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp exit(1) assert isinstance(token.value, str), "This is probably a bug in the lexer" proc_name = token.value - check_word_redefinition(token, memories, macros, procs) + check_word_redefinition(token, memories, macros, procs, consts) procs[proc_name] = Proc(addr=current_proc + 1, loc=token.loc) else: compiler_error_with_expansion_stack(token, "defining procedures inside of procedures is not allowed") @@ -2022,6 +2057,8 @@ def lex_lines(file_path: str, lines: List[str]) -> Generator[Token, None, None]: if text_of_token in KEYWORD_BY_NAMES: yield Token(TokenType.KEYWORD, text_of_token, loc, KEYWORD_BY_NAMES[text_of_token]) else: + # TODO: `69//` is recognized as a single word + # And not a number plus a comment if text_of_token.startswith("//"): break yield Token(TokenType.WORD, text_of_token, loc, text_of_token) diff --git a/tests/.gitignore b/tests/.gitignore index 07251da8..9a10c834 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -17,3 +17,4 @@ empty while-procs intrinsics if-orelse +consts \ No newline at end of file diff --git a/tests/consts.porth b/tests/consts.porth new file mode 100644 index 00000000..be21d6b0 --- /dev/null +++ b/tests/consts.porth @@ -0,0 +1,7 @@ +const N 69 end +const M 420 end +const K N M + end + +N print +M print +K print diff --git a/tests/consts.txt b/tests/consts.txt new file mode 100644 index 00000000..8a2aa4cd --- /dev/null +++ b/tests/consts.txt @@ -0,0 +1,11 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 11 +69 +420 +489 + +:b stderr 0 + diff --git a/tests/memory-definition-stack-underflow.txt b/tests/memory-definition-stack-underflow.txt index 8dd9ab58..e0a6615f 100644 --- a/tests/memory-definition-stack-underflow.txt +++ b/tests/memory-definition-stack-underflow.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 121 -./tests/memory-definition-stack-underflow.porth:1:11: ERROR: not enough arguments for `+` intrinsic in memory definition +:b stderr 100 +./tests/memory-definition-stack-underflow.porth:1:11: ERROR: not enough arguments for `+` intrinsic diff --git a/tests/memory-definition-unsupported-keyword.txt b/tests/memory-definition-unsupported-keyword.txt index 79ac0269..c1c5de1c 100644 --- a/tests/memory-definition-unsupported-keyword.txt +++ b/tests/memory-definition-unsupported-keyword.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 110 -./tests/memory-definition-unsupported-keyword.porth:2:3: ERROR: unsupported keyword `if` in memory definition +:b stderr 116 +./tests/memory-definition-unsupported-keyword.porth:2:3: ERROR: unsupported keyword `if` in compile time evaluation diff --git a/tests/memory-single-number.txt b/tests/memory-single-number.txt index d185fa41..e4898796 100644 --- a/tests/memory-single-number.txt +++ b/tests/memory-single-number.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 122 -./tests/memory-single-number.porth:1:14: ERROR: The result of expression in the memory definition must be a single number +:b stderr 124 +./tests/memory-single-number.porth:1:14: ERROR: The result of expression in compile time evaluation must be a single number diff --git a/tests/memory-unsupported-token-type.txt b/tests/memory-unsupported-token-type.txt index 2a2f0f49..f1d1ee70 100644 --- a/tests/memory-unsupported-token-type.txt +++ b/tests/memory-unsupported-token-type.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 104 -./tests/memory-unsupported-token-type.porth:1:14: ERROR: strings are not supported in memory definition +:b stderr 110 +./tests/memory-unsupported-token-type.porth:1:14: ERROR: strings are not supported in compile time evaluation diff --git a/tests/memory-unsupported-word.txt b/tests/memory-unsupported-word.txt index b31b5fdc..7421412e 100644 --- a/tests/memory-unsupported-word.txt +++ b/tests/memory-unsupported-word.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 94 -./tests/memory-unsupported-word.porth:1:14: ERROR: unsupported word in memory definition word +:b stderr 102 +./tests/memory-unsupported-word.porth:1:14: ERROR: unsupported word `word` in compile time evaluation diff --git a/tests/recursive-const.porth b/tests/recursive-const.porth new file mode 100644 index 00000000..094218e6 --- /dev/null +++ b/tests/recursive-const.porth @@ -0,0 +1 @@ +const N N 1 + end diff --git a/tests/recursive-const.txt b/tests/recursive-const.txt new file mode 100644 index 00000000..d2d58863 --- /dev/null +++ b/tests/recursive-const.txt @@ -0,0 +1,9 @@ +:i argc 0 +:b stdin 0 + +:i returncode 1 +:b stdout 0 + +:b stderr 90 +./tests/recursive-const.porth:1:9: ERROR: unsupported word `N` in compile time evaluation + From 8cb7cda7e05a8bafc56d9487709c07acb663d567 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 00:26:58 +0700 Subject: [PATCH 110/145] Document the Use Case for Porth --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 19ff7cf2..59721213 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ Porth is planned to be (these are not the selling points, but rather milestones of the development) +## The Use Case for The Language + +Porth is a Computer [Programming Language](https://en.wikipedia.org/wiki/Programming_language). It's designed to write programs for [Computers](https://en.wikipedia.org/wiki/Computer). + ## Examples Hello, World: From 14c2dce26d3822880653091eb4763484ba594f00 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 03:21:15 +0700 Subject: [PATCH 111/145] Partial implementation of local memory --- porth.py | 124 ++++++++++++++++++++++++------------------------------- 1 file changed, 54 insertions(+), 70 deletions(-) diff --git a/porth.py b/porth.py index 6f1b3467..9a233d93 100755 --- a/porth.py +++ b/porth.py @@ -92,6 +92,7 @@ class OpType(Enum): PUSH_STR=auto() PUSH_CSTR=auto() PUSH_MEM=auto() + PUSH_LOCAL_MEM=auto() INTRINSIC=auto() IF=auto() ORELSE=auto() @@ -181,7 +182,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): ip = 0 while ip < len(program.ops): - assert len(OpType) == 15, "Exhaustive op handling in simulate_little_endian_linux" + assert len(OpType) == 16, "Exhaustive op handling in simulate_little_endian_linux" op = program.ops[ip] try: if op.typ == OpType.PUSH_INT: @@ -217,6 +218,8 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): assert isinstance(op.operand, MemAddr), "This could be a bug in the parsing step" stack.append(mem_buf_ptr + op.operand) ip += 1 + elif op.typ == OpType.PUSH_LOCAL_MEM: + assert False, "Not implemented" elif op.typ == OpType.IF: a = stack.pop() if a == 0: @@ -246,8 +249,10 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" ip = op.operand elif op.typ == OpType.PREP_PROC: + assert False, "TODO: not implemented yet" ip += 1 elif op.typ == OpType.RET: + assert False, "TODO: not implemented yet" ip = ret_stack.pop() elif op.typ == OpType.CALL: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" @@ -553,7 +558,7 @@ def compiler_note(loc: Loc, message: str): compiler_diagnostic(loc, 'NOTE', message) def not_enough_arguments(op: Op): - assert len(OpType) == 15, f"Exhaustive handling of Op types in not_enough_arguments() (expected {len(OpType)}). Keep in mind that not all of the ops should be handled in here. Only those that consume elements from the stack." + assert len(OpType) == 16, f"Exhaustive handling of Op types in not_enough_arguments() (expected {len(OpType)}). Keep in mind that not all of the ops should be handled in here. Only those that consume elements from the stack." if op.typ == OpType.INTRINSIC: assert isinstance(op.operand, Intrinsic) compiler_error_with_expansion_stack(op.token, "not enough arguments for the `%s` intrinsic" % INTRINSIC_NAMES[op.operand]) @@ -588,7 +593,7 @@ def type_check_program(program: Program): contexts.pop() continue op = program.ops[ctx.ip] - assert len(OpType) == 15, "Exhaustive ops handling in type_check_program()" + assert len(OpType) == 16, "Exhaustive ops handling in type_check_program()" if op.typ == OpType.PUSH_INT: ctx.stack.append((DataType.INT, op.token)) ctx.ip += 1 @@ -602,6 +607,9 @@ def type_check_program(program: Program): elif op.typ == OpType.PUSH_MEM: ctx.stack.append((DataType.PTR, op.token)) ctx.ip += 1 + elif op.typ == OpType.PUSH_LOCAL_MEM: + ctx.stack.append((DataType.PTR, op.token)) + ctx.ip += 1 elif op.typ == OpType.SKIP_PROC: assert isinstance(op.operand, OpAddr) ctx.ip = op.operand @@ -1159,18 +1167,17 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" mov [ret_stack_rsp], rax\n") for ip in range(len(program.ops)): op = program.ops[ip] - assert len(OpType) == 15, "Exhaustive ops handling in generate_nasm_linux_x86_64" + assert len(OpType) == 16, "Exhaustive ops handling in generate_nasm_linux_x86_64" out.write("addr_%d:\n" % ip) + out.write(" ;; -- %s --\n" % op) if op.typ == OpType.PUSH_INT: assert isinstance(op.operand, int), "This could be a bug in the parsing step" - out.write(" ;; -- push int %d --\n" % op.operand) out.write(" mov rax, %d\n" % op.operand) out.write(" push rax\n") elif op.typ == OpType.PUSH_STR: assert isinstance(op.operand, str), "This could be a bug in the parsing step" value = op.operand.encode('utf-8') n = len(value) - out.write(" ;; -- push str --\n") out.write(" mov rax, %d\n" % n) out.write(" push rax\n") out.write(" push str_%d\n" % len(strs)) @@ -1178,48 +1185,46 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): elif op.typ == OpType.PUSH_CSTR: assert isinstance(op.operand, str), "This could be a bug in the parsing step" value = op.operand.encode('utf-8') + b'\0' - out.write(" ;; -- push str --\n") out.write(" push str_%d\n" % len(strs)) strs.append(value) elif op.typ == OpType.PUSH_MEM: assert isinstance(op.operand, MemAddr), "This could be a bug in the parsing step" - out.write(" ;; -- push mem --\n") out.write(" mov rax, mem\n") out.write(" add rax, %d\n" % op.operand) out.write(" push rax\n") + elif op.typ == OpType.PUSH_LOCAL_MEM: + assert isinstance(op.operand, MemAddr) + out.write(" mov rax, [ret_stack_rsp]\n"); + out.write(" add rax, %d\n" % op.operand) + out.write(" push rax\n") elif op.typ == OpType.IF: - out.write(" ;; -- if --\n") out.write(" pop rax\n") out.write(" test rax, rax\n") assert isinstance(op.operand, OpAddr), f"This could be a bug in the parsing step {op.operand}" out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.WHILE: - out.write(" ;; -- while --\n") + pass elif op.typ == OpType.ELSE: - out.write(" ;; -- else --\n") assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" out.write(" jmp addr_%d\n" % op.operand) elif op.typ == OpType.ORELSE: - out.write(" ;; -- elif --\n") assert isinstance(op.operand, OpAddr), f"This could be a bug in the parsing step: {op.operand}" out.write(" jmp addr_%d\n" % op.operand) elif op.typ == OpType.END: assert isinstance(op.operand, int), "This could be a bug in the parsing step" - out.write(" ;; -- end --\n") if ip + 1 != op.operand: out.write(" jmp addr_%d\n" % op.operand) elif op.typ == OpType.DO: - out.write(" ;; -- do --\n") out.write(" pop rax\n") out.write(" test rax, rax\n") assert isinstance(op.operand, int), "This could be a bug in the parsing step" out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.SKIP_PROC: - out.write(" ;; -- skip proc --\n") assert isinstance(op.operand, OpAddr), f"This could be a bug in the parsing step: {op.operand}" out.write(" jmp addr_%d\n" % op.operand) elif op.typ == OpType.PREP_PROC: - out.write(" ;; -- prep proc -- \n") + assert isinstance(op.operand, int) + out.write(" sub rsp, %d\n" % op.operand) out.write(" mov [ret_stack_rsp], rsp\n") out.write(" mov rsp, rax\n") elif op.typ == OpType.CALL: @@ -1230,31 +1235,29 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" mov [ret_stack_rsp], rsp\n") out.write(" mov rsp, rax\n") elif op.typ == OpType.RET: + assert isinstance(op.operand, int) out.write(" mov rax, rsp\n") out.write(" mov rsp, [ret_stack_rsp]\n") + out.write(" add rsp, %d\n" % op.operand) out.write(" ret\n") elif op.typ == OpType.INTRINSIC: assert len(Intrinsic) == 42, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: - out.write(" ;; -- plus --\n") out.write(" pop rax\n") out.write(" pop rbx\n") out.write(" add rax, rbx\n") out.write(" push rax\n") elif op.operand == Intrinsic.MINUS: - out.write(" ;; -- minus --\n") out.write(" pop rax\n") out.write(" pop rbx\n") out.write(" sub rbx, rax\n") out.write(" push rbx\n") elif op.operand == Intrinsic.MUL: - out.write(" ;; -- mul --\n") out.write(" pop rax\n") out.write(" pop rbx\n") out.write(" mul rbx\n") out.write(" push rax\n") elif op.operand == Intrinsic.DIVMOD: - out.write(" ;; -- mod --\n") out.write(" xor rdx, rdx\n") out.write(" pop rbx\n") out.write(" pop rax\n") @@ -1262,40 +1265,33 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" push rax\n"); out.write(" push rdx\n"); elif op.operand == Intrinsic.SHR: - out.write(" ;; -- shr --\n") out.write(" pop rcx\n") out.write(" pop rbx\n") out.write(" shr rbx, cl\n") out.write(" push rbx\n") elif op.operand == Intrinsic.SHL: - out.write(" ;; -- shl --\n") out.write(" pop rcx\n") out.write(" pop rbx\n") out.write(" shl rbx, cl\n") out.write(" push rbx\n") elif op.operand == Intrinsic.OR: - out.write(" ;; -- bor --\n") out.write(" pop rax\n") out.write(" pop rbx\n") out.write(" or rbx, rax\n") out.write(" push rbx\n") elif op.operand == Intrinsic.AND: - out.write(" ;; -- band --\n") out.write(" pop rax\n") out.write(" pop rbx\n") out.write(" and rbx, rax\n") out.write(" push rbx\n") elif op.operand == Intrinsic.NOT: - out.write(" ;; -- not --\n") out.write(" pop rax\n") out.write(" not rax\n") out.write(" push rax\n") elif op.operand == Intrinsic.PRINT: - out.write(" ;; -- print --\n") out.write(" pop rdi\n") out.write(" call print\n") elif op.operand == Intrinsic.EQ: - out.write(" ;; -- equal --\n") out.write(" mov rcx, 0\n"); out.write(" mov rdx, 1\n"); out.write(" pop rax\n"); @@ -1304,7 +1300,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" cmove rcx, rdx\n"); out.write(" push rcx\n") elif op.operand == Intrinsic.GT: - out.write(" ;; -- gt --\n") out.write(" mov rcx, 0\n"); out.write(" mov rdx, 1\n"); out.write(" pop rbx\n"); @@ -1313,7 +1308,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" cmovg rcx, rdx\n"); out.write(" push rcx\n") elif op.operand == Intrinsic.LT: - out.write(" ;; -- gt --\n") out.write(" mov rcx, 0\n"); out.write(" mov rdx, 1\n"); out.write(" pop rbx\n"); @@ -1322,7 +1316,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" cmovl rcx, rdx\n"); out.write(" push rcx\n") elif op.operand == Intrinsic.GE: - out.write(" ;; -- gt --\n") out.write(" mov rcx, 0\n"); out.write(" mov rdx, 1\n"); out.write(" pop rbx\n"); @@ -1331,7 +1324,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" cmovge rcx, rdx\n"); out.write(" push rcx\n") elif op.operand == Intrinsic.LE: - out.write(" ;; -- gt --\n") out.write(" mov rcx, 0\n"); out.write(" mov rdx, 1\n"); out.write(" pop rbx\n"); @@ -1340,7 +1332,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" cmovle rcx, rdx\n"); out.write(" push rcx\n") elif op.operand == Intrinsic.NE: - out.write(" ;; -- ne --\n") out.write(" mov rcx, 0\n") out.write(" mov rdx, 1\n") out.write(" pop rbx\n") @@ -1349,28 +1340,23 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" cmovne rcx, rdx\n") out.write(" push rcx\n") elif op.operand == Intrinsic.DUP: - out.write(" ;; -- dup --\n") out.write(" pop rax\n") out.write(" push rax\n") out.write(" push rax\n") elif op.operand == Intrinsic.SWAP: - out.write(" ;; -- swap --\n") out.write(" pop rax\n") out.write(" pop rbx\n") out.write(" push rax\n") out.write(" push rbx\n") elif op.operand == Intrinsic.DROP: - out.write(" ;; -- drop --\n") out.write(" pop rax\n") elif op.operand == Intrinsic.OVER: - out.write(" ;; -- over --\n") out.write(" pop rax\n") out.write(" pop rbx\n") out.write(" push rbx\n") out.write(" push rax\n") out.write(" push rbx\n") elif op.operand == Intrinsic.ROT: - out.write(" ;; -- rot --\n") out.write(" pop rax\n") out.write(" pop rbx\n") out.write(" pop rcx\n") @@ -1378,93 +1364,74 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" push rax\n") out.write(" push rcx\n") elif op.operand == Intrinsic.LOAD8: - out.write(" ;; -- @8 --\n") out.write(" pop rax\n") out.write(" xor rbx, rbx\n") out.write(" mov bl, [rax]\n") out.write(" push rbx\n") elif op.operand == Intrinsic.STORE8: - out.write(" ;; -- !8 --\n") out.write(" pop rax\n"); out.write(" pop rbx\n"); out.write(" mov [rax], bl\n"); elif op.operand == Intrinsic.LOAD16: - out.write(" ;; -- @16 --\n") out.write(" pop rax\n") out.write(" xor rbx, rbx\n") out.write(" mov bx, [rax]\n") out.write(" push rbx\n") elif op.operand == Intrinsic.STORE16: - out.write(" ;; -- !16 --\n") out.write(" pop rax\n"); out.write(" pop rbx\n"); out.write(" mov [rax], bx\n"); elif op.operand == Intrinsic.LOAD32: - out.write(" ;; -- @32 --\n") out.write(" pop rax\n") out.write(" xor rbx, rbx\n") out.write(" mov ebx, [rax]\n") out.write(" push rbx\n") elif op.operand == Intrinsic.STORE32: - out.write(" ;; -- !32 --\n") out.write(" pop rax\n"); out.write(" pop rbx\n"); out.write(" mov [rax], ebx\n"); elif op.operand == Intrinsic.LOAD64: - out.write(" ;; -- @64 --\n") out.write(" pop rax\n") out.write(" xor rbx, rbx\n") out.write(" mov rbx, [rax]\n") out.write(" push rbx\n") elif op.operand == Intrinsic.STORE64: - out.write(" ;; -- !64 --\n") out.write(" pop rax\n"); out.write(" pop rbx\n"); out.write(" mov [rax], rbx\n"); elif op.operand == Intrinsic.ARGC: - out.write(" ;; -- argc --\n") out.write(" mov rax, [args_ptr]\n") out.write(" mov rax, [rax]\n") out.write(" push rax\n") elif op.operand == Intrinsic.ARGV: - out.write(" ;; -- argv --\n") out.write(" mov rax, [args_ptr]\n") out.write(" add rax, 8\n") out.write(" push rax\n") elif op.operand == Intrinsic.HERE: value = ("%s:%d:%d" % op.token.loc).encode('utf-8') n = len(value) - out.write(" ;; -- here --\n") out.write(" mov rax, %d\n" % n) out.write(" push rax\n") out.write(" push str_%d\n" % len(strs)) strs.append(value) - elif op.operand == Intrinsic.CAST_PTR: - out.write(" ;; -- cast(ptr) --\n") - elif op.operand == Intrinsic.CAST_INT: - out.write(" ;; -- cast(int) --\n") - elif op.operand == Intrinsic.CAST_BOOL: - out.write(" ;; -- cast(bool) --\n") + elif op.operand in [Intrinsic.CAST_PTR, Intrinsic.CAST_INT, Intrinsic.CAST_BOOL]: + pass elif op.operand == Intrinsic.SYSCALL0: - out.write(" ;; -- syscall0 --\n") out.write(" pop rax\n") out.write(" syscall\n") out.write(" push rax\n") elif op.operand == Intrinsic.SYSCALL1: - out.write(" ;; -- syscall1 --\n") out.write(" pop rax\n") out.write(" pop rdi\n") out.write(" syscall\n") out.write(" push rax\n") elif op.operand == Intrinsic.SYSCALL2: - out.write(" ;; -- syscall2 --\n") out.write(" pop rax\n"); out.write(" pop rdi\n"); out.write(" pop rsi\n"); out.write(" syscall\n"); out.write(" push rax\n") elif op.operand == Intrinsic.SYSCALL3: - out.write(" ;; -- syscall3 --\n") out.write(" pop rax\n") out.write(" pop rdi\n") out.write(" pop rsi\n") @@ -1472,7 +1439,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" syscall\n") out.write(" push rax\n") elif op.operand == Intrinsic.SYSCALL4: - out.write(" ;; -- syscall4 --\n") out.write(" pop rax\n") out.write(" pop rdi\n") out.write(" pop rsi\n") @@ -1481,7 +1447,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" syscall\n") out.write(" push rax\n") elif op.operand == Intrinsic.SYSCALL5: - out.write(" ;; -- syscall5 --\n") out.write(" pop rax\n") out.write(" pop rdi\n") out.write(" pop rsi\n") @@ -1491,7 +1456,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" syscall\n") out.write(" push rax\n") elif op.operand == Intrinsic.SYSCALL6: - out.write(" ;; -- syscall6 --\n") out.write(" pop rax\n") out.write(" pop rdi\n") out.write(" pop rsi\n") @@ -1646,6 +1610,8 @@ class Memory: class Proc: addr: OpAddr loc: Loc + local_memories: Dict[str, Memory] + local_memory_capacity: int @dataclass class Const: @@ -1732,7 +1698,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp memories: Dict[str, Memory] = {} procs: Dict[str, Proc] = {} consts: Dict[str, Const] = {} - current_proc: Optional[OpAddr] = None + current_proc: Optional[Proc] = None # TODO: consider getting rid of the ip variable in parse_program_from_tokens() ip: OpAddr = 0; while len(rtokens) > 0: @@ -1757,6 +1723,9 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp elif token.value in consts: program.ops.append(Op(typ=OpType.PUSH_INT, token=token, operand=consts[token.value].value)) ip += 1 + elif current_proc is not None and token.value in current_proc.local_memories: + program.ops.append(Op(typ=OpType.PUSH_LOCAL_MEM, token=token, operand=current_proc.local_memories[token.value].offset)) + ip += 1 else: compiler_error_with_expansion_stack(token, "unknown word `%s`" % token.value) exit(1) @@ -1829,8 +1798,11 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops[ip].operand = while_ip program.ops[block_ip].operand = ip + 1 - elif program.ops[block_ip].typ == OpType.SKIP_PROC: - program.ops.append(Op(typ=OpType.RET, token=token)) + elif program.ops[block_ip].typ == OpType.PREP_PROC: + program.ops[block_ip].operand = current_proc.local_memory_capacity + block_ip = stack.pop() + assert program.ops[block_ip].typ == OpType.SKIP_PROC + program.ops.append(Op(typ=OpType.RET, token=token, operand=current_proc.local_memory_capacity)) program.ops[block_ip].operand = ip + 1 current_proc = None elif program.ops[block_ip].typ == OpType.IF: @@ -1900,6 +1872,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp const_value = eval_const_value(rtokens, macros, consts) consts[const_name] = Const(value=const_value, loc=const_loc) elif token.value == Keyword.MEMORY: + if len(rtokens) == 0: compiler_error_with_expansion_stack(token, "expected memory name but found nothing") exit(1) @@ -1910,10 +1883,17 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp assert isinstance(token.value, str), "This is probably a bug in the lexer" memory_name = token.value memory_loc = token.loc - check_word_redefinition(token, memories, macros, procs, consts) memory_size = eval_const_value(rtokens, macros, consts) - memories[memory_name] = Memory(offset=program.memory_capacity, loc=memory_loc) - program.memory_capacity += memory_size + if current_proc is None: + check_word_redefinition(token, memories, macros, procs, consts) + memories[memory_name] = Memory(offset=program.memory_capacity, loc=memory_loc) + program.memory_capacity += memory_size + else: + # TODO: local memory regions can shadow the global ones + # Is that something we actually want? + check_word_redefinition(token, current_proc.local_memories, macros, procs, consts) + current_proc.local_memories[memory_name] = Memory(offset=current_proc.local_memory_capacity, loc=memory_loc) + current_proc.local_memory_capacity += memory_size elif token.value == Keyword.MACRO: if len(rtokens) == 0: compiler_error_with_expansion_stack(token, "expected macro name but found nothing") @@ -1945,11 +1925,13 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp elif token.value == Keyword.PROC: if current_proc is None: program.ops.append(Op(typ=OpType.SKIP_PROC, token=token)) - current_proc = ip + proc_addr = ip + stack.append(ip) ip += 1 program.ops.append(Op(typ=OpType.PREP_PROC, token=token)) + stack.append(ip) ip += 1 if len(rtokens) == 0: @@ -1960,12 +1942,14 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp compiler_error_with_expansion_stack(token, "expected procedure name to be %s but found %s" % (human(TokenType.WORD), human(token.typ))) exit(1) assert isinstance(token.value, str), "This is probably a bug in the lexer" + proc_loc = token.loc proc_name = token.value check_word_redefinition(token, memories, macros, procs, consts) - procs[proc_name] = Proc(addr=current_proc + 1, loc=token.loc) + procs[proc_name] = Proc(addr=proc_addr + 1, loc=token.loc, local_memories={}, local_memory_capacity=0) + current_proc = procs[proc_name] else: compiler_error_with_expansion_stack(token, "defining procedures inside of procedures is not allowed") - compiler_note(program.ops[current_proc].token.loc, "the current procedure starts here") + compiler_note(current_proc.loc, "the current procedure starts here") else: assert False, 'unreachable'; else: From 20f6084071b2dc955111267fe9108a6ab0503f24 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 05:00:13 +0700 Subject: [PATCH 112/145] Fix global memories shadowing the local ones --- porth.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/porth.py b/porth.py index 9a233d93..fb116130 100755 --- a/porth.py +++ b/porth.py @@ -1714,6 +1714,9 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp compiler_error_with_expansion_stack(token, "the macro exceeded the expansion limit (it expanded %d times)" % token.expanded_count) exit(1) rtokens += reversed(expand_macro(macros[token.value], token)) + elif current_proc is not None and token.value in current_proc.local_memories: + program.ops.append(Op(typ=OpType.PUSH_LOCAL_MEM, token=token, operand=current_proc.local_memories[token.value].offset)) + ip += 1 elif token.value in memories: program.ops.append(Op(typ=OpType.PUSH_MEM, token=token, operand=memories[token.value].offset)) ip += 1 @@ -1723,9 +1726,6 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp elif token.value in consts: program.ops.append(Op(typ=OpType.PUSH_INT, token=token, operand=consts[token.value].value)) ip += 1 - elif current_proc is not None and token.value in current_proc.local_memories: - program.ops.append(Op(typ=OpType.PUSH_LOCAL_MEM, token=token, operand=current_proc.local_memories[token.value].offset)) - ip += 1 else: compiler_error_with_expansion_stack(token, "unknown word `%s`" % token.value) exit(1) From a4efd1783f0878bf3194871931dc537b0afc3f1d Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 05:00:24 +0700 Subject: [PATCH 113/145] Cleanup debug comments in the generated assembly --- porth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/porth.py b/porth.py index fb116130..a8c53363 100755 --- a/porth.py +++ b/porth.py @@ -1169,7 +1169,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): op = program.ops[ip] assert len(OpType) == 16, "Exhaustive ops handling in generate_nasm_linux_x86_64" out.write("addr_%d:\n" % ip) - out.write(" ;; -- %s --\n" % op) + out.write(" ;; -- %s:%d:%d: %s (%s) --\n" % (op.token.loc + (repr(op.token.text), op.typ))) if op.typ == OpType.PUSH_INT: assert isinstance(op.operand, int), "This could be a bug in the parsing step" out.write(" mov rax, %d\n" % op.operand) From ab6dcff985b49a7883a86a8942a81f63d816387a Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 05:15:10 +0700 Subject: [PATCH 114/145] Implement local memory for simulation mode --- porth.py | 20 +++++++++++++++----- tests/.gitignore | 3 ++- tests/2swap.porth | 16 ++++++++++++++++ tests/2swap.txt | 17 +++++++++++++++++ 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 tests/2swap.porth create mode 100644 tests/2swap.txt diff --git a/porth.py b/porth.py index a8c53363..2548aa72 100755 --- a/porth.py +++ b/porth.py @@ -19,6 +19,7 @@ SIM_NULL_POINTER_PADDING = 1 # just a little bit of a padding at the beginning of the memory to make 0 an invalid address SIM_STR_CAPACITY = 640_000 SIM_ARGV_CAPACITY = 640_000 +SIM_LOCAL_MEMORY_CAPACITY = 640_000 debug=False @@ -151,8 +152,9 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): CLOCK_MONOTONIC=1 stack: List[int] = [] + # TODO: I think ret_stack should be located in the local memory just like on x86_64 ret_stack: List[OpAddr] = [] - mem = bytearray(SIM_NULL_POINTER_PADDING + SIM_STR_CAPACITY + SIM_ARGV_CAPACITY + program.memory_capacity) + mem = bytearray(SIM_NULL_POINTER_PADDING + SIM_STR_CAPACITY + SIM_ARGV_CAPACITY + SIM_LOCAL_MEMORY_CAPACITY + program.memory_capacity) str_buf_ptr = SIM_NULL_POINTER_PADDING str_ptrs: Dict[int, int] = {} @@ -161,7 +163,10 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): argv_buf_ptr = SIM_NULL_POINTER_PADDING + SIM_STR_CAPACITY argc = 0 - mem_buf_ptr = SIM_NULL_POINTER_PADDING + SIM_STR_CAPACITY + SIM_ARGV_CAPACITY + local_memory_ptr = SIM_NULL_POINTER_PADDING + SIM_STR_CAPACITY + SIM_ARGV_CAPACITY + local_memory_rsp = local_memory_ptr + SIM_LOCAL_MEMORY_CAPACITY + + mem_buf_ptr = SIM_NULL_POINTER_PADDING + SIM_STR_CAPACITY + SIM_ARGV_CAPACITY + SIM_LOCAL_MEMORY_CAPACITY fds: List[BinaryIO] = [sys.stdin.buffer, sys.stdout.buffer, sys.stderr.buffer] @@ -219,7 +224,9 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): stack.append(mem_buf_ptr + op.operand) ip += 1 elif op.typ == OpType.PUSH_LOCAL_MEM: - assert False, "Not implemented" + assert isinstance(op.operand, MemAddr) + stack.append(local_memory_rsp + op.operand) + ip += 1 elif op.typ == OpType.IF: a = stack.pop() if a == 0: @@ -249,10 +256,12 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" ip = op.operand elif op.typ == OpType.PREP_PROC: - assert False, "TODO: not implemented yet" + assert isinstance(op.operand, int) + local_memory_rsp -= op.operand ip += 1 elif op.typ == OpType.RET: - assert False, "TODO: not implemented yet" + assert isinstance(op.operand, int) + local_memory_rsp += op.operand ip = ret_stack.pop() elif op.typ == OpType.CALL: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" @@ -1799,6 +1808,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops[ip].operand = while_ip program.ops[block_ip].operand = ip + 1 elif program.ops[block_ip].typ == OpType.PREP_PROC: + assert current_proc is not None program.ops[block_ip].operand = current_proc.local_memory_capacity block_ip = stack.pop() assert program.ops[block_ip].typ == OpType.SKIP_PROC diff --git a/tests/.gitignore b/tests/.gitignore index 9a10c834..314a6db0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -17,4 +17,5 @@ empty while-procs intrinsics if-orelse -consts \ No newline at end of file +consts +2swap \ No newline at end of file diff --git a/tests/2swap.porth b/tests/2swap.porth new file mode 100644 index 00000000..4c4d259f --- /dev/null +++ b/tests/2swap.porth @@ -0,0 +1,16 @@ +include "std.porth" + +proc 2swap + memory a sizeof(u64) end + memory b sizeof(u64) end + memory c sizeof(u64) end + memory d sizeof(u64) end + d !64 c !64 b !64 a !64 + c @64 d @64 a @64 b @64 +end + +1 2 3 4 +print print print print +"------------------------------\n" puts +1 2 3 4 2swap +print print print print diff --git a/tests/2swap.txt b/tests/2swap.txt new file mode 100644 index 00000000..7c64e144 --- /dev/null +++ b/tests/2swap.txt @@ -0,0 +1,17 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 47 +4 +3 +2 +1 +------------------------------ +2 +1 +4 +3 + +:b stderr 0 + From 7d297c1fa530757140649e25238bc3a5b8c545a5 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 06:47:07 +0700 Subject: [PATCH 115/145] [porth.porth]delete try_to_parse_word_as_int_or_fail_as_unknown_word --- porth.porth | 165 ++++++++++++++++++-------------------- porth.py | 2 +- std/std.porth | 16 ++++ tests/.gitignore | 3 +- tests/try-parse-int.porth | 17 ++++ tests/try-parse-int.txt | 10 +++ 6 files changed, 123 insertions(+), 90 deletions(-) create mode 100644 tests/try-parse-int.porth create mode 100644 tests/try-parse-int.txt diff --git a/porth.porth b/porth.porth index f848d2a0..2c777b0b 100644 --- a/porth.porth +++ b/porth.porth @@ -2,67 +2,67 @@ include "std.porth" -macro MEM_CAPACITY 640000 end -macro SIM_STACK_CAP 1024 end - -macro OP_PUSH_INT 0 end -macro OP_IF 1 end -macro OP_END 2 end -macro OP_INTRINSIC 3 end -macro COUNT_OPS 4 end - -macro INTRINSIC_PLUS 0 end -macro INTRINSIC_MINUS 1 end -macro INTRINSIC_MUL 2 end -macro INTRINSIC_DIVMOD 3 end -macro INTRINSIC_EQ 4 end -macro INTRINSIC_GT 5 end -macro INTRINSIC_LT 6 end -macro INTRINSIC_GE 7 end -macro INTRINSIC_LE 8 end -macro INTRINSIC_NE 9 end -macro INTRINSIC_SHR 10 end -macro INTRINSIC_SHL 11 end -macro INTRINSIC_OR 12 end -macro INTRINSIC_AND 13 end -macro INTRINSIC_NOT 14 end -macro INTRINSIC_PRINT 15 end -macro INTRINSIC_DUP 16 end -macro INTRINSIC_SWAP 17 end -macro INTRINSIC_DROP 18 end -macro INTRINSIC_OVER 19 end -macro INTRINSIC_ROT 20 end -macro INTRINSIC_LOAD8 21 end -macro INTRINSIC_STORE8 22 end -macro INTRINSIC_LOAD16 23 end -macro INTRINSIC_STORE16 24 end -macro INTRINSIC_LOAD32 25 end -macro INTRINSIC_STORE32 26 end -macro INTRINSIC_LOAD64 27 end -macro INTRINSIC_STORE64 28 end -macro INTRINSIC_CAST_PTR 29 end -macro INTRINSIC_CAST_INT 30 end -macro INTRINSIC_CAST_BOOL 31 end -macro INTRINSIC_ARGC 32 end -macro INTRINSIC_ARGV 33 end -macro INTRINSIC_HERE 34 end -macro INTRINSIC_SYSCALL0 35 end -macro INTRINSIC_SYSCALL1 36 end -macro INTRINSIC_SYSCALL2 37 end -macro INTRINSIC_SYSCALL3 38 end -macro INTRINSIC_SYSCALL4 39 end -macro INTRINSIC_SYSCALL5 40 end -macro INTRINSIC_SYSCALL6 41 end -macro COUNT_INTRINSICS 42 end - -macro OPS_CAP 1024 end -macro sizeof(Op) 16 end -macro Op.type 0 + end -macro Op.operand 8 + end -macro @Op.type Op.type @64 end -macro !Op.type Op.type !64 end -macro @Op.operand Op.operand @64 end -macro !Op.operand Op.operand !64 end +const MEM_CAPACITY 640000 end +const SIM_STACK_CAP 1024 end + +const OP_PUSH_INT 0 end +const OP_IF 1 end +const OP_END 2 end +const OP_INTRINSIC 3 end +const COUNT_OPS 4 end + +const INTRINSIC_PLUS 0 end +const INTRINSIC_MINUS 1 end +const INTRINSIC_MUL 2 end +const INTRINSIC_DIVMOD 3 end +const INTRINSIC_EQ 4 end +const INTRINSIC_GT 5 end +const INTRINSIC_LT 6 end +const INTRINSIC_GE 7 end +const INTRINSIC_LE 8 end +const INTRINSIC_NE 9 end +const INTRINSIC_SHR 10 end +const INTRINSIC_SHL 11 end +const INTRINSIC_OR 12 end +const INTRINSIC_AND 13 end +const INTRINSIC_NOT 14 end +const INTRINSIC_PRINT 15 end +const INTRINSIC_DUP 16 end +const INTRINSIC_SWAP 17 end +const INTRINSIC_DROP 18 end +const INTRINSIC_OVER 19 end +const INTRINSIC_ROT 20 end +const INTRINSIC_LOAD8 21 end +const INTRINSIC_STORE8 22 end +const INTRINSIC_LOAD16 23 end +const INTRINSIC_STORE16 24 end +const INTRINSIC_LOAD32 25 end +const INTRINSIC_STORE32 26 end +const INTRINSIC_LOAD64 27 end +const INTRINSIC_STORE64 28 end +const INTRINSIC_CAST_PTR 29 end +const INTRINSIC_CAST_INT 30 end +const INTRINSIC_CAST_BOOL 31 end +const INTRINSIC_ARGC 32 end +const INTRINSIC_ARGV 33 end +const INTRINSIC_HERE 34 end +const INTRINSIC_SYSCALL0 35 end +const INTRINSIC_SYSCALL1 36 end +const INTRINSIC_SYSCALL2 37 end +const INTRINSIC_SYSCALL3 38 end +const INTRINSIC_SYSCALL4 39 end +const INTRINSIC_SYSCALL5 40 end +const INTRINSIC_SYSCALL6 41 end +const COUNT_INTRINSICS 42 end + +const OPS_CAP 1024 end +const sizeof(Op) 16 end +proc Op.type 0 + end +proc Op.operand 8 + end +proc @Op.type Op.type @64 end +proc !Op.type Op.type !64 end +proc @Op.operand Op.operand @64 end +proc !Op.operand Op.operand !64 end memory comment sizeof(Str) end "//" comment !Str @@ -78,12 +78,12 @@ memory line_start sizeof(ptr) end memory sim-stack-count sizeof(u64) end memory sim-stack sizeof(u64) SIM_STACK_CAP * end memory ops-count sizeof(u64) end -macro @ops-count ops-count @64 end +proc @ops-count ops-count @64 end memory ops sizeof(Op) OPS_CAP * end memory out_fd sizeof(u64) end -macro @out_fd out_fd @64 end -macro !out_fd out_fd !64 end +proc @out_fd out_fd @64 end +proc !out_fd out_fd !64 end memory empty_envp sizeof(ptr) end memory nasm_argv sizeof(ptr) 4 * end @@ -153,25 +153,6 @@ proc sim-stack-pop // -- u64 sim-stack sim-stack-count @64 8 * + @64 end -proc try_to_parse_word_as_int_or_fail_as_unknown_word // n1 s1 - ret - 0 0 while dup word @Str.count < do - dup word @Str.data + @8 - - dup isdigit lnot if - file_path_cstr @64 cast(ptr) cstr-to-str eputs - ":" puts line_number @64 putd - ":" puts word @Str.data cast(int) line_start @64 - 1 + putd - ": ERROR: `" eputs word @Str eputs "` is unknown word\n" eputs - 1 exit - end - - '0' - - rot 10 * + - swap - 1 + - end drop -end - proc push-op // type operand -- @ops-count OPS_CAP >= if here eputs ": ERROR: ops overflow\n" eputs 1 exit @@ -592,8 +573,8 @@ proc compile-ops // -- end memory sim-ip sizeof(u64) end -macro @sim-ip sim-ip @64 end -macro !sim-ip sim-ip !64 end +proc @sim-ip sim-ip @64 end +proc !sim-ip sim-ip !64 end memory sim-op sizeof(Op) end proc simulate-ops // -- @@ -798,9 +779,9 @@ proc simulate-ops // -- end end -macro PARSE_BLOCK_STACK_CAP 1024 end +const PARSE_BLOCK_STACK_CAP 1024 end memory parse-block-stack-count sizeof(u64) end -macro @parse-block-stack-count parse-block-stack-count @64 end +proc @parse-block-stack-count parse-block-stack-count @64 end memory parse-block-stack sizeof(u64) PARSE_BLOCK_STACK_CAP * end proc parse-block-stack-push @@ -1024,7 +1005,15 @@ proc parse_file_path_cstr_into_ops OP_END @ops-count 1 + push-op else - OP_PUSH_INT try_to_parse_word_as_int_or_fail_as_unknown_word push-op + OP_PUSH_INT + word @Str try-parse-int lnot if + file_path_cstr @64 cast(ptr) cstr-to-str eputs + ":" puts line_number @64 putd + ":" puts word @Str.data cast(int) line_start @64 - 1 + putd + ": ERROR: `" eputs word @Str eputs "` is unknown word\n" eputs + 1 exit + end + push-op end end diff --git a/porth.py b/porth.py index 2548aa72..b8fdd63a 100755 --- a/porth.py +++ b/porth.py @@ -15,7 +15,7 @@ PORTH_EXT = '.porth' DEFAULT_EXPANSION_LIMIT=1000 EXPANSION_DIAGNOSTIC_LIMIT=10 -X86_64_RET_STACK_CAP=4096 +X86_64_RET_STACK_CAP=8192 SIM_NULL_POINTER_PADDING = 1 # just a little bit of a padding at the beginning of the memory to make 0 an invalid address SIM_STR_CAPACITY = 640_000 SIM_ARGV_CAPACITY = 640_000 diff --git a/std/std.porth b/std/std.porth index 71e5a639..f49cfa25 100644 --- a/std/std.porth +++ b/std/std.porth @@ -545,6 +545,22 @@ proc isdigit and end +proc try-parse-int // int ptr -- int err + memory input sizeof(Str) end + input !Str + + 0 while + input @Str.count 0 > if + input @Str.data @8 isdigit + else false end + do + 10 * input @Str.data @8 '0' - + + input str-chop-one-left + end + + input @Str.count 0 <= +end + // Custom logical not, since the intrinsic `not` is the bitwise one and does not allow // to properly invert a boolean. macro lnot diff --git a/tests/.gitignore b/tests/.gitignore index 314a6db0..80656f5f 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -18,4 +18,5 @@ while-procs intrinsics if-orelse consts -2swap \ No newline at end of file +2swap +try-parse-int \ No newline at end of file diff --git a/tests/try-parse-int.porth b/tests/try-parse-int.porth new file mode 100644 index 00000000..846e37c7 --- /dev/null +++ b/tests/try-parse-int.porth @@ -0,0 +1,17 @@ +include "std.porth" + +memory a sizeof(Str) end +"1234" a !Str + +memory b sizeof(Str) end +"abcd" b !Str + +a @Str try-parse-int if print else + drop + a @Str eputs " is not a number\n" eputs +end + +b @Str try-parse-int if print else + drop + b @Str eputs " is not a number\n" eputs +end diff --git a/tests/try-parse-int.txt b/tests/try-parse-int.txt new file mode 100644 index 00000000..917e4cb4 --- /dev/null +++ b/tests/try-parse-int.txt @@ -0,0 +1,10 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 5 +1234 + +:b stderr 21 +abcd is not a number + From 149c9bee9689261f7884d5169840e1b554982849 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 07:01:46 +0700 Subject: [PATCH 116/145] [std.porth] use local memory for streq --- std/std.porth | 19 ++++++++++--------- tests/.gitignore | 3 ++- tests/streq.porth | 4 ++++ tests/streq.txt | 10 ++++++++++ 4 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 tests/streq.porth create mode 100644 tests/streq.txt diff --git a/std/std.porth b/std/std.porth index f49cfa25..e7913fe6 100644 --- a/std/std.porth +++ b/std/std.porth @@ -522,20 +522,21 @@ proc str-chop-by-delim // delim line input 2drop end -memory streq_a sizeof(Str) end -memory streq_b sizeof(Str) end proc streq // n1 s1 n2 s2 - streq_a !Str - streq_b !Str - streq_a @Str.count streq_b @Str.count = if + memory a sizeof(Str) end + a !Str + memory b sizeof(Str) end + b !Str + + a @Str.count b @Str.count = if 0 while - dup streq_a @Str.count < if - dup streq_a @Str.data + @8 - over streq_b @Str.data + @8 + dup a @Str.count < if + dup a @Str.data + @8 + over b @Str.data + @8 = else false end do 1 + end - streq_a @Str.count >= + a @Str.count >= else false end end diff --git a/tests/.gitignore b/tests/.gitignore index 80656f5f..b90a3c98 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -19,4 +19,5 @@ intrinsics if-orelse consts 2swap -try-parse-int \ No newline at end of file +try-parse-int +streq \ No newline at end of file diff --git a/tests/streq.porth b/tests/streq.porth new file mode 100644 index 00000000..664295e6 --- /dev/null +++ b/tests/streq.porth @@ -0,0 +1,4 @@ +include "std.porth" + +"foo" "foo" streq print +"foo" "bar" streq print diff --git a/tests/streq.txt b/tests/streq.txt new file mode 100644 index 00000000..54219f1f --- /dev/null +++ b/tests/streq.txt @@ -0,0 +1,10 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 4 +1 +0 + +:b stderr 0 + From e7ac452f02f99f4bb0bd04a0ba77726cd3a9caa6 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 07:30:46 +0700 Subject: [PATCH 117/145] [std.porth] use local memory in memcpy --- porth.py | 15 ++++++++++++++- std/std.porth | 22 +++++++++++----------- tests/.gitignore | 3 ++- tests/memcpy.porth | 26 ++++++++++++++++++++++++++ tests/memcpy.txt | 16 ++++++++++++++++ 5 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 tests/memcpy.porth create mode 100644 tests/memcpy.txt diff --git a/porth.py b/porth.py index b8fdd63a..59acd7eb 100755 --- a/porth.py +++ b/porth.py @@ -1180,7 +1180,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("addr_%d:\n" % ip) out.write(" ;; -- %s:%d:%d: %s (%s) --\n" % (op.token.loc + (repr(op.token.text), op.typ))) if op.typ == OpType.PUSH_INT: - assert isinstance(op.operand, int), "This could be a bug in the parsing step" + assert isinstance(op.operand, int), f"This could be a bug in the parsing step {op.operand}" out.write(" mov rax, %d\n" % op.operand) out.write(" push rax\n") elif op.typ == OpType.PUSH_STR: @@ -1681,6 +1681,19 @@ def eval_const_value(rtokens: List[Token], macros: Dict[str, Macro], consts: Dic a = stack.pop() b = stack.pop() stack.append(a * b) + elif token.value == INTRINSIC_NAMES[Intrinsic.DIVMOD]: + if len(stack) < 2: + compiler_error_with_expansion_stack(token, f"not enough arguments for `{token.value}` intrinsic") + exit(1) + a = stack.pop() + b = stack.pop() + stack.append(b//a) + stack.append(b%a) + elif token.value == INTRINSIC_NAMES[Intrinsic.DROP]: + if len(stack) < 1: + compiler_error_with_expansion_stack(token, f"not enough arguments for `{token.value}` intrinsic") + exit(1) + stack.pop() elif token.value in macros: if token.expanded_count >= expansion_limit: compiler_error_with_expansion_stack(token, "the macro exceeded the expansion limit (it expanded %d times)" % token.expanded_count) diff --git a/std/std.porth b/std/std.porth index e7913fe6..de6bb3c9 100644 --- a/std/std.porth +++ b/std/std.porth @@ -594,16 +594,16 @@ end macro putd stdout fputd end macro eputd stderr fputd end -memory memcpy-size sizeof(u64) end -memory memcpy-src sizeof(ptr) end -memory memcpy-dst sizeof(ptr) end -proc memcpy // size src dst - memcpy-dst !64 - memcpy-src !64 - memcpy-size !64 - 0 while dup memcpy-size @64 < do - dup memcpy-src @64 cast(ptr) + @8 - over memcpy-dst @64 cast(ptr) + !8 - 1 + +proc memcpy // size src dst -- + memory src sizeof(ptr) end + memory dst sizeof(ptr) end + dst !64 + src !64 + while dup 0 > do + src @64 cast(ptr) @8 + dst @64 cast(ptr) !8 + src inc64 + dst inc64 + 1 - end drop end diff --git a/tests/.gitignore b/tests/.gitignore index b90a3c98..66cd2dff 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -20,4 +20,5 @@ if-orelse consts 2swap try-parse-int -streq \ No newline at end of file +streq +memcpy \ No newline at end of file diff --git a/tests/memcpy.porth b/tests/memcpy.porth new file mode 100644 index 00000000..921b29a8 --- /dev/null +++ b/tests/memcpy.porth @@ -0,0 +1,26 @@ +include "std.porth" + +const N 32 end +const K 8 end +const M N K / end + +memory a N end +memory b M end + +0 while dup M < do + dup 'a' + + over b + + !8 + 1 + +end drop + +0 while dup K < do + dup M * a + + M b rot memcpy + 1 + +end drop + +0 while dup K < do + N a puts "\n" puts + 1 + +end drop diff --git a/tests/memcpy.txt b/tests/memcpy.txt new file mode 100644 index 00000000..54b3eee3 --- /dev/null +++ b/tests/memcpy.txt @@ -0,0 +1,16 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 264 +abcdabcdabcdabcdabcdabcdabcdabcd +abcdabcdabcdabcdabcdabcdabcdabcd +abcdabcdabcdabcdabcdabcdabcdabcd +abcdabcdabcdabcdabcdabcdabcdabcd +abcdabcdabcdabcdabcdabcdabcdabcd +abcdabcdabcdabcdabcdabcdabcdabcd +abcdabcdabcdabcdabcdabcdabcdabcd +abcdabcdabcdabcdabcdabcdabcdabcd + +:b stderr 0 + From babb63020f9fd4861b4ca2bbea3b68c2738d1478 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 07:40:06 +0700 Subject: [PATCH 118/145] [std.porth] use local memory in fputd --- std/std.porth | 15 ++++++++------- tests/.gitignore | 3 ++- tests/fputd.porth | 4 ++++ tests/fputd.txt | 10 ++++++++++ 4 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 tests/fputd.porth create mode 100644 tests/fputd.txt diff --git a/std/std.porth b/std/std.porth index de6bb3c9..11b6bc46 100644 --- a/std/std.porth +++ b/std/std.porth @@ -568,17 +568,18 @@ macro lnot cast(int) 1 - cast(bool) end -macro PUTD_BUFFER_CAP 32 end -memory putd-buffer PUTD_BUFFER_CAP end -memory putd-fd sizeof(u64) end +const PUTD_BUFFER_CAP 32 end // TODO: fputd should fail if write call fails +// TODO: fputd does not print negative numbers proc fputd // value fd -- - putd-fd !64 + memory buffer PUTD_BUFFER_CAP end + memory fd sizeof(u64) end + fd !64 dup 0 = if - "0" putd-fd @64 fputs + "0" fd @64 fputs else - putd-buffer PUTD_BUFFER_CAP + + buffer PUTD_BUFFER_CAP + while over 0 > do 1 - dup rot 10 divmod @@ -586,7 +587,7 @@ proc fputd // value fd -- end dup - putd-buffer PUTD_BUFFER_CAP + swap - swap putd-fd @64 fputs + buffer PUTD_BUFFER_CAP + swap - swap fd @64 fputs end drop end diff --git a/tests/.gitignore b/tests/.gitignore index 66cd2dff..829fd720 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -21,4 +21,5 @@ consts 2swap try-parse-int streq -memcpy \ No newline at end of file +memcpy +fputd \ No newline at end of file diff --git a/tests/fputd.porth b/tests/fputd.porth new file mode 100644 index 00000000..20a82fef --- /dev/null +++ b/tests/fputd.porth @@ -0,0 +1,4 @@ +include "std.porth" + +"stderr: " stderr fputs 69 stderr fputd "\n" stderr fputs +"stdout: " stdout fputs 420 stdout fputd "\n" stdout fputs diff --git a/tests/fputd.txt b/tests/fputd.txt new file mode 100644 index 00000000..bcf2d763 --- /dev/null +++ b/tests/fputd.txt @@ -0,0 +1,10 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 12 +stdout: 420 + +:b stderr 11 +stderr: 69 + From cde20ef845cca63fcc07f3df1000dfbcd8d15edb Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 08:20:01 +0700 Subject: [PATCH 119/145] [porth.porth] Move external command related stuff to local memory --- porth.porth | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/porth.porth b/porth.porth index 2c777b0b..16ebaa7b 100644 --- a/porth.porth +++ b/porth.porth @@ -85,24 +85,11 @@ memory out_fd sizeof(u64) end proc @out_fd out_fd @64 end proc !out_fd out_fd !64 end -memory empty_envp sizeof(ptr) end -memory nasm_argv sizeof(ptr) 4 * end -// TODO: search for external utilities in $PATH -"/usr/bin/nasm"c nasm_argv 0 8 * + !64 -"-felf64"c nasm_argv 1 8 * + !64 -"output.asm"c nasm_argv 2 8 * + !64 - -memory ld_argv sizeof(ptr) 5 * end -"/usr/bin/ld"c ld_argv 0 8 * + !64 -"-o"c ld_argv 1 8 * + !64 -"output"c ld_argv 2 8 * + !64 -"output.o"c ld_argv 3 8 * + !64 - -memory output_argv sizeof(ptr) 2 * end -"./output"c output_argv 0 8 * + !64 - -memory wstatus sizeof(u64) end -proc cmd_echoed // argv +proc cmd-echoed // argv + memory wstatus sizeof(u64) end + memory empty_envp sizeof(ptr) end + 0 empty_envp !64 + "[CMD]" puts dup while dup @64 0 != do " " puts @@ -567,9 +554,26 @@ proc compile-ops // -- @out_fd close drop - nasm_argv cmd_echoed - ld_argv cmd_echoed - output_argv cmd_echoed + memory nasm-argv sizeof(ptr) 4 * end + // TODO: search for external utilities in $PATH + "/usr/bin/nasm"c nasm-argv 0 8 * + !64 + "-felf64"c nasm-argv 1 8 * + !64 + "output.asm"c nasm-argv 2 8 * + !64 + NULL nasm-argv 3 8 * + !64 + nasm-argv cmd-echoed + + memory ld-argv sizeof(ptr) 5 * end + "/usr/bin/ld"c ld-argv 0 8 * + !64 + "-o"c ld-argv 1 8 * + !64 + "output"c ld-argv 2 8 * + !64 + "output.o"c ld-argv 3 8 * + !64 + NULL ld-argv 4 8 * + !64 + ld-argv cmd-echoed + + memory output-argv sizeof(ptr) 2 * end + "./output"c output-argv 0 8 * + !64 + NULL output-argv 1 8 * + !64 + output-argv cmd-echoed end memory sim-ip sizeof(u64) end From e05bb15ec39e5f22e71f1472ed75ac79d1c2f477 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 08:43:50 +0700 Subject: [PATCH 120/145] [porth.porth] move out-fd to local memory --- porth.porth | 558 ++++++++++++++++++++++++++-------------------------- 1 file changed, 278 insertions(+), 280 deletions(-) diff --git a/porth.porth b/porth.porth index 16ebaa7b..b8c47206 100644 --- a/porth.porth +++ b/porth.porth @@ -81,10 +81,6 @@ memory ops-count sizeof(u64) end proc @ops-count ops-count @64 end memory ops sizeof(Op) OPS_CAP * end -memory out_fd sizeof(u64) end -proc @out_fd out_fd @64 end -proc !out_fd out_fd !64 end - proc cmd-echoed // argv memory wstatus sizeof(u64) end memory empty_envp sizeof(ptr) end @@ -187,57 +183,59 @@ end proc compile-ops // -- "[INFO] Generating output.asm\n" puts + memory out-fd sizeof(u64) end + 420 // mode O_CREAT O_WRONLY or // flags // TODO: the output file path should be based on the input file path "output.asm"c // pathname AT_FDCWD openat - !out_fd + out-fd !64 - @out_fd 0 < if + out-fd @64 0 < if "[ERROR] could not open `output.asm`\n" eputs 1 exit end - "BITS 64\n" @out_fd fputs - "segment .text\n" @out_fd fputs - "print:\n" @out_fd fputs - " mov r9, -3689348814741910323\n" @out_fd fputs - " sub rsp, 40\n" @out_fd fputs - " mov BYTE [rsp+31], 10\n" @out_fd fputs - " lea rcx, [rsp+30]\n" @out_fd fputs - ".L2:\n" @out_fd fputs - " mov rax, rdi\n" @out_fd fputs - " lea r8, [rsp+32]\n" @out_fd fputs - " mul r9\n" @out_fd fputs - " mov rax, rdi\n" @out_fd fputs - " sub r8, rcx\n" @out_fd fputs - " shr rdx, 3\n" @out_fd fputs - " lea rsi, [rdx+rdx*4]\n" @out_fd fputs - " add rsi, rsi\n" @out_fd fputs - " sub rax, rsi\n" @out_fd fputs - " add eax, 48\n" @out_fd fputs - " mov BYTE [rcx], al\n" @out_fd fputs - " mov rax, rdi\n" @out_fd fputs - " mov rdi, rdx\n" @out_fd fputs - " mov rdx, rcx\n" @out_fd fputs - " sub rcx, 1\n" @out_fd fputs - " cmp rax, 9\n" @out_fd fputs - " ja .L2\n" @out_fd fputs - " lea rax, [rsp+32]\n" @out_fd fputs - " mov edi, 1\n" @out_fd fputs - " sub rdx, rax\n" @out_fd fputs - " xor eax, eax\n" @out_fd fputs - " lea rsi, [rsp+32+rdx]\n" @out_fd fputs - " mov rdx, r8\n" @out_fd fputs - " mov rax, 1\n" @out_fd fputs - " syscall\n" @out_fd fputs - " add rsp, 40\n" @out_fd fputs - " ret\n" @out_fd fputs - "global _start\n" @out_fd fputs - "_start:\n" @out_fd fputs - " mov [args_ptr], rsp\n" @out_fd fputs + "BITS 64\n" out-fd @64 fputs + "segment .text\n" out-fd @64 fputs + "print:\n" out-fd @64 fputs + " mov r9, -3689348814741910323\n" out-fd @64 fputs + " sub rsp, 40\n" out-fd @64 fputs + " mov BYTE [rsp+31], 10\n" out-fd @64 fputs + " lea rcx, [rsp+30]\n" out-fd @64 fputs + ".L2:\n" out-fd @64 fputs + " mov rax, rdi\n" out-fd @64 fputs + " lea r8, [rsp+32]\n" out-fd @64 fputs + " mul r9\n" out-fd @64 fputs + " mov rax, rdi\n" out-fd @64 fputs + " sub r8, rcx\n" out-fd @64 fputs + " shr rdx, 3\n" out-fd @64 fputs + " lea rsi, [rdx+rdx*4]\n" out-fd @64 fputs + " add rsi, rsi\n" out-fd @64 fputs + " sub rax, rsi\n" out-fd @64 fputs + " add eax, 48\n" out-fd @64 fputs + " mov BYTE [rcx], al\n" out-fd @64 fputs + " mov rax, rdi\n" out-fd @64 fputs + " mov rdi, rdx\n" out-fd @64 fputs + " mov rdx, rcx\n" out-fd @64 fputs + " sub rcx, 1\n" out-fd @64 fputs + " cmp rax, 9\n" out-fd @64 fputs + " ja .L2\n" out-fd @64 fputs + " lea rax, [rsp+32]\n" out-fd @64 fputs + " mov edi, 1\n" out-fd @64 fputs + " sub rdx, rax\n" out-fd @64 fputs + " xor eax, eax\n" out-fd @64 fputs + " lea rsi, [rsp+32+rdx]\n" out-fd @64 fputs + " mov rdx, r8\n" out-fd @64 fputs + " mov rax, 1\n" out-fd @64 fputs + " syscall\n" out-fd @64 fputs + " add rsp, 40\n" out-fd @64 fputs + " ret\n" out-fd @64 fputs + "global _start\n" out-fd @64 fputs + "_start:\n" out-fd @64 fputs + " mov [args_ptr], rsp\n" out-fd @64 fputs 0 while dup @ops-count < do dup sizeof(Op) * ops + @@ -248,22 +246,22 @@ proc compile-ops // -- 1 exit end - "addr_" @out_fd fputs - over @out_fd fputd - ":\n" @out_fd fputs + "addr_" out-fd @64 fputs + over out-fd @64 fputd + ":\n" out-fd @64 fputs dup @Op.type OP_PUSH_INT = if - " ;; -- push int " @out_fd fputs dup @Op.operand @out_fd fputd " --\n" @out_fd fputs - " mov rax, " @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- push int " out-fd @64 fputs dup @Op.operand out-fd @64 fputd " --\n" out-fd @64 fputs + " mov rax, " out-fd @64 fputs dup @Op.operand out-fd @64 fputd "\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.type OP_IF = if - " ;; -- if --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " test rax, rax\n" @out_fd fputs - " jz addr_" @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs + " ;; -- if --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " test rax, rax\n" out-fd @64 fputs + " jz addr_" out-fd @64 fputs dup @Op.operand out-fd @64 fputd "\n" out-fd @64 fputs orelse dup @Op.type OP_END = if - " ;; -- end --\n" @out_fd fputs - " jmp addr_" @out_fd fputs dup @Op.operand @out_fd fputd "\n" @out_fd fputs + " ;; -- end --\n" out-fd @64 fputs + " jmp addr_" out-fd @64 fputs dup @Op.operand out-fd @64 fputd "\n" out-fd @64 fputs orelse dup @Op.type OP_INTRINSIC = if COUNT_INTRINSICS 42 != if here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs @@ -271,265 +269,265 @@ proc compile-ops // -- end dup @Op.operand INTRINSIC_PLUS = if - " ;; -- plus --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " add rax, rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- plus --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " add rax, rbx\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_MINUS = if - " ;; -- minus --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " sub rbx, rax\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- minus --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " sub rbx, rax\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_MUL = if - " ;; -- mul --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " mul rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- mul --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " mul rbx\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_DIVMOD = if - " ;; -- mod --\n" @out_fd fputs - " xor rdx, rdx\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " div rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs - " push rdx\n" @out_fd fputs + " ;; -- mod --\n" out-fd @64 fputs + " xor rdx, rdx\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " div rbx\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs + " push rdx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SHR = if - " ;; -- shr --\n" @out_fd fputs - " pop rcx\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " shr rbx, cl\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- shr --\n" out-fd @64 fputs + " pop rcx\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " shr rbx, cl\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SHL = if - " ;; -- shl --\n" @out_fd fputs - " pop rcx\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " shl rbx, cl\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- shl --\n" out-fd @64 fputs + " pop rcx\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " shl rbx, cl\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_OR = if - " ;; -- bor --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " or rbx, rax\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- bor --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " or rbx, rax\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_AND = if - " ;; -- band --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " and rbx, rax\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- band --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " and rbx, rax\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_NOT = if - " ;; -- not --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " not rax\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- not --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " not rax\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_PRINT = if - " ;; -- print --\n" @out_fd fputs - " pop rdi\n" @out_fd fputs - " call print\n" @out_fd fputs + " ;; -- print --\n" out-fd @64 fputs + " pop rdi\n" out-fd @64 fputs + " call print\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_EQ = if - " ;; -- equal --\n" @out_fd fputs - " mov rcx, 0\n" @out_fd fputs - " mov rdx, 1\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " cmp rax, rbx\n" @out_fd fputs - " cmove rcx, rdx\n" @out_fd fputs - " push rcx\n" @out_fd fputs + " ;; -- equal --\n" out-fd @64 fputs + " mov rcx, 0\n" out-fd @64 fputs + " mov rdx, 1\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " cmp rax, rbx\n" out-fd @64 fputs + " cmove rcx, rdx\n" out-fd @64 fputs + " push rcx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_GT = if - " ;; -- gt --\n" @out_fd fputs - " mov rcx, 0\n" @out_fd fputs - " mov rdx, 1\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " cmp rax, rbx\n" @out_fd fputs - " cmovg rcx, rdx\n" @out_fd fputs - " push rcx\n" @out_fd fputs + " ;; -- gt --\n" out-fd @64 fputs + " mov rcx, 0\n" out-fd @64 fputs + " mov rdx, 1\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " cmp rax, rbx\n" out-fd @64 fputs + " cmovg rcx, rdx\n" out-fd @64 fputs + " push rcx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_LT = if - " ;; -- gt --\n" @out_fd fputs - " mov rcx, 0\n" @out_fd fputs - " mov rdx, 1\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " cmp rax, rbx\n" @out_fd fputs - " cmovl rcx, rdx\n" @out_fd fputs - " push rcx\n" @out_fd fputs + " ;; -- gt --\n" out-fd @64 fputs + " mov rcx, 0\n" out-fd @64 fputs + " mov rdx, 1\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " cmp rax, rbx\n" out-fd @64 fputs + " cmovl rcx, rdx\n" out-fd @64 fputs + " push rcx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_GE = if - " ;; -- gt --\n" @out_fd fputs - " mov rcx, 0\n" @out_fd fputs - " mov rdx, 1\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " cmp rax, rbx\n" @out_fd fputs - " cmovge rcx, rdx\n" @out_fd fputs - " push rcx\n" @out_fd fputs + " ;; -- gt --\n" out-fd @64 fputs + " mov rcx, 0\n" out-fd @64 fputs + " mov rdx, 1\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " cmp rax, rbx\n" out-fd @64 fputs + " cmovge rcx, rdx\n" out-fd @64 fputs + " push rcx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_LE = if - " ;; -- gt --\n" @out_fd fputs - " mov rcx, 0\n" @out_fd fputs - " mov rdx, 1\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " cmp rax, rbx\n" @out_fd fputs - " cmovle rcx, rdx\n" @out_fd fputs - " push rcx\n" @out_fd fputs + " ;; -- gt --\n" out-fd @64 fputs + " mov rcx, 0\n" out-fd @64 fputs + " mov rdx, 1\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " cmp rax, rbx\n" out-fd @64 fputs + " cmovle rcx, rdx\n" out-fd @64 fputs + " push rcx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_NE = if - " ;; -- ne --\n" @out_fd fputs - " mov rcx, 0\n" @out_fd fputs - " mov rdx, 1\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " cmp rax, rbx\n" @out_fd fputs - " cmovne rcx, rdx\n" @out_fd fputs - " push rcx\n" @out_fd fputs + " ;; -- ne --\n" out-fd @64 fputs + " mov rcx, 0\n" out-fd @64 fputs + " mov rdx, 1\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " cmp rax, rbx\n" out-fd @64 fputs + " cmovne rcx, rdx\n" out-fd @64 fputs + " push rcx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_DUP = if - " ;; -- dup --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " push rax\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- dup --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SWAP = if - " ;; -- swap --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- swap --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_DROP = if - " ;; -- drop --\n" @out_fd fputs - " pop rax\n" @out_fd fputs + " ;; -- drop --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_OVER = if - " ;; -- over --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " push rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- over --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_ROT = if - " ;; -- rot --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " pop rcx\n" @out_fd fputs - " push rbx\n" @out_fd fputs - " push rax\n" @out_fd fputs - " push rcx\n" @out_fd fputs + " ;; -- rot --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " pop rcx\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs + " push rcx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_LOAD8 = if - " ;; -- @8 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " xor rbx, rbx\n" @out_fd fputs - " mov bl, [rax]\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- @8 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " xor rbx, rbx\n" out-fd @64 fputs + " mov bl, [rax]\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_STORE8 = if - " ;; -- !8 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " mov [rax], bl\n" @out_fd fputs + " ;; -- !8 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " mov [rax], bl\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_LOAD16 = if - " ;; -- @16 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " xor rbx, rbx\n" @out_fd fputs - " mov bx, [rax]\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- @16 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " xor rbx, rbx\n" out-fd @64 fputs + " mov bx, [rax]\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_STORE16 = if - " ;; -- !16 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " mov [rax], bx\n" @out_fd fputs + " ;; -- !16 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " mov [rax], bx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_LOAD32 = if - " ;; -- @32 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " xor rbx, rbx\n" @out_fd fputs - " mov ebx, [rax]\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- @32 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " xor rbx, rbx\n" out-fd @64 fputs + " mov ebx, [rax]\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_STORE32 = if - " ;; -- !32 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " mov [rax], ebx\n" @out_fd fputs + " ;; -- !32 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " mov [rax], ebx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_LOAD64 = if - " ;; -- @64 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " xor rbx, rbx\n" @out_fd fputs - " mov rbx, [rax]\n" @out_fd fputs - " push rbx\n" @out_fd fputs + " ;; -- @64 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " xor rbx, rbx\n" out-fd @64 fputs + " mov rbx, [rax]\n" out-fd @64 fputs + " push rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_STORE64 = if - " ;; -- !64 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rbx\n" @out_fd fputs - " mov [rax], rbx\n" @out_fd fputs + " ;; -- !64 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rbx\n" out-fd @64 fputs + " mov [rax], rbx\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_ARGC = if - " ;; -- argc --\n" @out_fd fputs - " mov rax, [args_ptr]\n" @out_fd fputs - " mov rax, [rax]\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- argc --\n" out-fd @64 fputs + " mov rax, [args_ptr]\n" out-fd @64 fputs + " mov rax, [rax]\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_ARGV = if - " ;; -- argv --\n" @out_fd fputs - " mov rax, [args_ptr]\n" @out_fd fputs - " add rax, 8\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- argv --\n" out-fd @64 fputs + " mov rax, [args_ptr]\n" out-fd @64 fputs + " add rax, 8\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_HERE = if here eputs ": TODO: intrinsic `here` is not implemented yet" eputs orelse dup @Op.operand INTRINSIC_CAST_PTR = if - " ;; -- cast(ptr) --\n" @out_fd fputs + " ;; -- cast(ptr) --\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_CAST_INT = if - " ;; -- cast(int) --\n" @out_fd fputs + " ;; -- cast(int) --\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_CAST_BOOL = if - " ;; -- cast(bool) --\n" @out_fd fputs + " ;; -- cast(bool) --\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SYSCALL0 = if - " ;; -- syscall0 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " syscall\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- syscall0 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " syscall\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SYSCALL1 = if - " ;; -- syscall1 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rdi\n" @out_fd fputs - " syscall\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- syscall1 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rdi\n" out-fd @64 fputs + " syscall\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SYSCALL2 = if - " ;; -- syscall2 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rdi\n" @out_fd fputs - " pop rsi\n" @out_fd fputs - " syscall\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- syscall2 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rdi\n" out-fd @64 fputs + " pop rsi\n" out-fd @64 fputs + " syscall\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SYSCALL3 = if - " ;; -- syscall3 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rdi\n" @out_fd fputs - " pop rsi\n" @out_fd fputs - " pop rdx\n" @out_fd fputs - " syscall\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- syscall3 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rdi\n" out-fd @64 fputs + " pop rsi\n" out-fd @64 fputs + " pop rdx\n" out-fd @64 fputs + " syscall\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SYSCALL4 = if - " ;; -- syscall4 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rdi\n" @out_fd fputs - " pop rsi\n" @out_fd fputs - " pop rdx\n" @out_fd fputs - " pop r10\n" @out_fd fputs - " syscall\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- syscall4 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rdi\n" out-fd @64 fputs + " pop rsi\n" out-fd @64 fputs + " pop rdx\n" out-fd @64 fputs + " pop r10\n" out-fd @64 fputs + " syscall\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SYSCALL5 = if - " ;; -- syscall5 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rdi\n" @out_fd fputs - " pop rsi\n" @out_fd fputs - " pop rdx\n" @out_fd fputs - " pop r10\n" @out_fd fputs - " pop r8\n" @out_fd fputs - " syscall\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- syscall5 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rdi\n" out-fd @64 fputs + " pop rsi\n" out-fd @64 fputs + " pop rdx\n" out-fd @64 fputs + " pop r10\n" out-fd @64 fputs + " pop r8\n" out-fd @64 fputs + " syscall\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs orelse dup @Op.operand INTRINSIC_SYSCALL6 = if - " ;; -- syscall6 --\n" @out_fd fputs - " pop rax\n" @out_fd fputs - " pop rdi\n" @out_fd fputs - " pop rsi\n" @out_fd fputs - " pop rdx\n" @out_fd fputs - " pop r10\n" @out_fd fputs - " pop r8\n" @out_fd fputs - " pop r9\n" @out_fd fputs - " syscall\n" @out_fd fputs - " push rax\n" @out_fd fputs + " ;; -- syscall6 --\n" out-fd @64 fputs + " pop rax\n" out-fd @64 fputs + " pop rdi\n" out-fd @64 fputs + " pop rsi\n" out-fd @64 fputs + " pop rdx\n" out-fd @64 fputs + " pop r10\n" out-fd @64 fputs + " pop r8\n" out-fd @64 fputs + " pop r9\n" out-fd @64 fputs + " syscall\n" out-fd @64 fputs + " push rax\n" out-fd @64 fputs else here eputs ": unreachable.\n" eputs 1 exit @@ -545,14 +543,14 @@ proc compile-ops // -- end drop - " mov rax, 60\n" @out_fd fputs - " mov rdi, 0\n" @out_fd fputs - " syscall\n" @out_fd fputs - "segment .bss\n" @out_fd fputs - "args_ptr: resq 1\n" @out_fd fputs - "mem: resb " @out_fd fputs MEM_CAPACITY @out_fd fputd "\n" @out_fd fputs + " mov rax, 60\n" out-fd @64 fputs + " mov rdi, 0\n" out-fd @64 fputs + " syscall\n" out-fd @64 fputs + "segment .bss\n" out-fd @64 fputs + "args_ptr: resq 1\n" out-fd @64 fputs + "mem: resb " out-fd @64 fputs MEM_CAPACITY out-fd @64 fputd "\n" out-fd @64 fputs - @out_fd close drop + out-fd @64 close drop memory nasm-argv sizeof(ptr) 4 * end // TODO: search for external utilities in $PATH From 7384a5bf28f987caad08ceb53e2e17e75a9f3a1a Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 08:46:39 +0700 Subject: [PATCH 121/145] [porth.porth] move file-path-cstr to local memory --- porth.porth | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/porth.porth b/porth.porth index b8c47206..73c81b7c 100644 --- a/porth.porth +++ b/porth.porth @@ -66,7 +66,6 @@ proc !Op.operand Op.operand !64 end memory comment sizeof(Str) end "//" comment !Str -memory file_path_cstr sizeof(ptr) end memory fd sizeof(u64) end memory statbuf sizeof(stat) end memory content sizeof(Str) end @@ -836,22 +835,25 @@ proc remove-comment // output input 2drop end -proc parse_file_path_cstr_into_ops +proc parse_file_path_cstr_into_ops // file-path-cstr + memory file-path-cstr sizeof(ptr) end + file-path-cstr !64 + 0 // mode O_RDONLY // flags - file_path_cstr @64 cast(ptr) // pathname + file-path-cstr @64 cast(ptr) // pathname AT_FDCWD // dirfd openat dup 0 < if - "ERROR: could not open file " eputs file_path_cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs + "ERROR: could not open file " eputs file-path-cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs 1 exit end fd !64 statbuf fd @64 fstat 0 < if - "ERROR: could not determine the size of file " eputs file_path_cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs + "ERROR: could not determine the size of file " eputs file-path-cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs 1 exit end @@ -867,7 +869,7 @@ proc parse_file_path_cstr_into_ops content !Str.data content @Str.data cast(int) 0 < if - "ERROR: could not memory map file " eputs file_path_cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs + "ERROR: could not memory map file " eputs file-path-cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs 1 exit end @@ -982,7 +984,7 @@ proc parse_file_path_cstr_into_ops OP_IF 0 push-op orelse word @Str "end" streq if @parse-block-stack-count 0 <= if - file_path_cstr @64 cast(ptr) cstr-to-str eputs + file-path-cstr @64 cast(ptr) cstr-to-str eputs ":" puts line_number @64 putd ":" puts word @Str.data cast(int) line_start @64 - 1 + putd ": ERROR: `end` can only close `if` for now\n" eputs @@ -994,7 +996,7 @@ proc parse_file_path_cstr_into_ops ops over sizeof(Op) * + dup @Op.type OP_IF != if - file_path_cstr @64 cast(ptr) cstr-to-str eputs + file-path-cstr @64 cast(ptr) cstr-to-str eputs ":" puts line_number @64 putd ":" puts word @Str.data cast(int) line_start @64 - 1 + putd ": ERROR: `end` can only close `if` for now\n" eputs @@ -1009,7 +1011,7 @@ proc parse_file_path_cstr_into_ops else OP_PUSH_INT word @Str try-parse-int lnot if - file_path_cstr @64 cast(ptr) cstr-to-str eputs + file-path-cstr @64 cast(ptr) cstr-to-str eputs ":" puts line_number @64 putd ":" puts word @Str.data cast(int) line_start @64 - 1 + putd ": ERROR: `" eputs word @Str eputs "` is unknown word\n" eputs @@ -1050,9 +1052,7 @@ proc main // -- 1 exit end - 2 nth_argv file_path_cstr !64 - - parse_file_path_cstr_into_ops + 2 nth_argv parse_file_path_cstr_into_ops simulate-ops orelse dup "com"c cstreq if @@ -1062,9 +1062,7 @@ proc main // -- 1 exit end - 2 nth_argv file_path_cstr !64 - - parse_file_path_cstr_into_ops + 2 nth_argv parse_file_path_cstr_into_ops compile-ops orelse dup "help"c cstreq if @@ -1077,9 +1075,7 @@ proc main // -- 1 exit end - 2 nth_argv file_path_cstr !64 - - parse_file_path_cstr_into_ops + 2 nth_argv parse_file_path_cstr_into_ops dump-ops else From 21327474d0191e3e8da2e162c76b03c1e5bc91ef Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 08:52:09 +0700 Subject: [PATCH 122/145] [porth.porth] move parsing related vars to local memory --- porth.porth | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/porth.porth b/porth.porth index 73c81b7c..eb780f8b 100644 --- a/porth.porth +++ b/porth.porth @@ -64,16 +64,6 @@ proc !Op.type Op.type !64 end proc @Op.operand Op.operand @64 end proc !Op.operand Op.operand !64 end -memory comment sizeof(Str) end "//" comment !Str - -memory fd sizeof(u64) end -memory statbuf sizeof(stat) end -memory content sizeof(Str) end -memory line-with-comment sizeof(Str) end -memory line sizeof(Str) end -memory word sizeof(Str) end -memory line_number sizeof(u64) end -memory line_start sizeof(ptr) end memory sim-stack-count sizeof(u64) end memory sim-stack sizeof(u64) SIM_STACK_CAP * end memory ops-count sizeof(u64) end @@ -822,6 +812,8 @@ proc str-starts-with // prefix-count prefix-data input-count input-data end proc remove-comment // output input + memory comment sizeof(Str) end "//" comment !Str + over 0 swap !Str.count 2dup @Str.data swap !Str.data while @@ -850,13 +842,16 @@ proc parse_file_path_cstr_into_ops // file-path-cstr 1 exit end + memory fd sizeof(u64) end fd !64 + memory statbuf sizeof(stat) end statbuf fd @64 fstat 0 < if "ERROR: could not determine the size of file " eputs file-path-cstr @64 cast(ptr) cstr-to-str eputs "\n" eputs 1 exit end + memory content sizeof(Str) end statbuf @stat.st_size content !Str.count 0 // offset @@ -873,6 +868,12 @@ proc parse_file_path_cstr_into_ops // file-path-cstr 1 exit end + memory line_number sizeof(u64) end + memory line-with-comment sizeof(Str) end + memory line sizeof(Str) end + memory word sizeof(Str) end + memory line_start sizeof(ptr) end + 1 line_number !64 while content @Str.count 0 > do '\n' line-with-comment content str-chop-by-delim From c4482524f567a185bbb39af71a96acd8578e6320 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 10:40:32 +0700 Subject: [PATCH 123/145] offset/reset --- editor/porth-mode.el | 2 +- editor/porth.vim | 2 +- porth.porth | 128 ++++++++++++++++++++------------------- porth.py | 30 +++++++-- std/std.porth | 17 +++--- tests/.gitignore | 3 +- tests/offset-reset.porth | 9 +++ tests/offset-reset.txt | 11 ++++ 8 files changed, 123 insertions(+), 79 deletions(-) create mode 100644 tests/offset-reset.porth create mode 100644 tests/offset-reset.txt diff --git a/editor/porth-mode.el b/editor/porth-mode.el index 15574b81..db61bda3 100644 --- a/editor/porth-mode.el +++ b/editor/porth-mode.el @@ -42,7 +42,7 @@ (eval-and-compile (defconst porth-keywords - '("if" "orelse" "else" "while" "do" "macro" "include" "memory" "proc" "const" "end"))) + '("if" "orelse" "else" "while" "do" "macro" "include" "memory" "proc" "const" "end" "offset" "reset"))) (defconst porth-highlights `((,(regexp-opt porth-keywords 'symbols) . font-lock-keyword-face))) diff --git a/editor/porth.vim b/editor/porth.vim index 2a2b8143..cd005130 100644 --- a/editor/porth.vim +++ b/editor/porth.vim @@ -13,7 +13,7 @@ endif syntax keyword porthTodos TODO XXX FIXME NOTE " Language keywords -syntax keyword porthKeywords if orelse else while do macro include memory proc const end +syntax keyword porthKeywords if orelse else while do macro include memory proc const end offset reset " Comments syntax region porthCommentLine start="//" end="$" contains=porthTodos diff --git a/porth.porth b/porth.porth index eb780f8b..bd20e0d6 100644 --- a/porth.porth +++ b/porth.porth @@ -5,70 +5,72 @@ include "std.porth" const MEM_CAPACITY 640000 end const SIM_STACK_CAP 1024 end -const OP_PUSH_INT 0 end -const OP_IF 1 end -const OP_END 2 end -const OP_INTRINSIC 3 end -const COUNT_OPS 4 end - -const INTRINSIC_PLUS 0 end -const INTRINSIC_MINUS 1 end -const INTRINSIC_MUL 2 end -const INTRINSIC_DIVMOD 3 end -const INTRINSIC_EQ 4 end -const INTRINSIC_GT 5 end -const INTRINSIC_LT 6 end -const INTRINSIC_GE 7 end -const INTRINSIC_LE 8 end -const INTRINSIC_NE 9 end -const INTRINSIC_SHR 10 end -const INTRINSIC_SHL 11 end -const INTRINSIC_OR 12 end -const INTRINSIC_AND 13 end -const INTRINSIC_NOT 14 end -const INTRINSIC_PRINT 15 end -const INTRINSIC_DUP 16 end -const INTRINSIC_SWAP 17 end -const INTRINSIC_DROP 18 end -const INTRINSIC_OVER 19 end -const INTRINSIC_ROT 20 end -const INTRINSIC_LOAD8 21 end -const INTRINSIC_STORE8 22 end -const INTRINSIC_LOAD16 23 end -const INTRINSIC_STORE16 24 end -const INTRINSIC_LOAD32 25 end -const INTRINSIC_STORE32 26 end -const INTRINSIC_LOAD64 27 end -const INTRINSIC_STORE64 28 end -const INTRINSIC_CAST_PTR 29 end -const INTRINSIC_CAST_INT 30 end -const INTRINSIC_CAST_BOOL 31 end -const INTRINSIC_ARGC 32 end -const INTRINSIC_ARGV 33 end -const INTRINSIC_HERE 34 end -const INTRINSIC_SYSCALL0 35 end -const INTRINSIC_SYSCALL1 36 end -const INTRINSIC_SYSCALL2 37 end -const INTRINSIC_SYSCALL3 38 end -const INTRINSIC_SYSCALL4 39 end -const INTRINSIC_SYSCALL5 40 end -const INTRINSIC_SYSCALL6 41 end -const COUNT_INTRINSICS 42 end - -const OPS_CAP 1024 end -const sizeof(Op) 16 end -proc Op.type 0 + end -proc Op.operand 8 + end -proc @Op.type Op.type @64 end -proc !Op.type Op.type !64 end -proc @Op.operand Op.operand @64 end -proc !Op.operand Op.operand !64 end - -memory sim-stack-count sizeof(u64) end -memory sim-stack sizeof(u64) SIM_STACK_CAP * end -memory ops-count sizeof(u64) end +const OP_PUSH_INT 1 offset end +const OP_IF 1 offset end +const OP_END 1 offset end +const OP_INTRINSIC 1 offset end +const COUNT_OPS reset end + +const INTRINSIC_PLUS 1 offset end +const INTRINSIC_MINUS 1 offset end +const INTRINSIC_MUL 1 offset end +const INTRINSIC_DIVMOD 1 offset end +const INTRINSIC_EQ 1 offset end +const INTRINSIC_GT 1 offset end +const INTRINSIC_LT 1 offset end +const INTRINSIC_GE 1 offset end +const INTRINSIC_LE 1 offset end +const INTRINSIC_NE 1 offset end +const INTRINSIC_SHR 1 offset end +const INTRINSIC_SHL 1 offset end +const INTRINSIC_OR 1 offset end +const INTRINSIC_AND 1 offset end +const INTRINSIC_NOT 1 offset end +const INTRINSIC_PRINT 1 offset end +const INTRINSIC_DUP 1 offset end +const INTRINSIC_SWAP 1 offset end +const INTRINSIC_DROP 1 offset end +const INTRINSIC_OVER 1 offset end +const INTRINSIC_ROT 1 offset end +const INTRINSIC_LOAD8 1 offset end +const INTRINSIC_STORE8 1 offset end +const INTRINSIC_LOAD16 1 offset end +const INTRINSIC_STORE16 1 offset end +const INTRINSIC_LOAD32 1 offset end +const INTRINSIC_STORE32 1 offset end +const INTRINSIC_LOAD64 1 offset end +const INTRINSIC_STORE64 1 offset end +const INTRINSIC_CAST_PTR 1 offset end +const INTRINSIC_CAST_INT 1 offset end +const INTRINSIC_CAST_BOOL 1 offset end +const INTRINSIC_ARGC 1 offset end +const INTRINSIC_ARGV 1 offset end +const INTRINSIC_HERE 1 offset end +const INTRINSIC_SYSCALL0 1 offset end +const INTRINSIC_SYSCALL1 1 offset end +const INTRINSIC_SYSCALL2 1 offset end +const INTRINSIC_SYSCALL3 1 offset end +const INTRINSIC_SYSCALL4 1 offset end +const INTRINSIC_SYSCALL5 1 offset end +const INTRINSIC_SYSCALL6 1 offset end +const COUNT_INTRINSICS reset end + +const offsetof(Op.type) sizeof(u64) offset end +const offsetof(Op.operand) sizeof(u64) offset end +const sizeof(Op) reset end +proc Op.type offsetof(Op.type) + end +proc @Op.type Op.type @64 end +proc !Op.type Op.type !64 end +proc Op.operand offsetof(Op.operand) + end +proc @Op.operand Op.operand @64 end +proc !Op.operand Op.operand !64 end + +memory sim-stack-count sizeof(u64) end +memory sim-stack sizeof(u64) SIM_STACK_CAP * end +const OPS_CAP 1024 end +memory ops-count sizeof(u64) end proc @ops-count ops-count @64 end -memory ops sizeof(Op) OPS_CAP * end +memory ops sizeof(Op) OPS_CAP * end proc cmd-echoed // argv memory wstatus sizeof(u64) end diff --git a/porth.py b/porth.py index 59acd7eb..1898de1a 100755 --- a/porth.py +++ b/porth.py @@ -37,6 +37,8 @@ class Keyword(Enum): MEMORY=auto() PROC=auto() CONST=auto() + OFFSET=auto() + RESET=auto() class DataType(IntEnum): INT=auto() @@ -1493,7 +1495,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("ret_stack_end:\n") out.write("mem: resb %d\n" % program.memory_capacity) -assert len(Keyword) == 11, "Exhaustive KEYWORD_NAMES definition." +assert len(Keyword) == 13, "Exhaustive KEYWORD_NAMES definition." KEYWORD_BY_NAMES: Dict[str, Keyword] = { 'if': Keyword.IF, 'orelse': Keyword.ORELSE, @@ -1506,6 +1508,8 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): 'proc': Keyword.PROC, 'end': Keyword.END, 'const': Keyword.CONST, + 'offset': Keyword.OFFSET, + 'reset': Keyword.RESET, } KEYWORD_NAMES: Dict[Keyword, str] = {v: k for k, v in KEYWORD_BY_NAMES.items()} @@ -1651,7 +1655,7 @@ def check_word_redefinition(token: Token, memories: Dict[str, Memory], macros: D compiler_note(consts[name].loc, "the original definition is located here") exit(1) -def eval_const_value(rtokens: List[Token], macros: Dict[str, Macro], consts: Dict[str, Const]) -> int: +def eval_const_value(rtokens: List[Token], macros: Dict[str, Macro], consts: Dict[str, Const], iota: List[int]) -> int: stack: List[int] = [] while len(rtokens) > 0: token = rtokens.pop() @@ -1659,6 +1663,16 @@ def eval_const_value(rtokens: List[Token], macros: Dict[str, Macro], consts: Dic assert isinstance(token.value, Keyword) if token.value == Keyword.END: break + elif token.value == Keyword.OFFSET: + if len(stack) < 1: + compiler_error_with_expansion_stack(token, f"not enough arguments for `{KEYWORD_NAMES[token.value]}` keyword") + exit(1) + offset = stack.pop() + stack.append(iota[0]) + iota[0] += offset + elif token.value == Keyword.RESET: + stack.append(iota[0]) + iota[0] = 0 else: compiler_error_with_expansion_stack(token, f"unsupported keyword `{KEYWORD_NAMES[token.value]}` in compile time evaluation") exit(1) @@ -1721,6 +1735,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp procs: Dict[str, Proc] = {} consts: Dict[str, Const] = {} current_proc: Optional[Proc] = None + iota: List[int] = [0] # TODO: consider getting rid of the ip variable in parse_program_from_tokens() ip: OpAddr = 0; while len(rtokens) > 0: @@ -1768,7 +1783,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops.append(Op(typ=OpType.PUSH_INT, operand=token.value, token=token)); ip += 1 elif token.typ == TokenType.KEYWORD: - assert len(Keyword) == 11, "Exhaustive keywords handling in parse_program_from_tokens()" + assert len(Keyword) == 13, "Exhaustive keywords handling in parse_program_from_tokens()" if token.value == Keyword.IF: program.ops.append(Op(typ=OpType.IF, token=token)) stack.append(ip) @@ -1892,7 +1907,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp const_name = token.value const_loc = token.loc check_word_redefinition(token, memories, macros, procs, consts) - const_value = eval_const_value(rtokens, macros, consts) + const_value = eval_const_value(rtokens, macros, consts, iota) consts[const_name] = Const(value=const_value, loc=const_loc) elif token.value == Keyword.MEMORY: @@ -1906,7 +1921,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp assert isinstance(token.value, str), "This is probably a bug in the lexer" memory_name = token.value memory_loc = token.loc - memory_size = eval_const_value(rtokens, macros, consts) + memory_size = eval_const_value(rtokens, macros, consts, iota) if current_proc is None: check_word_redefinition(token, memories, macros, procs, consts) memories[memory_name] = Memory(offset=program.memory_capacity, loc=memory_loc) @@ -1937,7 +1952,7 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp else: macro.tokens.append(token) if token.typ == TokenType.KEYWORD: - assert len(Keyword) == 11, "Exhaustive handling of keywords in parsing macro body" + assert len(Keyword) == 13, "Exhaustive handling of keywords in parsing macro body" if token.value in [Keyword.IF, Keyword.WHILE, Keyword.MACRO, Keyword.MEMORY, Keyword.PROC, Keyword.CONST]: nesting_depth += 1 elif token.value == Keyword.END: @@ -1973,6 +1988,9 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp else: compiler_error_with_expansion_stack(token, "defining procedures inside of procedures is not allowed") compiler_note(current_proc.loc, "the current procedure starts here") + elif token.value in [Keyword.OFFSET, Keyword.RESET]: + compiler_error_with_expansion_stack(token, f"keyword `{token.text}` is supported only in compile time evaluation context") + exit(1) else: assert False, 'unreachable'; else: diff --git a/std/std.porth b/std/std.porth index 11b6bc46..1ee12349 100644 --- a/std/std.porth +++ b/std/std.porth @@ -467,13 +467,16 @@ macro eputs stderr fputs end -macro sizeof(Str) 16 end -macro Str.count 0 + end -macro Str.data 8 + end -macro @Str.count Str.count @64 end -macro @Str.data Str.data @64 cast(ptr) end -macro !Str.count Str.count !64 end -macro !Str.data Str.data !64 end +const offsetof(Str.count) sizeof(u64) offset end +const offsetof(Str.data) sizeof(ptr) offset end +const sizeof(Str) reset end + +proc Str.count offsetof(Str.count) + end +proc Str.data offsetof(Str.data) + end +proc @Str.count Str.count @64 end +proc @Str.data Str.data @64 cast(ptr) end +proc !Str.count Str.count !64 end +proc !Str.data Str.data !64 end macro @Str dup @Str.count diff --git a/tests/.gitignore b/tests/.gitignore index 829fd720..9b9c088d 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -22,4 +22,5 @@ consts try-parse-int streq memcpy -fputd \ No newline at end of file +fputd +offset-reset \ No newline at end of file diff --git a/tests/offset-reset.porth b/tests/offset-reset.porth new file mode 100644 index 00000000..63476e19 --- /dev/null +++ b/tests/offset-reset.porth @@ -0,0 +1,9 @@ +const sizeof(u64) 8 end + +const Op.type sizeof(u64) offset end +const Op.operand sizeof(u64) offset end +const sizeof(Op) reset end + +Op.type print +Op.operand print +sizeof(Op) print diff --git a/tests/offset-reset.txt b/tests/offset-reset.txt new file mode 100644 index 00000000..f785acd8 --- /dev/null +++ b/tests/offset-reset.txt @@ -0,0 +1,11 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 7 +0 +8 +16 + +:b stderr 0 + From eb86a6cef0bebd2d0f249198eea5fcc7d58afb32 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 11:01:36 +0700 Subject: [PATCH 124/145] [std.porth] start using consts in appropriate places --- std/std.porth | 704 +++++++++++++++++++++++++------------------------- 1 file changed, 352 insertions(+), 352 deletions(-) diff --git a/std/std.porth b/std/std.porth index 1ee12349..1122be08 100644 --- a/std/std.porth +++ b/std/std.porth @@ -8,353 +8,357 @@ macro , @ end macro cstr-to-pstr cstr-to-str end memory mem 640000 end -macro NULL 0 end +const NULL 0 end macro nop end macro true 1 cast(bool) end macro false 0 cast(bool) end +const sizeof(u64) 8 end +const sizeof(u32) 4 end +const sizeof(ptr) sizeof(u64) end + /// Standard streams -macro stdin 0 end -macro stdout 1 end -macro stderr 2 end +const stdin 0 end +const stdout 1 end +const stderr 2 end /// Syscalls // Stolen from https://filippo.io/linux-syscall-table/ // Not all of the syscalls here are useful/implemented. I literally just copy-pasted them. // We can clean this up later. -macro SYS_read 0 end -macro SYS_write 1 end -macro SYS_open 2 end -macro SYS_close 3 end -macro SYS_stat 4 end -macro SYS_fstat 5 end -macro SYS_lstat 6 end -macro SYS_poll 7 end -macro SYS_lseek 8 end -macro SYS_mmap 9 end -macro SYS_mprotect 10 end -macro SYS_munmap 11 end -macro SYS_brk 12 end -macro SYS_rt_sigaction 13 end -macro SYS_rt_sigprocmask 14 end -macro SYS_rt_sigreturn 15 end -macro SYS_ioctl 16 end -macro SYS_pread64 17 end -macro SYS_pwrite64 18 end -macro SYS_readv 19 end -macro SYS_writev 20 end -macro SYS_access 21 end -macro SYS_pipe 22 end -macro SYS_select 23 end -macro SYS_sched_yield 24 end -macro SYS_mremap 25 end -macro SYS_msync 26 end -macro SYS_mincore 27 end -macro SYS_madvise 28 end -macro SYS_shmget 29 end -macro SYS_shmat 30 end -macro SYS_shmctl 31 end -macro SYS_dup 32 end -macro SYS_dup2 33 end -macro SYS_pause 34 end -macro SYS_nanosleep 35 end -macro SYS_getitimer 36 end -macro SYS_alarm 37 end -macro SYS_setitimer 38 end -macro SYS_getpid 39 end -macro SYS_sendfile 40 end -macro SYS_socket 41 end -macro SYS_connect 42 end -macro SYS_accept 43 end -macro SYS_sendto 44 end -macro SYS_recvfrom 45 end -macro SYS_sendmsg 46 end -macro SYS_recvmsg 47 end -macro SYS_shutdown 48 end -macro SYS_bind 49 end -macro SYS_listen 50 end -macro SYS_getsockname 51 end -macro SYS_getpeername 52 end -macro SYS_socketpair 53 end -macro SYS_setsockopt 54 end -macro SYS_getsockopt 55 end -macro SYS_clone 56 end -macro SYS_fork 57 end -macro SYS_vfork 58 end -macro SYS_execve 59 end -macro SYS_exit 60 end -macro SYS_wait4 61 end -macro SYS_kill 62 end -macro SYS_uname 63 end -macro SYS_semget 64 end -macro SYS_semop 65 end -macro SYS_semctl 66 end -macro SYS_shmdt 67 end -macro SYS_msgget 68 end -macro SYS_msgsnd 69 end -macro SYS_msgrcv 70 end -macro SYS_msgctl 71 end -macro SYS_fcntl 72 end -macro SYS_flock 73 end -macro SYS_fsync 74 end -macro SYS_fdatasync 75 end -macro SYS_truncate 76 end -macro SYS_ftruncate 77 end -macro SYS_getdents 78 end -macro SYS_getcwd 79 end -macro SYS_chdir 80 end -macro SYS_fchdir 81 end -macro SYS_rename 82 end -macro SYS_mkdir 83 end -macro SYS_rmdir 84 end -macro SYS_creat 85 end -macro SYS_link 86 end -macro SYS_unlink 87 end -macro SYS_symlink 88 end -macro SYS_readlink 89 end -macro SYS_chmod 90 end -macro SYS_fchmod 91 end -macro SYS_chown 92 end -macro SYS_fchown 93 end -macro SYS_lchown 94 end -macro SYS_umask 95 end -macro SYS_gettimeofday 96 end -macro SYS_getrlimit 97 end -macro SYS_getrusage 98 end -macro SYS_sysinfo 99 end -macro SYS_times 100 end -macro SYS_ptrace 101 end -macro SYS_getuid 102 end -macro SYS_syslog 103 end -macro SYS_getgid 104 end -macro SYS_setuid 105 end -macro SYS_setgid 106 end -macro SYS_geteuid 107 end -macro SYS_getegid 108 end -macro SYS_setpgid 109 end -macro SYS_getppid 110 end -macro SYS_getpgrp 111 end -macro SYS_setsid 112 end -macro SYS_setreuid 113 end -macro SYS_setregid 114 end -macro SYS_getgroups 115 end -macro SYS_setgroups 116 end -macro SYS_setresuid 117 end -macro SYS_getresuid 118 end -macro SYS_setresgid 119 end -macro SYS_getresgid 120 end -macro SYS_getpgid 121 end -macro SYS_setfsuid 122 end -macro SYS_setfsgid 123 end -macro SYS_getsid 124 end -macro SYS_capget 125 end -macro SYS_capset 126 end -macro SYS_rt_sigpending 127 end -macro SYS_rt_sigtimedwait 128 end -macro SYS_rt_sigqueueinfo 129 end -macro SYS_rt_sigsuspend 130 end -macro SYS_sigaltstack 131 end -macro SYS_utime 132 end -macro SYS_mknod 133 end -macro SYS_uselib 134 end -macro SYS_personality 135 end -macro SYS_ustat 136 end -macro SYS_statfs 137 end -macro SYS_fstatfs 138 end -macro SYS_sysfs 139 end -macro SYS_getpriority 140 end -macro SYS_setpriority 141 end -macro SYS_sched_setparam 142 end -macro SYS_sched_getparam 143 end -macro SYS_sched_setscheduler 144 end -macro SYS_sched_getscheduler 145 end -macro SYS_sched_get_priority_max 146 end -macro SYS_sched_get_priority_min 147 end -macro SYS_sched_rr_get_interval 148 end -macro SYS_mlock 149 end -macro SYS_munlock 150 end -macro SYS_mlockall 151 end -macro SYS_munlockall 152 end -macro SYS_vhangup 153 end -macro SYS_modify_ldt 154 end -macro SYS_pivot_root 155 end -macro SYS__sysctl 156 end -macro SYS_prctl 157 end -macro SYS_arch_prctl 158 end -macro SYS_adjtimex 159 end -macro SYS_setrlimit 160 end -macro SYS_chroot 161 end -macro SYS_sync 162 end -macro SYS_acct 163 end -macro SYS_settimeofday 164 end -macro SYS_mount 165 end -macro SYS_umount2 166 end -macro SYS_swapon 167 end -macro SYS_swapoff 168 end -macro SYS_reboot 169 end -macro SYS_sethostname 170 end -macro SYS_setdomainname 171 end -macro SYS_iopl 172 end -macro SYS_ioperm 173 end -macro SYS_create_module 174 end -macro SYS_init_module 175 end -macro SYS_delete_module 176 end -macro SYS_get_kernel_syms 177 end -macro SYS_query_module 178 end -macro SYS_quotactl 179 end -macro SYS_nfsservctl 180 end -macro SYS_getpmsg 181 end -macro SYS_putpmsg 182 end -macro SYS_afs_syscall 183 end -macro SYS_tuxcall 184 end -macro SYS_security 185 end -macro SYS_gettid 186 end -macro SYS_readahead 187 end -macro SYS_setxattr 188 end -macro SYS_lsetxattr 189 end -macro SYS_fsetxattr 190 end -macro SYS_getxattr 191 end -macro SYS_lgetxattr 192 end -macro SYS_fgetxattr 193 end -macro SYS_listxattr 194 end -macro SYS_llistxattr 195 end -macro SYS_flistxattr 196 end -macro SYS_removexattr 197 end -macro SYS_lremovexattr 198 end -macro SYS_fremovexattr 199 end -macro SYS_tkill 200 end -macro SYS_time 201 end -macro SYS_futex 202 end -macro SYS_sched_setaffinity 203 end -macro SYS_sched_getaffinity 204 end -macro SYS_set_thread_area 205 end -macro SYS_io_setup 206 end -macro SYS_io_destroy 207 end -macro SYS_io_getevents 208 end -macro SYS_io_submit 209 end -macro SYS_io_cancel 210 end -macro SYS_get_thread_area 211 end -macro SYS_lookup_dcookie 212 end -macro SYS_epoll_create 213 end -macro SYS_epoll_ctl_old 214 end -macro SYS_epoll_wait_old 215 end -macro SYS_remap_file_pages 216 end -macro SYS_getdents64 217 end -macro SYS_set_tid_address 218 end -macro SYS_restart_syscall 219 end -macro SYS_semtimedop 220 end -macro SYS_fadvise64 221 end -macro SYS_timer_create 222 end -macro SYS_timer_settime 223 end -macro SYS_timer_gettime 224 end -macro SYS_timer_getoverrun 225 end -macro SYS_timer_delete 226 end -macro SYS_clock_settime 227 end -macro SYS_clock_gettime 228 end -macro SYS_clock_getres 229 end -macro SYS_clock_nanosleep 230 end -macro SYS_exit_group 231 end -macro SYS_epoll_wait 232 end -macro SYS_epoll_ctl 233 end -macro SYS_tgkill 234 end -macro SYS_utimes 235 end -macro SYS_vserver 236 end -macro SYS_mbind 237 end -macro SYS_set_mempolicy 238 end -macro SYS_get_mempolicy 239 end -macro SYS_mq_open 240 end -macro SYS_mq_unlink 241 end -macro SYS_mq_timedsend 242 end -macro SYS_mq_timedreceive 243 end -macro SYS_mq_notify 244 end -macro SYS_mq_getsetattr 245 end -macro SYS_kexec_load 246 end -macro SYS_waitid 247 end -macro SYS_add_key 248 end -macro SYS_request_key 249 end -macro SYS_keyctl 250 end -macro SYS_ioprio_set 251 end -macro SYS_ioprio_get 252 end -macro SYS_inotify_init 253 end -macro SYS_inotify_add_watch 254 end -macro SYS_inotify_rm_watch 255 end -macro SYS_migrate_pages 256 end -macro SYS_openat 257 end -macro SYS_mkdirat 258 end -macro SYS_mknodat 259 end -macro SYS_fchownat 260 end -macro SYS_futimesat 261 end -macro SYS_newfstatat 262 end -macro SYS_unlinkat 263 end -macro SYS_renameat 264 end -macro SYS_linkat 265 end -macro SYS_symlinkat 266 end -macro SYS_readlinkat 267 end -macro SYS_fchmodat 268 end -macro SYS_faccessat 269 end -macro SYS_pselect6 270 end -macro SYS_ppoll 271 end -macro SYS_unshare 272 end -macro SYS_set_robust_list 273 end -macro SYS_get_robust_list 274 end -macro SYS_splice 275 end -macro SYS_tee 276 end -macro SYS_sync_file_range 277 end -macro SYS_vmsplice 278 end -macro SYS_move_pages 279 end -macro SYS_utimensat 280 end -macro SYS_epoll_pwait 281 end -macro SYS_signalfd 282 end -macro SYS_timerfd_create 283 end -macro SYS_eventfd 284 end -macro SYS_fallocate 285 end -macro SYS_timerfd_settime 286 end -macro SYS_timerfd_gettime 287 end -macro SYS_accept4 288 end -macro SYS_signalfd4 289 end -macro SYS_eventfd2 290 end -macro SYS_epoll_create1 291 end -macro SYS_dup3 292 end -macro SYS_pipe2 293 end -macro SYS_inotify_init1 294 end -macro SYS_preadv 295 end -macro SYS_pwritev 296 end -macro SYS_rt_tgsigqueueinfo 297 end -macro SYS_perf_event_open 298 end -macro SYS_recvmmsg 299 end -macro SYS_fanotify_init 300 end -macro SYS_fanotify_mark 301 end -macro SYS_prlimit64 302 end -macro SYS_name_to_handle_at 303 end -macro SYS_open_by_handle_at 304 end -macro SYS_clock_adjtime 305 end -macro SYS_syncfs 306 end -macro SYS_sendmmsg 307 end -macro SYS_setns 308 end -macro SYS_getcpu 309 end -macro SYS_process_vm_readv 310 end -macro SYS_process_vm_writev 311 end -macro SYS_kcmp 312 end -macro SYS_finit_module 313 end - -macro AT_FDCWD -100 end - -macro O_RDONLY 0 end -macro O_WRONLY 1 end -macro O_RDWR 2 end -macro O_CREAT 64 end - -macro CLOCK_MONOTONIC 1 end -macro TIMER_ABSTIME 1 end - -macro MAP_PRIVATE 2 end -macro PROT_READ 1 end - -macro sizeof(timespec) 16 end - -macro sizeof(stat) 144 end +const SYS_read 0 end +const SYS_write 1 end +const SYS_open 2 end +const SYS_close 3 end +const SYS_stat 4 end +const SYS_fstat 5 end +const SYS_lstat 6 end +const SYS_poll 7 end +const SYS_lseek 8 end +const SYS_mmap 9 end +const SYS_mprotect 10 end +const SYS_munmap 11 end +const SYS_brk 12 end +const SYS_rt_sigaction 13 end +const SYS_rt_sigprocmask 14 end +const SYS_rt_sigreturn 15 end +const SYS_ioctl 16 end +const SYS_pread64 17 end +const SYS_pwrite64 18 end +const SYS_readv 19 end +const SYS_writev 20 end +const SYS_access 21 end +const SYS_pipe 22 end +const SYS_select 23 end +const SYS_sched_yield 24 end +const SYS_mremap 25 end +const SYS_msync 26 end +const SYS_mincore 27 end +const SYS_madvise 28 end +const SYS_shmget 29 end +const SYS_shmat 30 end +const SYS_shmctl 31 end +const SYS_dup 32 end +const SYS_dup2 33 end +const SYS_pause 34 end +const SYS_nanosleep 35 end +const SYS_getitimer 36 end +const SYS_alarm 37 end +const SYS_setitimer 38 end +const SYS_getpid 39 end +const SYS_sendfile 40 end +const SYS_socket 41 end +const SYS_connect 42 end +const SYS_accept 43 end +const SYS_sendto 44 end +const SYS_recvfrom 45 end +const SYS_sendmsg 46 end +const SYS_recvmsg 47 end +const SYS_shutdown 48 end +const SYS_bind 49 end +const SYS_listen 50 end +const SYS_getsockname 51 end +const SYS_getpeername 52 end +const SYS_socketpair 53 end +const SYS_setsockopt 54 end +const SYS_getsockopt 55 end +const SYS_clone 56 end +const SYS_fork 57 end +const SYS_vfork 58 end +const SYS_execve 59 end +const SYS_exit 60 end +const SYS_wait4 61 end +const SYS_kill 62 end +const SYS_uname 63 end +const SYS_semget 64 end +const SYS_semop 65 end +const SYS_semctl 66 end +const SYS_shmdt 67 end +const SYS_msgget 68 end +const SYS_msgsnd 69 end +const SYS_msgrcv 70 end +const SYS_msgctl 71 end +const SYS_fcntl 72 end +const SYS_flock 73 end +const SYS_fsync 74 end +const SYS_fdatasync 75 end +const SYS_truncate 76 end +const SYS_ftruncate 77 end +const SYS_getdents 78 end +const SYS_getcwd 79 end +const SYS_chdir 80 end +const SYS_fchdir 81 end +const SYS_rename 82 end +const SYS_mkdir 83 end +const SYS_rmdir 84 end +const SYS_creat 85 end +const SYS_link 86 end +const SYS_unlink 87 end +const SYS_symlink 88 end +const SYS_readlink 89 end +const SYS_chmod 90 end +const SYS_fchmod 91 end +const SYS_chown 92 end +const SYS_fchown 93 end +const SYS_lchown 94 end +const SYS_umask 95 end +const SYS_gettimeofday 96 end +const SYS_getrlimit 97 end +const SYS_getrusage 98 end +const SYS_sysinfo 99 end +const SYS_times 100 end +const SYS_ptrace 101 end +const SYS_getuid 102 end +const SYS_syslog 103 end +const SYS_getgid 104 end +const SYS_setuid 105 end +const SYS_setgid 106 end +const SYS_geteuid 107 end +const SYS_getegid 108 end +const SYS_setpgid 109 end +const SYS_getppid 110 end +const SYS_getpgrp 111 end +const SYS_setsid 112 end +const SYS_setreuid 113 end +const SYS_setregid 114 end +const SYS_getgroups 115 end +const SYS_setgroups 116 end +const SYS_setresuid 117 end +const SYS_getresuid 118 end +const SYS_setresgid 119 end +const SYS_getresgid 120 end +const SYS_getpgid 121 end +const SYS_setfsuid 122 end +const SYS_setfsgid 123 end +const SYS_getsid 124 end +const SYS_capget 125 end +const SYS_capset 126 end +const SYS_rt_sigpending 127 end +const SYS_rt_sigtimedwait 128 end +const SYS_rt_sigqueueinfo 129 end +const SYS_rt_sigsuspend 130 end +const SYS_sigaltstack 131 end +const SYS_utime 132 end +const SYS_mknod 133 end +const SYS_uselib 134 end +const SYS_personality 135 end +const SYS_ustat 136 end +const SYS_statfs 137 end +const SYS_fstatfs 138 end +const SYS_sysfs 139 end +const SYS_getpriority 140 end +const SYS_setpriority 141 end +const SYS_sched_setparam 142 end +const SYS_sched_getparam 143 end +const SYS_sched_setscheduler 144 end +const SYS_sched_getscheduler 145 end +const SYS_sched_get_priority_max 146 end +const SYS_sched_get_priority_min 147 end +const SYS_sched_rr_get_interval 148 end +const SYS_mlock 149 end +const SYS_munlock 150 end +const SYS_mlockall 151 end +const SYS_munlockall 152 end +const SYS_vhangup 153 end +const SYS_modify_ldt 154 end +const SYS_pivot_root 155 end +const SYS__sysctl 156 end +const SYS_prctl 157 end +const SYS_arch_prctl 158 end +const SYS_adjtimex 159 end +const SYS_setrlimit 160 end +const SYS_chroot 161 end +const SYS_sync 162 end +const SYS_acct 163 end +const SYS_settimeofday 164 end +const SYS_mount 165 end +const SYS_umount2 166 end +const SYS_swapon 167 end +const SYS_swapoff 168 end +const SYS_reboot 169 end +const SYS_sethostname 170 end +const SYS_setdomainname 171 end +const SYS_iopl 172 end +const SYS_ioperm 173 end +const SYS_create_module 174 end +const SYS_init_module 175 end +const SYS_delete_module 176 end +const SYS_get_kernel_syms 177 end +const SYS_query_module 178 end +const SYS_quotactl 179 end +const SYS_nfsservctl 180 end +const SYS_getpmsg 181 end +const SYS_putpmsg 182 end +const SYS_afs_syscall 183 end +const SYS_tuxcall 184 end +const SYS_security 185 end +const SYS_gettid 186 end +const SYS_readahead 187 end +const SYS_setxattr 188 end +const SYS_lsetxattr 189 end +const SYS_fsetxattr 190 end +const SYS_getxattr 191 end +const SYS_lgetxattr 192 end +const SYS_fgetxattr 193 end +const SYS_listxattr 194 end +const SYS_llistxattr 195 end +const SYS_flistxattr 196 end +const SYS_removexattr 197 end +const SYS_lremovexattr 198 end +const SYS_fremovexattr 199 end +const SYS_tkill 200 end +const SYS_time 201 end +const SYS_futex 202 end +const SYS_sched_setaffinity 203 end +const SYS_sched_getaffinity 204 end +const SYS_set_thread_area 205 end +const SYS_io_setup 206 end +const SYS_io_destroy 207 end +const SYS_io_getevents 208 end +const SYS_io_submit 209 end +const SYS_io_cancel 210 end +const SYS_get_thread_area 211 end +const SYS_lookup_dcookie 212 end +const SYS_epoll_create 213 end +const SYS_epoll_ctl_old 214 end +const SYS_epoll_wait_old 215 end +const SYS_remap_file_pages 216 end +const SYS_getdents64 217 end +const SYS_set_tid_address 218 end +const SYS_restart_syscall 219 end +const SYS_semtimedop 220 end +const SYS_fadvise64 221 end +const SYS_timer_create 222 end +const SYS_timer_settime 223 end +const SYS_timer_gettime 224 end +const SYS_timer_getoverrun 225 end +const SYS_timer_delete 226 end +const SYS_clock_settime 227 end +const SYS_clock_gettime 228 end +const SYS_clock_getres 229 end +const SYS_clock_nanosleep 230 end +const SYS_exit_group 231 end +const SYS_epoll_wait 232 end +const SYS_epoll_ctl 233 end +const SYS_tgkill 234 end +const SYS_utimes 235 end +const SYS_vserver 236 end +const SYS_mbind 237 end +const SYS_set_mempolicy 238 end +const SYS_get_mempolicy 239 end +const SYS_mq_open 240 end +const SYS_mq_unlink 241 end +const SYS_mq_timedsend 242 end +const SYS_mq_timedreceive 243 end +const SYS_mq_notify 244 end +const SYS_mq_getsetattr 245 end +const SYS_kexec_load 246 end +const SYS_waitid 247 end +const SYS_add_key 248 end +const SYS_request_key 249 end +const SYS_keyctl 250 end +const SYS_ioprio_set 251 end +const SYS_ioprio_get 252 end +const SYS_inotify_init 253 end +const SYS_inotify_add_watch 254 end +const SYS_inotify_rm_watch 255 end +const SYS_migrate_pages 256 end +const SYS_openat 257 end +const SYS_mkdirat 258 end +const SYS_mknodat 259 end +const SYS_fchownat 260 end +const SYS_futimesat 261 end +const SYS_newfstatat 262 end +const SYS_unlinkat 263 end +const SYS_renameat 264 end +const SYS_linkat 265 end +const SYS_symlinkat 266 end +const SYS_readlinkat 267 end +const SYS_fchmodat 268 end +const SYS_faccessat 269 end +const SYS_pselect6 270 end +const SYS_ppoll 271 end +const SYS_unshare 272 end +const SYS_set_robust_list 273 end +const SYS_get_robust_list 274 end +const SYS_splice 275 end +const SYS_tee 276 end +const SYS_sync_file_range 277 end +const SYS_vmsplice 278 end +const SYS_move_pages 279 end +const SYS_utimensat 280 end +const SYS_epoll_pwait 281 end +const SYS_signalfd 282 end +const SYS_timerfd_create 283 end +const SYS_eventfd 284 end +const SYS_fallocate 285 end +const SYS_timerfd_settime 286 end +const SYS_timerfd_gettime 287 end +const SYS_accept4 288 end +const SYS_signalfd4 289 end +const SYS_eventfd2 290 end +const SYS_epoll_create1 291 end +const SYS_dup3 292 end +const SYS_pipe2 293 end +const SYS_inotify_init1 294 end +const SYS_preadv 295 end +const SYS_pwritev 296 end +const SYS_rt_tgsigqueueinfo 297 end +const SYS_perf_event_open 298 end +const SYS_recvmmsg 299 end +const SYS_fanotify_init 300 end +const SYS_fanotify_mark 301 end +const SYS_prlimit64 302 end +const SYS_name_to_handle_at 303 end +const SYS_open_by_handle_at 304 end +const SYS_clock_adjtime 305 end +const SYS_syncfs 306 end +const SYS_sendmmsg 307 end +const SYS_setns 308 end +const SYS_getcpu 309 end +const SYS_process_vm_readv 310 end +const SYS_process_vm_writev 311 end +const SYS_kcmp 312 end +const SYS_finit_module 313 end + +const AT_FDCWD -100 end + +const O_RDONLY 0 end +const O_WRONLY 1 end +const O_RDWR 2 end +const O_CREAT 64 end + +const CLOCK_MONOTONIC 1 end +const TIMER_ABSTIME 1 end + +const MAP_PRIVATE 2 end +const PROT_READ 1 end + +const sizeof(timespec) 16 end + +const sizeof(stat) 144 end macro stat.st_dev 0 + end macro stat.st_ino 8 + end macro stat.st_mode 24 + end @@ -369,19 +373,19 @@ macro stat.st_blocks 64 + end macro stat.st_atim 72 + end macro stat.st_mtim 88 + end macro stat.st_ctim 104 + end -macro sizeof(stat.st_dev) sizeof(u64) end -macro sizeof(stat.st_ino) sizeof(u64) end -macro sizeof(stat.st_mode) sizeof(u32) end -macro sizeof(stat.st_nlink) sizeof(u64) end -macro sizeof(stat.st_uid) sizeof(u32) end -macro sizeof(stat.st_gid) sizeof(u32) end -macro sizeof(stat.st_rdev) sizeof(u64) end -macro sizeof(stat.st_size) sizeof(u64) end -macro sizeof(stat.st_blksize) sizeof(u64) end -macro sizeof(stat.st_blocks) sizeof(u64) end -macro sizeof(stat.st_atim) sizeof(timespec) end -macro sizeof(stat.st_mtim) sizeof(timespec) end -macro sizeof(stat.st_ctim) sizeof(timespec) end +const sizeof(stat.st_dev) sizeof(u64) end +const sizeof(stat.st_ino) sizeof(u64) end +const sizeof(stat.st_mode) sizeof(u32) end +const sizeof(stat.st_nlink) sizeof(u64) end +const sizeof(stat.st_uid) sizeof(u32) end +const sizeof(stat.st_gid) sizeof(u32) end +const sizeof(stat.st_rdev) sizeof(u64) end +const sizeof(stat.st_size) sizeof(u64) end +const sizeof(stat.st_blksize) sizeof(u64) end +const sizeof(stat.st_blocks) sizeof(u64) end +const sizeof(stat.st_atim) sizeof(timespec) end +const sizeof(stat.st_mtim) sizeof(timespec) end +const sizeof(stat.st_ctim) sizeof(timespec) end // Wrappers for common syscalls macro write SYS_write syscall3 end @@ -425,10 +429,6 @@ macro dec32 dup @32 1 - swap !32 end -macro sizeof(u64) 8 end -macro sizeof(u32) 4 end -macro sizeof(ptr) sizeof(u64) end - proc cstrlen dup while dup @8 0 != do 1 + end From 8e2d154181d6e1c8607b6a79a84611f47d0777a3 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 25 Oct 2021 20:29:14 +0700 Subject: [PATCH 125/145] [porth.porth] move more stuff to local memory --- porth.porth | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/porth.porth b/porth.porth index bd20e0d6..ccf17651 100644 --- a/porth.porth +++ b/porth.porth @@ -65,6 +65,8 @@ proc Op.operand offsetof(Op.operand) + end proc @Op.operand Op.operand @64 end proc !Op.operand Op.operand !64 end +// TODO: implement reusable stack data structure + memory sim-stack-count sizeof(u64) end memory sim-stack sizeof(u64) SIM_STACK_CAP * end const OPS_CAP 1024 end @@ -565,16 +567,15 @@ proc compile-ops // -- output-argv cmd-echoed end -memory sim-ip sizeof(u64) end -proc @sim-ip sim-ip @64 end -proc !sim-ip sim-ip !64 end -memory sim-op sizeof(Op) end proc simulate-ops // -- - 0 !sim-ip - while @sim-ip @ops-count < do + memory sim-ip sizeof(u64) end + memory sim-op sizeof(Op) end + + 0 sim-ip !64 + while sim-ip @64 @ops-count < do sizeof(Op) - @sim-ip sizeof(Op) * ops + + sim-ip @64 sizeof(Op) * ops + sim-op memcpy @@ -590,10 +591,10 @@ proc simulate-ops // -- sim-stack-pop cast(bool) if sim-ip inc64 else - sim-op @Op.operand !sim-ip + sim-op @Op.operand sim-ip !64 end orelse sim-op @Op.type OP_END = if - sim-op @Op.operand !sim-ip + sim-op @Op.operand sim-ip !64 orelse sim-op @Op.type OP_INTRINSIC = if COUNT_INTRINSICS 42 != if here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs From 11603fa7a7f652f6078ad482f8536f3ed8510aeb Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 00:23:16 +0700 Subject: [PATCH 126/145] We don't need memory layout convention anymore We've got memory blocks --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 59721213..295f7d81 100644 --- a/README.md +++ b/README.md @@ -319,9 +319,3 @@ TBD TBD - -#### Memory Layout - -TBD - - From 52bd8da392b15b18ac14e454f340f9db8c65dafe Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 00:24:00 +0700 Subject: [PATCH 127/145] We don't really have any array conventions --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 295f7d81..a45e45c5 100644 --- a/README.md +++ b/README.md @@ -313,9 +313,3 @@ TBD TBD - -#### Arrays - -TBD - - From 8a7b8697cb5a5a0fcfd8163fba4b55b2f281328b Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 00:48:39 +0700 Subject: [PATCH 128/145] Move str-chop-by-delim stuff to local memory --- std/std.porth | 7 +++---- tests/.gitignore | 3 ++- tests/str-chop-by-delim.porth | 16 ++++++++++++++++ tests/str-chop-by-delim.txt | 13 +++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 tests/str-chop-by-delim.porth create mode 100644 tests/str-chop-by-delim.txt diff --git a/std/std.porth b/std/std.porth index 1122be08..afd1adb9 100644 --- a/std/std.porth +++ b/std/std.porth @@ -505,20 +505,19 @@ proc str-trim-left // input -- drop end -memory str-chop-by-delim-tmp sizeof(u64) end proc str-chop-by-delim // delim line input - rot str-chop-by-delim-tmp !64 + memory delim sizeof(u64) end + rot delim !64 2dup @Str.data swap !Str.data over 0 swap !Str.count while dup @Str.count 0 > if - dup @Str.data @8 str-chop-by-delim-tmp @64 != + dup @Str.data @8 delim @64 != else false end do dup str-chop-one-left swap dup Str.count inc64 swap end - // TODO: sus dup @Str.count 0 > if dup str-chop-one-left end diff --git a/tests/.gitignore b/tests/.gitignore index 9b9c088d..ff38ff72 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -23,4 +23,5 @@ try-parse-int streq memcpy fputd -offset-reset \ No newline at end of file +offset-reset +str-chop-by-delim diff --git a/tests/str-chop-by-delim.porth b/tests/str-chop-by-delim.porth new file mode 100644 index 00000000..2d306cf9 --- /dev/null +++ b/tests/str-chop-by-delim.porth @@ -0,0 +1,16 @@ +include "std.porth" + +memory content sizeof(Str) end +memory line sizeof(Str) end +memory word sizeof(Str) end + +"hello world\nfoo bar\n\n\ntest\n" content !Str + +while content @Str.count 0 > do + '\n' line content str-chop-by-delim + while line @Str.count 0 > do + line str-trim-left + ' ' word line str-chop-by-delim + "|" puts word @Str puts "|\n" puts + end +end diff --git a/tests/str-chop-by-delim.txt b/tests/str-chop-by-delim.txt new file mode 100644 index 00000000..df2bf7f6 --- /dev/null +++ b/tests/str-chop-by-delim.txt @@ -0,0 +1,13 @@ +:i argc 0 +:b stdin 0 + +:i returncode 0 +:b stdout 35 +|hello| +|world| +|foo| +|bar| +|test| + +:b stderr 0 + From b9bb98dc1745c225a11221de19872974ad0ab670 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 01:22:01 +0700 Subject: [PATCH 129/145] Outline what needs to be documented --- README.md | 157 ++++++++++++++++++++++++++++++++++++++++++++++++------ porth.py | 2 + 2 files changed, 143 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a45e45c5..c7ee20bc 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Porth is a Computer [Programming Language](https://en.wikipedia.org/wiki/Program Hello, World: -```pascal +```porth include "std.porth" "Hello, World\n" puts @@ -31,7 +31,7 @@ include "std.porth" Simple program that prints numbers from 0 to 99 in an ascending order: -```pascal +```porth include "std.porth" 100 0 while 2dup > do @@ -125,7 +125,7 @@ Currently an integer is anything that is parsable by [int](https://docs.python.o Example: -```pascal +```porth 10 20 + ``` @@ -144,7 +144,7 @@ Those, a single string pushes two values onto the data stack: the size and the p Example: -``` +```porth include "std.porth" "Hello, World" puts ``` @@ -159,7 +159,7 @@ The size and the pointer are provided by the string `"Hello, World"`. It's like a regular string but it does not push its size on the stack and implicitly ends with [NULL-terminator](https://en.wikipedia.org/wiki/Null-terminated_string). Designed specifically to interact with C code or any other kind of code that expects NULL-terminated strings. -``` +```porth include "std.porth" 0 O_RDONLY "input.txt"c AT_FDCWD openat @@ -187,7 +187,7 @@ When compiler encounters a character it pushes its value as an integer onto the Example: -``` +```porth 'E' print ``` @@ -256,7 +256,8 @@ This program pushes integer `69` onto the stack (since the ASCII code of letter #### System - `syscall` - perform a syscall with n arguments where n is in range `[0..6]`. (`syscall1`, `syscall2`, etc) -``` + +```porth syscall_number = pop() for i in range(n): @@ -269,24 +270,54 @@ for i in range(n): - `here (-- [len: int] [str: ptr])` - pushes a string `"::"` where `` is the path to the file where `here` is located, `` is the row on which `here` is located and `` is the column from which `here` starts. It is useful for reporting developer errors: -```pascal +```porth include "std.porth" here puts ": TODO: not implemented\n" puts 1 exit ``` + - `argc (-- [argc: int])` - `argv (-- [argv: ptr])` +### std.porth + +TBD + + + ### Control Flow -- ` if else end` - pops the element on top of the stack and if the element is not `0` executes the ``, otherwise ``. -- `while do end` - keeps executing both `` and `` until `` produces `0` at the top of the stack. Checking the result of the `` removes it from the stack. +#### if-condition + + + +```porth + if + +orelse if + +orelse if + +else + +end +``` + +#### while-loop + + + +```porth +while do + +end +``` ### Macros Define a new word `write` that expands into a sequence of tokens `stdout SYS_write syscall3` during the compilation. -``` +```porth macro write stdout SYS_write syscall3 end @@ -296,20 +327,114 @@ end Include tokens of file `file.porth` -``` +```porth include "file.porth" ``` +### Procedures + + + +```porth +proc seq // n -- + while dup 0 > do + dup print + 1 - + end drop +end +``` + +### Constants + + + +```porth +const N 69 end +const M 420 end +const K M N / end +``` + +### Memory + + + +#### Global Memory + +```porth +include "std.porth" + +const N 26 end +memory buffer N end + +0 while dup N < do + dup 'a' + + over buffer + + !8 + + 1 + +end drop + +N buffer puts +``` + +#### Local Memory + +```porth +include "std.porth" + +proc fib // n -- + memory a sizeof(u64) end + memory b sizeof(u64) end + + dup 1 > if + dup 1 - fib a !64 + dup 2 - fib b !64 + a @64 b @64 + + end +end +``` + +### offset/reset + + + +#### Enums + +```porth +include "std.porth" + +const MON 1 offset end +const TUE 1 offset end +const WED 1 offset end +const THU 1 offset end +const FRI 1 offset end +const SAT 1 offset end +const SUN 1 offset end +const WEEK_DAYS reset end + +"There is " puts WEEK_DAYS putd " days in a week\n" puts +``` + +#### Structs + +```porth +include "std.porth" + +const offsetof(Str.count) sizeof(u64) offset end +const offsetof(Str.data) sizeof(ptr) offset end +const sizeof(Str) reset end +``` + ### Type Checking TBD -### Conventions +#### Types of Porth -#### Structures +- `int` - 64 bit integer +- `bool` - boolean +- `ptr` - pointer TBD - - diff --git a/porth.py b/porth.py index 1898de1a..a860aedf 100755 --- a/porth.py +++ b/porth.py @@ -1986,6 +1986,8 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp procs[proc_name] = Proc(addr=proc_addr + 1, loc=token.loc, local_memories={}, local_memory_capacity=0) current_proc = procs[proc_name] else: + # TODO: forbid constant definition inside of proc + # TODO: forbid macro definition inside of proc compiler_error_with_expansion_stack(token, "defining procedures inside of procedures is not allowed") compiler_note(current_proc.loc, "the current procedure starts here") elif token.value in [Keyword.OFFSET, Keyword.RESET]: From 36f3b060eee967ceb094da14a6d8002cb3fffca7 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 03:41:46 +0700 Subject: [PATCH 130/145] Implement if* --- README.md | 4 +- editor/porth-mode.el | 2 +- editor/porth.vim | 2 +- examples/fizz-buzz.porth | 4 +- porth.py | 102 +++++++++++++++++++++------------------ 5 files changed, 61 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index c7ee20bc..d37c3726 100644 --- a/README.md +++ b/README.md @@ -294,9 +294,9 @@ TBD ```porth if -orelse if +else if* -orelse if +else if* else diff --git a/editor/porth-mode.el b/editor/porth-mode.el index db61bda3..df586b8a 100644 --- a/editor/porth-mode.el +++ b/editor/porth-mode.el @@ -42,7 +42,7 @@ (eval-and-compile (defconst porth-keywords - '("if" "orelse" "else" "while" "do" "macro" "include" "memory" "proc" "const" "end" "offset" "reset"))) + '("if" "else" "while" "do" "macro" "include" "memory" "proc" "const" "end" "offset" "reset"))) (defconst porth-highlights `((,(regexp-opt porth-keywords 'symbols) . font-lock-keyword-face))) diff --git a/editor/porth.vim b/editor/porth.vim index cd005130..8336aa04 100644 --- a/editor/porth.vim +++ b/editor/porth.vim @@ -13,7 +13,7 @@ endif syntax keyword porthTodos TODO XXX FIXME NOTE " Language keywords -syntax keyword porthKeywords if orelse else while do macro include memory proc const end offset reset +syntax keyword porthKeywords if else while do macro include memory proc const end offset reset " Comments syntax region porthCommentLine start="//" end="$" contains=porthTodos diff --git a/examples/fizz-buzz.porth b/examples/fizz-buzz.porth index 281d9f6f..f205d1d2 100644 --- a/examples/fizz-buzz.porth +++ b/examples/fizz-buzz.porth @@ -3,9 +3,9 @@ include "std.porth" 1 while dup 100 < do dup 15 mod 0 = if "FizzBuzz\n" puts - orelse dup 3 mod 0 = if + else dup 3 mod 0 = if* "Fizz\n" puts - orelse dup 5 mod 0 = if + else dup 5 mod 0 = if* "Buzz\n" puts else dup print diff --git a/porth.py b/porth.py index a860aedf..5484446f 100755 --- a/porth.py +++ b/porth.py @@ -27,7 +27,7 @@ class Keyword(Enum): IF=auto() - ORELSE=auto() + IFSTAR=auto() ELSE=auto() END=auto() WHILE=auto() @@ -98,7 +98,7 @@ class OpType(Enum): PUSH_LOCAL_MEM=auto() INTRINSIC=auto() IF=auto() - ORELSE=auto() + IFSTAR=auto() ELSE=auto() END=auto() WHILE=auto() @@ -229,7 +229,7 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): assert isinstance(op.operand, MemAddr) stack.append(local_memory_rsp + op.operand) ip += 1 - elif op.typ == OpType.IF: + elif op.typ in [OpType.IF, OpType.IFSTAR]: a = stack.pop() if a == 0: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" @@ -241,9 +241,6 @@ def simulate_little_endian_linux(program: Program, argv: List[str]): elif op.typ == OpType.ELSE: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" ip = op.operand - elif op.typ == OpType.ORELSE: - assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" - ip = op.operand elif op.typ == OpType.END: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" ip = op.operand @@ -1095,6 +1092,18 @@ def type_check_program(program: Program): assert isinstance(op.operand, OpAddr) contexts.append(Context(stack=copy(ctx.stack), ip=op.operand, ret_stack=copy(ctx.ret_stack))) ctx = contexts[-1] + elif op.typ == OpType.IFSTAR: + if len(ctx.stack) < 1: + not_enough_arguments(op) + exit(1) + a_type, a_token = ctx.stack.pop() + if a_type != DataType.BOOL: + compiler_error_with_expansion_stack(op.token, "Invalid argument for the `if*` condition. Expected BOOL.") + exit(1) + ctx.ip += 1 + assert isinstance(op.operand, OpAddr) + contexts.append(Context(stack=copy(ctx.stack), ip=op.operand, ret_stack=copy(ctx.ret_stack))) + ctx = contexts[-1] elif op.typ == OpType.WHILE: ctx.ip += 1 elif op.typ == OpType.END: @@ -1103,9 +1112,6 @@ def type_check_program(program: Program): elif op.typ == OpType.ELSE: assert isinstance(op.operand, OpAddr) ctx.ip = op.operand - elif op.typ == OpType.ORELSE: - assert isinstance(op.operand, OpAddr) - ctx.ip = op.operand elif op.typ == OpType.DO: assert isinstance(op.operand, OpAddr) if len(ctx.stack) < 1: @@ -1208,7 +1214,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write(" mov rax, [ret_stack_rsp]\n"); out.write(" add rax, %d\n" % op.operand) out.write(" push rax\n") - elif op.typ == OpType.IF: + elif op.typ in [OpType.IF, OpType.IFSTAR]: out.write(" pop rax\n") out.write(" test rax, rax\n") assert isinstance(op.operand, OpAddr), f"This could be a bug in the parsing step {op.operand}" @@ -1218,9 +1224,6 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): elif op.typ == OpType.ELSE: assert isinstance(op.operand, OpAddr), "This could be a bug in the parsing step" out.write(" jmp addr_%d\n" % op.operand) - elif op.typ == OpType.ORELSE: - assert isinstance(op.operand, OpAddr), f"This could be a bug in the parsing step: {op.operand}" - out.write(" jmp addr_%d\n" % op.operand) elif op.typ == OpType.END: assert isinstance(op.operand, int), "This could be a bug in the parsing step" if ip + 1 != op.operand: @@ -1498,7 +1501,7 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): assert len(Keyword) == 13, "Exhaustive KEYWORD_NAMES definition." KEYWORD_BY_NAMES: Dict[str, Keyword] = { 'if': Keyword.IF, - 'orelse': Keyword.ORELSE, + 'if*': Keyword.IFSTAR, 'else': Keyword.ELSE, 'while': Keyword.WHILE, 'do': Keyword.DO, @@ -1788,34 +1791,42 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops.append(Op(typ=OpType.IF, token=token)) stack.append(ip) ip += 1 - elif token.value == Keyword.ORELSE: - program.ops.append(Op(typ=OpType.ORELSE, token=token)) - if_ip = stack.pop() - if program.ops[if_ip].typ != OpType.IF: - compiler_error_with_expansion_stack(program.ops[if_ip].token, '`orelse` can come after `if`') + elif token.value == Keyword.IFSTAR: + if len(stack) == 0: + compiler_error_with_expansion_stack(token, '`if*` can only come after `else`') exit(1) - if len(stack) > 0 and program.ops[stack[-1]].typ == OpType.ORELSE: - prev_orelse_ip = stack.pop() - program.ops[prev_orelse_ip].operand = ip - - program.ops[if_ip].operand = ip + 1 + else_ip = stack[-1] + if program.ops[else_ip].typ != OpType.ELSE: + compiler_error_with_expansion_stack(program.ops[else_ip].token, '`if*` can only come after `else`') + exit(1) + program.ops.append(Op(typ=OpType.IFSTAR, token=token)) stack.append(ip) ip += 1 elif token.value == Keyword.ELSE: - program.ops.append(Op(typ=OpType.ELSE, token=token)) - if_ip = stack.pop() - if program.ops[if_ip].typ != OpType.IF: - compiler_error_with_expansion_stack(program.ops[if_ip].token, '`else` can only come after `if`') + if len(stack) == 0: + compiler_error_with_expansion_stack(token, '`else` can only come after `if` or `if*`') exit(1) - if len(stack) > 0 and program.ops[stack[-1]].typ == OpType.ORELSE: - orelse_ip = stack.pop() - program.ops[orelse_ip].operand = ip + if_ip = stack.pop() + if program.ops[if_ip].typ == OpType.IF: + program.ops[if_ip].operand = ip + 1 + stack.append(ip) + program.ops.append(Op(typ=OpType.ELSE, token=token)) + ip += 1 + elif program.ops[if_ip].typ == OpType.IFSTAR: + else_ip = None if len(stack) == 0 else stack.pop() + assert else_ip is not None and program.ops[else_ip].typ == OpType.ELSE, "At this point we should've already checked that `if*` comes after `else`. Otherwise this is a compiler bug." + + program.ops[if_ip].operand = ip + 1 + program.ops[else_ip].operand = ip - program.ops[if_ip].operand = ip + 1 - stack.append(ip) - ip += 1 + stack.append(ip) + program.ops.append(Op(typ=OpType.ELSE, token=token)) + ip += 1 + else: + compiler_error_with_expansion_stack(if_program.ops[if_ip].token, f'`else` can only come after `if` or `if*`') + exit(1) elif token.value == Keyword.END: block_ip = stack.pop() @@ -1843,14 +1854,12 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops.append(Op(typ=OpType.RET, token=token, operand=current_proc.local_memory_capacity)) program.ops[block_ip].operand = ip + 1 current_proc = None + elif program.ops[block_ip].typ == OpType.IFSTAR: + assert False, "TODO: implement" elif program.ops[block_ip].typ == OpType.IF: program.ops.append(Op(typ=OpType.END, token=token)) program.ops[block_ip].operand = ip program.ops[ip].operand = ip + 1 - - if len(stack) > 0 and program.ops[stack[-1]].typ == OpType.ORELSE: - orelse_ip = stack.pop() - program.ops[orelse_ip].operand = ip else: # NOTE: the closing of `macro` blocks is handled in its own separate place, not here compiler_error_with_expansion_stack(program.ops[block_ip].token, '`end` can only close `if`, `else`, `do`, `macro` or `proc` blocks for now') @@ -2109,10 +2118,12 @@ def cmd_call_echoed(cmd: List[str], silent: bool) -> int: print("[CMD] %s" % " ".join(map(shlex.quote, cmd))) return subprocess.call(cmd) +# TODO: test.py never touches generate_control_flow_graph_as_dot_file +# Which leads to constantly forgetting to update the implementation def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): with open(dot_path, "w") as f: f.write("digraph Program {\n") - assert len(OpType) == 15, f"Exhaustive handling of OpType in generate_control_flow_graph_as_dot_file(), {len(OpType)}" + assert len(OpType) == 16, f"Exhaustive handling of OpType in generate_control_flow_graph_as_dot_file(), {len(OpType)}" for ip in range(len(program.ops)): op = program.ops[ip] if op.typ == OpType.INTRINSIC: @@ -2135,10 +2146,11 @@ def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): assert isinstance(op.operand, int) f.write(f" Node_{ip} [label=\"mem({op.operand})\"]\n") f.write(f" Node_{ip} -> Node_{ip + 1};\n") - elif op.typ == OpType.IF: - if op.operand is None: - compiler_note(op.token.loc, "sus") - exit(1) + elif op.typ == OpType.PUSH_LOCAL_MEM: + assert isinstance(op.operand, int) + f.write(f" Node_{ip} [label=\"local_mem({op.operand})\"]\n") + f.write(f" Node_{ip} -> Node_{ip + 1};\n") + elif op.typ in [OpType.IF, OpType.IFSTAR]: assert isinstance(op.operand, OpAddr), f"{op.operand}" f.write(f" Node_{ip} [shape=record label=if];\n") f.write(f" Node_{ip} -> Node_{ip + 1} [label=true];\n") @@ -2155,10 +2167,6 @@ def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): assert isinstance(op.operand, OpAddr) f.write(f" Node_{ip} [shape=record label=else];\n") f.write(f" Node_{ip} -> Node_{op.operand};\n") - elif op.typ == OpType.ORELSE: - assert isinstance(op.operand, OpAddr) - f.write(f" Node_{ip} [shape=record label=orelse];\n") - f.write(f" Node_{ip} -> Node_{op.operand};\n") elif op.typ == OpType.END: assert isinstance(op.operand, OpAddr) f.write(f" Node_{ip} [shape=record label=end];\n") From 8fd75e9ab15ddf98cded7fe3fb770c6e2a3323b8 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 03:51:39 +0700 Subject: [PATCH 131/145] Fix if-orelse test case --- porth.py | 18 +++++++++++++----- tests/if-orelse.porth | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/porth.py b/porth.py index 5484446f..69ba464a 100755 --- a/porth.py +++ b/porth.py @@ -1815,17 +1815,17 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops.append(Op(typ=OpType.ELSE, token=token)) ip += 1 elif program.ops[if_ip].typ == OpType.IFSTAR: - else_ip = None if len(stack) == 0 else stack.pop() - assert else_ip is not None and program.ops[else_ip].typ == OpType.ELSE, "At this point we should've already checked that `if*` comes after `else`. Otherwise this is a compiler bug." + else_before_ifstar_ip = None if len(stack) == 0 else stack.pop() + assert else_before_ifstar_ip is not None and program.ops[else_before_ifstar_ip].typ == OpType.ELSE, "At this point we should've already checked that `if*` comes after `else`. Otherwise this is a compiler bug." program.ops[if_ip].operand = ip + 1 - program.ops[else_ip].operand = ip + program.ops[else_before_ifstar_ip].operand = ip stack.append(ip) program.ops.append(Op(typ=OpType.ELSE, token=token)) ip += 1 else: - compiler_error_with_expansion_stack(if_program.ops[if_ip].token, f'`else` can only come after `if` or `if*`') + compiler_error_with_expansion_stack(program.ops[if_ip].token, f'`else` can only come after `if` or `if*`') exit(1) elif token.value == Keyword.END: block_ip = stack.pop() @@ -1855,7 +1855,13 @@ def parse_program_from_tokens(tokens: List[Token], include_paths: List[str], exp program.ops[block_ip].operand = ip + 1 current_proc = None elif program.ops[block_ip].typ == OpType.IFSTAR: - assert False, "TODO: implement" + else_before_ifstar_ip = None if len(stack) == 0 else stack.pop() + assert else_before_ifstar_ip is not None and program.ops[else_before_ifstar_ip].typ == OpType.ELSE, "At this point we should've already checked that `if*` comes after `else`. Otherwise this is a compiler bug." + + program.ops.append(Op(typ=OpType.END, token=token)) + program.ops[block_ip].operand = ip + program.ops[else_before_ifstar_ip].operand = ip + program.ops[ip].operand = ip + 1 elif program.ops[block_ip].typ == OpType.IF: program.ops.append(Op(typ=OpType.END, token=token)) program.ops[block_ip].operand = ip @@ -2118,6 +2124,8 @@ def cmd_call_echoed(cmd: List[str], silent: bool) -> int: print("[CMD] %s" % " ".join(map(shlex.quote, cmd))) return subprocess.call(cmd) +# TODO: with a lot of procs the control flow graphs becomes useless even on small programs +# Maybe we should eliminate unreachable code or something # TODO: test.py never touches generate_control_flow_graph_as_dot_file # Which leads to constantly forgetting to update the implementation def generate_control_flow_graph_as_dot_file(program: Program, dot_path: str): diff --git a/tests/if-orelse.porth b/tests/if-orelse.porth index f994f759..44ea2237 100644 --- a/tests/if-orelse.porth +++ b/tests/if-orelse.porth @@ -2,6 +2,6 @@ include "std.porth" true if 69 print -orelse false if +else false if* 420 print end From 56b47312cfd7660919b9d888108e48a549267a99 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 03:52:37 +0700 Subject: [PATCH 132/145] Fix else-not-inside-if-error test case --- tests/else-not-inside-if-error.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/else-not-inside-if-error.txt b/tests/else-not-inside-if-error.txt index 265fc49a..0dacc352 100644 --- a/tests/else-not-inside-if-error.txt +++ b/tests/else-not-inside-if-error.txt @@ -4,6 +4,6 @@ :i returncode 1 :b stdout 0 -:b stderr 83 -./tests/else-not-inside-if-error.porth:2:1: ERROR: `else` can only come after `if` +:b stderr 92 +./tests/else-not-inside-if-error.porth:2:1: ERROR: `else` can only come after `if` or `if*` From 9303aa3ba5fb1db81d38f9076fb7d25fd52fe0be Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 03:57:24 +0700 Subject: [PATCH 133/145] Fix compilation of porth.porth --- porth.porth | 276 ++++++++++++++++++++++++++-------------------------- 1 file changed, 138 insertions(+), 138 deletions(-) diff --git a/porth.porth b/porth.porth index ccf17651..440af1cc 100644 --- a/porth.porth +++ b/porth.porth @@ -99,7 +99,7 @@ proc cmd-echoed // argv "[ERROR] could not exec external program\n" eputs 1 exit end - orelse dup 0 > if + else dup 0 > if* drop // TODO: handle the result of wait4 NULL 0 wstatus -1 wait4 drop @@ -148,11 +148,11 @@ proc print-op-type dup OP_PUSH_INT = if "OP_PUSH_INT" puts - orelse dup OP_INTRINSIC = if + else dup OP_INTRINSIC = if* "OP_INTRINSIC" puts - orelse dup OP_IF = if + else dup OP_IF = if* "OP_IF" puts - orelse dup OP_END = if + else dup OP_END = if* "OP_END" puts else here eputs ": Unknown op type\n" eputs 1 exit @@ -247,15 +247,15 @@ proc compile-ops // -- " ;; -- push int " out-fd @64 fputs dup @Op.operand out-fd @64 fputd " --\n" out-fd @64 fputs " mov rax, " out-fd @64 fputs dup @Op.operand out-fd @64 fputd "\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.type OP_IF = if + else dup @Op.type OP_IF = if* " ;; -- if --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " test rax, rax\n" out-fd @64 fputs " jz addr_" out-fd @64 fputs dup @Op.operand out-fd @64 fputd "\n" out-fd @64 fputs - orelse dup @Op.type OP_END = if + else dup @Op.type OP_END = if* " ;; -- end --\n" out-fd @64 fputs " jmp addr_" out-fd @64 fputs dup @Op.operand out-fd @64 fputd "\n" out-fd @64 fputs - orelse dup @Op.type OP_INTRINSIC = if + else dup @Op.type OP_INTRINSIC = if* COUNT_INTRINSICS 42 != if here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs 1 exit @@ -267,19 +267,19 @@ proc compile-ops // -- " pop rbx\n" out-fd @64 fputs " add rax, rbx\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_MINUS = if + else dup @Op.operand INTRINSIC_MINUS = if* " ;; -- minus --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " sub rbx, rax\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_MUL = if + else dup @Op.operand INTRINSIC_MUL = if* " ;; -- mul --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " mul rbx\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_DIVMOD = if + else dup @Op.operand INTRINSIC_DIVMOD = if* " ;; -- mod --\n" out-fd @64 fputs " xor rdx, rdx\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs @@ -287,40 +287,40 @@ proc compile-ops // -- " div rbx\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs " push rdx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SHR = if + else dup @Op.operand INTRINSIC_SHR = if* " ;; -- shr --\n" out-fd @64 fputs " pop rcx\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " shr rbx, cl\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SHL = if + else dup @Op.operand INTRINSIC_SHL = if* " ;; -- shl --\n" out-fd @64 fputs " pop rcx\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " shl rbx, cl\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_OR = if + else dup @Op.operand INTRINSIC_OR = if* " ;; -- bor --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " or rbx, rax\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_AND = if + else dup @Op.operand INTRINSIC_AND = if* " ;; -- band --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " and rbx, rax\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_NOT = if + else dup @Op.operand INTRINSIC_NOT = if* " ;; -- not --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " not rax\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_PRINT = if + else dup @Op.operand INTRINSIC_PRINT = if* " ;; -- print --\n" out-fd @64 fputs " pop rdi\n" out-fd @64 fputs " call print\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_EQ = if + else dup @Op.operand INTRINSIC_EQ = if* " ;; -- equal --\n" out-fd @64 fputs " mov rcx, 0\n" out-fd @64 fputs " mov rdx, 1\n" out-fd @64 fputs @@ -329,7 +329,7 @@ proc compile-ops // -- " cmp rax, rbx\n" out-fd @64 fputs " cmove rcx, rdx\n" out-fd @64 fputs " push rcx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_GT = if + else dup @Op.operand INTRINSIC_GT = if* " ;; -- gt --\n" out-fd @64 fputs " mov rcx, 0\n" out-fd @64 fputs " mov rdx, 1\n" out-fd @64 fputs @@ -338,7 +338,7 @@ proc compile-ops // -- " cmp rax, rbx\n" out-fd @64 fputs " cmovg rcx, rdx\n" out-fd @64 fputs " push rcx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_LT = if + else dup @Op.operand INTRINSIC_LT = if* " ;; -- gt --\n" out-fd @64 fputs " mov rcx, 0\n" out-fd @64 fputs " mov rdx, 1\n" out-fd @64 fputs @@ -347,7 +347,7 @@ proc compile-ops // -- " cmp rax, rbx\n" out-fd @64 fputs " cmovl rcx, rdx\n" out-fd @64 fputs " push rcx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_GE = if + else dup @Op.operand INTRINSIC_GE = if* " ;; -- gt --\n" out-fd @64 fputs " mov rcx, 0\n" out-fd @64 fputs " mov rdx, 1\n" out-fd @64 fputs @@ -356,7 +356,7 @@ proc compile-ops // -- " cmp rax, rbx\n" out-fd @64 fputs " cmovge rcx, rdx\n" out-fd @64 fputs " push rcx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_LE = if + else dup @Op.operand INTRINSIC_LE = if* " ;; -- gt --\n" out-fd @64 fputs " mov rcx, 0\n" out-fd @64 fputs " mov rdx, 1\n" out-fd @64 fputs @@ -365,7 +365,7 @@ proc compile-ops // -- " cmp rax, rbx\n" out-fd @64 fputs " cmovle rcx, rdx\n" out-fd @64 fputs " push rcx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_NE = if + else dup @Op.operand INTRINSIC_NE = if* " ;; -- ne --\n" out-fd @64 fputs " mov rcx, 0\n" out-fd @64 fputs " mov rdx, 1\n" out-fd @64 fputs @@ -374,28 +374,28 @@ proc compile-ops // -- " cmp rax, rbx\n" out-fd @64 fputs " cmovne rcx, rdx\n" out-fd @64 fputs " push rcx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_DUP = if + else dup @Op.operand INTRINSIC_DUP = if* " ;; -- dup --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SWAP = if + else dup @Op.operand INTRINSIC_SWAP = if* " ;; -- swap --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_DROP = if + else dup @Op.operand INTRINSIC_DROP = if* " ;; -- drop --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_OVER = if + else dup @Op.operand INTRINSIC_OVER = if* " ;; -- over --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_ROT = if + else dup @Op.operand INTRINSIC_ROT = if* " ;; -- rot --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs @@ -403,87 +403,87 @@ proc compile-ops // -- " push rbx\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs " push rcx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_LOAD8 = if + else dup @Op.operand INTRINSIC_LOAD8 = if* " ;; -- @8 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " xor rbx, rbx\n" out-fd @64 fputs " mov bl, [rax]\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_STORE8 = if + else dup @Op.operand INTRINSIC_STORE8 = if* " ;; -- !8 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " mov [rax], bl\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_LOAD16 = if + else dup @Op.operand INTRINSIC_LOAD16 = if* " ;; -- @16 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " xor rbx, rbx\n" out-fd @64 fputs " mov bx, [rax]\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_STORE16 = if + else dup @Op.operand INTRINSIC_STORE16 = if* " ;; -- !16 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " mov [rax], bx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_LOAD32 = if + else dup @Op.operand INTRINSIC_LOAD32 = if* " ;; -- @32 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " xor rbx, rbx\n" out-fd @64 fputs " mov ebx, [rax]\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_STORE32 = if + else dup @Op.operand INTRINSIC_STORE32 = if* " ;; -- !32 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " mov [rax], ebx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_LOAD64 = if + else dup @Op.operand INTRINSIC_LOAD64 = if* " ;; -- @64 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " xor rbx, rbx\n" out-fd @64 fputs " mov rbx, [rax]\n" out-fd @64 fputs " push rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_STORE64 = if + else dup @Op.operand INTRINSIC_STORE64 = if* " ;; -- !64 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rbx\n" out-fd @64 fputs " mov [rax], rbx\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_ARGC = if + else dup @Op.operand INTRINSIC_ARGC = if* " ;; -- argc --\n" out-fd @64 fputs " mov rax, [args_ptr]\n" out-fd @64 fputs " mov rax, [rax]\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_ARGV = if + else dup @Op.operand INTRINSIC_ARGV = if* " ;; -- argv --\n" out-fd @64 fputs " mov rax, [args_ptr]\n" out-fd @64 fputs " add rax, 8\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_HERE = if + else dup @Op.operand INTRINSIC_HERE = if* here eputs ": TODO: intrinsic `here` is not implemented yet" eputs - orelse dup @Op.operand INTRINSIC_CAST_PTR = if + else dup @Op.operand INTRINSIC_CAST_PTR = if* " ;; -- cast(ptr) --\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_CAST_INT = if + else dup @Op.operand INTRINSIC_CAST_INT = if* " ;; -- cast(int) --\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_CAST_BOOL = if + else dup @Op.operand INTRINSIC_CAST_BOOL = if* " ;; -- cast(bool) --\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SYSCALL0 = if + else dup @Op.operand INTRINSIC_SYSCALL0 = if* " ;; -- syscall0 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " syscall\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SYSCALL1 = if + else dup @Op.operand INTRINSIC_SYSCALL1 = if* " ;; -- syscall1 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rdi\n" out-fd @64 fputs " syscall\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SYSCALL2 = if + else dup @Op.operand INTRINSIC_SYSCALL2 = if* " ;; -- syscall2 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rdi\n" out-fd @64 fputs " pop rsi\n" out-fd @64 fputs " syscall\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SYSCALL3 = if + else dup @Op.operand INTRINSIC_SYSCALL3 = if* " ;; -- syscall3 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rdi\n" out-fd @64 fputs @@ -491,7 +491,7 @@ proc compile-ops // -- " pop rdx\n" out-fd @64 fputs " syscall\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SYSCALL4 = if + else dup @Op.operand INTRINSIC_SYSCALL4 = if* " ;; -- syscall4 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rdi\n" out-fd @64 fputs @@ -500,7 +500,7 @@ proc compile-ops // -- " pop r10\n" out-fd @64 fputs " syscall\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SYSCALL5 = if + else dup @Op.operand INTRINSIC_SYSCALL5 = if* " ;; -- syscall5 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rdi\n" out-fd @64 fputs @@ -510,7 +510,7 @@ proc compile-ops // -- " pop r8\n" out-fd @64 fputs " syscall\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs - orelse dup @Op.operand INTRINSIC_SYSCALL6 = if + else dup @Op.operand INTRINSIC_SYSCALL6 = if* " ;; -- syscall6 --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " pop rdi\n" out-fd @64 fputs @@ -587,15 +587,15 @@ proc simulate-ops // -- sim-op @Op.type OP_PUSH_INT = if sim-op @Op.operand sim-stack-push sim-ip inc64 - orelse sim-op @Op.type OP_IF = if + else sim-op @Op.type OP_IF = if* sim-stack-pop cast(bool) if sim-ip inc64 else sim-op @Op.operand sim-ip !64 end - orelse sim-op @Op.type OP_END = if + else sim-op @Op.type OP_END = if* sim-op @Op.operand sim-ip !64 - orelse sim-op @Op.type OP_INTRINSIC = if + else sim-op @Op.type OP_INTRINSIC = if* COUNT_INTRINSICS 42 != if here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs 1 exit @@ -606,18 +606,18 @@ proc simulate-ops // -- sim-stack-pop + sim-stack-push - orelse sim-op @Op.operand INTRINSIC_MINUS = if + else sim-op @Op.operand INTRINSIC_MINUS = if* sim-stack-pop sim-stack-pop swap - sim-stack-push - orelse sim-op @Op.operand INTRINSIC_MUL = if + else sim-op @Op.operand INTRINSIC_MUL = if* sim-stack-pop sim-stack-pop * sim-stack-push - orelse sim-op @Op.operand INTRINSIC_DIVMOD = if + else sim-op @Op.operand INTRINSIC_DIVMOD = if* sim-stack-pop sim-stack-pop swap @@ -625,84 +625,84 @@ proc simulate-ops // -- swap sim-stack-push sim-stack-push - orelse sim-op @Op.operand INTRINSIC_SHR = if + else sim-op @Op.operand INTRINSIC_SHR = if* sim-stack-pop sim-stack-pop swap shr sim-stack-push - orelse sim-op @Op.operand INTRINSIC_SHL = if + else sim-op @Op.operand INTRINSIC_SHL = if* sim-stack-pop sim-stack-pop swap shl sim-stack-push - orelse sim-op @Op.operand INTRINSIC_OR = if + else sim-op @Op.operand INTRINSIC_OR = if* sim-stack-pop sim-stack-pop or sim-stack-push - orelse sim-op @Op.operand INTRINSIC_AND = if + else sim-op @Op.operand INTRINSIC_AND = if* sim-stack-pop sim-stack-pop and sim-stack-push - orelse sim-op @Op.operand INTRINSIC_NOT = if + else sim-op @Op.operand INTRINSIC_NOT = if* sim-stack-pop not sim-stack-push - orelse sim-op @Op.operand INTRINSIC_PRINT = if + else sim-op @Op.operand INTRINSIC_PRINT = if* sim-stack-pop print - orelse sim-op @Op.operand INTRINSIC_EQ = if + else sim-op @Op.operand INTRINSIC_EQ = if* sim-stack-pop sim-stack-pop = sim-stack-push - orelse sim-op @Op.operand INTRINSIC_GT = if + else sim-op @Op.operand INTRINSIC_GT = if* sim-stack-pop sim-stack-pop swap > sim-stack-push - orelse sim-op @Op.operand INTRINSIC_LT = if + else sim-op @Op.operand INTRINSIC_LT = if* sim-stack-pop sim-stack-pop swap < sim-stack-push - orelse sim-op @Op.operand INTRINSIC_GE = if + else sim-op @Op.operand INTRINSIC_GE = if* sim-stack-pop sim-stack-pop swap >= sim-stack-push - orelse sim-op @Op.operand INTRINSIC_LE = if + else sim-op @Op.operand INTRINSIC_LE = if* sim-stack-pop sim-stack-pop swap <= sim-stack-push - orelse sim-op @Op.operand INTRINSIC_NE = if + else sim-op @Op.operand INTRINSIC_NE = if* sim-stack-pop sim-stack-pop != sim-stack-push - orelse sim-op @Op.operand INTRINSIC_DUP = if + else sim-op @Op.operand INTRINSIC_DUP = if* sim-stack-pop dup sim-stack-push sim-stack-push - orelse sim-op @Op.operand INTRINSIC_SWAP = if + else sim-op @Op.operand INTRINSIC_SWAP = if* sim-stack-pop sim-stack-pop swap sim-stack-push sim-stack-push - orelse sim-op @Op.operand INTRINSIC_DROP = if + else sim-op @Op.operand INTRINSIC_DROP = if* sim-stack-pop drop - orelse sim-op @Op.operand INTRINSIC_OVER = if + else sim-op @Op.operand INTRINSIC_OVER = if* sim-stack-pop sim-stack-pop dup @@ -710,7 +710,7 @@ proc simulate-ops // -- swap sim-stack-push sim-stack-push - orelse sim-op @Op.operand INTRINSIC_ROT = if + else sim-op @Op.operand INTRINSIC_ROT = if* sim-stack-pop sim-stack-pop sim-stack-pop @@ -719,47 +719,47 @@ proc simulate-ops // -- swap sim-stack-push sim-stack-push - orelse sim-op @Op.operand INTRINSIC_LOAD8 = if + else sim-op @Op.operand INTRINSIC_LOAD8 = if* here eputs ": TODO: `@8` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_STORE8 = if + else sim-op @Op.operand INTRINSIC_STORE8 = if* here eputs ": TODO: `!8` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_LOAD16 = if + else sim-op @Op.operand INTRINSIC_LOAD16 = if* here eputs ": TODO: `@16` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_STORE16 = if + else sim-op @Op.operand INTRINSIC_STORE16 = if* here eputs ": TODO: `!16` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_LOAD32 = if + else sim-op @Op.operand INTRINSIC_LOAD32 = if* here eputs ": TODO: `@32` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_STORE32 = if + else sim-op @Op.operand INTRINSIC_STORE32 = if* here eputs ": TODO: `!32` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_LOAD64 = if + else sim-op @Op.operand INTRINSIC_LOAD64 = if* here eputs ": TODO: `@64` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_STORE64 = if + else sim-op @Op.operand INTRINSIC_STORE64 = if* here eputs ": TODO: `!64` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_ARGC = if + else sim-op @Op.operand INTRINSIC_ARGC = if* here eputs ": TODO: `argc` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_ARGV = if + else sim-op @Op.operand INTRINSIC_ARGV = if* here eputs ": TODO: `argv` is not implemented yet" eputs 1 exit - orelse sim-op @Op.operand INTRINSIC_HERE = if + else sim-op @Op.operand INTRINSIC_HERE = if* here eputs ": TODO: `here` is not implemented yet" eputs - orelse sim-op @Op.operand INTRINSIC_CAST_PTR = if + else sim-op @Op.operand INTRINSIC_CAST_PTR = if* nop - orelse sim-op @Op.operand INTRINSIC_CAST_INT = if + else sim-op @Op.operand INTRINSIC_CAST_INT = if* nop - orelse sim-op @Op.operand INTRINSIC_CAST_BOOL = if + else sim-op @Op.operand INTRINSIC_CAST_BOOL = if* nop - orelse sim-op @Op.operand INTRINSIC_SYSCALL0 = if + else sim-op @Op.operand INTRINSIC_SYSCALL0 = if* here eputs ": TODO: `syscall0` is not implemented yet" eputs - orelse sim-op @Op.operand INTRINSIC_SYSCALL1 = if + else sim-op @Op.operand INTRINSIC_SYSCALL1 = if* here eputs ": TODO: `syscall1` is not implemented yet" eputs - orelse sim-op @Op.operand INTRINSIC_SYSCALL2 = if + else sim-op @Op.operand INTRINSIC_SYSCALL2 = if* here eputs ": TODO: `syscall2` is not implemented yet" eputs - orelse sim-op @Op.operand INTRINSIC_SYSCALL3 = if + else sim-op @Op.operand INTRINSIC_SYSCALL3 = if* here eputs ": TODO: `syscall3` is not implemented yet" eputs - orelse sim-op @Op.operand INTRINSIC_SYSCALL4 = if + else sim-op @Op.operand INTRINSIC_SYSCALL4 = if* here eputs ": TODO: `syscall4` is not implemented yet" eputs - orelse sim-op @Op.operand INTRINSIC_SYSCALL5 = if + else sim-op @Op.operand INTRINSIC_SYSCALL5 = if* here eputs ": TODO: `syscall5` is not implemented yet" eputs - orelse sim-op @Op.operand INTRINSIC_SYSCALL6 = if + else sim-op @Op.operand INTRINSIC_SYSCALL6 = if* here eputs ": TODO: `syscall6` is not implemented yet" eputs else here eputs ": unreachable.\n" eputs @@ -899,94 +899,94 @@ proc parse_file_path_cstr_into_ops // file-path-cstr // Intrinsics word @Str "+" streq if OP_INTRINSIC INTRINSIC_PLUS push-op - orelse word @Str "-" streq if + else word @Str "-" streq if* OP_INTRINSIC INTRINSIC_MINUS push-op - orelse word @Str "*" streq if + else word @Str "*" streq if* OP_INTRINSIC INTRINSIC_MUL push-op - orelse word @Str "divmod" streq if + else word @Str "divmod" streq if* OP_INTRINSIC INTRINSIC_DIVMOD push-op - orelse word @Str "print" streq if + else word @Str "print" streq if* OP_INTRINSIC INTRINSIC_PRINT push-op - orelse word @Str "=" streq if + else word @Str "=" streq if* OP_INTRINSIC INTRINSIC_EQ push-op - orelse word @Str ">" streq if + else word @Str ">" streq if* OP_INTRINSIC INTRINSIC_GT push-op - orelse word @Str "<" streq if + else word @Str "<" streq if* OP_INTRINSIC INTRINSIC_LT push-op - orelse word @Str ">=" streq if + else word @Str ">=" streq if* OP_INTRINSIC INTRINSIC_GE push-op - orelse word @Str "<=" streq if + else word @Str "<=" streq if* OP_INTRINSIC INTRINSIC_LE push-op - orelse word @Str "!=" streq if + else word @Str "!=" streq if* OP_INTRINSIC INTRINSIC_NE push-op - orelse word @Str "shr" streq if + else word @Str "shr" streq if* OP_INTRINSIC INTRINSIC_SHR push-op - orelse word @Str "shl" streq if + else word @Str "shl" streq if* OP_INTRINSIC INTRINSIC_SHL push-op - orelse word @Str "or" streq if + else word @Str "or" streq if* OP_INTRINSIC INTRINSIC_OR push-op - orelse word @Str "and" streq if + else word @Str "and" streq if* OP_INTRINSIC INTRINSIC_AND push-op - orelse word @Str "not" streq if + else word @Str "not" streq if* OP_INTRINSIC INTRINSIC_NOT push-op - orelse word @Str "dup" streq if + else word @Str "dup" streq if* OP_INTRINSIC INTRINSIC_DUP push-op - orelse word @Str "swap" streq if + else word @Str "swap" streq if* OP_INTRINSIC INTRINSIC_SWAP push-op - orelse word @Str "drop" streq if + else word @Str "drop" streq if* OP_INTRINSIC INTRINSIC_DROP push-op - orelse word @Str "over" streq if + else word @Str "over" streq if* OP_INTRINSIC INTRINSIC_OVER push-op - orelse word @Str "rot" streq if + else word @Str "rot" streq if* OP_INTRINSIC INTRINSIC_ROT push-op - orelse word @Str "!8" streq if + else word @Str "!8" streq if* OP_INTRINSIC INTRINSIC_STORE8 push-op - orelse word @Str "@8" streq if + else word @Str "@8" streq if* OP_INTRINSIC INTRINSIC_LOAD8 push-op - orelse word @Str "!16" streq if + else word @Str "!16" streq if* OP_INTRINSIC INTRINSIC_STORE16 push-op - orelse word @Str "@16" streq if + else word @Str "@16" streq if* OP_INTRINSIC INTRINSIC_LOAD16 push-op - orelse word @Str "!32" streq if + else word @Str "!32" streq if* OP_INTRINSIC INTRINSIC_STORE32 push-op - orelse word @Str "@32" streq if + else word @Str "@32" streq if* OP_INTRINSIC INTRINSIC_LOAD32 push-op - orelse word @Str "!64" streq if + else word @Str "!64" streq if* OP_INTRINSIC INTRINSIC_STORE64 push-op - orelse word @Str "@64" streq if + else word @Str "@64" streq if* OP_INTRINSIC INTRINSIC_LOAD64 push-op - orelse word @Str "cast(ptr)" streq if + else word @Str "cast(ptr)" streq if* OP_INTRINSIC INTRINSIC_CAST_PTR push-op - orelse word @Str "cast(int)" streq if + else word @Str "cast(int)" streq if* OP_INTRINSIC INTRINSIC_CAST_INT push-op - orelse word @Str "cast(bool)" streq if + else word @Str "cast(bool)" streq if* OP_INTRINSIC INTRINSIC_CAST_BOOL push-op - orelse word @Str "argc" streq if + else word @Str "argc" streq if* OP_INTRINSIC INTRINSIC_ARGC push-op - orelse word @Str "argv" streq if + else word @Str "argv" streq if* OP_INTRINSIC INTRINSIC_ARGV push-op - orelse word @Str "here" streq if + else word @Str "here" streq if* OP_INTRINSIC INTRINSIC_HERE push-op - orelse word @Str "syscall0" streq if + else word @Str "syscall0" streq if* OP_INTRINSIC INTRINSIC_SYSCALL0 push-op - orelse word @Str "syscall1" streq if + else word @Str "syscall1" streq if* OP_INTRINSIC INTRINSIC_SYSCALL1 push-op - orelse word @Str "syscall2" streq if + else word @Str "syscall2" streq if* OP_INTRINSIC INTRINSIC_SYSCALL2 push-op - orelse word @Str "syscall3" streq if + else word @Str "syscall3" streq if* OP_INTRINSIC INTRINSIC_SYSCALL3 push-op - orelse word @Str "syscall4" streq if + else word @Str "syscall4" streq if* OP_INTRINSIC INTRINSIC_SYSCALL4 push-op - orelse word @Str "syscall5" streq if + else word @Str "syscall5" streq if* OP_INTRINSIC INTRINSIC_SYSCALL5 push-op - orelse word @Str "syscall6" streq if + else word @Str "syscall6" streq if* OP_INTRINSIC INTRINSIC_SYSCALL6 push-op // Keywords - orelse word @Str "if" streq if + else word @Str "if" streq if* @ops-count parse-block-stack-push OP_IF 0 push-op - orelse word @Str "end" streq if + else word @Str "end" streq if* @parse-block-stack-count 0 <= if file-path-cstr @64 cast(ptr) cstr-to-str eputs ":" puts line_number @64 putd @@ -1059,7 +1059,7 @@ proc main // -- 2 nth_argv parse_file_path_cstr_into_ops simulate-ops - orelse dup "com"c cstreq if + else dup "com"c cstreq if* argc 3 < if stderr usage "ERROR: no input file is provided for the `com` subcommand\n" eputs @@ -1069,10 +1069,10 @@ proc main // -- 2 nth_argv parse_file_path_cstr_into_ops compile-ops - orelse dup "help"c cstreq if + else dup "help"c cstreq if* stdout usage 0 exit - orelse dup "dump"c cstreq if + else dup "dump"c cstreq if* argc 3 < if stderr usage "ERROR: no input file is provided for the `dump` subcommand\n" eputs From 09de6287c66bb48be1961e5d3db13cd4b4c26db0 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 04:13:46 +0700 Subject: [PATCH 134/145] Deprecate nop --- porth.porth | 3 --- std/std.porth | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/porth.porth b/porth.porth index 440af1cc..d90e8c26 100644 --- a/porth.porth +++ b/porth.porth @@ -742,11 +742,8 @@ proc simulate-ops // -- else sim-op @Op.operand INTRINSIC_HERE = if* here eputs ": TODO: `here` is not implemented yet" eputs else sim-op @Op.operand INTRINSIC_CAST_PTR = if* - nop else sim-op @Op.operand INTRINSIC_CAST_INT = if* - nop else sim-op @Op.operand INTRINSIC_CAST_BOOL = if* - nop else sim-op @Op.operand INTRINSIC_SYSCALL0 = if* here eputs ": TODO: `syscall0` is not implemented yet" eputs else sim-op @Op.operand INTRINSIC_SYSCALL1 = if* diff --git a/std/std.porth b/std/std.porth index afd1adb9..a56e97e1 100644 --- a/std/std.porth +++ b/std/std.porth @@ -1,4 +1,4 @@ -// Deprecated Words +// Deprecated Words ////////// macro .64 swap !64 end macro ,64 @64 end macro ! !8 end @@ -7,11 +7,11 @@ macro . swap ! end macro , @ end macro cstr-to-pstr cstr-to-str end memory mem 640000 end +macro nop end +////////////////////////////// const NULL 0 end -macro nop end - macro true 1 cast(bool) end macro false 0 cast(bool) end From 9f23650b66cc7f678e778ffedc240b7818587bd8 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 05:49:08 +0700 Subject: [PATCH 135/145] Refactor try-parse-int test case --- tests/try-parse-int.porth | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/try-parse-int.porth b/tests/try-parse-int.porth index 846e37c7..6bfecc49 100644 --- a/tests/try-parse-int.porth +++ b/tests/try-parse-int.porth @@ -1,17 +1,20 @@ include "std.porth" -memory a sizeof(Str) end -"1234" a !Str +proc test-try-parse-int + memory a sizeof(Str) end + a !Str + a @Str try-parse-int if print else + drop + a @Str eputs " is not a number\n" eputs + end +end -memory b sizeof(Str) end -"abcd" b !Str +// // TODO: "./tests/try-parse-int.asm:2291: warning: no operand for data declaration" +// // TODO: try-parse-int does not fail on empty list +// "" test-try-parse-int +"1234" test-try-parse-int +"abcd" test-try-parse-int -a @Str try-parse-int if print else - drop - a @Str eputs " is not a number\n" eputs -end +// TODO: try-parse-int does not parse negative numbers +// "-1234" test-try-parse-int -b @Str try-parse-int if print else - drop - b @Str eputs " is not a number\n" eputs -end From 71b1c5b3dcbf5d1953bcf69b2a70426503b38a05 Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 07:16:36 +0700 Subject: [PATCH 136/145] Rename putd -> putu --- examples/checker.porth | 4 ++-- examples/gol.porth | 4 ++-- porth.porth | 28 ++++++++++++++-------------- std/std.porth | 26 +++++++++++++++++--------- test.py | 1 + tests/fputd.porth | 4 ++-- 6 files changed, 38 insertions(+), 29 deletions(-) diff --git a/examples/checker.porth b/examples/checker.porth index 6284fc8c..b57f0033 100644 --- a/examples/checker.porth +++ b/examples/checker.porth @@ -66,8 +66,8 @@ openat end "P6\n" @fd fputs -WIDTH @fd fputd +WIDTH @fd fputu " " @fd fputs -HEIGHT @fd fputd +HEIGHT @fd fputu " 255\n" @fd fputs WIDTH HEIGHT * sizeof(pixel) * canvas @fd fputs diff --git a/examples/gol.porth b/examples/gol.porth index 164ee60d..fff5b3c7 100644 --- a/examples/gol.porth +++ b/examples/gol.porth @@ -155,8 +155,8 @@ proc main swap_boards NULL delta_time 0 CLOCK_MONOTONIC clock_nanosleep drop - "\033[" puts ROWS putd "A" puts - "\033[" puts COLS putd "D" puts + "\033[" puts ROWS putu "A" puts + "\033[" puts COLS putu "D" puts end end diff --git a/porth.porth b/porth.porth index d90e8c26..607a498d 100644 --- a/porth.porth +++ b/porth.porth @@ -164,9 +164,9 @@ proc dump-ops // -- 0 while dup @ops-count < do // ptr ptr dup sizeof(Op) * ops + - "IP: " puts over putd "\n" puts + "IP: " puts over putu "\n" puts "Type: " puts dup @Op.type print-op-type "\n" puts - "Operand: " puts @Op.operand putd "\n" puts + "Operand: " puts @Op.operand putu "\n" puts "----------\n" puts 1 + end @@ -240,21 +240,21 @@ proc compile-ops // -- end "addr_" out-fd @64 fputs - over out-fd @64 fputd + over out-fd @64 fputu ":\n" out-fd @64 fputs dup @Op.type OP_PUSH_INT = if - " ;; -- push int " out-fd @64 fputs dup @Op.operand out-fd @64 fputd " --\n" out-fd @64 fputs - " mov rax, " out-fd @64 fputs dup @Op.operand out-fd @64 fputd "\n" out-fd @64 fputs + " ;; -- push int " out-fd @64 fputs dup @Op.operand out-fd @64 fputu " --\n" out-fd @64 fputs + " mov rax, " out-fd @64 fputs dup @Op.operand out-fd @64 fputu "\n" out-fd @64 fputs " push rax\n" out-fd @64 fputs else dup @Op.type OP_IF = if* " ;; -- if --\n" out-fd @64 fputs " pop rax\n" out-fd @64 fputs " test rax, rax\n" out-fd @64 fputs - " jz addr_" out-fd @64 fputs dup @Op.operand out-fd @64 fputd "\n" out-fd @64 fputs + " jz addr_" out-fd @64 fputs dup @Op.operand out-fd @64 fputu "\n" out-fd @64 fputs else dup @Op.type OP_END = if* " ;; -- end --\n" out-fd @64 fputs - " jmp addr_" out-fd @64 fputs dup @Op.operand out-fd @64 fputd "\n" out-fd @64 fputs + " jmp addr_" out-fd @64 fputs dup @Op.operand out-fd @64 fputu "\n" out-fd @64 fputs else dup @Op.type OP_INTRINSIC = if* COUNT_INTRINSICS 42 != if here eputs ": Assertion Failed: Exhaustive handling of Intrinsics in compile-ops\n" eputs @@ -541,7 +541,7 @@ proc compile-ops // -- " syscall\n" out-fd @64 fputs "segment .bss\n" out-fd @64 fputs "args_ptr: resq 1\n" out-fd @64 fputs - "mem: resb " out-fd @64 fputs MEM_CAPACITY out-fd @64 fputd "\n" out-fd @64 fputs + "mem: resb " out-fd @64 fputs MEM_CAPACITY out-fd @64 fputu "\n" out-fd @64 fputs out-fd @64 close drop @@ -986,8 +986,8 @@ proc parse_file_path_cstr_into_ops // file-path-cstr else word @Str "end" streq if* @parse-block-stack-count 0 <= if file-path-cstr @64 cast(ptr) cstr-to-str eputs - ":" puts line_number @64 putd - ":" puts word @Str.data cast(int) line_start @64 - 1 + putd + ":" puts line_number @64 putu + ":" puts word @Str.data cast(int) line_start @64 - 1 + putu ": ERROR: `end` can only close `if` for now\n" eputs 1 exit end @@ -998,8 +998,8 @@ proc parse_file_path_cstr_into_ops // file-path-cstr dup @Op.type OP_IF != if file-path-cstr @64 cast(ptr) cstr-to-str eputs - ":" puts line_number @64 putd - ":" puts word @Str.data cast(int) line_start @64 - 1 + putd + ":" puts line_number @64 putu + ":" puts word @Str.data cast(int) line_start @64 - 1 + putu ": ERROR: `end` can only close `if` for now\n" eputs 1 exit end @@ -1013,8 +1013,8 @@ proc parse_file_path_cstr_into_ops // file-path-cstr OP_PUSH_INT word @Str try-parse-int lnot if file-path-cstr @64 cast(ptr) cstr-to-str eputs - ":" puts line_number @64 putd - ":" puts word @Str.data cast(int) line_start @64 - 1 + putd + ":" puts line_number @64 putu + ":" puts word @Str.data cast(int) line_start @64 - 1 + putu ": ERROR: `" eputs word @Str eputs "` is unknown word\n" eputs 1 exit end diff --git a/std/std.porth b/std/std.porth index a56e97e1..89c3542e 100644 --- a/std/std.porth +++ b/std/std.porth @@ -8,8 +8,12 @@ macro , @ end macro cstr-to-pstr cstr-to-str end memory mem 640000 end macro nop end +macro fputd fputu end +macro eputd eputu end +macro putd putu end ////////////////////////////// +// TODO: NULL should have type ptr const NULL 0 end macro true 1 cast(bool) end @@ -19,6 +23,8 @@ const sizeof(u64) 8 end const sizeof(u32) 4 end const sizeof(ptr) sizeof(u64) end +proc @ptr @64 cast(ptr) end + /// Standard streams const stdin 0 end const stdout 1 end @@ -570,18 +576,18 @@ macro lnot cast(int) 1 - cast(bool) end -const PUTD_BUFFER_CAP 32 end -// TODO: fputd should fail if write call fails -// TODO: fputd does not print negative numbers -proc fputd // value fd -- - memory buffer PUTD_BUFFER_CAP end +const PUTU_BUFFER_CAP 32 end +// TODO: fputu should fail if write call fails +// TODO: fputu does not print negative numbers +proc fputu // value fd -- + memory buffer PUTU_BUFFER_CAP end memory fd sizeof(u64) end fd !64 dup 0 = if "0" fd @64 fputs else - buffer PUTD_BUFFER_CAP + + buffer PUTU_BUFFER_CAP + while over 0 > do 1 - dup rot 10 divmod @@ -589,13 +595,15 @@ proc fputd // value fd -- end dup - buffer PUTD_BUFFER_CAP + swap - swap fd @64 fputs + buffer PUTU_BUFFER_CAP + swap - swap fd @64 fputs end drop end -macro putd stdout fputd end -macro eputd stderr fputd end +proc putu stdout fputu end +proc eputu stderr fputu end + +// TODO: there is no fputi function that would print signed integers proc memcpy // size src dst -- memory src sizeof(ptr) end diff --git a/test.py b/test.py index d144e29b..d0608902 100755 --- a/test.py +++ b/test.py @@ -246,6 +246,7 @@ def usage(exe_name: str): run_test_for_folder('./tests/') run_test_for_folder('./examples/') run_test_for_folder('./euler/') + # TODO: do run_test_for_file on porth.porth with ./tests/intrinsics.porth cmd_run_echoed([sys.executable, './porth.py', 'com', './porth.porth']) elif subcommand == 'help': usage(exe_name) diff --git a/tests/fputd.porth b/tests/fputd.porth index 20a82fef..1dde3563 100644 --- a/tests/fputd.porth +++ b/tests/fputd.porth @@ -1,4 +1,4 @@ include "std.porth" -"stderr: " stderr fputs 69 stderr fputd "\n" stderr fputs -"stdout: " stdout fputs 420 stdout fputd "\n" stdout fputs +"stderr: " stderr fputs 69 stderr fputu "\n" stderr fputs +"stdout: " stdout fputs 420 stdout fputu "\n" stdout fputs From e8d057c1b992e59cc197e1353033fe29923d0461 Mon Sep 17 00:00:00 2001 From: mjz19910 Date: Tue, 12 Oct 2021 16:13:28 -0700 Subject: [PATCH 137/145] Change the openat syscall so it takes 4 arguments Change the syscall intrinsic the openat macro uses to the 4 argument syscall intrinsic. When I was refactoring the porth.porth program to actually write output to a file, I encountered the inability to set the permission mask when `openat`ing the out.asm file. --- std/std.porth | 1 + 1 file changed, 1 insertion(+) diff --git a/std/std.porth b/std/std.porth index 89c3542e..ab113be9 100644 --- a/std/std.porth +++ b/std/std.porth @@ -407,6 +407,7 @@ macro getpid SYS_getpid syscall0 end macro execve SYS_execve syscall3 end macro wait4 SYS_wait4 syscall4 end + macro 2dup over over end macro 2drop drop drop end From 0b0d184eb6786d6ab8ab8e3fba162146ab605935 Mon Sep 17 00:00:00 2001 From: mjz19910 Date: Wed, 13 Oct 2021 04:13:39 -0700 Subject: [PATCH 138/145] Some porth programming, get porth to fork and run a different program. Stuff I already did... Porth, open and write the asm to a file. Stuff I will work on... Python, want to figure out constant folding optimization next --- porth.porth | 12 ++++++++++++ porth.py | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/porth.porth b/porth.porth index 607a498d..ca0e1cbc 100644 --- a/porth.porth +++ b/porth.porth @@ -1087,4 +1087,16 @@ proc main // -- drop end +macro O_WRONLY + 1 +end + +macro O_TRUNC + 512 +end + +macro O_CREAT + 64 +end + main diff --git a/porth.py b/porth.py index 69ba464a..b6e142a0 100755 --- a/porth.py +++ b/porth.py @@ -2214,6 +2214,26 @@ def usage(compiler_name: str): print(" -s Silent mode. Don't print any info about compilation phases.") print(" -cf Dump Control Flow graph of the program in a dot format.") print(" help Print this help to stdout and exit with 0 code") +class IntrinRepr: + def __init__(self, string_value): + self.repr_string = string_value + def __repr__(val): + return "OpType.Intrinsic" + +Intrinsic.as_string = IntrinRepr("OpType.Intrinsic") + +def op_repr(op): + if op.typ == OpType.INTRINSIC: + return (Intrinsic.as_string, INTRINSIC_NAMES[op.operand]) + return () + +def print_whole_program(program): + for i in range(64): + op = program[i] + if op.typ == OpType.INTRINSIC: + print(op_repr(op)) + else: + print(op_repr(op),(op.typ, op.operand)) if __name__ == '__main__' and '__file__' in globals(): argv = sys.argv @@ -2274,6 +2294,7 @@ def usage(compiler_name: str): control_flow = False run = False output_path = None + print_program = False while len(argv) > 0: arg, *argv = argv if arg == '-r': @@ -2288,6 +2309,8 @@ def usage(compiler_name: str): output_path, *argv = argv elif arg == '-cf': control_flow = True + elif arg == "-p": + print_program = True else: program_path = arg break From e6edf33adf1f1e6b4dca589d6cae7388a9d76b36 Mon Sep 17 00:00:00 2001 From: rexim Date: Wed, 13 Oct 2021 18:06:33 +0700 Subject: [PATCH 139/145] Cleanup logging during Control Flow graph generation --- porth.porth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/porth.porth b/porth.porth index ca0e1cbc..c4968a52 100644 --- a/porth.porth +++ b/porth.porth @@ -1,5 +1,5 @@ // In progress rewrite of ./porth.py in Porth - +//patch_rebase_fix1 include "std.porth" const MEM_CAPACITY 640000 end From 6c195da1ca298843d9d6da06e0691f6f216bc711 Mon Sep 17 00:00:00 2001 From: mjz19910 Date: Thu, 14 Oct 2021 08:50:13 -0700 Subject: [PATCH 140/145] Grab upstream from gitlab and apply stash --- porth.porth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/porth.porth b/porth.porth index c4968a52..3a543596 100644 --- a/porth.porth +++ b/porth.porth @@ -1,5 +1,5 @@ // In progress rewrite of ./porth.py in Porth -//patch_rebase_fix1 +//patch_rebase_fix2 include "std.porth" const MEM_CAPACITY 640000 end From 618e7e0de75bd48a9fb7588231c27b934d15af02 Mon Sep 17 00:00:00 2001 From: mjz19910 Date: Mon, 25 Oct 2021 17:15:35 -0700 Subject: [PATCH 141/145] Manual rebase(checkout -p upstream) --- porth.porth | 12 ------------ porth.py | 23 ----------------------- std/std.porth | 1 - tests/consts.txt | 11 ++++++----- 4 files changed, 6 insertions(+), 41 deletions(-) diff --git a/porth.porth b/porth.porth index 3a543596..d65c5f5f 100644 --- a/porth.porth +++ b/porth.porth @@ -1087,16 +1087,4 @@ proc main // -- drop end -macro O_WRONLY - 1 -end - -macro O_TRUNC - 512 -end - -macro O_CREAT - 64 -end - main diff --git a/porth.py b/porth.py index b6e142a0..69ba464a 100755 --- a/porth.py +++ b/porth.py @@ -2214,26 +2214,6 @@ def usage(compiler_name: str): print(" -s Silent mode. Don't print any info about compilation phases.") print(" -cf Dump Control Flow graph of the program in a dot format.") print(" help Print this help to stdout and exit with 0 code") -class IntrinRepr: - def __init__(self, string_value): - self.repr_string = string_value - def __repr__(val): - return "OpType.Intrinsic" - -Intrinsic.as_string = IntrinRepr("OpType.Intrinsic") - -def op_repr(op): - if op.typ == OpType.INTRINSIC: - return (Intrinsic.as_string, INTRINSIC_NAMES[op.operand]) - return () - -def print_whole_program(program): - for i in range(64): - op = program[i] - if op.typ == OpType.INTRINSIC: - print(op_repr(op)) - else: - print(op_repr(op),(op.typ, op.operand)) if __name__ == '__main__' and '__file__' in globals(): argv = sys.argv @@ -2294,7 +2274,6 @@ def print_whole_program(program): control_flow = False run = False output_path = None - print_program = False while len(argv) > 0: arg, *argv = argv if arg == '-r': @@ -2309,8 +2288,6 @@ def print_whole_program(program): output_path, *argv = argv elif arg == '-cf': control_flow = True - elif arg == "-p": - print_program = True else: program_path = arg break diff --git a/std/std.porth b/std/std.porth index ab113be9..89c3542e 100644 --- a/std/std.porth +++ b/std/std.porth @@ -407,7 +407,6 @@ macro getpid SYS_getpid syscall0 end macro execve SYS_execve syscall3 end macro wait4 SYS_wait4 syscall4 end - macro 2dup over over end macro 2drop drop drop end diff --git a/tests/consts.txt b/tests/consts.txt index 8a2aa4cd..5828c0f1 100644 --- a/tests/consts.txt +++ b/tests/consts.txt @@ -2,10 +2,11 @@ :b stdin 0 :i returncode 0 -:b stdout 11 -69 -420 -489 - +:b stdout 10 +0 +1 +2 +3 +4 :b stderr 0 From 1ee34dc07701e6c012bde7d261257cf2fb4474a1 Mon Sep 17 00:00:00 2001 From: mjz19910 Date: Mon, 25 Oct 2021 17:16:21 -0700 Subject: [PATCH 142/145] add settings and spelling json --- .vscode/settings.json | 6 + cspell.json | 257 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 263 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 cspell.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..32a3e0f3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "cSpell.enabled": true, + "cSpell.words": [ + "O_WRONLY" + ] +} \ No newline at end of file diff --git a/cspell.json b/cspell.json new file mode 100644 index 00000000..661246ef --- /dev/null +++ b/cspell.json @@ -0,0 +1,257 @@ +{ + "version": "0.2", + "ignorePaths": [], + "dictionaryDefinitions": [], + "dictionaries": [], + "words": [ + "ABSTIME", + "adjtime", + "adjtimex", + "Alexey", + "alist", + "autoload", + "capget", + "capset", + "chdir", + "cmove", + "cmovg", + "cmovge", + "cmovl", + "cmovle", + "cmovne", + "creat", + "cstr", + "cstreq", + "cstrlen", + "curr", + "dcookie", + "defconst", + "dirfd", + "divmod", + "elif", + "ENOENT", + "epoll", + "eputs", + "eval", + "eventfd", + "execve", + "faccessat", + "fadvise", + "fallocate", + "fanotify", + "fchdir", + "fchmod", + "fchmodat", + "fchown", + "fchownat", + "fcntl", + "fdatasync", + "FDCWD", + "felf", + "fgetxattr", + "finit", + "flistxattr", + "fputs", + "fremovexattr", + "fsetxattr", + "fstat", + "fstatfs", + "ftruncate", + "futex", + "futimesat", + "getaffinity", + "getcpu", + "getcwd", + "getdents", + "getegid", + "geteuid", + "getevents", + "getgid", + "getgroups", + "getitimer", + "getoverrun", + "getparam", + "getpeername", + "getpgid", + "getpgrp", + "getpid", + "getpmsg", + "getppid", + "getpriority", + "getres", + "getresgid", + "getresuid", + "getrlimit", + "getrusage", + "getscheduler", + "getsetattr", + "getsid", + "getsockname", + "getsockopt", + "gettid", + "gettime", + "gettimeofday", + "getuid", + "getxattr", + "inotify", + "Intrin", + "ioperm", + "iopl", + "ioprio", + "kcmp", + "kexec", + "keyctl", + "Kutepov", + "lchown", + "lgetxattr", + "linkat", + "listxattr", + "llistxattr", + "lremovexattr", + "lseek", + "lsetxattr", + "madvise", + "malloc", + "MALLOC", + "mbind", + "mempolicy", + "mincore", + "mkdir", + "mkdirat", + "mknod", + "mknodat", + "mlock", + "mlockall", + "mmap", + "mprotect", + "mremap", + "msgctl", + "msgget", + "msgrcv", + "msgsnd", + "msync", + "munlock", + "munlockall", + "munmap", + "nanosleep", + "nasm", + "newfstatat", + "nfsservctl", + "openat", + "porth", + "ppoll", + "prctl", + "pread", + "preadv", + "prlimit", + "pselect", + "pstr", + "ptrace", + "ptrs", + "putd", + "PUTD", + "putpmsg", + "pwait", + "pwrite", + "pwritev", + "quotactl", + "RDONLY", + "readahead", + "readlink", + "readlinkat", + "readv", + "recvfrom", + "recvmmsg", + "recvmsg", + "removexattr", + "renameat", + "resb", + "resq", + "rtokens", + "sched", + "semctl", + "semget", + "semop", + "semtimedop", + "sendfile", + "sendmmsg", + "sendmsg", + "sendto", + "setaffinity", + "setdomainname", + "setfsgid", + "setfsuid", + "setgid", + "setgroups", + "sethostname", + "setitimer", + "setns", + "setparam", + "setpgid", + "setpriority", + "setq", + "setregid", + "setresgid", + "setresuid", + "setreuid", + "setrlimit", + "setscheduler", + "setsid", + "setsockopt", + "settime", + "settimeofday", + "setuid", + "setxattr", + "shmat", + "shmctl", + "shmdt", + "shmget", + "sigaction", + "sigaltstack", + "signalfd", + "sigpending", + "sigprocmask", + "sigqueueinfo", + "sigreturn", + "sigsuspend", + "sigtimedwait", + "socketpair", + "statfs", + "strs", + "struct", + "swapoff", + "swapon", + "symlinkat", + "syms", + "syncfs", + "syscall", + "syscalls", + "Syscalls", + "sysfs", + "sysinfo", + "tgkill", + "tgsigqueueinfo", + "timedreceive", + "timedsend", + "timerfd", + "tkill", + "TRUNC", + "Tsvg", + "tuxcall", + "unlinkat", + "uselib", + "ustat", + "utime", + "utimensat", + "utimes", + "vfork", + "vhangup", + "vmsplice", + "vserver", + "waitid", + "waitpid", + "writev" + ], + "ignoreWords": [], + "import": [] +} From ad51df3a19b65a24d32bbdf99ff94c67d2ff0711 Mon Sep 17 00:00:00 2001 From: mjz19910 Date: Mon, 25 Oct 2021 17:49:07 -0700 Subject: [PATCH 143/145] add cspell words --- cspell.json | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/cspell.json b/cspell.json index 661246ef..ae322cf6 100644 --- a/cspell.json +++ b/cspell.json @@ -9,7 +9,9 @@ "adjtimex", "Alexey", "alist", + "atim", "autoload", + "blksize", "capget", "capset", "chdir", @@ -19,18 +21,23 @@ "cmovl", "cmovle", "cmovne", + "consts", "creat", "cstr", "cstreq", "cstrlen", + "ctim", "curr", "dcookie", "defconst", + "delim", "dirfd", "divmod", "elif", "ENOENT", + "envp", "epoll", + "eputd", "eputs", "eval", "eventfd", @@ -51,6 +58,7 @@ "fgetxattr", "finit", "flistxattr", + "fputd", "fputs", "fremovexattr", "fsetxattr", @@ -95,9 +103,12 @@ "getxattr", "inotify", "Intrin", + "Intrinsics", + "INTRINSICS", "ioperm", "iopl", "ioprio", + "isdigit", "kcmp", "kexec", "keyctl", @@ -107,6 +118,7 @@ "linkat", "listxattr", "llistxattr", + "lnot", "lremovexattr", "lseek", "lsetxattr", @@ -114,7 +126,9 @@ "malloc", "MALLOC", "mbind", + "memcpy", "mempolicy", + "ment", "mincore", "mkdir", "mkdirat", @@ -130,6 +144,7 @@ "msgrcv", "msgsnd", "msync", + "mtim", "munlock", "munlockall", "munmap", @@ -137,6 +152,8 @@ "nasm", "newfstatat", "nfsservctl", + "nlink", + "offsetof", "openat", "porth", "ppoll", @@ -144,6 +161,7 @@ "pread", "preadv", "prlimit", + "procs", "pselect", "pstr", "ptrace", @@ -155,7 +173,9 @@ "pwrite", "pwritev", "quotactl", + "rdev", "RDONLY", + "RDWR", "readahead", "readlink", "readlinkat", @@ -167,6 +187,7 @@ "renameat", "resb", "resq", + "returncode", "rtokens", "sched", "semctl", @@ -216,9 +237,12 @@ "sigsuspend", "sigtimedwait", "socketpair", + "statbuf", "statfs", + "streq", "strs", "struct", + "Structs", "swapoff", "swapon", "symlinkat", @@ -234,6 +258,7 @@ "timedreceive", "timedsend", "timerfd", + "timespec", "tkill", "TRUNC", "Tsvg", @@ -250,7 +275,8 @@ "vserver", "waitid", "waitpid", - "writev" + "writev", + "wstatus" ], "ignoreWords": [], "import": [] From 1d0f1d748266f08bac709b896097e41944d3b2d1 Mon Sep 17 00:00:00 2001 From: mjz19910 Date: Mon, 25 Oct 2021 17:58:10 -0700 Subject: [PATCH 144/145] add cspell words --- cspell.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cspell.json b/cspell.json index ae322cf6..c68fc563 100644 --- a/cspell.json +++ b/cspell.json @@ -60,6 +60,7 @@ "flistxattr", "fputd", "fputs", + "fputu", "fremovexattr", "fsetxattr", "fstat", @@ -169,6 +170,7 @@ "putd", "PUTD", "putpmsg", + "putu", "pwait", "pwrite", "pwritev", From 157023fe7247cc4ab8995af9092edc2cd994b14c Mon Sep 17 00:00:00 2001 From: rexim Date: Tue, 26 Oct 2021 08:48:15 +0700 Subject: [PATCH 145/145] [std.porth] remove all macro usage --- porth.py | 2 + std/std.porth | 150 ++++++++++++++++++++------------------------- tests/memcpy.porth | 6 +- 3 files changed, 70 insertions(+), 88 deletions(-) diff --git a/porth.py b/porth.py index 69ba464a..d169936c 100755 --- a/porth.py +++ b/porth.py @@ -50,6 +50,8 @@ class Intrinsic(Enum): PLUS=auto() MINUS=auto() MUL=auto() + # TODO: split divmod intrinsic into div and mod back + # It was never useful DIVMOD=auto() EQ=auto() GT=auto() diff --git a/std/std.porth b/std/std.porth index 89c3542e..c3ab2dd3 100644 --- a/std/std.porth +++ b/std/std.porth @@ -1,23 +1,9 @@ -// Deprecated Words ////////// -macro .64 swap !64 end -macro ,64 @64 end -macro ! !8 end -macro @ @8 end -macro . swap ! end -macro , @ end -macro cstr-to-pstr cstr-to-str end -memory mem 640000 end -macro nop end -macro fputd fputu end -macro eputd eputu end -macro putd putu end -////////////////////////////// - // TODO: NULL should have type ptr const NULL 0 end -macro true 1 cast(bool) end -macro false 0 cast(bool) end +// TODO: using procs for true/false is kinda wasteful +proc true 1 cast(bool) end +proc false 0 cast(bool) end const sizeof(u64) 8 end const sizeof(u32) 4 end @@ -365,20 +351,20 @@ const PROT_READ 1 end const sizeof(timespec) 16 end const sizeof(stat) 144 end -macro stat.st_dev 0 + end -macro stat.st_ino 8 + end -macro stat.st_mode 24 + end -macro stat.st_nlink 16 + end -macro stat.st_uid 28 + end -macro stat.st_gid 32 + end -macro stat.st_rdev 40 + end -macro stat.st_size 48 + end -macro @stat.st_size stat.st_size @64 end -macro stat.st_blksize 56 + end -macro stat.st_blocks 64 + end -macro stat.st_atim 72 + end -macro stat.st_mtim 88 + end -macro stat.st_ctim 104 + end +proc stat.st_dev 0 + end +proc stat.st_ino 8 + end +proc stat.st_mode 24 + end +proc stat.st_nlink 16 + end +proc stat.st_uid 28 + end +proc stat.st_gid 32 + end +proc stat.st_rdev 40 + end +proc stat.st_size 48 + end +proc @stat.st_size stat.st_size @64 end +proc stat.st_blksize 56 + end +proc stat.st_blocks 64 + end +proc stat.st_atim 72 + end +proc stat.st_mtim 88 + end +proc stat.st_ctim 104 + end const sizeof(stat.st_dev) sizeof(u64) end const sizeof(stat.st_ino) sizeof(u64) end const sizeof(stat.st_mode) sizeof(u32) end @@ -394,46 +380,35 @@ const sizeof(stat.st_mtim) sizeof(timespec) end const sizeof(stat.st_ctim) sizeof(timespec) end // Wrappers for common syscalls -macro write SYS_write syscall3 end -macro read SYS_read syscall3 end -macro openat SYS_openat syscall4 end -macro fstat SYS_fstat syscall2 end -macro close SYS_close syscall1 end -macro exit SYS_exit syscall1 drop end -macro mmap SYS_mmap syscall6 end -macro clock_nanosleep SYS_clock_nanosleep syscall4 end -macro fork SYS_fork syscall0 end -macro getpid SYS_getpid syscall0 end -macro execve SYS_execve syscall3 end -macro wait4 SYS_wait4 syscall4 end - -macro 2dup over over end -macro 2drop drop drop end - -macro / divmod drop end -macro % divmod swap drop end -macro mod % end -macro div / end - -macro nth_argv +proc write SYS_write syscall3 end +proc read SYS_read syscall3 end +proc openat SYS_openat syscall4 end +proc fstat SYS_fstat syscall2 end +proc close SYS_close syscall1 end +proc exit SYS_exit syscall1 drop end +proc mmap SYS_mmap syscall6 end +proc clock_nanosleep SYS_clock_nanosleep syscall4 end +proc fork SYS_fork syscall0 end +proc getpid SYS_getpid syscall0 end +proc execve SYS_execve syscall3 end +proc wait4 SYS_wait4 syscall4 end + +proc 2dup over over end +proc 2drop drop drop end + +proc / divmod drop end +proc % divmod swap drop end +proc mod % end +proc div / end + +proc nth_argv 8 * argv + @64 cast(ptr) end -macro inc64 - dup @64 1 + swap !64 -end - -macro dec64 - dup @64 1 - swap !64 -end - -macro inc32 - dup @32 1 + swap !32 -end - -macro dec32 - dup @32 1 - swap !32 -end +proc inc64 dup @64 1 + swap !64 end +proc dec64 dup @64 1 - swap !64 end +proc inc32 dup @32 1 + swap !32 end +proc dec32 dup @32 1 - swap !32 end proc cstrlen dup @@ -456,22 +431,12 @@ proc cstreq and end -macro cstr-to-str - dup cstrlen swap -end +proc cstr-to-str dup cstrlen swap end // TODO: fputs should crash the app if write fails -macro fputs - write drop -end - -macro puts - stdout fputs -end - -macro eputs - stderr fputs -end +proc fputs write drop end +proc puts stdout fputs end +proc eputs stderr fputs end const offsetof(Str.count) sizeof(u64) offset end const offsetof(Str.data) sizeof(ptr) offset end @@ -484,7 +449,7 @@ proc @Str.data Str.data @64 cast(ptr) end proc !Str.count Str.count !64 end proc !Str.data Str.data !64 end -macro @Str +proc @Str dup @Str.count swap @Str.data end @@ -572,7 +537,7 @@ end // Custom logical not, since the intrinsic `not` is the bitwise one and does not allow // to properly invert a boolean. -macro lnot +proc lnot cast(int) 1 - cast(bool) end @@ -591,7 +556,7 @@ proc fputu // value fd -- while over 0 > do 1 - dup rot 10 divmod - rot swap '0' + . swap + rot swap '0' + swap !8 swap end dup @@ -618,3 +583,18 @@ proc memcpy // size src dst -- 1 - end drop end + +// Deprecated Words ////////// +proc .64 swap !64 end +proc ,64 @64 end +proc ! !8 end +proc @ @8 end +proc . swap ! end +proc , @ end +proc cstr-to-pstr cstr-to-str end +memory mem 640000 end +proc nop end +proc fputd fputu end +proc eputd eputu end +proc putd putu end +////////////////////////////// diff --git a/tests/memcpy.porth b/tests/memcpy.porth index 921b29a8..19f99e92 100644 --- a/tests/memcpy.porth +++ b/tests/memcpy.porth @@ -1,8 +1,8 @@ include "std.porth" -const N 32 end -const K 8 end -const M N K / end +const N 32 end +const K 8 end +const M N K divmod drop end memory a N end memory b M end