{"id":20761925,"url":"https://github.com/james-p-d/forthish","last_synced_at":"2025-10-26T01:32:08.341Z","repository":{"id":170649430,"uuid":"194167393","full_name":"James-P-D/Forthish","owner":"James-P-D","description":"A C# Console implementation for a Forth-like stack-based language","archived":false,"fork":false,"pushed_at":"2021-07-04T20:39:50.000Z","size":124,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-26T01:31:51.281Z","etag":null,"topics":["csharp","forth","forth-like","interpreter","stack"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/James-P-D.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-27T21:47:36.000Z","updated_at":"2024-01-11T00:19:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"c8b946b6-a604-4be8-ab79-ae6556bb9c3e","html_url":"https://github.com/James-P-D/Forthish","commit_stats":null,"previous_names":["james-p-d/forthish"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/James-P-D/Forthish","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/James-P-D%2FForthish","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/James-P-D%2FForthish/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/James-P-D%2FForthish/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/James-P-D%2FForthish/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/James-P-D","download_url":"https://codeload.github.com/James-P-D/Forthish/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/James-P-D%2FForthish/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281047793,"owners_count":26435124,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","forth","forth-like","interpreter","stack"],"created_at":"2024-11-17T10:27:54.256Z","updated_at":"2025-10-26T01:32:08.303Z","avatar_url":"https://github.com/James-P-D.png","language":"C#","readme":"# Forthish\nA C# Console application for a [Forth](https://en.wikipedia.org/wiki/Forth_(programming_language))-like stack-based language\n\n![Screenshot](https://github.com/James-P-D/Forthish/blob/master/screenshot.gif)\n\n## Introduction\n\n[Forth](https://en.wikipedia.org/wiki/Forth_(programming_language)) is an low-level, imperative stack-based language, invented in the late 1960s. Due to its small size and closeness to assembler, the language was often used for robotics and astronomy, most famously at [NASA](https://www.forth.com/resources/space-applications/), in applications where microprocessor memory was minimal.\n\nThe language is rarely used nowadays, although it has heavily influenced a number of [Golfing](https://codegolf.stackexchange.com/) languages such as [CJam](https://esolangs.org/wiki/CJam), [05AB1E](https://github.com/Adriandmen/05AB1E) and [GolfScript](http://www.golfscript.com/golfscript/). Stack machines also form the basis of both the [Java Virtual Machine](https://en.wikipedia.org/wiki/Java_virtual_machine) and [WebAssembly](https://en.wikipedia.org/wiki/WebAssembly).\n\nThis implemention is by no means complete, nor matching the specific syntax of most Forth specifications. This decision is based on the somewhat unorthodox choice of keywords in most traditional implementions, most obviously the choice of using `if..else..endif` rather than the traditional syntax of `if..else..then` (where `then` is actually an `endif`) which might confuse novice users. Other design decisions include introducing a `::` operator for redefining definitions, whereas in classic Forth, this isn't possible, with definitions only ever being set once.\n\nFor a more complete Forth implementation I'd recommend the [FastForth](https://osdn.net/projects/fast-forth/) compiler/interpreter and [J.V. Noble's A Beginner's Guide to Forth](http://galileo.phys.virginia.edu/classes/551.jvn.fall01/primer.htm).\n\n## Contents\n\n1. [Basics](#Basics)\n2. [Output String](#Output-String)\n3. [Stack Operators](#Stack-Operators)\n4. [Maths Operators](#Maths-Operators)\n5. [Logical Operators](#Logical-Operators)\n6. [Definitions](#Definitions)\n7. [If..else..endif](#If-else-endif)\n8. [Loop..endloop and repeat..until](#Loop-endloop-and-repeat-until)\n9. [Variables, Values and Constants](#Variables-Values-and-Constants)\n10. [Arrays](#Arrays)\n11. [Example Programs](#Example-Programs)\n\n## Basics\n\nThis Forth interpreter has been implemented as an interactive console application. After running you should see something like the following:\n\n```\nForth Interpreter\nPress \u003cESC\u003e to exit\n\nLoading resource: Constants\nLoading resource: Definitions\n\nReady!\n```\n\nIt is also possible to load Forth source-code files by passing them as parameters to the executable:\n\n```\nC:\\\u003eForth.exe c:\\source\\code.for\nForth Interpreter\nPress \u003cESC\u003e to exit\n\nLoading resource: Constants\nLoading resource: Definitions\nLoading source file: c:\\source\\code.for\n\nReady!\n```\n\nAs already mentioned in the introduction, Forth is a stack-based language and most programming will consist of pushing values to the stack and then calling various operators which will operate on the stack, usually pushing the result of the operation back onto the stack.\n\nWe can add items to the stack simply by typing numbers and pressing enter:\n\n```\n2019\n```\n\nThe interpreter will respond with the following which indicates that the process was successful:\n\n```\n\u003e OK\n```\n\nWe can pop items from the stack and display them using `.`:\n\n```\n.\n\u003e 2019 OK\n```\n\nWe can push multiple items on a single line:\n\n```\n1 2 3 4 5\n\u003e OK\n```\n\n..and we can pop them back off in a single line:\n\n```\n. . . . .\n\u003e 5 4 3 2 1 OK\n```\n\nNote that because of the last-in-first-out nature of stacks, the numbers are read back to us in the opposite order to which they were added.\n\nWe can only pop items from the stack if the stack is not empty. If we attempt to pop more items than there are available, we'll see a stack underflow exception:\n\n```\n11 22 33 . . . .\n\u003e 33 22 11 ERROR: Stack Underflow\n```\n\nIn the above we successfully push values to the stack, then used the `.` operator to pop and display them. The interpreter was able to pop and display the first three values, but when it tried for the fourth item, we generated an exception.\n\nWe can also push and pop in a single line:\n\n```\n1 2 3 . . 4 5 . . .\n\u003e 3 2 5 4 1 OK\n```\n\nIn Forth comments are places between parentheses:\n\n```\n1 2 (3 4) 5 . . .\n\u003e 5 2 1 OK\n```\n\nIn the above example, `1` and `2` are pushed to the stack, but then the interpretor encounters a comment, so it stops processing until the brackets are closed. Having reached the end of the comment, the interpreter continues and pushes the number `5` before popping each item and displaying it.\n\nUsually Forth will process input as soon as enter is pressed. If you wish to write a multi-line program you must use the `\\` character to terminate each line. The interpreter will not begin execution until a line is entered which doesn't terminate in `\\`:\n\n```\n1 2 3 4 \\\n5 6 7 8 \\\n. . . . \\\n. . . .\n\u003e 8 7 6 5 4 3 2 1 OK\n```\n\nSo far we have only been pushing integers to our stack, but it is also possible to push floats, characters and hexadecimal numbers using the commands `fractional`, `char`, and `hex`. For example:\n\n```\nhex bd34 .\n\u003e BD34 OK\n0a0c .\n\u003e A0C OK\nfractional\n\u003e OK\n12.34 .\n\u003e 12.34 OK\nchar\n\u003e OK\n'd' .\n\u003e d OK\ndecimal 1234 .\n\u003e 1234 OK\n```\n\nBecause Forth stores everything in 32-bit cells, how it displays the information popped from the stack will depend upon what mode you are in. If you aren't careful you may see some strange output if in the wrong number-format:\n\n```\nfractional 12.34\n\u003e OK\ndecimal .\n\u003e 1095069860 OK\nchar 'A'\n\u003e OK\ndecimal .\n\u003e 65 OK\nhex abcd\n\u003e OK\ndecimal .\n\u003e 43981 OK\n```\n\n## Output String\n\nWe have already seen how to pop and display the top of the stack using the `.` operator, but you may also find you need to display strings. This is accomplished with the `.\"\"` operator. For example:\n\n```\n.\"Hello, world! \"\n\u003e Hello, world! OK\n```\n\nWe can also add new-lines with the carriage-return command `cr`:\n\n```\ncr .\"Hello, \" cr .\"world!\" cr\n\u003e\nHello,\nworld!\nOK\n```\n\n## Stack Operators\n\nSince Forth is a stack-based language, there are multiple built-in operators for manipulating the stack.\n\n`dup` duplicates the top value on the stack and pushes it again:\n\n```\n1 2 3 4 dup . . . . .\n\u003e 4 4 3 2 1 OK\n```\n\n`swap` swaps the top two items on the stack:\n\n```\n1 2 3 4 swap . . . .\n\u003e 3 4 2 1 OK\n```\n\n`drop` deletes the top of the stack:\n\n```\n1 2 3 4 drop . . .\n\u003e 3 2 1 OK\n```\n\n`rot` rotates the top three items:\n\n```\n1 2 3 4 rot . . .\n\u003e 2 4 3 1 OK\n```\n\n`over` duplicates the second item on the stack and pushes it to the top:\n\n```\n1 2 3 4 over . . . . .\n\u003e 3 4 3 2 1 OK\n```\n\n`tuck` duplicates the top item on the stack and pushes it below the second:\n\n```\n1 2 3 4 tuck . . . . .\n\u003e 4 3 4 2 1 OK\n```\n\n`pick` accepts a parameter `N` which is then used to duplicate the Nth element onto the top of the stack. Note that `0 pick` is effectively the same as `rot`:\n\n```\n1 2 3 4 5 6 7 8 9 10\n\u003e OK\n5 pick\n\u003e OK\n. . . . . . . . . . .\n\u003e 5 10 9 8 7 6 5 4 3 2 1 OK\n```\n\n`roll` accepts a paramter `N` which is then removed from the stack and placed at the top:\n\n```\n1 2 3 4 5 6 7 8 9 10\n\u003e OK\n3 roll . . . . . . . . . .\n\u003e 7 10 9 8 6 5 4 3 2 1 OK\n```\n\n## Maths Operators\n\nThere are multiple mathematical operators which pop two items from the stack, perform an operation, and push the result back to the stack:\n\n```\n20 15 + .\n\u003e 35 OK\n```\n\nYou will need to think carefully about the order in which you push numbers and operators in Forth since we have no concept of operator-precedence (multiplication binds closer than addition in most C-like languages), and no option of using parentheses. If we wished to compute `(3 + 4) * (-5 + 13)` we would need to enter the following:\n\n```\n3 4 + -5 13 + * .\n\u003e 56 OK\n```\n\nWhat's happening here? First we push `3` and `4`, then the addition operator pops and adds them together before putting the result (`7`) on the stack. Then `-5` and `13` are pushed and another operator pops them, adds them, and pushes the result (`8`) to the stack. Finally, the multiplication operator pops our two calculations, multiplies them and puts the final result `56`) back on the stack before popping it and displaying it.\n\nWe can also do integer division/modulus:\n\n```\n13 5 / .\n\u003e 2 OK\n13 5 mod .\n\u003e 3 OK\n```\n\n## Logical Operators\n\nIn Forth, `true` is considered to be zero and `false` as `-1`. Infact, both `true` and `false` are defined an constants in the interpreter (more on Constants, Variables and Values later on.) We can confirm the values of `true` and `false` easily enough:\n\n```\ntrue .\n\u003e 0 OK\nfalse .\n\u003e -1 OK\n```\n\nWe also have the basic equality operators `=`, `!=`, `\u003c`, `\u003e`, `\u003c=`, `\u003e=`, all of which pop two items from the stack and push the result:\n\n```\n3 4 = .\n\u003e -1 OK\n3 4 != .\n\u003e 0 OK\n3 4 \u003c .\n\u003e -1 OK\n```\n\nFinally, we have the usual logical operators `and`, `or` and `not` respectively:\n\n```\ntrue true and .\n\u003e 0 OK\ntrue false and .\n\u003e -1 OK\ntrue false or .\n\u003e 0 OK\nfalse false or .\n\u003e -1 OK\ntrue not .\n\u003e -1 OK\nfalse not .\n\u003e 0 OK\n```\n\n## Definitions\n\nFor operations that occur frequently, we can create definitions. These take the form `: N C1..CN ;` where `N` is the name of the definition, and `C1` to `CN` are a series of commands.\n\nFor example, to create a definition that squares a number we would write:\n\n```\n: square dup * ;\n\u003e OK\n```\n\nOur definition duplicates whatever is on the stack and adds it to the stack, then the multiplication operator pops the top two values, multiples them, and pushes the result. To square the value `3` and then display it we would use the following:\n\n```\n3 square .\n\u003e 9 OK\n```\n\nWe can even call the same definition multiple times:\n\n```\n3 square square .\n\u003e 81 OK\n```\n\nThe above pushes `3` to the stack, then our definition squares it and pushes the result (`9`) to the stack, then the definition is called again and `9` is popped, squared, and the result (`81`) pushed. Finally we pop the final result and display it.\n\nDefinitions can also call other definitions, so we can easily make a definition for cubing values:\n\n```\n: cubed square square ;\n```\n\nWe can then use the following:\n\n```\n3 cubed .\n\u003e 81 OK\n```\n\nIf you want to change a definition, you must use the `::` operator:\n\n```\n: cubed dup * dup * ;\n\u003e ERROR: 'cubed' is already defined\n:: cubed dup * dup * ;\n\u003e OK\n3 cubed .\n\u003e 81 OK\n```\n\nIt is also possible to call definitions recursively, but watch out for overflowing the processing stack. For example, the following definition adds `1` to the item on the stack, displays it, then calls itself again:\n\n```\n: add1 1 + dup . add1 ;\n```\n\nIf we now push `0` to the stack and call `add1`, we see the following:\n\n```\n0 add1\n\u003e 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 ERROR: Processing stack overflow\n```\n\nWe will learn how to avoid this by using base cases for recursive calls to definitions when we discuss the `if..else..endif` commands.\n\nFinally, it is possible to get details of all existing definitions with the `viewdefinitions` command.\n\n## If else endif\n\nThe `if..endif` construct pops a value from the stack, and if true will execute all commands until it reaches the `endif`:\n\n```\n: ispositive 0 \u003e if .\"positive\" cr endif ;\n\u003e OK\n5 ispositive\n\u003e positive OK\n-5 ispositive\n\u003e OK\n```\n\nWhen we run the program it correctly outputted `positive` for `5` but it did nothing when `if` evaluated to false. If we want to execute commands when the `if` fails, we can use `else`:\n\n```\n:: ispositive 0 \u003e if .\"positive\" else .\"negative\" endif cr ;\n\u003e OK\n5 ispositive\n\u003e positive OK\n-5 ispositive\n\u003e negative OK\n```\n\nIt is possible to have `if..else..if..` ladders:\n\n```\n:: ispositive              \\\ndup 0 \u003c if                 \\\n        .\"positive \"       \\\nelse 0 \u003e if                \\\n            .\"negative \"   \\\n        else               \\\n            .\"zero \"       \\\n\tendif              \\\nendif ;\n\u003e OK\n-5 ispositive\n\u003e negative OK\n5 ispositive\n\u003e positive OK\n0 ispositive\n\u003e zero OK\n```\n\nFinally, it is possible to have `if`s within other `if`s:\n\n```\n: size                           \\\ndup 10 \u003e if                      \\\n        dup 5 \u003e if               \\\n                .\"very small \"   \\\n        else                     \\\n                .\"quite small \"  \\\n        endif                    \\\nelse                             \\\n        dup 15 \u003e if              \\\n                .\"quite big \"    \\\n        else                     \\\n                .\"very big \"     \\\n        endif                    \\\nendif                            \\\ndrop ;\n\u003e OK\n2 size\n\u003e very small OK\n7 size\n\u003e quite small OK\n12 size\n\u003e quite big OK\n99 size\n\u003e very big OK\n```\n\n## Loop endloop and repeat until\n\n`loop..endloop` constucts accept three parameters specifying the end condition, the start condition, and the increment:\n\n```\n10 0 2 loop swap dup . swap endloop\n\u003e 0 2 4 6 8 OK\n```\n\n`repeat..until` constructs repeat a sequence of commands until `true` is on the stack:\n\n```\n5 repeat .\" hello! \" 1 - dup 0 = until drop\n\u003e  hello!  hello!  hello!  hello!  hello! OK\n```\n\nIn the above code, we push `5` to the stack and then repeat a sequence of commands that decrements the counter until it is zero. The final `drop` removes the counter.\n\n## Variables, Values and Constants\n\nSo far, all literal objects we have created have been put on the stack. For global objects such as variables, values, constants and arrays, we will need to allocate space in memory whenever they are declared. This Forth implementation has been designed for 32-bit memory allocations, which means all values will be stored in 4 byte chunks called a `cell`. We will need to know the size of cells quite frequently, so there is an in-built constant at our disposal:\n\n```\ncell .\n\u003e 4 OK\n```\n\nWe also have a command called `here` which will push to the stack the current memory pointer. When you first interrogate the value of `here`, you will be told where the current memory pointer is:\n\n```\nhere .\n\u003e 16 OK\n```\n\n`here` will continue to be 16 until we start declaring objects, at which point we should start to see the pointer increment. More on that shortly..\n\nFor many programming applications we may need values which do not change throughout the duration of the program. To achieve this we use the `constant` keyword which pops a value from the stack and must then be proceeded by a valid name:\n\n```\n20 constant twenty\n\u003e OK\n```\n\nHaving created our constant, we can retrieve our value by pushing it to the stack and displaying it:\n\n```\ntwenty .\n\u003e 20 OK\n```\n\nSince constants should not change their value during the running of a program, attempting to assign another value to a constant, will cause an error:\n\n```\n30 constant twenty\n\u003e ERROR: 'twenty' is already defined\n```\n\nHaving created our constant, if we interrogate `here` again, we will see that the memory pointer has been increased by the size of 1 cell (4 bytes):\n\n```\nhere .\n\u003e 20 OK\n```\n\nFor creating named-objects whose values can change, we can use variables. To declare a variable called `my_var` we would use:\n\n```\nvariable my_var\n\u003e OK\n```\n\nAgain, if we interrogate `here` we will see that the memory pointer has been incremented again:\n\n```\nhere.\n\u003e 24 OK\n```\n\nNote that when we declared our variable we didn't give it any default value. If we push the variable to the stack and then pop it's value, we might expect it to be zero, but instead we get the following:\n\n```\nmy_var .\n\u003e 20 OK\n```\n\nThis is because the value of the variable is the pointer to a memory location, in this case 20, which was by no small coincidence, the value of `here` before we declared `my_var`.\n\nTo set the value of the cell pointed to by a variable, we use the `!` (store) command which expects two values on the stack specifying the value to store, and the memory location of the variable:\n\n```\n1234 my_var !\n\u003e OK\n```\n\nIn the above code, we push the value `1234` to the stack, then push the value of `my_var` (which is the memory location `20`) and then call `!` to store the value in memory.\n\nTo retrieve the value of a cell in memory, we can use the `@` (fetch) command, which pops a memory location from the stack, retrieves the value for that cell, and then pushes it to the stack:\n\n```\nmy_var @ .\n\u003e 1234 OK\n```\n\nIn the above code, we push the value of `my_var` (the memory location `20`) to the stack, then `@` pops it, grabs the value of the cell at this location, and pushes it to the stack. Finally, we pop the value off and display it.\n\nIf we don't care about memory locations, we can use `value`s instead of variables. The syntax for creating values is the same as for creating constants:\n\n```\n2019 value this_year\n\u003e OK\n```\n\nWe can then push the value of `this_year` to the stack and display it:\n\n```\nthis_year .\n\u003e 2019 OK\n```\n\nWe can change the value of `this_year` with `to`:\n\n```\n2020 to this_year\n\u003e OK\nthis_year .\n\u003e 2020 OK\n```\n\nFinally you can view all constants, variables and values with `viewobjects`.\n\n## Arrays\n\nAs already discussed, entering `variable some_name` pushes the next available memory location to the stack. For the creation of arrays, Forth offers the `cells` operator which takes one parameter and multiplies it by the size of `cell` and pushes the result to the stack. Forth also offers the `alloc` operator which takes two parameters, first the initial memory location, and then the number of bytes to allocate.\n\nUsing these two operators we can allocate a 6-cell array using the following:\n\n```\nvariable my_array 5 cells allot \n\u003e OK\n```\n\nIn the above, `variable my_array` pushes the next available memory location to the stack, then `5` is pushed, then `cells` pops the number 5 and memory location from the stack, multiplies them and pushes the result to the stack. Finally, `allot` allocates the memory.\n\nWe can now assign values to each cell in our array:\n\n```\n11 my_array 0 cells + ! \\\n22 my_array 1 cells + ! \\\n33 my_array 2 cells + ! \\\n44 my_array 3 cells + ! \\\n55 my_array 4 cells + ! \\\n66 my_array 5 cells + ! \n\u003e OK\n```\n\n...and retrieve the values:\n\n```\nmy_array 0 cells + @ . \\\nmy_array 1 cells + @ . \\\nmy_array 2 cells + @ . \\\nmy_array 3 cells + @ . \\\nmy_array 4 cells + @ . \\\nmy_array 5 cells + @ . \n\u003e 11 22 33 44 55 66 OK\n```\n\n## Example Programs\n\nAs previously mentioned, it is possible to call definitions recursively, but we need to have a base-case to ensure there isn't infinite recursion. Below is an example definition for calculating a factorial:\n\n```\n:: factorial dup 1 \u003c if dup 1 - factorial * else drop 1 endif ;\n\u003e OK\n5 factorial .\n\u003e 120 OK\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjames-p-d%2Fforthish","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjames-p-d%2Fforthish","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjames-p-d%2Fforthish/lists"}