⚠️ ARCHIVED - This documentation is from an archived repository. For current documentation, visit: https://github.com/forthix/forthic
Forthic is intended to be the "top" of the application. It relies on a host language like Python and Javascript to provide lower level capability and constructs. Forthic is designed to coordinate chunks of code written in the host languages.
A Forthic program consists of whitespace-separated words that are executed sequentially. Words pass data to each other via the parameter stack. Some words push data onto the stack, while some words consume data from the stack, possibly pushing new data onto the stack.
Literal words push their own values onto the stack. For example:
1
2.4
2021-02-18
09:30
Comments and strings are in the style of Python:
# This is a comment
"Double quoted string"
'Single quoted string'
"""Triple double quoted string"""
'''Triple single quoted string'''
Parentheses are whitespace and have no other syntactic meaning.
Arrays are constructed using a "start array token" '[' and an "end array token" ']':
[ 1 2 3 ]
Because arrays are constructed using words and not syntactical elements, things like this are possible:
1 [ SWAP 2 3 ] # Has the same effect as [ 1 2 3 ]
The start/end array tokens are recognized directly by the Tokenizer, and so whitespace is not required when using them:
[1 2 3] # Same as [ 1 2 3 ]
New words can be created out of existing words using "start definition" (:) and end definition (;) tokens:
: DOUBLE 2 * ;
Definitions can use previously defined words:
: DOUBLE 2 *;
: QUADRUPLE DOUBLE DOUBLE; # yum
In the interpreter, definitions are stored in an array rather than a dictionary. This allows them to be redefined without affecting existing definitions that already use them:
: DOUBLE 2 *;
: QUADRUPLE DOUBLE DOUBLE;
3 DOUBLE # 6
: DOUBLE [ SWAP DUP ];
3 QUADRUPLE # 12
3 DOUBLE # [3 3]
A definition can be memo-ized by using @: instead of :. When a memoized definition is called for the first
time, it behaves like an ordinary definition except that it stores a copy of the top of the stack. On subsequent
calls, it simply returns that stored copy. The memoized value can be refreshed by calling the memoized definition
with a ! appended. For example:
@: TICKETS JQL FIELDS jira.SEARCH;
TICKETS # Will do the Jira search and save a copy of the results
TICKETS # Will return the results from the previous call
TICKETS! # Will perform the Jira search again and save a new copy of the results
TICKETS # Will return the latest copy of the results
All definitions are stored within Forthic modules. A Forthic application can use any number of modules, but two of them are special: the app module and the global module. The app module is the default module for a Forthic program. For a plain Forthic app, new definitions are added here. The global module contains all of the "built-in" Forthic words (like SWAP, DUP, MAP, *, +, etc.)
Modules can be created within Forthic using the start/end module tokens. For example, this creates a my-module module with a definition of DOUBLE inside of it:
{my-module
: DOUBLE 2 *;
}
If a module already exists, you can "open it" again using the same syntax:
{my-module
: DOUBLE 2 *;
}
{my-module
4 DOUBLE # 8
}
When a module is created, it is created within the context of the current module. In the previous example, my-module is created within the app module. When modules are nested, a "module stack" is created:
{first
{second
{third
# At this point, the current module is `third`
# The next module in the module stack is `second`
# The final module in the module stack is `first`
}
}
}
When a word is to be executed, the modules are searched in this order:
- Current module and the active module stack
- App module
- Global module
New Forthic modules can be created in the host language by subclassing Module. Words defined and exported from a module become available to a Forthic program when imported via USE-MODULES.
Like definitions, variables exist within modules. The words used to create, read, and set variables are defined in the global module:
["x" "y"] VARIABLES # Defines variables `x` and `y` in the current module
20 x ! # The `!` word sets the value of x to 20
5 y ! # Set y to 5
x @ # The `@` word pushes the current value of `x` onto the stack
y @ # Stack is now (20 5)
* # 100
The global module defines all of the core words in Forthic. While similar in spirit to C's Standard Library, the global module also defines base words for doing things that could arguably be built into the language (e.g., defining and using variables). This keeps the Forthic interpreter as simple as possible.
The global module contains words that are expected of every Forthic interpreter regardless of host language, but it may also contain words that are specific to a host language. For instance, an interpreter running in a web browser has the global HASH-PARAMS word to get the hash parameters for the current page's URL.
The global module defines the following types of words: base words, array/record words, string words, date/time words, math words, profiling words, and misc words. For more details, see the global module documentation.
VARIABLESdefines variables in a module!sets value of a variable@gets value of a variableINTERPRETruns a Forthic stringMEMOmemoizes the results of a Forthic string (deprecated, use@:instead)EXPORTmakes words from the current module importableUSE-MODULESimports words from the specified modulesRECcreates a record object (like a Python dictionary)REC@retrieves the value of a field from a record<REC!sets the value of a record's field, leaving the record on the stackSCREEN!stores a "screen" of Forthic codeSCREENreturns a "screen" of Forthic code as a stringLOAD-SCREENloads and runs a "screen" of Forthic codePOPpops a value from the parameter stack and throws it awayDUPpushes a copy (reference) of the top of the parameter stackSWAPreverses the order of the top two elements of the parameter stack
APPENDadds an element to an array/recordREVERSEreverses the order of an arrayUNIQUEensures unique values<DELdeletes an elementRELABELchanges indexes/keys in an array/recordBY-FIELDorganizes elements by a fieldGROUP-BY-FIELDgroups elements by a fieldGROUP-BYgroups elements using a Forthic stringGROUP-BY-w/KEYis the same asGROUP-BY, but Forthic string expects a key and valueGROUPS-OFsplits a collection of elements into groups of a specified lengthMAPmaps a Forthic string over a collection of elementsMAP-w/KEYis the same asMAPbut the Forthic string expects a key and valueFOREACHruns a Forthic string over a collection of elementsFOREACH-w/KEYis the same asFOREACHbut the Forthic string expects a key and valueZIPtakes two containers and returns a new container containing corresponding pairs of elementsZIP-WITHis likeZIPbut uses a Forthic string to return the element valuesKEYSreturns the indexes/keys of a containerVALUESreturns the values of a containerLENGTHreturns the number of items in a containerSLICEreturns a slice of elements from a containerDIFFERENCEreturns the items in the first container that are missing from the secondINTERSECTIONreturns the items that are in both containersUNIONreturns items that are in either containerSELECTfilters a container using a Forthic string predicateSELECT-w/KEYis likeSELECT, but the Forthic string expects a key and a valueTAKEreturns the firstnelements of a containerDROPreturns a container with the firstnelements droppedROTATEtakes the last element of a container and puts it at the startROTATE-ELEMENTtakes an element and puts it at the startSHUFFLErandomizes the order of elements in a containerSORTsorts the container using the default comparisonSORT-w/FORTHICsorts a container using a Forthic key functionSORT-w/KEY-FUNCsorts a container using a host language key functionFIELD-KEY-FUNCreturns a host language key function that returns the value of an element's fieldNTHreturns the nth element of a containerLASTreturns the last element of a containerUNPACKtakes an array and pushes its elements on the stackFLATTENtakes an array of nested arrays and removes all levels of nestingKEY-OFreturns the index/key of an element in a containerREDUCEreduces a container using an initial value and a Forthic string
CONCATconcatenates two strings or an array of stringsSPLITtakes a string and splits it by a separator into an array of stringsJOINtakes an array of strings and a separator and joins them by the separator/Npushes a newline (\n) onto the stack/Rpushes a carriage return (\r) onto the stack/Tpushes a tab (\t) onto the stack|LOWERconverts a string to lowercase|UPPERconverts a string to uppercase|ASCIIstrips out non-ASCII characters from a stringSTRIPremoves surrounding whitespace from a stringREPLACEreplaces subtrings in a string with another substringRE-MATCHperforms a regular expression match on a stringRE-MATCH-GROUPreturns the nth match from the result ofRE-MATCH>STRconverts the top of the stack to a stringURL-ENCODEescapes a string so it can be added as a query param in a URLURL-DECODEunescapes a url-encoded string
AMforces a time literal to AM (e.g., 20:30 becomes 8:30 AM)PMforces a time literal to PM (e.g., 8:30 becomes 20:30)NOWreturns the current time>TIMEconverts an object to a time value if possible<TZ!specifies the timezone of a time valueTIME>STRconverts a time value to a string (military time)>DATEconverts an object to a date if possibleTODAYreturns the current dateMONDAYreturns the date of the Monday of this weekTUESDAYreturns the date of the Tuesday of this weekWEDNESDAYreturns the date of the Wednesday of this weekTHURSDAYreturns the date of the Thursday of this weekFRIDAYreturns the date of the Friday of this weekSATURDAYreturns the date of the Saturday of this weekSUNDAYreturns the date of the Sunday of this weekNEXTis used withMONDAY,TUESDAY, etc. to return the nextMONDAY,TUESDAY, etc.+DAYSadds a number of days to a dateSUBTRACT-DATESreturns the number of days difference between two datesSUBTRACT-TIMESreturns the difference between two times in secondsDATE>STRconverts a date to a string like "2021-02-18"DATE-TIME>DATETIMEcombines a date and time into a datetime valueDATETIME>TIMESTAMPreturns the unix timestamp for a datetimeTIMESTAMP>DATETIMEreturns the datetime for a unix timestamp
FALSEpushes the host language's idea of false onto the stackTRUEpushes the host language's idea of true onto the stack+adds two numbers-subtracts two numbers*multiplies two numbers/divides two numbersMODreturns the modulo of two numbersROUNDrounds a number to the nearest integer==checks if two items are equal!=checks if two items are not equal>checks if the earlier stack item is greater than the top of the stack>=checks if the earlier stack item is greater than or equal to the top of the stack<checks if the earlier stack item is less than the top of the stack<=checks if the earlier stack item is less than or equal to the top of the stackORreturnsTRUEeither of the top two stack items isTRUE; if top is an array, thenTRUEif any of the array elements isTRUEANDreturnsTRUEif the top two stack items areTRUE; if top is an array, thenTRUEif all array elements areTRUENOTinverts a Boolean valueINreturnsTRUEif an item is in an arrayANYreturnsTRUEif any item in an array is in another arrayALLreturnsTRUEif all items in an array are in another array>BOOLconverts a value to a Boolean>INTconverts a value to an integer>FLOATconverts a value to a floating point numberUNIFORM-RANDOMreturns a random value between two numbers
PROFILE-STARTbegins profiling a Forthic programPROFILE-TIMESTAMPadds a timestamped label to a profiling runPROFILE-ENDstops the profiling of a Forthic program and returns an object for analyzing expensive callsPROFILE-DATAreturns stats for the most recent profiling runPROFILE-REPORTreturns a formatted string version ofPROFILE-DATA
NULLreturns the host language'snullvalue (e.g.,Nonein Python)QUOTE-CHARreturns an untypeable ASCII character for quoting arbitrary strings sent between two different Forthic interpreters (e.g., browser and server)DEFAULTif the top of stack isNULLthen replaces it with a specified value*DEFAULTif top of stack isNULLruns a Forthic string to replace its value<REPEATrepeatedly executes a Forthic string a specified number of times against an item, leaving the original item on the stackIDENTITYdoes nothing>FIXEDconverts a number to a string with a specified number of significant digits>JSONconverts an object to a JSON stringJSON>converts a JSON string to an object>TSVconverts an array of items to a TSV string>TSVconverts a TSV string to an array of itemsRECS>TSVconverts an array of records to a TSV string with a header corresponding to the record fieldsTSV>RECSconverts a TSV string with a header row to an array of records with fields corresponding to the header row.sprints the parameter stack to the console and drops the Forthic interpreter into the host language's debugger