{"id":15062802,"url":"https://github.com/benzap/fif","last_synced_at":"2025-06-28T18:06:50.181Z","repository":{"id":62432669,"uuid":"128111889","full_name":"benzap/fif","owner":"benzap","description":"Stack-based Programming in Clojure(script)","archived":false,"fork":false,"pushed_at":"2019-08-17T21:02:13.000Z","size":379,"stargazers_count":78,"open_issues_count":0,"forks_count":1,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-06-11T21:16:11.636Z","etag":null,"topics":["clojure","edn","fif","forth","programming-language","scripting","scripting-language","stack-based"],"latest_commit_sha":null,"homepage":"http://benzaporzan.me/fif-playground/","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benzap.png","metadata":{"files":{"readme":"readme.org","changelog":"changelog.org","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-04T19:35:31.000Z","updated_at":"2024-05-31T07:49:15.000Z","dependencies_parsed_at":"2022-11-01T21:15:58.315Z","dependency_job_id":null,"html_url":"https://github.com/benzap/fif","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/benzap/fif","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benzap%2Ffif","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benzap%2Ffif/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benzap%2Ffif/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benzap%2Ffif/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benzap","download_url":"https://codeload.github.com/benzap/fif/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benzap%2Ffif/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260646411,"owners_count":23041665,"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":["clojure","edn","fif","forth","programming-language","scripting","scripting-language","stack-based"],"created_at":"2024-09-24T23:46:45.597Z","updated_at":"2025-06-28T18:06:50.164Z","avatar_url":"https://github.com/benzap.png","language":"Clojure","readme":"#+TITLE: fif - Stack-based Programming in Clojure(script)\n#+AUTHOR: Benjamin Zaporzan\n#+DATE: 2018-04-04\n#+EMAIL: benzaporzan@gmail.com\n#+LANGUAGE: en\n#+OPTIONS: H:2 num:t toc:t \\n:nil ::t |:t ^:t f:t tex:t\n\n\n[[http://benzaporzan.me/fif-playground/][Try it Online!]]\n\n[[https://travis-ci.org/benzap/fif][https://travis-ci.org/benzap/fif.svg?branch=master]]\n\n[[https://clojars.org/fif][https://img.shields.io/clojars/v/fif-lang/fif.svg]]\n\n[[./doc/logo.svg]]\n\n\n*fif* is a Stack-based Programming Language in Clojure(script). It is\ninterpreted, extensible, and simple. It was developed to be its own\nsandboxed scripting language to perform operations on clojure(script)\napplications. *fif* is based off of Forth, but has adopted clojures\ncore libraries for familiarity.\n\n\n#+BEGIN_SRC clojure\n  (require '[fif.core :as fif])\n\n  (fif/reval \"Hello World!\" . cr) ;; =\u003e '()\n  ;; \u003cstdout\u003e: Hello World!\n\n  (fif/reval 1 1 +) ;; =\u003e '(2)\n\n  (fif/reval\n   fn yeaa!\n     #_\"( n -- ) Prints yeeee{n}hhh!\"\n     \"yeeee\" .\n     0 do \"a\" . loop\n     \"hhh!\" . cr\n   endfn\n\n   5 yeaa!) ;; =\u003e '()\n  ;; \u003cstdout\u003e: yeeeeaaaaaahhh!\n\n  (fif/reval\n\n   fn factorial\n     dup 1 \u003e if dup dec factorial * then\n   endfn\n     \n   5 factorial) ;; =\u003e '(120)\n\n  ;;\n  ;; Most of the clojure functions are available, with more being ported\n  ;; and tested.\n  ;;\n\n  (fif/reval (1 2 3 4) rest) ;; =\u003e '((2 3 4))\n\n  (fif/reval (1 2 3 4) first) ;; =\u003e '(1)\n\n  (fif/reval (1 2 3 4) 5 conj) ;; =\u003e '((5 1 2 3 4))\n\n  (fif/reval [1 2 3 4] 5 conj) ;; =\u003e '([1 2 3 4 5])\n\n  ;;\n  ;; More Advanced Features\n  ;;\n\n  ;; Functional Programming\n\n  (fif/reval *+ [1 2 3 4] reduce) ;; =\u003e '(10)\n\n  (fif/reval *inc [1 2 3 4] map) ;; =\u003e '([2 3 4 5])\n\n  (fif/reval *even? [1 2 3 4] filter) ;; =\u003e '([2 4])\n\n  ;; Inner Sequence Evaluation (Termed \"Realizing\")\n\n  (fif/reval [4 1 do i inc loop] ?) ;; =\u003e '([2 3 4 5])\n\n\n#+END_SRC\n\n* Requirements\n \n  *fif* requires clojure 1.9+\n\n  This could be relaxed to clojure 1.7 with interest.\n\n* Installation\n\n  For the latest version, please visit [[https://clojars.org/fif-lang/fif][clojars.org]]\n\n  *Leiningen/Boot*\n  \n  #+BEGIN_SRC clojure\n\n  [fif-lang/fif \"1.3.1\"]\n\n  #+END_SRC\n\n  *Clojure CLI/deps.edn*\n\n  #+BEGIN_SRC clojure\n\n  fif-lang/fif {:mvn/version \"1.3.1\"}\n\n  #+END_SRC\n\n  *Gradle*\n\n  #+BEGIN_SRC groovy\n\n  compile 'fif-lang:fif:1.3.1'\n\n  #+END_SRC\n\n  *Maven*\n\n  #+BEGIN_SRC xml\n\n  \u003cdependency\u003e\n    \u003cgroupId\u003efif-lang\u003c/groupId\u003e\n    \u003cartifactId\u003efif\u003c/artifactId\u003e\n    \u003cversion\u003e1.3.1\u003c/version\u003e\n  \u003c/dependency\u003e\n\n  #+END_SRC\n\n* Introduction\n  In stack-based programming, operations are performed on data using\n  *Postfix Notation*: ex. ~1 2 +~. This is the complete opposite of\n  *Polish Notation* used in lisp languages: ex. ~+ 1 2~.\n  \n  The basic principles behind how stack-based programming operates is\n  by pushing values onto a stack, and having defined symbols, called\n  *words* perform operations on the pushed stack values.\n\n  #+BEGIN_SRC clojure\n  (fif/reval 1 2) ;; =\u003e '(1 2)\n  (fif/reval 1 2 +) ;; =\u003e '(3)\n  #+END_SRC\n\n  *Forth* is one of the more well known languages which uses this\n  approach, and it is used as a baseline for the implementation of\n  *fif*.\n\n  Although *fif* is similar to *forth* in a lot of ways, I like to\n  think that *fif* is less restrictive, but also more\n  error-prone (hopefully less so with later developments). Forth has\n  a compile mode, which only allows certain defined words to be used\n  while defining new words. None of this exists in *fif*. Everything\n  is interpreted the moment a dribble of data appears to the\n  stack-machine.\n\n  #+BEGIN_SRC clojure\n    ;; conditionals are compile-mode only in Forth, but allowed in fif\n    (fif/reval 1 0 = if \"Ya\" else \"Nah\" then) ;; =\u003e '(\"Nah\")\n\n    ;; do loop is compile-mode only in Forth, along with the rest of the\n    ;; conditional-loops. All of this is allowed in fif.\n    (fif/reval 4 0 do i loop) ;; =\u003e '(0 1 2 3 4)\n\n    ;; defining functions inside functions doesn't exist in forth to the\n    ;; best of my knowledge.\n    (fif/reval fn func_define_add\n                 fn add2 2 + endfn\n               endfn\n\n               func_define_add\n               2 add2) ;; =\u003e '(4)\n\n  #+END_SRC\n\n  \n\n** Clojure Language Interoperability and Data Representation\n   \n   Code is presented to *fif* in the form of the edn data format,\n   which means that only valid data values in clojure are allowed\n   within *fif*. This comes as a huge advantage, since it means *fif*\n   has a wealth of data structures at its disposal, and allows for\n   seamless interoperability within the clojure environment.\n   \n   #+BEGIN_SRC clojure\n   \n   (fif/reval 1 has-flag? namespace/value.thing why!?!? {:a 123} [1 2 3] #{:mental-asylum :ledger})\n   ;; =\u003e (1 has-flag? namespace/value.thing why!?!? {:a 123} [1 2 3] #{:ledger :mental-asylum})\n   \n   (defn self-destruct [] \"yes\")\n   (fif/reval (self-destruct) fn self-destruct \"no\" endfn self-destruct) ;; =\u003e '((self-destruct) \"no\")\n\n   #+END_SRC\n\n   For a detailed breakdown on valid data that can be passed to *fif*\n   please refer to the *Built-in elements* section in the [[https://github.com/edn-format/edn][edn format github page]].\n\n** Printing to Standard Output\n\n   *fif* maintains a few operators for displaying to standard output.\n\n   #+BEGIN_SRC clojure\n\n   ;; Drop the Top value and display it on standard output\n   (fif/reval 1 2 .) ;; =\u003e '(1)\n   ;; \u003cstdout\u003e: 2\n\n   ;; Carriage return is provided with `cr`\n   (fif/reval \"Hello \" . cr \"There!\" . cr) ;; =\u003e '()\n   ;; \u003cstdout\u003e: Hello \n   :: \u003cstdout\u003e: There!\n   ;; \u003cstdout\u003e: \n\n   ;;\n   ;; Clojure equivalent print functions have been maintained\n   ;;   \n\n   (fif/reval \"Hello World!\" println) ;; =\u003e '()\n   ;; \u003cstdout\u003e: Hello World!\n   ;; \u003cstdout\u003e: \n\n   (fif/reval \"Hello World!\" print) ;; =\u003e '()\n   ;; \u003cstdout\u003e: Hello World!\n\n   (fif/reval \"Hello World!\" prn) ;; =\u003e '()\n   ;; \u003cstdout\u003e: \"Hello World!\"\n   ;; \u003cstdout\u003e: \n\n   (fif/reval \"Hello World!\" pr) ;; =\u003e '()\n   ;; \u003cstdout\u003e: \"Hello World!\"\n\n   #+END_SRC\n\n** Basic Arithmetic and Stack Manipulation\n\n   Note that these examples are similar to [[https://learnxinyminutes.com/docs/forth/][Learn Forth in Y Minutes]]\n\n   #+BEGIN_SRC clojure\n   \n   ;;\n   ;; Arithmetic\n   ;;\n\n   ;; Addition\n   (fif/reval 5 4 +) ;; =\u003e '(9)\n   \n   ;; Subtraction\n   (fif/reval 5 4 -) ;; =\u003e '(1)\n\n   ;; Multiplication\n   (fif/reval 6 8 *) ;; =\u003e '(48)\n\n   ;; Division\n   (fif/reval 12 4 /) ;; =\u003e '(3)\n\n   ;; Modulo\n   (fif/reval 13 2 mod) ;; =\u003e '(1)\n\n   ;; Negation\n   (fif/reval 99 negate) ;; =\u003e '(-99)\n\n   ;; Absolute Value\n   (fif/reval -99 abs) ;; =\u003e '(99)\n\n   ;; Maximum and Minimum Value\n   (fif/reval 52 23 max) ;; =\u003e '(52)\n   (fif/reval 52 23 min) ;; =\u003e '(23)\n\n   ;; Increment and Decrement Value\n   (fif/reval 1 inc) ;; =\u003e '(2)\n   (fif/reval 2 dec) ;; =\u003e '(1)\n\n   ;;\n   ;; Stack Manipulation\n   ;;\n\n   ;; Duplicate Stack Value\n   (fif/reval 3 dup dup) ;; =\u003e '(3 3 3)\n\n   ;; Swap First and Second Values\n   (fif/reval 2 5 swap) ;; =\u003e '(5 2)\n\n   ;; Rotate Top 3 Values\n   (fif/reval 1 2 3 rot) ;; =\u003e '(2 3 1)\n\n   ;; Drop Top Value\n   (fif/reval 1 2 drop) ;; =\u003e '(1)\n\n   ;; Drop the Second Value\n   (fif/reval 1 2 3 nip) ;; =\u003e '(1 3)\n\n   ;;\n   ;; More Advanced Stack Manipulation\n   ;;\n   \n   ;; Duplicate the Top Value, and place it between the Second Value and Third Value\n   (fif/reval 1 2 3 4 tuck) ;; =\u003e '(1 2 4 3 4)\n\n   ;; Duplicate the Second Value, and place on the top\n   (fif/reval 1 2 3 4 over) ;; =\u003e '(1 2 3 4 3)\n\n   #+END_SRC\n\n** Conditional Operators\n   \n   Conditionals produce the clojure equivalent boolean ~true~ and\n   ~false~ values. However, conditional flags within *fif* also treat\n   0 as ~false~ and any non-zero number as ~true~.\n\n   Note: The implementation of this can be found at ~fif.stdlib.conditional/condition-true?~\n\n   #+BEGIN_SRC clojure\n\n   (fif/reval 5 3 \u003c)    ;; =\u003e '(false)\n   (fif/reval 5 5 \u003c=)   ;; =\u003e '(true)\n   (fif/reval 1 0 =)    ;; =\u003e '(false)\n   (fir/reval 1 0 not=) ;; =\u003e '(true)\n   (fif/reval 5 2 \u003e)    ;; =\u003e '(true)\n   (fif/reval 3 1 \u003e=)   ;; =\u003e '(true)\n\n   #+END_SRC\n\n   The only conditional structures within *fif* are:\n\n   ~\u003ccondition\u003e if \u003ctrue-body\u003e then~\n\n   ~\u003ccondition\u003e if \u003ctrue-body\u003e else \u003cfalse-body\u003e then~\n\n   Examples:\n\n   #+BEGIN_SRC clojure\n\n   ;; zero values are considered false\n   (fif/reval 0 if 1 then) ;; =\u003e '()\n   (fif/reval nil if 1 then) ;; =\u003e '()\n   (fif/reval false if 1 then) ;; =\u003e '()\n\n   ;; non-zero values are considered true\n   (fif/reval 1 if 1 then) ;; =\u003e '(1)\n   (fif/reval -1 if 1 then) ;; =\u003e '(1)\n   (fif/reval true if 1 then) ;; =\u003e '(1)\n\n   ;; Anything else is evaluated by passing to `clojure.core/boolean`\n   (fif/reval [] if 1 then) ;; =\u003e '(1)\n\n   (fif/reval 0 if 1 else 2 then) ;; =\u003e '(2)\n   (fif/reval 1 1 - if 1 else 2 then) ;; =\u003e '(2)\n\n   ;; if conditions can be nested\n   (reval\n    fn check-age\n      dup 18 \u003c  if drop \"You are underage\"      else\n      dup 50 \u003c  if drop \"You are the right age\" else\n      dup 50 \u003e= if drop \"You are too old\"       else\n      then then then\n    endfn\n\n    12 check-age\n    24 check-age\n    51 check-age) ;; =\u003e '(\"You are underage\" \"You are the right age\" \"You are too old\")\n\n   #+END_SRC\n\n** Creating Functions\n\n   Functions within *fif* are called *word definitions* and have the syntax:\n\n   ~fn \u003cname\u003e \u003cbody...\u003e endfn~\n\n   Functions are stored globaly within the stack machine. This holds\n   true when you attempt to define functions while within a function.\n\n   Few Examples:\n\n   #+BEGIN_SRC clojure\n\n   (fif/reval\n    \n    fn square dup * endfn\n\n    5 square) ;; =\u003e (25)\n\n   (fif/reval\n    \n    fn add2 2 + endfn\n    fn add4 add2 add2 endfn\n   \n    4 add4) ;; =\u003e '(8)\n\n   #+END_SRC\n\n** Loops\n   \n   There are currently four standard loops in *fif*:\n\n   ~\u003cend\u003e \u003cstart\u003e do \u003cbody\u003e loop~\n\n   ~\u003cend\u003e \u003cstart\u003e do \u003cbody\u003e \u003cstep\u003e +loop~\n\n  \n   ~begin \u003cbody\u003e \u003cflag\u003e until~\n\n   ~begin \u003cflag\u003e while \u003cbody\u003e repeat~\n\n   Examples:\n\n   #+BEGIN_SRC clojure\n\n   ;; do loops are inclusive\n   (fif/reval 2 0 do \"Hello!\" loop) ;; =\u003e '(\"Hello!\" \"Hello!\" \"Hello!\")\n\n   ;; do loops also have special index words i, j and k\n   (fif/reval 2 0 do i loop) ;; =\u003e '(0 1 2)\n\n   ;; These are useful for nested loops\n   (-\u003e\u003e (fif/reval 2 0 do 3 0 do j i loop loop)\n        (partition 2))\n   ;; =\u003e ((0 0) (0 1) (0 2) (0 3) (1 0) (1 1) (1 2) (1 3) (2 0) (2 1) (2 2) (2 3))\n   \n   ;; do loops have a special increment based loop with +loop\n   (fif/reval 10 0 do i 2 +loop) ;; =\u003e '(0 2 4 6 8 10)\n\n   ;; begin-until performs the action until its clause is true\n   (fif/reval begin 1 true until) ;; =\u003e '(1)\n\n   (fif/reval begin 1 false until) ;; =\u003e '(1 1 1 1 1 ........\n\n   (fif/reval 0 begin dup inc dup 5 = until) ;; =\u003e '(0 1 2 3 4 5)\n\n   ;; begin-while-repeat performs the action while its while clause is true\n   (fif/reval begin false while 1 repeat) ;; =\u003e '()\n\n   (fif/reval begin true while 1 repeat) ;; =\u003e '(1 1 1 1 1 .......\n\n   (fif/reval 0 begin dup 5 \u003c while dup inc repeat) ;; =\u003e '(0 1 2 3 4 5)\n   \n   ;; You can break out of any loop prematurely using `leave`\n   (fif/reval begin true while leave repeat) ;; =\u003e '() No Infinite Loop!\n\n   (fif/reval 0 begin true while dup inc dup 5 = if leave then repeat) ;; =\u003e '(0 1 2 3 4 5)\n\n   #+END_SRC\n\n** Word Referencing\n\n   *fif* uses the concept of *Word Referencing*, which is a means of\n   pushing already defined words onto the stack. This becomes useful\n   for setting variables and for functional programming as shown in\n   the next two sections.\n\n   #+BEGIN_SRC clojure\n\n     ;; Already defined words won't end up on the stack\n     (fif/reval 2 2 +) ;; =\u003e '(4)\n\n     (fif/reval +) ;; ERROR\n\n     ;; A word reference involves placing an asterisk '*' infront of the\n     ;; word you want on the stack.\n\n     (fif/reval 2 2 *+) ;; =\u003e '(2 2 +)\n     (fif/reval *+) ;; =\u003e '(+)\n\n     ;; These can be chained for deeper referencing\n\n     (fif/reval **+) ;; =\u003e '(*+)\n\n     (fif/reval ***+) ;; =\u003e '(**+)\n\n     (fif/reval ********+) ;; =\u003e ....\n\n     ;; Multiplication remains unaffected\n\n     (fif/reval 2 2 *) ;; =\u003e '(4)\n\n   #+END_SRC\n\n** Functional Programming\n\n   *fif* supports some of the usual functional programming idioms seen\n   in other popular languages. The currently implemented functional\n   programming operators are *reduce*, *map*, and *filter*.\n\n   ~\u003cfn ( xs x -- 'xs )\u003e \u003ccoll\u003e reduce~\n\n   ~\u003cfn ( item -- 'item )\u003e \u003ccoll\u003e map~\n\n   ~\u003cfn ( item -- boolean )\u003e \u003ccoll\u003e filter~\n\n   \n   #+BEGIN_SRC clojure\n\n   (fif/reval *+ [1 2 3 4] reduce) ;; =\u003e '(10)\n\n   (fif/reval *inc [1 2 3 4] map) ;; =\u003e '((2 3 4 5))\n\n   (fif/reval *even? [1 2 3 4 5] filter) ;; =\u003e '((2 4))\n\n   (fif/reval *inc [1 2 3 4] map) ;; =\u003e '((2 3 4 5))\n\n   #+END_SRC\n\n*** Lambda Expressions\n\n    The base functional operators can also be passed a sequence in\n    place of a function, which will be treated as a lambda expression.\n\n    #+BEGIN_SRC clojure\n\n    (fif/reval (2 +) [1 2 3 4] map) ;; =\u003e '((3 4 5 6))    \n\n    (fif/reval (:eggs not=) [:eggs :ham :green-eggs :eggs] filter)\n    ;; =\u003e '((:ham :green-eggs))\n\n    #+END_SRC\n\n\n** Variables\n\n   *fif* strays away from Forth in the way it sets and gets\n   variables. Since *fif* uses *Word Referencing*, the ability\n   to get Word Variables simply requires you to place the word on the\n   stack to retrieve the value. Setting the variable requires you to\n   provide a *Word Reference*, as shown in the examples below.\n \n   Global variables within *fif* are declared using ~def~, and are\n   treated as word definitions. They can be set using the word\n   operator ~setg~. Local variables are declared using ~let~, and can\n   be set programmatically using ~setl~.\n\n   Examples\n\n   #+BEGIN_SRC clojure\n\n   (fif/reval\n    \n    ;;\n    ;; Globally Scoped Variables\n    ;;\n\n    *X 2 2 + setg\n\n    X . cr ;; =\u003e '(4)\n\n    ;; Set X to 10\n    def X 10\n\n    ;; Get X\n    X\n\n    ;; Set X to 20 \n    *X 20 setg\n\n    ;;\n    ;; Locally Scoped Variables\n    ;;\n    ;; Note that functions have a local dynamic scope.\n\n    let y true\n\n    y ;; =\u003e '(true)\n\n    ;; They can be set programmatically with `setl`\n\n    *y false setl\n\n    y ;; =\u003e '(false)\n    )\n\n   #+END_SRC\n\n** Macros\n   \n   *Macros* are somewhat experimental, but for future macros, it would\n   be interesting to see how easily it might be to manipulate the code\n   stack in new and interesting ways. A very primitive macro system is\n   implemented. As an example, I implemented an incomplete `?do` loop\n   from *Forth*\n\n   Example:\n\n   #+BEGIN_SRC clojure\n\n   (reval\n    macro ?do\n      over over \u003e\n      if\n        _! inc do !_\n      else\n        _! do leave !_\n      then\n    endmacro\n\n    fn yeaa!\n      #_\"(n -- ) Prints yeaa with 'n' a's\"\n      \"yeeee\" .\n      0 ?do \"a\" . loop\n      \"hhh!\" . cr\n    endfn\n \n    0 yeaa!\n    5 yeaa!) ;; =\u003e '()\n    ;; \u003cstdout\u003e: yeeeehhh!\n    ;; \u003cstdout\u003e: yeeeeaaaaahhh!\n\n   #+END_SRC\n\n* Extending fif within Clojure\n\n  One interesting by-product of creating *fif* within clojure is how\n  easy it is to extend *fif* from within clojure. There is a wealth of\n  functionality that can be easily included in *fif* with only a few\n  lines of code.\n\n** Extending fif with clojure functions\n\n   As an example, i'm going to make two functions. One function that\n   adds items to a vector, and another which retrieves the vector.\n\n   #+BEGIN_SRC clojure\n\n   (def *secret-notes (atom []))\n   (defn add-note! [s] (swap! *secret-notes conj s))\n   (defn get-notes [] @*secret-notes)\n\n   (add-note! \"They're in the trees\")\n   (add-note! {:date \"March 14, 2018\" :name \"Stephen Hawking\"})\n\n   (get-notes) ;; =\u003e [\"They're in the trees\" {:date \"March 14, 2018\" :name \"Stephen Hawking\"}]\n\n   #+END_SRC\n   \n   I want two functions in *fif* to closely resemble the clojure\n   equivalents, notably:\n\n   *add-note!*, which takes one value, and returns nothing\n\n   *get-notes*, which takes no values, and returns the list\n\n   Using the default stack machine ~fif.core/*default-stack*~, we can\n   extend it to include this functionality:\n\n   #+BEGIN_SRC clojure\n   (require '[fif.core :as fif])\n   (require '[fif.def :refer [wrap-procedure-with-arity\n                              wrap-function-with-arity\n                              set-word-function]])\n\n   ;; Wrap add-note! as a procedure which accepts 1 value from the\n   ;; stack. Note that the procedure wrapper does not return the result\n   ;; of our function to the stack.\n   (def op-add-note! (wrap-procedure-with-arity 1 add-note!))\n\n   ;; Wrap get-notes as a function. Note that the function wrapper will\n   ;; return its result to the stack.\n   (def op-get-notes (wrap-function-with-arity 0 get-notes))\n\n   (def extended-stack-machine\n     (-\u003e fif/*default-stack*\n         (set-word-function 'add-note! op-add-note!)\n         (set-word-function 'get-notes op-get-notes)))\n\n   ;; Let's take our new functionality for a spin\n   (reset! *secret-notes [])\n   (fif/with-stack extended-stack-machine\n     (fif/reval \"I Hate Mondays\" add-note!) ;; =\u003e '()\n     (fif/reval-string \"\\\"Kill Switch: Pineapple\\\" add-note!\") ;; =\u003e '()\n     (fif/reval get-notes)) ;; =\u003e '([\"I Hate Mondays\" \"Kill Switch: Pineapple\"])\n\n   #+END_SRC\n\n   More advanced functions can make use of the full stack machine, and\n   a few of these functions can be seen in the ~fif.stdlib.ops~\n   namespace.\n\n** Implementing a fif Programmable Repl (prepl)\n   \n   *fif* isn't that useful interactively without facilities to capture\n   stdout and stderr. A Programmable Repl (prepl) can be easily\n   implemented within fif using `fif.core/prepl-eval`.\n\n   For this example, i'm going to create a prepl from the\n   *default-stack* which will change state within an atom. Additional\n   atoms will be used to capture stdout and stderr.\n\n   #+BEGIN_SRC clojure\n     (require '[clojure.string :as str])\n     (require '[fif.core :as fif])\n\n     (def *sm (atom fif/*default-stack*))\n     (def *stdout-results (atom []))\n     (def *stderr-results (atom []))\n\n\n     (defn prepl-reset! []\n       (reset! *sm fif/*default-stack*)\n       (reset! *stdout-results [])\n       (reset! *stderr-results []))\n\n\n     (defn output-fn\n       \"Standard Output/Error Handler Function. \"\n       [{:keys [tag value]}]\n       (let [;; Remove platform specific newlines\n             value (str/replace value #\"\\r\\n\" \"\\n\")]\n         (cond\n          (= tag :out)\n          (swap! *stdout-results conj value)\n  \n          (= tag :error)\n          (swap! *stderr-results conj value))))\n\n\n      (defn prepl [sinput]\n        (swap! *sm fif/prepl-eval sinput output-fn)\n        {:stack (-\u003e @*sm fif/get-stack reverse)\n         :stdout @*stdout-results\n         :stderr @*stderr-results})\n       \n      (prepl \"2 2\") ;; =\u003e {:stack '(2 2) :stdout [] :stderr []}\n      \n      (prepl \"+\") ;; =\u003e {:stack '(4) :stdout [] :stderr []}\n\n      (prepl \"println\") ;; =\u003e {:stack '() :stdout [\"4\\n\"] :stderr []}\n\n      (prepl-reset!)\n\n   #+END_SRC\n\n   The fif prepl functionality works in clojurescript, however,\n   clojurescript lacks a standard error output, so it is not likely\n   the :error tag would appear to the output function.\n\n** fif and clojure interoperability\n   \n   Although this might not be taken as a feature, *fif* can have\n   clojure s-exps evaluated within its comfy confines. The default set\n   of *fif* evaluators over clojure data are subject to the same\n   clojure reader shortfalls that prevent it from being used as a data\n   format.\n\n   *Note that reading in data as a string representation does not\n   suffer from these shortfalls as discussed in another section*\n\n   #+BEGIN_SRC clojure\n\n   (fif/reval 1 #=(+ 1 1) +) ;; =\u003e '(3) Yikes!\n\n   (defn boiling-point-c [] 100)\n\n   (fif/reval #=(boiling-point-c) 1 +) ;; =\u003e '(101) Russians!\n\n   #+END_SRC\n\n   However, the preferred way to include additional data within *fif*\n   is by either passing values onto the stackmachine, or by setting *fif*\n   variables which can be accessed from within fif.\n\n   #+BEGIN_SRC clojure\n  \n   (require '[fif.core :as fif])\n   (require '[fif.stack-machine :as stack])\n   (require '[fif.def :refer [set-word-variable]])\n\n   (defn secret-stack-machine\n     \"Returns a stack machine with a `secret` value stored in the fif\n     variable 'secret\"\n     [secret]\n     (-\u003e fif/*default-stack*\n         (set-word-variable 'secret secret)))\n   \n   \n   (fif/with-stack (secret-stack-machine :fooey)\n     (fif/reval secret)) ;; =\u003e (:fooey)\n   \n   \n   (defn pill-popping-stack-machine\n     \"Returns a stack machine with the values within `pills` placed on\n     the stack\"\n     [\u0026 pills]\n     (loop [sm fif.core/*default-stack*\n            pills pills]\n       (if-let [pill (first pills)]\n         (recur (stack/push-stack sm pill)\n                (rest pills))\n         sm)))\n   \n   \n   (fif/with-stack (pill-popping-stack-machine :pink :green :blue)\n     (fif/reval \"The pill on the top of the stack is: \" . .))\n     ;; =\u003e '(:pink :green)\n     ;; \u003cstdout\u003e: The pill on the top of the stack is: :blue\n\n\n   #+END_SRC\n\n   An additional alternative was introduced, which is to generate the\n   quoted form with additonally evaluated clojure code included\n   through an escape sequence. If the escape sequence is provided,\n   '%=, the next value in the sequence is evaluated as clojure\n   code. This would be useful when generating code from a client to\n   plug into a fif stack machine as a server command.\n\n   #+BEGIN_SRC clojure\n\n   (require '[fif.core :as fif])\n   (require '[fif.client :refer [form-string]])\n\n\n   (def secret-message \"The Cake is a Lie\")\n\n\n   (fif/reval-string (form-string \"The secret message is: \" %= secret-message str println))\n   ;; \u003cstdout\u003e: The secret message is: The Cake is a Lie\n   ;; \u003cstdout\u003e: \n\n   #+END_SRC\n   \n** Making fif safer, because Russians...?\n\n   Although using *fif* from within clojure might have its shortfalls,\n   *fif* can avoid these shortfalls of clojure by passing in strings\n   containing EDN data.\n\n   The same unsafe example from before:\n\n   #+BEGIN_SRC\n\n   (require '[fif.core :as fif])\n\n   (fif/reval-string \"1 1 +\") ;; =\u003e '(2)\n\n   (fif/reval-string \"1 #=(+ 1 1) +\") ;; ERROR\n   ;; Unhandled clojure.lang.ExceptionInfo\n   ;; No reader function for tag =.\n   ;; {:type :reader-exception, :ex-kind :reader-error}\n\n   #+END_SRC\n\n   This means that *fif* could potentially (without liability on the\n   author's part) be used for remote execution. It could be used as a\n   sandboxed environment which only extends to clojure functions which\n   are deemed safe.\n\n   This brings me to the issue of erroneous infinite loops. The *fif*\n   stack machine has the ability to limit stack operation to a max\n   number of execution steps.\n\n   #+BEGIN_SRC clojure\n   (require '[fif.core :as fif])\n   (require '[fif.stack-machine :as stack])\n\n   (defn limited-stack-machine [step-max]\n     (-\u003e fif/*default-stack*\n         (stack/set-step-max step-max)))\n   \n   \n   (def default-step-max 200)\n   (defn eval-incoming [s]\n     (let [sm (limited-stack-machine default-step-max)\n           evaluated-sm (fif/with-stack sm (fif/eval-string s))\n           max-steps (stack/get-step-max evaluated-sm)\n           num-steps (stack/get-step-num evaluated-sm)]\n       (if (\u003e= num-steps max-steps)\n         \"Exceeded Max Step Execution\"\n         (-\u003e evaluated-sm stack/get-stack reverse))))\n   \n   \n   (def incoming-fif-eval \"3 0 do :data-value i loop\")\n   (eval-incoming incoming-fif-eval) ;; =\u003e (:data-value 0 :data-value 1 :data-value 2 :data-value 3)\n   \n   \n   (def infinite-fif-eval \"begin true while :data-value 1 repeat\")\n   (eval-incoming infinite-fif-eval) ;; =\u003e \"Exceeded Max Step Execution\"\n   \n   \n   (def malicious-fif-eval \"begin #=(fork-main-thread) false until\")\n   (eval-incoming malicious-fif-eval) ;; ERROR\n   ;; Unhandled clojure.lang.ExceptionInfo\n   ;; No reader function for tag =.\n   ;; {:type :reader-exception, :ex-kind :reader-error}\n\n   #+END_SRC\n\n\n** Running a fif Socket Repl Server\n   *fif* has the ability to start a socket repl server with a\n   designated stack-machine which can be accessed through a raw socket\n   connection. This has the benefit of providing a simple interface\n   for configuring a server, while only exposing limited\n   functionality.\n\n   #+BEGIN_SRC clojure\n\n   (require '[fif.core :as fif])\n   (require '[fif.stack-machine :as stack])\n   (require '[fif.server.core :as fif.server])\n\n   (def server-name \"Example Socket Server\")\n   (def server-port 5005)\n\n   (def custom-stack-machine\n     (-\u003e fif/*default-stack*\n         ;; prevents system error handler from throwing an error,\n         ;; places it on the stack instead\n         stack/enable-debug))\n\n   (defn start-socket-server []\n     (fif.server/start-socket-server custom-stack-machine server-name :port server-port))\n\n   (defn stop-socket-server []\n     (fif.server/stop-socket-server server-name))\n\n   #+END_SRC\n\n   Testing this server on linux can be done using netcat: ~netcat localhost 5005~\n   \n   If you are on Windows, it can be accessed with putty with these additional\n   configuration options:\n\n   - Set *Connection Type* to /Raw/\n   - Under the *Terminal* Setting Category, enable /Implicit CR in every LF/\n\n* Using fif from the commandline\n\n  fif supports a fairly straightforward commandline repl, which is\n  located at `fif.commandline/-main`. The commandline repl has the\n  ability to load scripts containing fif/edn code, and also includes\n  additional standard library word definitions for reading and writing\n  files on the filesystem. These additional word definitions are\n  located in the :stdlib.io group\n\n  The fif commandline can be accessed with ~lein run -- \u003carguments\u003e~\n\n* Native Executable\n\n  As of version 1.0.1, *fif* can be used as a standalone scripting\n  language. Compilation into a native executable is done by using\n  [[http://www.graalvm.org][GraalVM]] with the ~native-image~ commandline-tool.\n\n  To generate this executable yourself:\n\n  - clone this repository\n  - make sure you have [[https://leiningen.org][leiningen]] installed\n  - download and unpack a copy of [[https://github.com/oracle/graal/releases][the graal repository]]\n  - set the environment variable GRAAL_HOME as the root path of this\n    graal repository\n  - While at the root of the fif repository, run the ~build-native.sh~\n    script.\n\n  The generated executable should be placed in the ./bin/ folder of\n  the repository.\n\n  #+BEGIN_SRC sh\n\n  $ fif -e 2 2 + println\n  4\n  $ fif -h\n  fif Language Commandline repl/eval\n  \n  Usage:\n    fif [options]\n    fif \u003cfilename\u003e [arguments..] [options]\n  \n  Options:\n    -h, --help    Show this screen.\n    -e            Evaluate Commandline Arguments\n\n  Website:\n    github.com/benzap/fif\n\n  Notes:\n    * Commandline Arguments are placed in the word variable $vargs\n    * The :stdlib.io group includes additional io operations for reading\n    and writing files\n  \n  $ fif\n  Fif Repl\n   'help' for Help Message\n   'bye' to Exit.\n  \u003e 2 2 + println\n  4\n  \u003e bye\n  For now, bye!\n\n  #+END_SRC\n\n  The resulting binary starts incredibly fast (\u003c20ms), and has the advantage of directly\n  manipulating EDN configuration files.\n\n  #+BEGIN_SRC sh\n\n  $ fif -e '\"./deps.edn\" dup load-file [:deps fif] {:mvn/version \"1.0.2\"} assoc-in spit'\n\n  #+END_SRC\n\n  It can also be used like any standard scripting language. As an\n  example, i'm going to write a primitive script to add, remove and\n  list dependencies from a \"deps.edn\" file called ~clj-deps~\n\n  #+BEGIN_SRC clojure\n\n#!/usr/bin/env fif\n\ndef help-message \"clj dependency tool\n\nUsage:\n  clj-deps add \u003cpackage\u003e \u003cversion\u003e\n  clj-deps remove \u003cpackage\u003e\n  clj-deps list\n\nExample:\n  clj-deps add fif 1.0.2\n\"\n\n*cargs $vargs count setg\n*command $vargs first setg\n*package $vargs second dup nil? not if read-string first then setg\n*version $vargs 2 get setg\n\ncargs 3 =\ncommand \"add\" =\nand\nif\n  \"deps.edn\" read-file first\n  [:deps package] ?\n  {} :mvn/version version assoc assoc-in\n  \"deps.edn\" \u003c\u003e spit\nelse\n\ncargs 2 =\ncommand \"remove\" =\nand\nif\n  \"deps.edn\" read-file first\n  dup :deps get package dissoc :deps \u003c\u003e assoc\n  \"deps.edn\" \u003c\u003e spit\nelse\n\ncargs 1 =\ncommand \"list\" =\nand\nif\n  \"deps.edn\" read-file first\n  :deps get (dup first . \":\" . second :mvn/version get . cr nil) \u003c\u003e map\nelse\n  help-message println\nthen then then\n\n  #+END_SRC\n\n  An example of it's use:\n\n  #+BEGIN_SRC sh\n\n  $ echo \"{}\" \u003e deps.edn\n  $ clj-deps add fif 1.0.2\n  $ clj-deps add clock 0.3.2\n  cat deps.edn\n  {:deps {fif {:mvn/version \"1.0.2\"}, clock {:mvn/version \"0.3.2\"}}}\n  $ clj-deps remove clock\n  $ clj-deps list\n  fif:1.0.2\n  $ clj-deps\n  clj dependency tool\n  \n  Usage:\n    clj-deps add \u003cpackage\u003e \u003cversion\u003e\n    clj-deps remove \u003cpackage\u003e\n    clj-deps list\n  \n  Example:\n    clj-deps add fif 1.0.2\n\n  #+END_SRC\n\n* Development\n  \n  You can pull the project from github. Clojure tests are run via\n  ~lein test~, and Clojurescript tests are run via ~lein doo~.\n  Clojurescript tests require you to have ~node~ on your\n  Environment PATH.\n\n  I welcome any and all pull requests that further improve what is\n  currently here, especially things which further improve security and\n  improve error messages.\n\n  I'm still not sure where to go with respect to the standard library,\n  and i'm open to suggestions for making manipulation of clojure data\n  as painless as possible.\n\n* Upcoming Features\n  \n  A few things to look out for:\n\n  - +Implementation in Clojurescript+ *included since 0.3.0-snapshot*\n  - +Regex Support (#\"\" tagged literal is not valid EDN)+ *use 'regex' word definition*\n  - +Improved Error Messages+\n  - +Socket Repl+ *included since 0.4.0-snapshot*\n  - +Commandline Repl+ *included since 0.4.0-snapshot*\n  - +Programmable Repl in Clojure and Clojurescript+ *included since 0.4.0-snapshot*\n  - +Improved repl word definitions+ *On-Going*\n  - +Additional Standard Library Word Definitions+ *On-Going*\n  - Improved Fif Macros\n  - A Time Machine Debugger\n\n* Related Readings\n\n  - [[https://www.forth.com/starting-forth/][Starting Forth - Online Book]]\n  - [[https://nakkaya.com/2010/12/02/a-simple-forth-interpreter-in-clojure/][A Simple Forth Interpreter in Clojure - Blog Post]]\n  - [[https://learnxinyminutes.com/docs/forth/][Learn Forth In Y Minutes]]\n  - [[https://github.com/edn-format/edn][Extensible Data Notation - Github Page]]\n  - [[https://www.gnu.org/software/gforth/][GForth - Forth Implementation of the GNU Project]]\n\n* FAQ\n** Why fif?\n\n   *fif* is meant to be a play on *forth*. The name *forth* was originally\n   meant to be spelt *fourth*, but had to be reduced in order to fit\n   within the restrictions of computers at the time of it's creation,\n   and so the name stuck. I recommend you check out\n   [[https://en.wikipedia.org/wiki/Forth_(programming_language)][the wiki page]] for an interesting read.\n\n   It also helps to note that fif kind of sounds like you /have a lisp/ :)\n\n** Do you plan on using fif in production?\n\n   It's at the point where it is a viable scripting language for my\n   own projects. It has the benefits of being completely sandboxed,\n   and with the addition of the socket repl server, it could be used\n   as an alternative to exposing functionality for setting and getting\n   server configuration data, or even for automating certain\n   functionality with external scripts.\n\n   \n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenzap%2Ffif","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenzap%2Ffif","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenzap%2Ffif/lists"}