{"id":13393454,"url":"https://github.com/zeroflag/punyforth","last_synced_at":"2025-04-05T06:09:52.231Z","repository":{"id":78687269,"uuid":"53200763","full_name":"zeroflag/punyforth","owner":"zeroflag","description":"Forth inspired programming language for the ESP8266","archived":false,"fork":false,"pushed_at":"2023-10-11T10:58:30.000Z","size":4538,"stargazers_count":419,"open_issues_count":17,"forks_count":41,"subscribers_count":47,"default_branch":"master","last_synced_at":"2025-03-29T05:09:13.665Z","etag":null,"topics":["concatenative-language","embedded-devices","esp8266","forth","iot","microcontroller","programming-language","raspberry-pi"],"latest_commit_sha":null,"homepage":"","language":"Forth","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zeroflag.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","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":"2016-03-05T12:40:44.000Z","updated_at":"2025-03-24T02:43:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"c0d4a5f4-cf74-46d3-a0e3-b959b1662959","html_url":"https://github.com/zeroflag/punyforth","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeroflag%2Fpunyforth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeroflag%2Fpunyforth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeroflag%2Fpunyforth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeroflag%2Fpunyforth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zeroflag","download_url":"https://codeload.github.com/zeroflag/punyforth/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247294541,"owners_count":20915340,"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","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":["concatenative-language","embedded-devices","esp8266","forth","iot","microcontroller","programming-language","raspberry-pi"],"created_at":"2024-07-30T17:00:53.135Z","updated_at":"2025-04-05T06:09:52.208Z","avatar_url":"https://github.com/zeroflag.png","language":"Forth","readme":"[![Build Status](https://travis-ci.org/zeroflag/punyforth.svg?branch=master)](https://travis-ci.org/zeroflag/punyforth)\n\n# Punyforth\n\n\u003cimg src=\"screenshot/esp8266.jpg\" align=\"left\"\u003e\n\nPunyforth is a simple, stack-based, [Forth](https://en.wikipedia.org/wiki/Forth_(programming_language)) inspired programming language that primarily targets Internet of Things (IOT) devices, like the [ESP8266](https://en.wikipedia.org/wiki/ESP8266). The ESP8266 is a low-cost Wi-Fi capable chip with a 80 MHz Xtensa LX3 32 bit CPU, TCP/IP stack, GPIO pins and 512 KiB to 4 MiB flash memory. It is widely used in IoT applications and home automation projects.\n\nPunyforth also runs on x86 (Linux), ARM (Raspberry PI) but these are *not* the primary supported targets.\n\n## Design goals\n\n* Simple\n* Highly interactive\n* Extensible\n* Small memory footprint and resource efficiency\n\n## Quick start\n\nThe easiest way to try out Punyforth is to use a ESP8266 based development board that has USB to serial interface on board (Geekcreit/Doit, Amica, WeMos, LoLin). Connect the development board to your computer via USB. Let's assume the serial port is COM4.\n\n```bash\n$ cd arch/esp8266/bin\n$ python flash.py COM4\n```\n\nThe flash.py utility will store the Punyforth binary and modules source code on the flash memory of the esp8266.\n\nOpen a serial terminal\u003csup\u003e[1](#serial)\u003c/sup\u003e on port COM4 then type:\n\n```forth\nprintln: \"Hello world!\"\n```\n\n\u003cimg src=\"screenshot/helloworld.png\" align=\"center\" height=\"494\" width=\"697\" \u003e\n\n\u003ca name=\"serial\"\u003e1\u003c/a\u003e: Baud rate: 115200 bps. Local echo: on, line mode: enabled. You can find some free terminal emulators [here](https://learn.sparkfun.com/tutorials/terminal-basics/all).\n\nNote that flash.py flashes with Quad I/O speed (qio) by default. This is the fastest mode but not all devices support this. If you have trouble while flashing try adding a --flashmode dio parameter.\n\n##### Now let's do some simple arithmetics.\n\n```forth\n4\ndup\n+\n.\n```\n\nThis should give you the following output.\n\n```lisp\n(stack)\n(stack 4)\n(stack 4 4)\n(stack 8)\n(stack)\n8\n```\nCongratulation, you've just doubled a number and printed out the result in the [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop).\n\nFor a detailed getting started guide see [Developing and deploying Punyforth applications](https://github.com/zeroflag/punyforth/wiki/Developing-and-deploying-Punyforth-applications).\n\n## About the language\n\nPunyforth is a simple, imperative, stack-based, [concatenative programming language](https://en.wikipedia.org/wiki/Concatenative_programming_language) and interactive environment with good metaprogramming support and extensibility.\n\nThe Forth environment combines the compiler with an interactive shell (REPL), where the user can define functions called words.\n\nPunyforth does not have local variables, instead values are kept on a stack. This stack is used only for storing data. There is a separate return stack that stores information about nested subroutine calls. Both stacks are first-class in the language.\n\nAs a consequence of the stack, Punyforth uses a form of syntax known as [Reverse Polish or Postfix Notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation).\n\nIf you type the following code in the REPL:\n\n```forth\n\n1 2 +\n\n```\n\nThe interpreter pushes the number 1 then the number 2 onto the data stack. It executes the word *+*, which pops the two top level items off the stack, calculates their sum, and pushes the result back to the stack.\n\nThe following code calculates *a * a + b * b*.\n\n```forth\n2 3                  \\ let's say a is 2, b is 3\ndup * swap dup * + . \\ prints out 13\n```\n\nThe word *dup* duplicates the top level item of the stack. The word *swap* exchanges the two top level items of the stack.\n\nStack visualization:\n\u003cpre\u003e\n2 3  3  9  2   2 4 13\n  2  3  2  9   2 9\n     2         9\n\u003c/pre\u003e\n\n*dup* and *swap* are [stack shuffle](http://wiki.laptop.org/go/Forth_stack_operators) words. Excessive use of words like them makes the code hard to follow, so it is advisable to use them sparingly. There are many ways to reduce the number of stack shuffles, one of them is to use [quotations and combinators](http://elasticdog.com/2008/12/beginning-factor-shufflers-and-combinators/).\n\nFor example the above code could have been expressed the following way:\n\n```forth\n2 3 { square } bi@ +\n```\n\nWhere square is defined as _dup *_.\n\nSee the chapter about quotations and combinators for more information.\n\n### Differences between Punyforth and other Forth systems\n\nPunyforth is heavily inspired by the [Forth](https://en.wikipedia.org/wiki/Forth_(programming_language)) programming language. It uses the same compilation model (outer interpreter, compiler, modes, dictionary, immediate words, etc.) as other Forth systems. Punyforth is [bootstrapped](http://www.lispcast.com/two-kinds-of-bootstrapping) from a small set of [primitives](arch/x86/primitives.S) written in assembly language. The compiler targets these primitives and compiles [indirect-threaded code](https://en.wikipedia.org/wiki/Threaded_code). Higher level abstractions are built on top of the primitives therefore most of the system is written in itself (in Forth).\n\n#### Some of the differences\n* Punyforth is case sensitive\n* Strings are null-terminated\n* String literals (\"Hello World\") and character literals ($A) are supported\n* Strings can be printed out differently (*print: \"foobar\"* instead of *.\" foobar\"*)\n* Parsing words are ended with a colon character by convention (including *variable:*, *constant:*, *create: does\u003e*)\n* Defining a word in terms of itself results recursion by default (use the *override* word to alter this behaviour)\n* Curly brackets denote quotations instead of locals\n\nPunyforth supports exception handling, multitasking, socket and GPIO APIs and comes with a UART and a TCP REPL.\n\n\n### Programming\n\nDuring programming, the user uses the REPL to write and test small piece of codes or to extend the languge with new words (which are called subroutines or functions in other languages).\n\nThe REPL (also known as the Forth Outer/Text Interpreter) operates in 2 modes. In interpretation mode, it immediately executes the words that the user typed in. In compilation mode (when you start a new word definition), its action depends on the compilation semantic of the current word. In most cases it compiles the execution token (pointer to the word) into the word to be defined. However, if the current word is flagged as immediate, the compiler executes the word at compile time so the word can define its own compilation semantic. This is a bit similar to Lisp macros. Control structures are implemented as immediate words in Forth.\n\n### The syntax\n\nForth has almost no syntax. It grabs tokens separated by whitespace, looks them up in a dictionary then executes either their compilation or interpretation semantic. If the token is not found in the dictionary, it tries to convert it to a number. *Eeverything in Forth is either a word or a number*. Because of the postfix notation there are no precedence rules and parentheses.\n\n```forth\n This is an example of\n valid   Forth syntax 123  *\u0026^%$#@2\n```\n\n### Extending the dictionary\n\nWords are stored in a *dictionary*. The dictionary maps words to executable code or data structures.\n\nYou can use *defining words* to extend the dictionary with new definitions. The most basic defining words is the *:* (colon). This adds a new word to the dictionary with the behavior defined in terms of existing words. A colon definition begins with a colon and ends with a semicolon.\n\n```forth\n: square ( n -- n^2 ) dup * ;\n\n4 square .      \\ prints 16\n```\n\nIn the above example we created a new word called *square* that takes a number off the stack, multiplies it with itself, then leaves the result on the stack. The *( n -- n^2 )* is the optional stack effect comment indicating the input and output parameters.\n\nOther common defining words are *variable:* and *constant:*.\n\n```forth\nvariable: var1                \\ create a variable 'var1' without initializing\n54 init-variable: var2        \\ create a variable 'var2' and initialize it to 54\n42 constant: answer           \\ create a constant 'answer' with the value 42\n\nvar2 @ var1 !   \\ assigns the value of var2 to var1\nvar1 ?          \\ prints out 54\nanswer .        \\ prints out 42\n```\n\n### Control structures\n\nPunyforth supports the regular Forth conditional and loop words.\n\n#### Conditionals\n\nGeneral form of *if else then*.\n\n```forth\n\u003cbool\u003e if \u003cconsequent\u003e else \u003calternative\u003e then\n```\n\nFor example:\n```forth\n: max ( a b -- max )\n  2dup \u003c if nip else drop then ;\n\n10 100 max . \\ prints 100\n```\n\nThe else part can be omitted.\n\n```forth\n: abs ( n -- absn )\n  dup 0\u003c if -1 * then ;\n\n-10 abs . \\ prints 10\n```\n\n#### Case statement\n\nPunyforth also supports switch-case like flow control logic as shown in the following example.\n\n```forth\n: day ( n -- )\n  case\n    1 of print: \"Monday\" endof\n    2 of print: \"Tuesday\" endof\n    3 of print: \"Wednesday\" endof\n    4 of print: \"Thursday\" endof\n    5 of print: \"Friday\" endof\n    6 of print: \"Saturday\" endof\n    7 of print: \"Sunday\" endof\n    print: \"Unknown day: \" .\n  endcase ;\n````\n\n#### Count-controlled loops\n\nThe *limit* and *start* before the word *do* defines the number of times the loop will run.\n\n```forth\n\u003climit\u003e \u003cstart\u003e do \u003cloop-body\u003e loop\n```\n\n*Do* loops iterate through integers by starting at *start* and incrementing until you reach the *limit*. The word *i* pushes the loop index onto the stack. In a nested loop, the inner loop may access the loop variable of the outer loop by using the word *j*.\n\nFor example:\n```forth\n5 0 do i . loop \\ prints 01234\n```\n\nThere is an other version of the *do* loop where you can define the increment (which can be negative as well).\n\n```forth\n\u003climit\u003e \u003cstart\u003e do \u003cloop-body\u003e \u003cincrement\u003e +loop\n```\n\nFor example:\n\n```forth\n10 0 do i . 2 +loop \\ prints 02468\n```\n\nIf the increment is negative then *limit* is inclusive.\n\n```forth\n0 8 do i . -2 +loop \\ prints 86420\n```\n\nIt is important to know that *Do* loops store the loop index on the return stack. You can break the semantics of *i* and *j* if you use the return stack to store temporary data. Also you can't simply *exit* a word from inside a do loop without clearing the return stack first. See *unloop* for more information.\n\n#### Condition-controlled loops\n\n##### until loop\n\n```forth\nbegin \u003cloop-body\u003e \u003cbool\u003e until\n```\nThe *begin*...*until* loop repeats until a condition is true. This loop always executes at least one time.\n\nFor example:\n\n```forth\n: countdown ( n -- )\n  begin\n    dup .\n    1- dup\n  0 \u003c until\n  drop ;\n\n5 countdown \\ prints 543210\n```\n\nIf you replace *until* with *again* and omit the condition then the loop will run indefinitely.\n\n```forth\nbegin \u003cloop-body\u003e again\n```\n\n##### while loop\n\n```forth\nbegin .. \u003cbool\u003e while \u003cloop-body\u003e repeat\n```\nFor example:\n```forth\n: countdown ( n -- )\n  begin\n    dup 0 \u003e=\n  while\n    dup . 1-\n  repeat\n  drop ;\n\n5 countdown \\ prints 543210\n```\n\n\nYou can use the *exit* word to exit from the current word as well from the loop.\n\nBut this won't work with do loops. The reason for this is because do loops store the loop index on the return stack. You can use the *unloop* word to clear the return stack before exiting a do loop.\n\n```forth\n: some-word ( -- )\n  10 0 do\n    i 5 = if unloop exit then\n  loop ;\n```\n\n An *unloop* is required for each nesting level before the definition may be *exited*.\n\n```forth\n: nested-exit ( -- )\n  5 0 do\n    5 i 1+ do\n      j i + 7 = if\n        i . space j . cr\n        unloop unloop               \\ clear the return stack before exiting\n        exit\n      then\n    loop\n  loop ;\n```\n\nControl structres are compile time words with no interpretation semantics. They can be used only in compilation mode, that is inside a word definition.\n\n### Exception handling\n\nIf a word faces an error condition it can *throw* an exception. Your can provide exception handlers to *catch* exceptions.\n\nFor example:\n\n```forth\nexception: EZERODIV\n\n: div ( q d -- r | throws:EZERODIV ) \\ this word throws an exception in case of division by zero\n  dup 0= if\n    EZERODIV throw\n  else\n    /\n  then ;\n```\n\n```forth\n: test-div ( q d -- r )\n  ['] div catch\n    case\n      EZERODIV of\n        println: '/ by zero'                 \\ print exception in case of zero division\n        2drop                                \\ drop q d\n      endof\n      throw                                  \\ rethrow if it wasn't EZERODIV, or there was no exception (code=0)\n    endcase ;\n```\n\nThe word *catch* expects an execution token of a word that potentially throws an exception.\n\nThe exeption mechanism in Punyforth follows the \"catch everything and re-throw if needed\" semantics. The instruction *0 throw* is essentially a no-op and indicates no error.\n\n#### Uncaught exception handler\n\nAn uncaught exception causes the program to print out the error and the stack trace to the standard output and terminate.\n\nYou can modify this behaviour by overriding the *unhandled* deferred word.\n\n```forth\n: my-uncaught-exception-handler ( code -- )\n  cr print: \"Uncaught exception: \" ex-type\n  abort ;\n\n' unhandled is: my-uncaught-exception-handler\n```\n\nThe implementation of exceptions is based on the idea of [William Bradley](http://www.complang.tuwien.ac.at/anton/euroforth/ef98/milendorf98.pdf).\n\n### Immediate words\n\nImmediate words are executed at compile time. Loops and control structures are implemented with immediate words that compile the required semantics.\n\n```forth\n: begin\n  here                   \\ saves the absolute address of the beginning of the loop to the stack\n; immediate\n\n: until\n  ['] branch0 ,          \\ compiles a conditional branch\n  here - cell - ,        \\ calculate then compile the relative address\n; immediate\n```\n\n### Parsing words\n\nParsing words can parse the input stream. One example of a parsing word is the comment. There are 2 types of comments.\n\n```forth\n( this is a comment )\n\\ this is an other comment\n```\n\n```forth\n: (                                \\ comments start with ( character\n  begin                            \\ consume the stream until ) character is found\n    key ')' =\n  until\n; immediate\n```\n\n```forth\n: \\                                \\ single line comments start with \\ character\n  begin\n    key dup\n    'cr' = swap\n    'lf' = or\n  until                            \\ consume the stream until cr or lf character is found\n; immediate\n```\n\nThe word *hex:* is an other example of a parsing word.\n\n```forth\nhex: FF \\ pushes 255 onto the stack\n```\n\nThis word interprets the input as a hexadecimal number then pushes it to the stack. Parsing words are similar to reader macros in Lisp.\n\n### Deferred words\n\n Punyforth relies on a [Hyper Static Global Environment](http://c2.com/cgi/wiki?HyperStaticGlobalEnvironment). This means redefining a word will create a new definition, but the words continue to refer to the definition that existed when they were defined. You can alter this behaviour by using deferred words.\n\nFor example\n\n```forth\n: myword1 ( -- )\n  print: 'foo' ;\n\n: myword2 ( -- )\n  myword1\n  print: 'bar' ;\n\n: myword1 ( -- ) \\ redefining myword1 to print out baz instead of foo\n  print: 'baz' ;\n\nmyword2 \\ myword2 will print out foobar, not bazbar\n```\n\nRedefinition has no effect on myword2. Let's try it again. This time using the *defer:*/*is:* words.\n\n```forth\ndefer: myword1\n\n: myword2 ( -- )\n  myword1                       \\ I can define myword2 in terms of the (yet undefined) myword1\n  print: 'bar' ;\n\n: printfoo ( -- ) print: 'foo' ;\n: printbaz ( -- ) print: 'baz' ;\n\n' myword1 is: printfoo          \\ redefine the deferred word to print out foo\nmyword2                         \\ this prints out foobar\n\n' myword1 is: printbaz          \\ redefine the deferred word to print out baz\nmyword2                         \\ this prints out bazbar\n```\n\n### Override\n\nYou might want to redefine a word in terms of it's older definition.\n\nFor example:\n\n```forth\n: myword ( -- )\n  print: 'foo' ;\n\n: myword ( -- )\n  myword\n  print: 'bar' ;\n\nmyword \\ infinite recursion\n```\n\nUnfortunately this won't work because the *myword* inside the second defintion will refer to the new word, resulting infinite recursion. You can avoid this by marking the word with *override*.\n\n```forth\n: myword ( -- )\n  print: 'foo' ;\n\n: myword ( -- ) override\n  myword\n  print: 'bar' ;\n\nmyword \\ prints out foobar\n```\n\nBecause the usage of *override*, the *myword* in the second defintion will refer to the old *myword*. Therefore the execution of *myword* will print out foobar.\n\n### Quotations\n\nA quotation is an anonymous word inside an other word, similar than a lambda expression in other languages. Quotations don't act as lexical closures, because there are no locals in Forth to close over. The word *{* starts compiling the quotation body into the current word definition. The word *}* ends the quotation by compiling an exit word into the quotation.\n\n```forth\n: a-word-definition ( -- )\n  ( .. )\n  { ( ..quotation body.. ) }\n  ( .. ) ;\n```\n\nAt runtime the quotation pushes its execution token onto the stack, therefore it can be used with execute, catch or combinators.\n\n```forth\n: demo ( -- n )\n  3 { 1+ 5 * } execute ;\n\n% demo\n(stack 20)\n```\n\n#### Quotations and exception handling\n\n```forth\n  { \"AF01z\" hex\u003eint } catch\n  if\n    println: 'invalid hex number'\n    abort\n  then\n```\n\n#### Quotations and Factor style combinators\n\nPunyforth supports a few [Factor](https://factorcode.org/) style combinators.\n\n##### dip ( x quot -- x )\n\nCalls a quotation while temporarily hiding the top item on the stack.\n\n```forth\n  1 2 4 { + } dip    \\ Same as: 1 2 4 \u003er + r\u003e\n  (stack 3 4)\n```\n\n##### keep ( x quot -- x )\n\nCalls a quotation with an item on the stack, restoring that item after the quotation returns.\n\n```forth\n  1 2 4 { + } keep    \\ Same as: 1 2 4 dup \u003er + r\u003e\n  (stack 1 6 4)\n```\n\n##### bi ( x p q -- )\n\nApplies quotation p to x, then applies quotation q to x.\n\n```forth\n  \\ given a rectangle(width=3, height=4)\n  rectangle { .width @ } { .height @ } bi *    \\ Same as: rectangle dup .width @ swap .height @ *\n  (stack 12)\n```\n\n##### bi* ( x y p q -- )\n\nApplies quotation p to x, then applies quotation q to y.\n\n```forth\n  \"john\" \".doe\" { 1+ c@ } { 2 + c@ } bi* =    \\ Same as: \"john\" \".doe\" swap 1+ c@ swap 2 + c@ =\n  (stack -1)\n```\n\n##### bi@ ( x y quot -- )\n\nApplies the quotation to x, then to y.\n\n ```forth\n  \"john\" \".doe\" { strlen } bi@ =    \\ Same as: \"john\" \".doe\" swap strlen swap strlen =\n  (stack -1)\n```\n\n### The word *create: does\u003e*\n\nThe word *create:* and *does\u003e* lets you combine a data structure with an action. You can create multiple instances with different data content and with the same action.\n\n*create:* is a defining word like the *:* (colon). It creates a new dictionary entry with the header but without the body. The name of the newly created definition comes from the input stream. Then you can lay out some data using the *,* (comma) word. The action which will operate on this data is the sequence of words that comes after the *does\u003e* part. The pointer to the data is pushed to the stack before invoking the action.\n\n#### Examples\n\nOne of the simplest application of *create: does\u003e* is the definition of a constant.\n\n``` forth\n: constant:\n  create: ,\n  does\u003e @ ;\n\n80 constant: COLUMNS\n\nCOLUMNS . \\ prints out 80\n```\n\n- First we push the value 80 to the data stack\n- Then we invoke the *constant:* word\n- The word *create:* reads the name of the constant (COLUMNS) from the input stream and creates a new dictionary header\n- The word *,* stores the value on the stack (80) in the body of the newly created dictionary entry\n- The *does\u003e* sets the action to be the *@* (fetch) word which will read the constant value from the body\n\n#### Other examples of create: does\u003e\n\n##### Indexed array\n\n```forth\n: array: ( size \"name\" -- ) ( index -- addr )\n  create: cells allot\n  does\u003e swap cells + ;\n\n10 array: numbers \\ create an array with 10 elements\n\n12 3 numbers !      \\ store 12 in the 3rd element\n3 numbers @         \\ fetch the 3rd element\n\n```\n\n##### Structs\n\n```forth\n: struct 0 ;\n\n: field:\n  create: over , +\n  does\u003e @ + ;\n\nstruct\n  cell field: .width\n  cell field: .height\nconstant Rect\n\n: new-rect: ( \"name\" -- )\n  Rect create: allot ;\n\n: area ( rect -- area )\n  dup .width @ swap .height @ * ;\n\nnew-rect: r1\n3 r1 .width !\n5 r1 .height !\nr1 area .\n```\n\n### Unit testing\n\nWords with name starting with the *test:* prefix are treated as unit tests. Unit testing words typically use *assert* or *=assert* to validate the correctness of an other word.\n\n```forth\n: test:add 1 2 + 3 =assert ;\n: test:sub 8 3 - 5 =assert ;\n\ntest\n```\n\nThe *=assert* word asserts that two top-most items on the stack are equal. The *assert* words asserts that the top-most item of the stack is true.\n\nThe *test* word runs all unit tests and gives back a simple report.\n\n```text\n2 tests, 2 passed, 0 failed, 0 errors\nAll passed\n```\n\nA unit test can either pass/fail or raise an error. Failure means an assertion wasn't met. Error occurs in case of an unhandled exception.\n\n```forth\n: test:add 1 2 + 4 =assert ;\n: test:sub some-exception throw ;\n\ntest\n```\n```text\n2 tests, 0 passed, 1 failed, 1 errors\ntest:add(3 4 \u003c\u003e) FAIL\ntest:sub ERROR: some-exception\nThere were failures\n```\n\n## ESP8266 specific things\n\n### WIFI\n\nThe ESP8266 has a built in Wi-Fi chip that can be used both in access point and station mode (wireless client).\n\nIn station mode, the ESP8266 connects to an existing Wi-Fi access point.\n\n```forth\n\"password\" \"existing-ssid\" wifi-connect\n```\n\nThe station mode Wi-Fi settings are persistently stored by the ESP8266, there is no need to setup the Wi-Fi at every startup.\n\nIn AP mode, the ESP8266 acts as an central connection point, which wireless clients (smartphones, laptops) can connect to. In this mode you have to choose an IP address for the ESP and an IP range for the clients. Client IP addresses are assigned by the [DHCP](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol) server.\n\n```forth\n172 16 0 1 \u003eipv4 wifi-set-ip                                      \\ AP ip is 172.16.0.1\n4 3 0 AUTH_WPA2_PSK \"1234567890\" \"my-ssid\" wifi-softap            \\ max connections = 4\n8 172 16 0 2 \u003eipv4 dhcpd-start                                    \\ dhcp max_leases = 8, first client ip is 172.16.0.2\n```\n\nThe dhcp max_leases parameter should not be smaller than the maximum allowed connections.\n\n### GPIO\n\n#### Examples\n\n```forth\n2 constant: PIN\nPIN GPIO_OUT gpio-mode\nPIN GPIO_HIGH gpio-write\n250 ms\nPIN GPIO_LOW gpio-write\n```\n\nSee [Philips Hue lightswitch example](arch/esp8266/forth/examples/example-philips-hue-lightswitch.forth) for more information.\n\n### Netconn\n\nNetconn is a sequential API on top of the [lightweight TCP/IP stack](https://en.wikipedia.org/wiki/LwIP) of [FreeRTOS](https://en.wikipedia.org/wiki/FreeRTOS). Punyforth provides a wrapper around the Netconn API.\n\n#### Simple HTTP request\n\n```forth\n512 buffer: line\n\n: fetch ( netcon -- )\n  begin\n    dup 512 line netcon-readln -1 \u003c\u003e\n  while\n    line type cr\n  repeat\n  drop ;\n\n80 \"google.com\" TCP netcon-connect constant: socket\nsocket \"GET / HTTP/1.1\\r\\n\\r\\n\" netcon-write\nsocket fetch\nsocket netcon-dispose\n```\n\n#### UDP client\n\n```forth\n\"Lorem ipsum\" constant: data\n\"192.168.0.3\" constant: SERVER_IP\n8005 constant: SERVER_PORT\nSERVER_PORT SERVER_IP UDP netcon-connect\ndup data 11 netcon-send-buf\nnetcon-dispose\n```\n\n##### Python test server\n\n```python\nimport select, socket\n\ns = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\ns.bind(('0.0.0.0', 8005))\ns.setblocking(0)\n\nwhile True:\n    result = select.select([s],[],[])\n    msg = result[0][0].recv(1024)\n    print(msg.strip())\n```\n\n#### UDP server\n\n```forth\n\"192.168.0.15\" constant: HOST\n8000 constant: PORT\n128 buffer: data\n\nPORT HOST netcon-udp-server\ndup 128 data netcon-readln\nprint: 'received bytes: ' . cr\ndata type\nnetcon-dispose\n```\n\n##### Python test client\n\n```python\nimport socket\ns = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\ns.sendto(b'hello\\r\\n', ('192.168.0.15',8000))\n```\n\nSee [Simple HTTP Server](arch/esp8266/forth/examples/example-http-server.forth) for more information.\n\n### Tasks\n\nPunyforth supports cooperative multitasking which enables users to run more than one task simultaneously. For example one task may wait for input on a socket, while another one receives commands through the serial port. Punyforth never initiates a context switch by its own. Instead, tasks voluntarily yield control periodically using the word *pause*. Tasks are executed in a round robin fashion.\n\nIn order to run some code in the background, one must create a new task first, using the *task:* parsing word. A tasks can be activated inside a word. This word usually does something in a loop and calls *pause* periodically to yield controll to other tasks.\n\n```forth\n0 task: mytask\n\n: my-word\n  mytask activate\n  [...] pause [...]\n  deactivate\n```\n\nTo start the task, first you have to switch to multi tasking mode first by executing the word *multi*. Then simply call the word that was associated to the task.\n\n```forth\nmulti\nmy-word\n```\n\n#### Mailboxes\n\nOften tasks need to communicate with each other. A mailbox is a fixed size blocking queue where messages can be left for a task. Receiving from an empty mailbox or sending to a full mailbox blocks the current task.\n\n```forth\n\\ create a mailbox with size 5\n5 mailbox: mailbox1\n\n\\ create a task for the consumer\n0 task: task-consumer\n\n\\ this word is executed by the task\n: consumer ( task -- )\n  activate                            \\ activate task\n  begin\n    mailbox1 mailbox-receive .        \\ receive and print one item from the mailbox\n    println: \"received by consumer\"\n    pause                             \\ allow other tasks to run\n  again\n  deactivate ;                        \\ deactivate task\n\nmulti                                 \\ switch to multitask mode\ntask-consumer consumer                \\ run the consumer\n123 mailbox1 mailbox-send             \\ send some numbers to the consumer\n456 mailbox1 mailbox-send\n```\n\n#### Examples\n\n```forth\n\\ create a task for the counter\ntask: task-counter\n\n\\ this word is executed by the task\n: counter ( task -- )\n    activate                              \\ activate task\n    100 0 do\n        i . cr\n        500 ms\n    loop\n    deactivate ;                          \\ deactivate task\n\nmulti                                     \\ switch to multitask mode\ntask-counter counter                      \\ run the consumer\n```\n\n### Misc\n\n```forth\n\\ Returns the available free dictionary space.\nfreemem ( -- bytes )\n\n\\ Returns the available free memory.\nosfreemem ( -- bytes )\n\n\\ Blocks all running tasks for the specified number of millisecond.\nms ( msec -- )\n\n\\ Blocks for the specified number of microsecond. This is implemented as busy loop. Use it if you need high precision delay.\nus ( usec -- )\n\n\\ Sets the baud rate of the specied uart.\nuart-set-bps ( bps uart-number -- )\n\n\\ print out available words\nhelp ( -- )\n```\n\nYou can see some example code under the [examples](arch/esp8266/forth/examples) directory.\n\nBuild instructions and further information is available at [punyforth wiki](https://github.com/zeroflag/punyforth/wiki).\n\n## Contact\n\nAttila Magyar\n\n[![Twitter Follow](https://img.shields.io/twitter/url/http/shields.io.svg?style=social\u0026label=%40zeroflag\u0026maxAge=2592000?style=flat-square)](https://twitter.com/zeroflag) [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/punyforth/Lobby)\n","funding_links":[],"categories":["Forth"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeroflag%2Fpunyforth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzeroflag%2Fpunyforth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeroflag%2Fpunyforth/lists"}