{"id":13782976,"url":"https://github.com/howerj/pickle","last_synced_at":"2025-12-26T23:44:31.523Z","repository":{"id":39166445,"uuid":"151298187","full_name":"howerj/pickle","owner":"howerj","description":"Improvements to picol: A TCL like interpreter suitable as an shell in an embedded system","archived":false,"fork":false,"pushed_at":"2023-06-27T20:09:16.000Z","size":523,"stargazers_count":23,"open_issues_count":0,"forks_count":3,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-08-03T18:18:02.564Z","etag":null,"topics":["c","embedded","interpreter","picol","tcl"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/howerj.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}},"created_at":"2018-10-02T17:51:21.000Z","updated_at":"2024-03-01T23:32:00.000Z","dependencies_parsed_at":"2024-04-24T13:44:40.279Z","dependency_job_id":null,"html_url":"https://github.com/howerj/pickle","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/howerj%2Fpickle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/howerj%2Fpickle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/howerj%2Fpickle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/howerj%2Fpickle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/howerj","download_url":"https://codeload.github.com/howerj/pickle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253604379,"owners_count":21934851,"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":["c","embedded","interpreter","picol","tcl"],"created_at":"2024-08-03T18:01:49.937Z","updated_at":"2025-12-26T23:44:31.447Z","avatar_url":"https://github.com/howerj.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"% pickle(1) | A Small TCL like interpreter\n\n# NAME\n\nPICKLE - A Small and Embeddable TCL like interpreter and library\n\n# SYNOPSES\n\npickle files...\n\npickle\n\n# DESCRIPTION\n\n\tAuthor:     Richard James Howe / Salvatore Sanfilippo\n\tLicense:    BSD\n\tRepository: \u003chttps://github.com/howerj/pickle\u003e\n\tEmail:      howe.r.j.89@gmail.com\n\tCopyright:  2007-2016 Salvatore Sanfilippo\n\tCopyright:  2018-2020 Richard James Howe\n\nThis is a copy, and modification, of a small interpreter written by Antirez in\nabout 500 lines of C, this interpreter is for a small TCL like language. The\nblog post describing this interpreter can be found at\n\u003chttp://oldblog.antirez.com/post/picol.html\u003e, along with the code itself at\n\u003chttp://antirez.com/picol/picol.c.txt\u003e. It does a surprising amount for such a\nsmall amount of code. This project is a little bit bigger than the original at\naround ~6000 lines.\n\n## LICENSE\n\nThe files [pickle.c][] and [pickle.h][] are licensed under the 2 clause\n[BSD License][], as are all the other files in this project.\n\n## BUILDING\n\nTo build you will need a [C][] compiler and [Make][].\n\nType 'make' to build the executable 'pickle' (or 'pickle.exe') on Windows. To\nrun type 'make run', which will drop you into a pickle shell. 'make test' will\nrun the built in unit tests and the unit tests in [shell][].\n\n## RUNNING\n\nTo run the project you will need to build it, the default makefile target will\ndo this, type:\n\n\tmake\n\nOr:\n\n\tmake pickle\n\nThis will build the pickle library and then link this library with an example\nprogram contained in [main.c][]. This example program is very simple and adds a\nfew commands to the interpreter that do not exist in the library (\"gets\", \"puts\",\n\"clock\", \"getenv\", \"exit\", \"source\", \"clock\" and \"heap\"), this is the minimal\nset of commands that are needed to get a usable shell up and running and do\nperformance optimization.\n\nThe executable 'pickle' that is built is quite simple, it executes all arguments\ngiven to it on given to it on the command line as scripts. There are no\noptions, flags or detection of an interactive session with [isatty][]. This\nmakes usage of the interpreter in interactive sessions challenging, instead,\nthe language itself can be used to define a shell and process command line\narguments. This is done in a program called '[shell][]'. As mentioned it\ncontains the unit tests for the project, as well as other subprograms, and most\nimportantly it contains the interactive shell that reads a line at a time and\nprints the result.\n\nThe code in [shell][] is quite large, if you do not want to use it an\nincredibly minimal shell can be defined with the code:\n\n\t#!./pickle\n\n\tset r 0\n\twhile { } {\n\t\tputs -nonewline \"pickle\u003e \"\n\t\tset r [catch [list eval [gets]] v]\n\t\tputs \"\\[$r\\] $v\"\n\t}\n\nFor those experienced with [TCL][] some differences in the 'while' and 'gets'\ncommand should be apparent.\n\n## The Picol Language\n\nThe internals of the interpreter do not deviate much from the original\ninterpreter, so the document on the [picol][] language still applies. The\nlanguage is like a simplified version of [TCL][], where everything is\na command and the primary data structure is the string.\n\nSome programmers seem to an obsessive interest in their language of choice,\ndo not become one of those programmers. This language, like any other\nlanguage, will not solve all of your problems and may be entirely unsuitable\nfor the task you want to achieve. It is up to you to evaluate whether this\nlanguage, and implementation of it, is suitable.\n\nLanguage and implementation advantages:\n\n* Small and compact, easy to integrate into a wide variety of platforms and\nprograms. (4000 [LoC][] is the limit for the core library in [pickle.c][]).\n* Fairly good at string handling.\n* Can be ported to a variety of platforms. There are few system dependencies\nand the design of the program allows it to be ported to an embedded platform.\n* Customizable.\n* Suitable as a command language and shell.\n\nDisadvantages:\n\n* Everything is a string (math operations and data structure manipulation\nwill be slow).\n* This is a Do-It-Yourself solution, it may require that you modify the library\nitself and will almost certainly require that you define your own new commands\nin [C][].\n* Lacks Unicode/UTF-8 support.\n* The language interpreter is not well tested and is likely to be insecure. If\nyou find a bug, please report it. It is however better tested that most\n(re)implementations or extensions to the [picol][] interpreter and far less\nlikely to segfault or crash if you misuse the interpreter.\n\nPotential Improvements:\n\n* Many, many more commands could be written in order to make this interpreter\nmore usable.\n* The following small libraries can be used to either extend or modify the\ninterpreter to suite your purposes:\n  - UTF-8: \u003chttps://www.cprogramming.com/tutorial/unicode.html\u003e\n  - Data Packing/Unpacking: \u003chttps://beej.us/guide/bgnet/html/multi/advanced.html#serialization\u003e\n  - Base-64: \u003chttps://stackoverflow.com/questions/342409/\u003e\n  - Line editing: \u003chttps://github.com/antirez/linenoise\u003e\n  - Fixed point (Q16.16, signed) library: \u003chttps://github.com/howerj/q\u003e\n\nThe interpreter is fairly small, on my 64-bit x86 machine the interpreter\nweighs in at 100KiB (stripped, dynamically linked to C library, Linux ELF). The\ninterpreter can be configured so that it is even smaller, for example:\n\n\tOn Debian Linux, x86_64, dyanmically linking against glibc, using\n\tgcc version 8.3.0, with version 4.1.4 of the interpreter:\n\tSize    Options/Notes\n\t100KiB  Normal target, optimized for speed (-O2).\n\t84KiB   No debugging (-DNDEBUG), optimized for speed (-O2).\n\t54KiB   No debugging (-DNDEBUG), 32-bit target (-m32), optimized\n\t        for size (-Os), stripped, No features disabled.\n\t34KiB   No debugging, 32-bit target, optimized for size, stripped,\n\t        with as many features disabled as possible.\n\n\tOn Debian Linux, x86_64, statically linked against musl C library:\n\t147KiB  Normal target, optimized for speed, statically linked.\n\nThis is still larger than I would like it to be, the original picol interpreter\nin the smallest configuration (32 bit target, optimized for size), comes in at\n18KiB.\n\n### The language itself\n\nPicol, and [TCL][], are dynamic languages with only one real data type, the\nstring. This might seem inefficient but it is fine for a glue language whose\nmain purpose is to bind lots of things written in C together. It is similar to\n[lisp][] in ways, it is [homoiconic][], and is simple with very little in the\nway of syntax.\n\nThe following table sums up the different language constructs:\n\n\tstring  called if first argument\n\t{ }     quote, used to prevent evaluation\n\t[ ]     command substitution\n\t\" \"     string\n\t$var    variable lookup\n\t\\c      escape a character\n\t#       comment\n\t;       terminates a command\n\nA Picol program consists of a series of commands and arguments to those\ncommands. Before a command is evaluated, variables are looked up and strings\nsubstituted.\n\nYou may have noticed that things such as 'if' or 'while', and even procedure\ndefinition, are not part of the languages syntax. Instead, they are built in\ncommands and are called like any other command.\n\nExamples of commands:\n\n\tputs \"Hello, World\"\n\t\"puts\" \"Hello, World\"\n\n\t# prints \"Hello, World\"\n\tset cmd puts\n\t$cmd \"Hello, World\"\n\n\t# prints \"Hello, World\"\n\tset a pu\n\tset b ts\n\t$a$b \"Hello, World\"\n\n\t+ 2 2\n\t- 4 5\n\tif {bool 4} { puts \"TRUE\"}\n\n\tproc x {a b} { + $a $b }\n\tputs \"x(3, 9) == [x 3 9]\"\n\n\t# prints 4 to 10 inclusive\n\tset z 3\n\twhile {\u003c $z 10} { set z [+ $z 1]; puts $z }\n\nTo best understand the language, play around with it, and look at the source,\nthere really is not that much there.\n\n### Internally Defined Commands\n\nPicol defines the commands in this section internally, in a default build all\nof the commands in this section will be available. There are some build options\nto remove some commands (such as the string function, the math functions, and\nthe list functions).\n\nThe options passed to the command and type are indicated after the command, a\nquestion mark suffix on an argument indicates an optional command, an ellipsis\nindicates an optional series of arguments.\n\nFor some concrete examples of commands being run, see [unit.tcl][], which\ncontains [unit tests][] for the project.\n\n* argv\n\n'argv' is a variable, not a function, which should contain the arguments passed\nto the pickle interpreter on the command line in a TCL list.\n\n* set variable value?\n\nCreate a variable, or overwrite an existing variable, with a value. If only one\nargument is given, it returns the value of that variable if it exists or an error\nif it does not.\n\n* if {condition} {true clause}  *OR*  if {condition} {true clause} else {false clause}\n\n*if* is the command used to implement conditional execution of either one\nclause, or one clause or (exclusive or) another clause. Like in every other\nprogramming language ever (or more accurately the languages with more than one\nuser, the implementer).\n\n* while {condition} {clause}\n\nKeep executing the while clause whilst the condition is true (ie. is non-zero).\n\n* break\n\nBreak out of a while loop. This will continue to break out of a things until\nthe return code is caught by a loop, or 'catch'.\n\n* continue\n\nDesist from executing the rest of the clause in a while loop, and go back to\ntesting the condition.\n\n* proc identifier {argument list} {function body}\n\nCreate a new command with the name 'identifier', or function if you prefer,\nwith the arguments in 'argument list', and code to be executed in the 'function\nbody'. If the final command is not a 'return' then the result of the last\ncommand is used.\n\nThere is a special case whereby the last argument in the argument list is\ncalled 'args', if this is the case then the renaming arguments are concatenated\ntogether and passed in to the function body. This allows variadic functions to\nbe created.\n\n* return string? number?\n\nOptionally return a string, optionally with an internal number that can affect\ncontrol flow.\n\n* uplevel number strings...\n\nEvaluate the 'strings...' in the scope indicated by 'number'. A special case\nis '#0', which is the global context. The strings are concatenated together as\nwith if they have been run through the 'concat' command. A scope of 0 is the\ncurrent scope, of 1, the caller, of 2, the caller's caller, and so on. A '#'\nprefix is meant to reverse the search and start from the global scope and work\ndown through the call stack, however only '#0' is supported.\n\n* upvar number otherVar myVar\n\nForm a link from myVar to otherVar in the scope specified by number. A\nspecial case is '#0', which is the global context, see 'uplevel' for a\ndescription of the scoping traversal rules implied by the number argument.\n\nYou may have noticed that 'upvar' and 'uplevel', which come from [TCL][], are\nstrange, very strange. No arguments from me.\n\n* unset string\n\nUnset a variable, removing it from the current scope.\n\n* eval strings...\n\nConcatenate a list of strings with a space in-between them, as with 'concat',\nthen evaluate the string, returning the result of the evaluation.\n\n* apply {{arg-list} {body}} args\n\nApplies an argument list to a function body, substituting the provided\narguments into the variables.\n\nExamples:\n\n\t# Returns 4\n\tapply {{x} {* $x $x}} 2\n\t# Returns 7\n\tapply {{x y} {+ $x $y}} 3 4\n\nIt essential allows for anonymous functions to be made.\n\n* mathematical operations\n\nThe following mathematical operations are defined:\n\n'+', '-', '\\*', '/', 'mod', '\u0026lt;', '\u0026lt;=', '\u0026gt;', '\u0026gt;=', '==', '!=',\n'min', 'max', 'pow', and 'log'. It should be obvious what each one does.\n\nIt should be noted that because all variables are stored internally as strings,\nmathematical operations are egregiously slow. Numbers are first converted to\nstrings, the operation performed, then converted back to strings. There are\nalso some bitwise operations; 'lshift', 'rshift', 'and', 'or', 'xor'. These\nmathematical operations can accept a list integers. '\u0026', '|' and '^' are\naliases for 'and', 'or' and 'xor' respectively. '\u0026\u0026' and '||' implement logical\n'and' and 'or', *but all arguments are evaluated -- and it is not a bug!*.\n\nThere are also the following unary mathematical operators defined: 'not'/'!'\n(logical negation), 'invert'/'~' (bitwise inversion), 'abs' (absolute value),\n'bool' (turn number into a boolean 0 or 1), 'negate' (negate a number). '-' is\nnot defined as negate, as that symbol is already used for subtraction.\n\nNumbers conversion is strict, an invalid number will not be silently converted\ninto a zero, or a string containing a part of a number will not become that\nnumber, for example: \"0\", \"-1\" and \"12\" are valid numbers, whilst; \"0a\", \"x\",\n\"--2\", \"22x\" are not.\n\n* catch expr varname\n\nThis allows arbitrary codes to be caught, 'catch' evaluates an expression and\nputs the return code into 'varname', the string returned is the result of the\nevaluation of 'expr'.\n\n* command item number *OR* command\n\nThis function is used to inspect the currently defined commands in the system.\n\nIf no arguments are given then the number of commands defined is returned. If\nan item is given a number indicates which command that it applies to. Commands\nare indexed by numbers. Defining new command may change the index of other\ncommands. Commands are either user defined or built in commands.\n\n - args: get a functions arguments (returns '{built-in pointer pointer}' for built in commands)\n - body: get a functions body (returns '{built-in pointer pointer}' for built in commands)\n - name: get a functions name\n\n* join {list} string\n\nGiven a [TCL][] list, 'join' will flatten that list and return a string by\ninserting a String in-between its elements. For example \"join {a b c} ,\" yields\n\"a,b,c\".\n\n* conjoin string arguments\\*\n\n'conjoin' works the same as 'join' except instead of a list it joins its\narguments, for example:\n\n\tjoin {a b c} ,\n\tconjoin , a b c\n\nAre equivalent.\n\n* for {start} {test} {next} {body}\n\nImplements a for loop.\n\n* rename function-name new-name\n\nRename a function to 'new-name', this will fail if the function does not exist\nor a function by the same name exists for the name we are trying to rename to.\nA special case exists when the new-name is an empty string, the function gets\ndeleted.\n\n* llength list\n\nGet the length a list. A TCL list consists of a specially formatted string\nargument, each element of that list is separated by either space or is a string\nor quote. For example the following lists each contain three elements:\n\n\t\"a b c\"\n\t\"a { b } c\"\n\t\"a \\\" b \\\" c\"\n\nThe list is the basic higher level data structure in Pickle, and as you can\nsee, there is nothing special about them. They are just strings treated in a\nspecial way. Processing these lists is incredibility inefficient as everything\nis stored as a string - a list needs to be parsed before it can be manipulated\nat all. This applies to all of the list functions. A more efficient, non-TCL\ncompatible, set of list functions could be designed, or the internals of the\nlibrary could be changed so they are more complex (which would help speeding\nup the mathematical functions), but either option is undesirable for different\nreasons.\n\n* lindex list index\n\nSee 'llength'.\n\nIndex into a list, retrieving an element from that list. Indexing starts at\nzero, the first element being the zeroth element.\n\n* lrepeat number string\n\nRepeat a string a number of times to form a list.\n\nExamples:\n\n\tpickle\u003e lrepeat 3 abc\n\tabc abc abc\n\tpickle\u003e lrepeat 2 {x x}\n\t{x x} {x x}\n\n* lset variable index value\n\nLook up a variable containing a list and set the element specified by an index to\nbe equal to 'value'.\n\n* linsert list index value\n\nInsert a value into a list at a specified index, indices less than zero are\ntreated as zero and greater than the last element are appended to the end of\nthe list.\n\n* lreplace list first last values...\n\nReplace ranges of elements within a list, the function has a number of special\ncases.\n\n* lsort opts... list\n\nThis command sorts a list, it uses [insertion sort][] internally and lacks\nmany of the options of the full command. It does implement the following\noptions:\n\n  - '-increasing' (default)\n\nSort the list in increasing order.\n\n  - '-decreasing'\n\nSort the list in decreasing order.\n\n  - '-ascii' (default)\n\nThe list is a series of strings that should be stored in [ASCII][] order.\n\n  - '-integer'\n\nThe list is a series of numbers that should be sorted numerically.\n\n* lreverse list\n\nReverse the elements in a list.\n\n* lrange list lower upper\n\nExtract a range from a list.\n\n* lsearch opts... list pattern\n\nThe search command attempts to find a pattern within a list and if found it\nreturns the index as which the pattern was found within the list, or '-1' if\nit was not found.\n\n  - '-nocase'\n\nDo a case insensitive search, beware this is ASCII only!\n\n  - '-not'\n\nInvert the selection, matching patterns that *do not* match.\n\n  - '-exact'\n\nPattern is an exact string to search for.\n\n  - '-integer'\n\nThe pattern is a number to search for.\n\n  - '-glob' (default)\n\nThis subcommand uses the same [regex][] syntax (and engine) as the\n'string match' subcommand, it is quite limited, and it is the default search\noption.\n\n  - '-inline'\n\nInstead of returning the index, return the found element.\n\n  - '-start index'\n\nStart at the specified index instead of at zero.\n\n* split string splitter\n\nSplit a string into a list, the value to split on is not a regular expression,\nbut a string literal. There is a special case where the value to split on is\nthe empty string, in this case it splits a string into a list of its\nconstituent characters.\n\n* lappend variable values...\n\nAppend values to a list, stored in a variable, the function returns the newly\ncreated list.\n\n* list args...\n\nTurn arguments into a list, arguments with spaces in them are quoted, the\nlist command returns the concatenation of the escaped elements.\n\n* concat args...\n\nTrim arguments before concatenating them into a string.\n\n* reg opts... regex string\n\n'reg' implements a small regular expression engine that can be used to extract\nmatches from text. It has a few options that can be passed to it, and a few\nvirtues; lazy, greedy and possessive.\n\n - -nocase\n\nIgnore case when matching a string.\n\n - -start index\n\nSet the start of the string to match from, numbers less than zero are treated\nas zero, and numbers greater than the length of the string are treated as\nreferring to the end of the string.\n\n - -lazy\n\nMatch the shortest string possible.\n\n - -greedy (default)\n\nMatch the longest string possible.\n\n - -possessive\n\nMatch the longest string possible, with no backtracking. If backtracking is\nnecessary the match fails.\n\n* unknown cmd args...\n\nThis command is *not* defined at startup, but can be defined by the user to\ncatch command-not-found exceptions.\n\nWhen the interpreter encounters a command that has not been defined it attempts\nto find the 'unknown' command and execute that. If it is not found, it performs\nits default action, which is to throw an error and return an error string\nindicating the command has not been found. If the 'unknown' command has been\nfound then it is executed with the command and its arguments being passed to\n'unknown' as a list.\n\nFor example, defining:\n\n\tproc unknown {args} { system \"$args\" }\n\nWould mean any command the interpreter does know know about will be executed by\nthe system shell, including its arguments, provided the *system* command is\ndefined.\n\nIf an unknown command is found within the unknown function then a generic error\nmessage is returned instead.\n\n* trace on *OR* trace off *OR* trace status\n\nThis command can be used to turn tracing on, off, or to query the status of\ntracing. The [TCL trace command][] is quite powerful, this one is far more\nlimited.\n\n* tracer cmd args...\n\nThis command *not* defined at startup, but can be defined by the user. This can\nbe used to trace the execution of the program.\n\nThe commands executed within *tracer* will not be traced.\n\n* info subcommand args...\n\nThe 'info' command is used to query the status of the interpreter and supports\nmany subcommands. The subcommands that are supported are:\n\n- commands match?\n\nMatch defaults to '\\*'. Get a list of all defined commands filtered on 'match'.\n\n- procs match?\n\nMatch defaults to '\\*'. Get a list of all commands defined with 'proc' filtered\non 'match'.\n\n- functions match?\n\nMatch defaults to '\\*'. Get a list of all mathematical functions filtered on\n'match'.\n\n- locals match?\n\nMatch defaults to '\\*'. Get a list of all defined locals filtered on 'match'.\n\n- globals match?\n\nMatch defaults to '\\*'. Get a list of all defined globals filtered on 'match'.\n\n- level\n\nGet the current 'level' of the interpreter, which is the degree of nesting or\nscopes that exist relative to the top level scope. Entering a function\nincreases the level by one, for example.\n\n- cmdcount\n\nGet the number of commands executed since startup, this can be used as a crude\nform of a performance counter if the command *clock* is not available.\n\n- version\n\nReturn the version number of the interpreter in list format \"major minor patch\",\n[semantic versioning](https://semver.org/) is used.\n\n- complete line\n\nDoes the 'line' constitute a command that can be called (which may result in an\nerror)? Or 'does this line parse correctly'? \"0\" is returned if it cannot, \"1\"\nis returned if it can.\n\n- exists variable\n\nDoes 'variable' exist in the current scope, \"0\" is returned if it does not\nwhilst \"1\" is returned if it does.\n\n- args name\n\nGet the arguments of the named function. Functions that are defined in C will\nreturned the string 'built-in', otherwise a list is returned containing the\nfunction arguments.\n\n- body name\n\nGet the body of the named function. Functions that are built in functions\ndefined in C will return a function pointer that represents that C function.\nFunctions defined with 'proc' will return the body of the function as a string.\n\n- private name\n\nGet the private data of a function.\n\n- system attribute\n\nThe \"system\" subcommand is used to access various attributes that have\nbeen set in the interpreter at compile time or due to the environment\nthat the system is compiled for.\n\nAttributes that can be looked up are:\n\n1. \"pointer\": size of a pointer in bits.\n2. \"number\": size of a number in bits.\n3. \"recursion\": recursion depth limit.\n4. \"length\": maximum length of a string or -1 if string length is unlimited.\n5. \"min\": minimum size of a signed number.\n6. \"max\": maximum size of a signed number.\n7. \"string\": are string operations defined?.\n8. \"maths\": are math operations defined?.\n9. \"list\": are list operations defined?.\n10. \"regex\": are regular expression operations defined?.\n11. \"help\": are help strings compiled in?.\n12. \"debugging\": is debugging turned on?.\n13. \"strict\": is strict numeric conversion turned on?.\n\n#### String Operator\n\n* string option arg *OR* string option arg arg *OR* string option arg arg arg\n\nThe 'string' command in [TCL][] implements nearly every string command you\ncould possibly want, however this version of 'string' is more limited and\nbehaves differently in many circumstances. 'string' also pulls in more standard\nC library functions from '[ctype.h][]' and '[string.h][]'.\n\nSome of the commands that are implemented:\n\n  - string match -nocase? pattern String\n\nThis command is a primitive regular expression matcher, as available from\n\u003chttp://c-faq.com/lib/regex.html\u003e. What it lacks in functionality, safety and\nusability, it makes up for by being only ten lines long (in the original). It\nis meant more for wildcard expansion of file names (so '?' replaces the meaning\nof '.' is most regular expression languages). '\\\\' is used as an escape\ncharacter, which escapes the next character.\n\nThe following operations are supported: '\\*' (match any string) and '?' (match\nany character). By default all patterns are anchored to match the entire\nstring, but the usual behavior can be emulated by prefixing the suffixing the\npattern with '\\*'.\n\n  - string trimleft  String Class?\n\nIf 'class' is empty, a white-space class is used. 'trimleft' removes leading\ncharacters in a Class from the given String.\n\n  - string trimright String Class?\n\nIf 'class' is empty, a white-space class is used. 'trimleft' removes trailing\ncharacters in a class from the given String.\n\n  - string trim      String Class?\n\nIf 'class' is empty, a white-space class is used. 'trimleft' removes both\nleading and trailing characters in a class from the given String.\n\n  - string length  String\n\nGet the length of String. This is a simple byte length excluding an ASCII NUL\nterminator.\n\n  - string tolower String\n\nConvert an ASCII String to lower case.\n\n  - string toupper String\n\nConvert an ASCII String to upper case.\n\n  - string reverse String\n\nReverse a string.\n\n  - string equal   String1 String2\n\nCompare two strings for equality. Returns '1' if equal, '0' if not equal. This\ncomparison is case sensitive.\n\n  - string compare String1 String2\n\nCompare two strings.\n\n  - string index   String Index\n\nRetrieve a character from a String at the specified Index. The index starts at\nzero for the first character up to the last character. Indices past the last\ncharacter return the last character. Negative indexes starting counting from\nthe last character (the last character being -1) and count downward, negative\nindexes that go before the first character return the first character.\n\n  - string is Class String\n\n'is' determines whether a given String belongs to a Class. Most class tests\naccept a zero length string as matching that class with a few exceptions. Most\nclass tests test that a class contains only certain characters (such as 'alpha'\nwhich checks that a string only contains the characters 'a-z' and 'A-Z', or\n'digit', which checks that a string only contains the characters '0-9'. Other\nclass tests test that a string matches a specific format, such as 'integer'\n(which does not accept a zero length string), it excepts the string to contain\na decimal number with an optional '+' or '-' prefix.\n\nClass can be:\n\n    - [alnum][]\n    - [alpha][]\n    - [digit][]\n    - [graph][]\n    - [lower][]\n    - [print][]\n    - [punct][]\n    - [space][]\n    - [upper][]\n    - [xdigit][]\n    - ascii\n    - [control][]\n    - integer\n\nAny other Class is invalid. Most classes are based on a C function (or macro)\navailable in the [ctype.h][] header.\n\n  - string repeat String Count\n\nRepeat a String 'Count' many times. 'Count' must be positive, inclusive of\nzero.\n\n  - string first Needle Haystack StartIndex?\n\nFind a Needle in a Haystack, optionally starting from 'StartIndex'. The index\ninto the string where the first character of found of Needle in Haystack is\nreturned if the string has been found, negative one if it has not been found.\n\n  - string ordinal String\n\nConvert the first character in a string to a number that represents that\ncharacter.\n\n  - string char Number\n\nConvert a number to its character representation.\n\n  - string hex2dec HexString\n\nConvert a lower or uppercase hexadecimal number to its decimal representation.\n\n  - string dec2hex Number\n\nConvert a decimal number to its lowercase hexadecimal representation.\n\n  - string hash String\n\nHash a string returning the hash of that string as a number.\n\n  - string range String Index1 Index2\n\nCreate a sub-string from Index1 to Index2 from a String. If Index1 is greater\nthan Index2 an empty string is returned. If Index1 is less than zero, it is set\nto zero, if Index2 is greater than the index of the last character, it is set\nto the index of the last character. Indexing starts at zero and goes up to one\nless than the strings length (or zero of empty string), which is the index of\nthe last character. The characters from Index1 to Index2 inclusive form the\nsub-string.\n\n - string tr d set string *OR* string tr r set1 set2 string\n\nMuch like the Unix utility 'tr', this performs various translations given a set\n(or two sets of characters). 'tr' can delete characters in the set of\ncharacters in 'set' from 'string' if the option provided to it is 'd', or it\ncan perform a translation from one set to another if the 'r' specifier is\ngiven. If the second set is larger than the first for the 'r' command the last\ncharacter applies to the rest of the characters in 'set2'.\n\nBoth 'r' and 'd' options can both have the additional specifier 'c', which\ncompliments the given 'set' or characters.\n\n'r' can also have the 's' specifier, which will squeeze repeated characters in\nthe set\n\nExample:\n\n\tproc lowercase {x} {\n\t\tstring tr r abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ $x\n\t}\n\nWhich creates a function with the same functionality as 'string lowercase $x'.\n\n - string replace old-string first last new-string\n\nThis subcommands replaces a substring starting at 'first' and ending at 'last'.\nThe 'new-string' replaces the removed section of the 'old-string'.\n\n* eq string string\n\nReturns '0' is two strings are not equal and '1' if they are. Unlike '==' this\nacts on the entire string.\n\n* ne string string\n\nReturns '1' is two strings are not equal and '0' if they are. Unlike '!=' this\nacts on the entire string.\n\n* incr variable number?\n\nIncrement a variable by 1, or by an optional value. 'incr' returns the\nincremented variable. 'incr' being implemented in C is usually a lot more\nefficient then defining 'incr' in TCL, like so:\n\n\tproc incr {x} { upvar 1 $x i; set i [+ $i 1] }\n\nAnd it is used often in looping constructs.\n\n* subst opts... string\n\nOptionally perform substitutions on a string, controllable via three flags.\nWhen you enter a string in a [TCL][] program substitutions are automatically\nperformed on that string, 'subst' can be used to perform a subset of those\nsubstitutions (command execution, variable substitutions, or escape character\nhandling) a string.\n\n - -nobackslashes\n\nDisable escape characters.\n\n - -novariables\n\nDo not process variables.\n\n - -nocommands\n\nDo not process command substitutions.\n\n### Extension Commands\n\nThese commands are present in the [main.c][] file and have been added to the\ninterpreter by extending it. They deal with I/O.\n\n* gets\n\nRead in a new-line delimited string, returning the string on success, on End Of\nFile it returns 'EOF' with a return code of 'break'.\n\n* puts *OR* puts string *OR* puts -nonewline string\n\nWrite a line to *stdout*, the option '-nonewline' may be specified, which\nmeans no newline with be appended to the string.\n\nIf no string is given, then a single new line is printed out.\n\n* getenv string\n\nRetrieve an environment variable by the name 'string', returning it as a\nstring.\n\n* exit *OR* exit number\n\nExit the program with a status of 0, or with the provided status number.\n\n* clock seconds *OR* clock format time time-spec? *OR* clock clicks\n\nA simplified version of the TCL command 'clock' the subcommands it supports\nare:\n\n - clicks\n\nReturn the CPU clock.\n\n - seconds\n\nReturn the seconds since the Unix Epoch.\n\n - format time time-spec?\n\nThe format command a time in seconds since the Unix Epoch against an optional\ntime-specification (the default time specification is \"%a %b %d %H:%M:%S %Z %Y\").\nThe formatting is done entirely by the function [strftime][].\n\nThere are internal limits on this string length (512 bytes excluding the NUL\nterminator).\n\n* heap option\n\nThis command is useful for inspecting the size of the heap, it can report the\nnumber of bytes allocator, the number of frees, the number of allocations, and\nother statistics.\n\nThe options are:\n\n- frees\n\nThis is the number of frees that have taken place, excluding freeing 'NULL'.\n\n- allocations\n\nThis is the number of allocations that have taken place, including any\nreallocations.\n\n- total\n\nThis is the total number of bytes that have been allocated.\n\n- reallocations\n\nThis is the number of reallocations that have been performed on an already\nallocated pointer.\n\nThe \"heap\" command has another subcommand \"fail-after\", which is used for\ninternal testing purposes, it takes a number and after that many calls to\nthe allocation function it causes it to return a failure, which is fatal to\nthe interpreter (but should not cause a crash). You should not need to use\nthis subcommand. Calling this subcommand again resets the count until failure,\nsetting the count to zero disables deliberate failure. This feature could be\nused as a crude watchdog, but it would be inadvisable to do so.\n\n* source file-name\n\nRead and then evaluate a file off of disk. This may fail because the file could\nnot be read or something when wrong during the evaluation.\n\n## Compile Time Options\n\nI am not a big fan of using the [C Preprocessor][] to define a myriad of\ncompile time options. It leads to messy and unreadable code.\n\nThat said the following compile time options are available:\n\n* NDEBUG\n\nIf defined this will disable assertions. It will also disable unit tests\nfunctions from being compiled. Assertions are used heavily to check that the\nlibrary is being used correctly and to check the libraries internals, this\napplies both to the block allocation routines and pickle itself.\n\nThere are other compile time options within [pickle.c][] that control;\nmaximum string length and whether to use one, whether to provide the default\nallocator or not, whether certain functions are to be made available to the\ninterpreter or not (such as the command 'string', the mathematical operators\nand the list functions), and whether strict numeric conversion is used.\nThese options are semi-internal, they are subject to change and removal, you\nshould use the source to determine what they are and be aware that they may\nchange across releases.\n\nInevitably when an interpreter is made for a new language,\n[readline][] (or [linenoise][]) integration is a build option, usually because\nthe author is tired of pressing the up arrow key and seeing '^\\[\\[A'. Naturally\nthis increases the complexity of the build system, adds more options, and adds\nmore code. Instead you can use [rlwrap][], or an alternative, as a wrapper\naround your program.\n\n## Custom Allocator / C Library usage\n\nTo aid in porting the system to embedded platforms, [pickle.c][] contains no\nInput and Output functions (they are added in by registering commands in\n[main.c][]). [pickle.c][] does include [stdio.h][], but only to access\n[vsnprintf][]. The big problem with porting a string heavy language to an\nembedded platform, unlike a language like [FORTH][], is memory allocation. It\nis unavoidable that some kind of dynamic memory allocation is required. For\nthis purpose it is possible to provide your own allocator to the pickle\nlibrary. If an allocator is not provided, malloc will be used, you can remove\nthis from the initialization function in to stop your build system pulling in\nyour platforms allocator.\n\nThe block allocation library provided in [block.c][] can be optionally\nused, but unlike [malloc][] will require tweaking to suite your purposes. The\nmaximum block size available to the allocator will also determine the maximum\nstring size that can be used by pickle.\n\nApart from [vsnprintf][], the other functions pulled in from the C\nlibrary are quite easy to implement. They include (but are not necessarily\nlimited to); strlen, memcpy, memchr, memset and abort.\n\n## C API\n\nThe language can be extended by defining new commands in [C][] and registering\nthose commands with the *pickle\\_command\\_register* function. The internal\nstructures used are mostly opaque and can be interacted with from within the\nlanguage. As stated a custom allocator can be used and a block allocator is\nprovided, it is possible to do quite a bit with this scripting language whilst\nonly allocating about 32KiB of memory total on a 64-bit machine (for example\nall of the unit tests and example programs run within that amount).\n\nThe C API is small and regular. All of the functions exported return the same\nerror codes and implementing an interpreter loop is trivial.\n\nThe language can be extended with new functions written in C, each function\naccepts an integer length, and an array of pointers to ASCIIZ strings - much\nlike the 'main' function in C.\n\nUser defined commands can be registered with the 'pickle\\_command\\_register'\nfunction. With in the user defined callbacks the 'pickle\\_result\\_set' family of\nfunctions can be used. The callbacks passed to 'pickle\\_command\\_set' look\nlike this:\n\n\ttypedef int (*pickle_func_t)(pickle_t *i, int argc, char **argv, void *privdata);\n\nThe callbacks accept a pointer to an instance of the pickle interpreter, and\na list of strings (in 'argc' and 'argv'). Arbitrary data may be passed to the\ncustom callback when the command is registered.\n\nThe function returns one of the following status codes:\n\n\tPICKLE_ERROR    = -1 (Throw an error until caught)\n\tPICKLE_OK       =  0 (Signal success, continue execution)\n\tPICKLE_RETURN   =  1 (Return out of a function)\n\tPICKLE_BREAK    =  2 (Break out of a while loop)\n\tPICKLE_CONTINUE =  3 (Immediately proceed to next iteration of while loop)\n\nThese error codes can affect the flow control within the interpreter. The\nactual return string of the callback is set with 'pickle\\_result\\_set' functions.\n\nVariables can be set either within or outside of the user defined callbacks\nwith the 'pickle\\_var\\_set' family of functions.\n\nThe pickle library does not come with many built in functions, and comes with\nno Input/Output functions (even those available in the C standard library) to\nmake porting to non-hosted environments easier. The example test driver program\ndoes add functions available in the standard library.\n\nThe following is the source code for a simple interpreter loop that reads a\nline and then evaluates it:\n\n\t#include \"pickle.h\"\n\t#include \u003cstdio.h\u003e\n\t#include \u003cstdlib.h\u003e\n\n\tstatic void *allocator(void *arena, void *ptr, size_t oldsz, size_t newsz) {\n\t\tif (newsz ==     0) { free(ptr); return NULL; }\n\t\tif (newsz  \u003e oldsz) { return realloc(ptr, newsz); }\n\t\treturn ptr;\n\t}\n\n\tstatic int prompt(FILE *f, int err, const char *value) {\n\t\tif (fprintf(f, \"[%d]: %s\\n\u003e \", err, value) \u003c 0)\n\t\t\treturn -1;\n\t\treturn fflush(f) \u003c 0 ? -1 : 0;\n\t}\n\n\tint main(void) {\n\t\tpickle_t *p = NULL;\n\t\tif (pickle_new(\u0026p, allocator, NULL) \u003c 0)\n\t\t\treturn 1;\n\t\tif (prompt(stdout, 0, \"\") \u003c 0)\n\t\t\treturn 1;\n\t\tfor (char buf[512] = { 0 }; fgets(buf, sizeof buf, stdin);) {\n\t\t\tconst char *r = NULL;\n\t\t\tconst int er = pickle_eval(p, buf);\n\t\t\tif (pickle_result_get(p, \u0026r) != PICKLE_OK)\n\t\t\t\treturn 1;\n\t\t\tif (prompt(stdout, 0, r) \u003c 0)\n\t\t\t\treturn 1;\n\t\t}\n\t\treturn pickle_delete(p);\n\t}\n\nIt should be obvious that the interface presented is not efficient for many\nuses, treating everything as a string has a cost. It is however simple and\nsufficient for many tasks.\n\nWhile API presented in 'pickle.h' is small there are a few areas of\ncomplication. They are: The memory allocation API, registering a command, the\ngetopt function and the unit tests. The most involved is the memory allocation\nAPI and there is not too much to it, you do not even need to use it and can\npass a NULL to 'pickle\\_new' if for the allocator argument if you want to use\nthe built in malloc/realloc/free based allocator (provided the library was\nbuilt with support for it).\n\nIt may not be obvious from the API how to go about designing functions to\nintegrate with the interpreter. The C API is deliberately kept as simple as\npossible, more could be exported but there is a trade-off in doing so; it\nplaces more of a burden on backwards compatibility, limits the development\nof the library internals and makes the library more difficult to use. It is\nalways possible to hack your own personal copy of the library to suite your\npurpose, the library is small enough that this should be possible.\n\nThe Pickle interpreter has no way of registering different types with it, the\nstring is king. As such, it is not immediately clear what the best way of\nadding functionality that requires manipulating non-string data (such as\nfile handles or pointers to binary blobs) is. There are several ways of doing\nthis:\n\n1. Convert the pointer to a string and add functions which deal with this string.\n2. Put data into the private data field\n3. Create a function which registers another function that contains private data.\n\nOption '1' may seem natural, but it is much more error prone. It is possible\nto pass the wrong string around and cause the program to crash. Option '2' is\nlimiting, the C portion of the program is entirely in control of what resources\nget added, and only one handle to a resource can be controlled. Option '2' is\na good option for certain cases.\n\nOption '3' is the most general and allows an arbitrary resource to be managed\nby the interpreter. The idea is to create a function that acquires the resource\nto be managed and registers a new function in the pickle global function\nnamespace with the resource in the private data field of the newly registered\nfunction. The newly created function, a limited form of a closure, can then\nperform operations on the handle. It can also cleanup the resource by release\nthe object in its private data field, and then deleting itself with the\n 'pickle\\_command\\_rename' function. An example of this is the 'fopen' command,\nit returns a closure which contains a file handle.\n\nAn example of using the 'fopen' command and the returned function from within\nthe pickle interpeter is:\n\n\tset fh [fopen file.txt rb]\n\tset line [$fh -gets]\n\t$fh -close\n\nAnd an example of how this might be implemented in C is:\n\n\tint pickleCommandFile(pickle_t *i, int argc, char **argv, void *pd) {\n\t\tFILE *fh = (FILE*)pd;\n\t\tif (!strcmp(argv[1], \"-close\")) { /* delete self */\n\t\t\tfclose(fh);                                /* free handle */\n\t\t\treturn pickle_command_rename(argv[0], \"\"); /* delete self */\n\t\t}\n\t\tif (!strcmp(argv[1], \"-gets\")) {\n\t\t\tchar buf[512];\n\t\t\tfgets(buf, sizeof buf, fh);\n\t\t\treturn pickle_result_set(i, \"%s\", buf);\n\t\t}\n\t\treturn pickle_result_set(i, PICKLE_ERROR, \"invalid option\");\n\t}\n\n\tint pickleCommandFopen(pickle_t *i, int argc, char **argv, void *pd) {\n\t\tchar name[64];\n\t\tFILE *fh = fopen(argv[1], argv[2]);\n\t\tsprintf(name, \"%p\", fh); /* unique name */\n\t\tpickle_command_register(i, name, pickleCommandFile, fh);\n\t\treturn pickle_set_result(i, \"%s\", name);\n\t}\n\nThe code illustrates the point, but lacks the assertions, error checking,\nand functionality of the real 'fopen' command. The 'pickleCommandFopen' should\nbe registered with 'pickle\\_command\\_rename', the 'pickleCommandFile' is not\nas 'pickleCommandFopen' does the registering when needed.\n\nIt should be possible to implement the commands 'update', 'after' and 'vwait',\nextending the interpreter with task management like behavior without any changes\nto the API. It is possible to implement most commands, although it might\nbe awkward to do so. Cleanup is still a problem.\n\n## Style Guide\n\nStyle/coding guide and notes, for the file [pickle.c][]:\n\n- 'pickle\\_' and snake\\_case is used for exported functions/variables/types\n- 'picol'  and camelCase  is used for internal functions/variables/types,\nwith a few exceptions, such as 'advance' and 'compare', which are internal\nfunctions whose names are deliberately kept short.\n- Use asserts wherever you can for as many preconditions, postconditions\nand invariants that you can think of.\n- Make sure you make your functions static unless they are meant to\nbe exported. You can use 'objdump -t | awk '$2 ~ /[Gg]/' to find\nall global functions. 'objdump -t | grep '\\\\\\*UND\\\\\\*' can be used to\ncheck that you are not pulling in functions from the C library you\ndo not intend as well.\n- The core project is written strictly in C99 and uses only things\nthat can be found in the standard library, and only things that are\neasy to implement on a microcontroller.\n- Error messages should begin with the string 'Invalid', even it is\ndoes not make the best grammatical sense, this is so error messages can\nbe grepped for easily.\n\nThe callbacks all have their 'argv' argument defined as 'char\\*',\nas they do not modify their arguments. However adding this in just adds\na lot of noise to the function definitions. Also see\n\u003chttp://c-faq.com/ansi/constmismatch.html\u003e.\n\n## Notes\n\n* Other implementations\n\nThere are other implementations of [TCL][] and other extensions of the original\n[picol][] interpreter, here are a few:\n\n- Picol Extension \u003chttps://wiki.tcl-lang.org/page/Picol\u003e\n- TCL reimplementation \u003chttp://jim.tcl.tk/index.html/doc/www/www/index.html\u003e\n- An entire list of implementations \u003chttps://blog.tcl.tk/17975\u003e\n- Another Picol Extension \u003chttps://chiselapp.com/user/dbohdan/repository/picol/index\u003e\n- Partcl \u003chttps://github.com/zserge/partcl\u003e, a complete reimplementation, also\nwith a blog post \u003chttps://zserge.com/posts/tcl-interpreter/\u003e describing it.\n\nAnd I am sure if you were to search \u003chttps://github.com\u003e you would find more.\n\n* Internal memory usage\n\nOne of the goals of the interpreter is low(ish) memory usage, there are a few\ndesign decisions that go against this, along with the language itself, however\nan effort has been made to make sure memory usage is kept low.\n\nSome of the (internal) decisions made:\n\n- The use of 'compact\\_string\\_t' where possible. This can be used to store a\n  string within a union of a small character array or a pointer, this requires\n  a bit be available elsewhere to store which is used.\n- Compact, small, structures for structures that are used a lot; call frames (2\n  pointers), variables (3 pointers and a bit-field), and commands (4 pointers).\n- Linked-lists are used, which increase overall memory usage but mean large\n  chunks of memory do not have to be allocated and reallocate for things like\n  tables of functions and variables.\n- The parser operates on a full program string and tokens to the string a\n  indices into the string, which means a large [AST][] does not\n  have to be assembled.\n- Memory is allocated on the stack where possible, with the function\n  'picolStackOrHeapAlloc' helping with this, it moves allocations to the\n  heap if they become too large for the stack. This could conceivably be used\n  for more allocations we do, for example when creating argument lists, but is\n  currently just used for some unbounded string operations. (Of note, it might\n  be worth creating memory pools for small arguments lists as they are\n  generated fairly often, or even a pool of medium size buffers we could lock).\n  This could also speed things up as we spend a lot of time allocating objects,\n  the working set may be small but the number of temporary objects created is\n  not, 'picolArgsGrow' could be targeted for this.\n- The empty string could be treated specially by the interpreter and all empty\n  strings never freed nor allocated.\n- Also of note is that the interpreter is designed to gracefully handle out of\n  memory conditions, it may not live up to this fact, but it is possible to\n  test this by returning NULL in the allocator provided randomly.\n- There dictionary is currently initialized at startup from a list of functions\n  and their names, this takes up RAM and not ROM, a two level hashing function\n  could be used instead. Calling 'rename' on these built-in functions could\n  be handled with a single bit per entry separate from the built-in function\n  hash table, or by disallowing 'rename' on built in functions. A not trivial\n  (for an embedded system) amount of memory is used by this table. The\n  trade-off is more lines of code, more complexity, and a bigger executable.\n\nSome of the design decisions made that prevent and hamper memory usage and\nthings that could be done:\n\n- Defined procedures are strings and Lack of Byte Code Compilation\n\nIt would be possible to include some simple complication on the procedures that\nare stored, turning certain keywords into bytes codes that fall outside of the\n[UTF-8][] and [ASCII][] character ranges, as well as removing runs of white\nspace and comments entirely. This would be possible to implement without\nchanging the interface and would both speed things up and reduce memory usage,\nhowever it would increase the complexity of the implementation (perhaps by\nabout 500 LoC if that can be thought of as a proxy for complexity).\n\n- Within [pickle.c][] there is a table of functions that are register on\n  startup, registering means taking each entry in this array and entering it\n  into a hash-table that contains all of the other defined procedures and\n  built-in functions, there is obviously some redundancy here. It might be\n  worth treating the built-in core functions specially with a binary search\n  tree just for them instead of adding them into the hash table (this would\n  complicate the 'rename' command as well).\n\n* [vsnprintf][]\n\nIf you need an implementation of [vsnprintf][] the [Musl C library][] has one.\nThis is the most complicate C function in use from the standard library and the\none most likely not to be available in an embedded platform (although the base\nsoftware packages are getting better nowadays). It is not difficult to make\nyour own version of [vsnprintf][] function usable by this library as you do not\nneed to support all of the functionality library function, for example,\nfloating point numbers are not used within this library.\n\n* Too big\n\nThe list functions are also far to complex, big, and error prone, they should\nbe rewritten.\n\nIt might be nice to go back to the original source, with what I know now, and\ncreate a very small version of this library with a goal of compiling to under\n30KiB. The 'micro' makefile target does this somewhat, or just starting from\nscratch and making my own version. A smaller API could be made as well, there\nreally only needs to be; pickle\\_new, pickle\\_delete, pickle\\_eval,\npickle\\_command\\_register, and pickle\\_result\\_set.\n\n* A module system and some modules\n\nThis interpreter lacks a module system, there are a few small and simple\nmodules that could be integrated with the library quite easily, see;\nConstant Data Base Library \u003chttps://github.com/howerj/cdb\u003e, A HTTP 1.1\nclient \u003chttps://github.com/howerj/httpc\u003e, Tiny compression routines\n\u003chttps://github.com/howerj/shrink\u003e, and a fixed point arithmetic\nlibrary \u003chttps://github.com/howerj/q\u003e, and UTF-8 string handling\n\u003chttps://github.com/howerj/utf8\u003e These would have to be external modules\nthat could be integrated with this library.\n\nThe current project that attempts to remedy this is available at:\n\n\u003chttps://github.com/howerj/mod-pickle\u003e\n\nA proper module system would also allow Shared Objects / Dynamically Linked\nLibraries to be loaded at run time into the interpreter. This complicates the\nlibrary, but a Lisp Interpreter where I have done this, see\n\u003chttps://github.com/howerj/liblisp\u003e.\n\n* Things that are missing that will not be added.\n\n - The [coroutine][] words are missing, which will require interpreter\n support to implement efficiently. If you need coroutines (which are very\n useful) then access to the internals is needed.\n - Event handling in [vwait][] and [update][] and the like could be added\n later on.\n - The control structures 'foreach' and 'switch' - whilst very useful - will\n most likely not be added. There is no reason that they cannot be added as\n extensions however.\n - Lack of Floating point support, which should not really be expected either\n given the primary usage for this interpreter, as a command language in\n embedded devices.\n\n## Interpreter Limitations\n\nKnown limitations of the interpreter include:\n\n* Recursion Depth - 128, set via a compile time option.\n* Maximum size of file - 2GiB\n* 'clock' command has a limited string available for formatting (512 bytes).\n\n[ASCII]: https://en.wikipedia.org/wiki/ASCII\n[AST]: https://en.wikipedia.org/wiki/Abstract_syntax_tree\n[BSD License]: https://en.wikipedia.org/wiki/BSD_licenses\n[C Preprocessor]: https://en.wikipedia.org/wiki/C_preprocessor\n[C]: https://en.wikipedia.org/wiki/C_%28programming_language%29\n[FORTH]: https://en.wikipedia.org/wiki/Forth_(programming_language)\n[Lua]: https://www.lua.org/\n[MIT License]: https://en.wikipedia.org/wiki/MIT_License\n[Make]: https://en.wikipedia.org/wiki/Make_(software)\n[Musl C library]: https://git.musl-libc.org/cgit/musl/tree/src/stdio\n[TCL trace command]: https://www.tcl.tk/man/tcl8.5/TclCmd/trace.htm\n[TCL]: https://en.wikipedia.org/wiki/Tcl\n[UTF-8]: https://en.wikipedia.org/wiki/UTF-8\n[alnum]: http://www.cplusplus.com/reference/cctype/isalnum/\n[alpha]: http://www.cplusplus.com/reference/cctype/isalpha/\n[cdb]: https://github.com/howerj/cdb\n[control]: http://www.cplusplus.com/reference/cctype/iscntrl/\n[coroutine]: \u003chttps://www.tcl.tk/man/tcl8.7/TclCmd/coroutine.htm\u003e\n[ctype.h]: http://www.cplusplus.com/reference/cctype/\n[digit]: http://www.cplusplus.com/reference/cctype/isdigit/\n[graph]: http://www.cplusplus.com/reference/cctype/isgraph/\n[homoiconic]: https://en.wikipedia.org/wiki/Homoiconicity\n[insertion sort]: https://en.wikipedia.org/wiki/Insertion_sort\n[linenoise]: https://github.com/antirez/linenoise\n[lisp]: https://en.wikipedia.org/wiki/Lisp_(programming_language)\n[loc]: https://en.wikipedia.org/wiki/Source_lines_of_code\n[lower]: http://www.cplusplus.com/reference/cctype/islower/\n[main.c]: main.c\n[malloc]: https://en.wikipedia.org/wiki/C_dynamic_memory_allocation\n[pickle.c]: pickle.c\n[pickle.h]: pickle.h\n[picol]: http://oldblog.antirez.com/post/picol.html\n[print]: http://www.cplusplus.com/reference/cctype/isprint/\n[punct]: http://www.cplusplus.com/reference/cctype/ispunct/\n[python]: https://www.python.org/\n[readline]: https://tiswww.case.edu/php/chet/readline/rltop.html\n[readme.md]: readme.md\n[regex]: http://c-faq.com/lib/regex.html\n[rlwrap]: https://linux.die.net/man/1/rlwrap\n[shell]: shell\n[snprintf]: http://www.cplusplus.com/reference/cstdio/snprintf/\n[space]: http://www.cplusplus.com/reference/cctype/isspace/\n[stdin]: http://www.cplusplus.com/reference/cstdio/stdin/\n[stdio.h]: http://www.cplusplus.com/reference/cstdio/\n[stdout]: http://www.cplusplus.com/reference/cstdio/stdout/\n[strftime]: http://www.cplusplus.com/reference/ctime/strftime/\n[string.h]: http://www.cplusplus.com/reference/cstring/\n[unit tests]: https://en.wikipedia.org/wiki/Unit_testing\n[unit.tcl]: unit.tcl\n[update]: https://www.tcl.tk/man/tcl8.4/TclCmd/update.htm\n[upper]: http://www.cplusplus.com/reference/cctype/isupper/\n[vsnprintf]: http://www.cplusplus.com/reference/cstdio/vsnprintf/\n[vwait]: https://www.tcl.tk/man/tcl8.4/TclCmd/vwait.htm\n[xdigit]: http://www.cplusplus.com/reference/cctype/isxdigit/\n[isatty]: http://man7.org/linux/man-pages/man3/isatty.3.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhowerj%2Fpickle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhowerj%2Fpickle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhowerj%2Fpickle/lists"}