slope/static.go

220 lines
54 KiB
Go

package main
const licenseText string = `
Copyright © 2021 sloum <sloum@rawtext.club>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
`
const historyFilename string = "slope-repl.history"
const magicCustomEnv symbol = "_!sce!_"
// This holds help text for built-in items
var usageStrings = map[string]string{
"!": "(! [exception-text: string]) => exception\n\nReturn an error with the given text. Depending on the error mode this may result in a runtime panic or simply in the exception value being passed to the calling lambda",
"+": "(+ [number...]) => number\n\nAdds the given numbers together, left to right, and returns the sum. If only one number is provided it is simply returned",
"-": "(- [number...]) => number\n\nSubtracts the given numbers from each other, left to right, and returns the result. If only one number is provided its sign is inversed and it is returned",
"*": "(* [number...]) => number\n\nMultiplys the given numbers and returns the result, If only one number is provided then 1 is divided by it and the result is returned",
"/": "(/ [number...]) => number\n\nDivides the given numbers, left to right, and returns the result. If only one number is provided it is simply returned",
"%": "(% [number] [number]) => number\n\nReturns the modulus of two numbers",
"&": "(& [number] [number]) => number\n\nReturns the bitwise AND of two numbers",
"|": "(| [number] [number]) => number\n\nReturns the bitwise OR of two numbers",
"^": "(^ [number] [[number]]) => number\n\nReturns the bitwise XOR of two numbers, or the bitwise NOT of a single number",
"<<": "(<< [number] [number]) => number\n\nBit shifts the first number left by the second number",
">>": "(>> [number] [number]) => number\n\nBit shifts the first number right by the second number",
"sqrt": "(sqrt [number]) => number\n\nReturns the square root of the given number",
"atan": "(atan [number] [[number]]) => number\n\nIf one number is given returns the atan of that number, if two numbers are given returns the atan2 of the two given numbers",
"tan": "(tan [number] [[number]]) => number\n\nReturns the tan of the given number",
"log": "(log [number]) => number\n\nReturns the natural log of the given number",
"cos": "(cos [number]) => number\n\nReturns the cos of the given number",
"sin": "(sin [number]) => number\n\nReturns the sin of the given number",
">": "(> [number] [number]) => bool\n\nChecks of the first number is greater than the second number",
">=": "(>= [number] [number]) => bool\n\nChecks if the first number is greater than or equal to the second number",
"<": "(< [number] [number]) => bool\n\nChecks if the first number is less than the second number",
"<=": "(<= [number] [number]) => bool\n\nChecks if the first number is less than or equal to the second number",
"~bool": "(~bool [value]) => bool\n\n`~bool` will convert a given value to a boolean value using a looser set of rules than `if` would normally use: 0, 0.0, the empty list, an empty string, and a closed io-handle will be considered falsy (in addition to #f)",
"abs": "(abs [number]) => number\n\nReturns the absolute value of a given number",
"and": "(and [expression...]) => bool\n\nChecks the truthiness of all expressions passed to it. If any expression is falsy then false is returned and no further expressions are checked; otherwise returns true",
"append": "(append [list|value] [[value...]]) => list|string\n\nAdds the given value(s) to the end of the given list|string. If a list alone is given it is simply returned. If a single value is given it will be coerced to string and returned. If multiple values are given they will be concatenated and a string will be returned; any non-string values will be coerced to string before concatenation",
"apply": "(apply [procedure] [arguments: list]) => value\n\nSpreads the list values as arguments to the given procedure. This results in behavior where the following two examples are equivalent:\n\n\t(+ 1 2 3)\n\t(apply + [1 2 3])",
"assoc": "(assoc [association-list] [key: value] [[value]] [[relative?: bool]]) => value|list\n\nIf two arguments are given then `assoc` retrieves the value of the key. If a third argument is given `assoc` will set the value for the given key to the given value, returning the updated list. If `relative?` is `#t` and an existing field is gettings set, it will be set in place via relative reference. This does not reliably function for adding a new key to the association and you should always store the new association with `set!` when adding a new key. However, execution may be faster with `relative?` set due to optimizations that can potentially exist in the underlying implementation",
"assoc?": "(assoc? [value]) => bool\n\nDetermines whether or not the given value is an association list",
"assoc-has-key?": "(assoc-has-key? [assoc: list] [key: value]) => bool\n\nDetermines if the given `key` is present in the given `assoc`. `assoc` must be an associative list or an exception will be raised",
"atom?": "(atom? [value]) => bool\n\nDetermines whether a given value is atomic (in practice this mostly works out to: not a list)",
"begin": "(begin [expression...]) => value\n\nEvaluates a series of expressions and returns the value of the last expression that is evaluated",
"begin0": "(begin [expression...]) => value\n\nEvaluates a series of expressions and returns the value of the first expression that is evaluated",
"bool?": "(bool? [value]) => bool\n\nChecks whether the given value is a boolean: either the value #t or the value #f",
"byte?": "(byte? [value]) => bool\n\nChecks whether the given value is a byte, a number between from 0 to 255",
"bytes->string": "(bytes->string [number|list]) => string\n\nIf a single number is given, a string of the given value will be returned. If a list is given, that list should only contain numbers. The list of numbers will be treated as a list of bytes and will be converted to a string",
"car": "(car [list]) => value|list\n\nReturns the first value in the given list, which may be an atomic value or another list",
"case": "(case [target-value] [([match-value: expression] [#t-branch: expression])...]) => value\n\nWill check if each the given target-value matches each match-value in turn and evaluate to the #t-branch of the first such match that is equal. If no match is found an empty list will be returned. The match-value `else` is a sepcial case, the #t-branch of this match list will be returned if no other match is found to be truthy",
"cdr": "(cdr [list]) => list\n\nReturns the given list without its first value. Can be thought of as the remainder of 'car'",
"ceil": "(ceil [number]) => number\n\nReturn the ceiling value of the given number.\n\n\t(ceil 1.23) => 2\n\t(ceil 1) => 1",
"chdir": "(chdir [filepath: string]) => ()\n\nChanges the current working directory to the given filepath. Will return an exception if it was unable to change to the given filepath",
"chmod": "(chmod [filepath: string] [file-mode: number]) => ()\n\nChanges the filepermissions for the given file to the given file-mode number. Will return an exception if it was not possible to apply the given file-mode to the given filepath",
"close": "(close [io-handle]) => ()\n\nCloses the given io-handle. Can be called on an already closed io-handle without error",
"coeval": "(coeval [proc-and-args: list]...) => ()\n\nCoeval can be given any number of lists. Each list should contain a procedure as its first item and a list of arguments for that procedure as its second item (the list is optional if there are no arguments tot he procedure). Each procedure and argument list will be called concurrently as part of a wait group udner the hood. As such, coeval will return once all of the procedures have returned, but they will evaluate concurrently",
"cond": "(cond [([condition: expression] [#t-branch: expression])...]) => value\n\nWill evaluate each condition in turn and evaluate to the #t-branch of the first condition that is truthy. If no condition is truthy then an empty list will be returned. The value `else` is a sepcial case allowed as the final condition expression, the #t-branch of this condition list will be returned if no other condition is found to be truthy",
"cons": "(cons [value] [list]) => list\n\nPrepends the given value to the given list. See also: append",
"date": "(date [[format: string]]) => string\n\nReturns the current date/time, optionally formatted by a format string argument",
"date-format": "(date-format [start-format: string] [date: string] [end-format: string]) => string\n\nDate format/pattern substitutions:\n\t%a - 12 hour segment, lowercase: pm\n\t%A - 12 hour segment, uppercase: PM\n\t%d - Day without leading zero: 4\n\t%D - Day with leading zero: 04\n\t%e - Day without leading zero, but with space padding: 4\n\t%f - Month name, short: Jan\n\t%F - Month name, long: January\n\t%g or %G - Hour, 24 hour format: 15\n\t%h - Hour, 12 hour format without leading zero: 2\n\t%H - Hour, 12 hour format with leading zero: 02\n\t%i - Minutes without leading zero: 4\n\t%I - Minutes with leading zero: 04\n\t%m - Month number, without leading zero: 6\n\t%M - Month number, with leading zero: 06\n\t%o - Timezone offset, without minutes: -07\n\t%O - Timezone offset, with minutes: -0700\n\t%s - Seconds, without leading zero: 5\n\t%S - Seconds, with leading zero: 05\n\t%w - Short weekeday: Wed\n\t%W - Long weekday: Wednesday\n\t%y - Two digit year: 21\n\t%Y - Four digit year: 2021\n\t%Z - Time zone, as three chars: UTC, PST, etc.\n\t%% - Will yield a literal percent sign: %\n\tanything else - A percent followed by an unrecognized char will yield a ?, as will a hanging % as the last char in the string\n\nSo the string: \"%F %e, %Y %h:%I%a\" would be equivalend to something like: \"August 8, 2021 4:03pm\"\n\nWhen a string is converted to a date and no time zone information is present, slope will assume the user's local time.",
"date->timestamp": "(date->timestamp [date: string] [format: string]) => number (timestamp)\n\nDate format/pattern substitutions:\n\t%a - 12 hour segment, lowercase: pm\n\t%A - 12 hour segment, uppercase: PM\n\t%d - Day without leading zero: 4\n\t%D - Day with leading zero: 04\n\t%e - Day without leading zero, but with space padding: 4\n\t%f - Month name, short: Jan\n\t%F - Month name, long: January\n\t%g or %G - Hour, 24 hour format: 15\n\t%h - Hour, 12 hour format without leading zero: 2\n\t%H - Hour, 12 hour format with leading zero: 02\n\t%i - Minutes without leading zero: 4\n\t%I - Minutes with leading zero: 04\n\t%m - Month number, without leading zero: 6\n\t%M - Month number, with leading zero: 06\n\t%o - Timezone offset, without minutes: -07\n\t%O - Timezone offset, with minutes: -0700\n\t%s - Seconds, without leading zero: 5\n\t%S - Seconds, with leading zero: 05\n\t%w - Short weekeday: Wed\n\t%W - Long weekday: Wednesday\n\t%y - Two digit year: 21\n\t%Y - Four digit year: 2021\n\t%Z - Time zone, as three chars: UTC, PST, etc.\n\t%% - Will yield a literal percent sign: %\n\tanything else - A percent followed by an unrecognized char will yield a ?, as will a hanging % as the last char in the string\n\nSo the string: \"%F %e, %Y %h:%I%a\" would be equivalend to something like: \"August 8, 2021 4:03pm\"\n\nWhen a string is converted to a date and no time zone information is present, slope will assume the user's local time.",
"date-default-format": "(date-default-format) => string\n\nReturns a format string for the default format produced by 'date'",
"define": "(define [var-name] [value|expression|procedure]) => value | expression | procedure\n\nReturns the value that var-name was set to, in addition to creating the var symbol and setting it to the given value. Define is lexically scoped (using define within a lambda will create a variable that is only visible to that lambda and its child scopes, but not to any parent scopes). `var-name` may be an expression that evaluates to a symbol, or a raw unquoted symbol",
"devnull": "devnull => io-handle (file: write-only)\n\nA constant that represents the devnull file",
"display": "(display [value...]) => ()\n\nPrint the given values to stdout. Similar to: (write \"...\"), but can take multiple arguments and can only write to stdout",
"display-lines": "(display-lines [value...]) => ()\n\nPrints the given values to stdout while adding a terminating newline to each value",
"E": "E => number\n\nThe mathematical constant",
"env": "(env [[env-key: string]] [[env-value: string]]) => association-list|string|()\n\nWhen passed with no arguments will return an association list of the current environment. When passed with an env-key, will return an env-value as a string, or an empty string if the key could not be found. If passed an env-key and an env-value env will set the given key to the given value in the current env. Example:\n\n\t(env \"SHELL\") => /bin/bash",
"equal?": "(equal? [value...]) => bool\n\nChecks if the given values are equal. If only one value is given equal? will return #t",
"eval": "(eval [expression|value] [[treat-string-as-code: bool]]) => value\n\ntreat-string-as-code defaults to #f. As such, a string will be evaluated to itself, a string. If true is passed, then the string will be parsed as code and the first, hopefully only, top level expression in the string will be evaluated. Aside from evaluating string it is common to use eval to evaluate quoted code: `(eval '(1 2 3))` would result in the list (1 2 3)",
"exception?": "(exception? [value: any]) => bool\n\nDetermines if the given value is an exception",
"exception-mode-panic": "(exception-mode-panic) => ()\n\nSets the exception mode such that if an exception is encountered a runtime panic will be triggered",
"exception-mode-panic?": "(exception-mode-panic?) => bool\n\nDetermines whether the current exception mode is 'panic'",
"exception-mode-pass": "(exception-mode-pass) => ()\n\nSets the exception mode such that if an exception is encountered it is returned to the calling scope",
"exception-mode-pass?": "(exception-mode-pass?) => bool\n\nDetermines whether the current exception mode is 'pass'",
"exists?": "(exists? [symbol|string...]) => bool\n\nDetermines whether the given symbol(s) (or string(s) representing symbols) exist in the current environment. If multiple values are passed `#t` will only be returned if all of the symbols are present. All symbols given to `exists?` should be quoted:\n\n`(exists? '+) ; => #t\n\nStrings representing symbols may also be passed:\n\n`(exists? \"+\") ; => #t",
"exit": "(exit [[exit-code: number]]) => ()\n\nCauses the program to exit with the optional exit code; defaults to 0",
"exp": "(exp [base: number] [exponent: number]) => number\n\nRaises base to exponent and returns the result",
"file-append-to": "(file-append-to [filepath: string] [string...]) => ()\n\nAppends the given strings to the given file. If the file does not exist, it is created. Appending to the file is an atomic opperation and should not lock the file. The file does not require a call to close. An empty list is returned",
"file-create": "(file-create [filepath: string]) => io-handle\n\nCreates a file at the given filepath. If a file already exists, the file is truncated. Returns an open io-handle representing the file or an exception if the file could not be created",
"file-create-temp": "(file-create-temp [filename-pattern: string]) => io-handle\n\nCreates a temporary file in the system default location for temporary files. The filename is generated by taking the filename-pattern and adding a random string to the end. If the filename-pattern includes a \"*\", the random string replaces the last \"*\". Returns an open io-handle representing the file. To get the file name see:\n\t(usage file-name)",
"file-name": "(file-name [io-handle]) => string\n\nReturns a string representing the name of the file associated with the given io-handle",
"file-open-read": "(file-open-read [filepath: string]) => io-handle\n\nReturns an open io-handle, or exception, for the file represented by the given filepath. The file is in read mode",
"file-open-write": "(file-open-write [filepath: string]) => io-handle\n\nReturns an open io-handle, or exception, for the file represented by the given filepath. The file is in write mode",
"file-stat": "(file-stat [filepath: string]) => assoc-list|#f\n\n#f will be returned if the file does not exist; all other file access errors will raise an exception.\n\nThe returned associative list will have the following keys:\n\tname (string)\n\tsize (bytes: number)\n\tmode (number)\n\tmod-time (number)\n\tis-dir? (bool)\n\tis-symlink? (bool)\n\tpath (string)\n\tuser (string)\n\tgroup (string)\n\tmodestring (string)\n\n'mode' will default to decimal,as all numbers do in slope, but is available as an octal string in the value `modestring`.\n\n'path' will be an absolute path after following a symlink, if present, or the path as provided to 'stat' in the absense of a symlink",
"filter": "(filter [test: procedure] [list]) => list\n\nThe test procedure supplied to filter should take one value and will have its return value treated as a bool. Any value in the list that returns a true when fed to the test procedure will be added to the list that filter returns",
"floor": "(floor [number]) => number\n\nReturns the floor value of the given number. For example:\n\n\t(floor 2.78) => 2\n\t(floor 2) => 2\n\t(floor 2.123455) => 2",
"for": "(for ([([symbol] [value] [update: expression])]...) ([test: expression] [return: value|expression]) [body: expression...]) => value\n\n`for` is a general purpose looping construct. Since slope does not offer tail recursion elimination it is the safest and most performant way to do extended looping for which the stack would overflow if recursion was used. `for` is general purpose and can be used to create other looping structures via macros or lambdas. Example usage that prints the numbers 1-5:\n\n(for ((x 1 (+ x 1))) ((< x 6) x) (display x))\n; Prints: 12345\n; => 6",
"for-each": "(for-each [procedure] [list...]) => empty-list\n\nWorks via the same rules as `map`, but is used specifically for its side effects, rather than return values. See `map` for further details",
"hostname": "(hostname) => string\n\nReturns the host name reported by the kernel as a string",
"if": "(if [condition: expression] [#t-branch: expression] [[#f-branch: expression]]) => value\n\nAny value other than #f is considered true for the purposes of the `condition`, any expression given to `if` as a `condition` will be evaluated and its value will be taken as truthy or falsy by the above truthiness rule",
"io-handle?": "(io-handle? [value]) => bool\n\nChecks if the given value is an io-handle",
"inspect": "(inspect) => bool\n\nRuns a subrepl at the given place in the code. Any changes to the environment will persist once inspect is returned from. `inspect` is intended to be used for debugging and having live access to an environment within a scope. To leave the `inspect` procedure you must call 'uninspect' as the only thing called on a given line at the repl. So: `(uninspect)`. This built-in will return `#t` if it was able to function. The only time it will return `#f` (and have no other effect) is if the given program is not attached to a tty, such as when the program is called as part of a shell pipeline",
"lambda": "(lambda [([[argument: sumbol...]])] [expression...]) => procedure\n\n`lambda` creates its own scope and any define statements within the lambda will be scoped to the lambda (available to it and any child scopes)",
"length": "(length [list|string|exception|io-handle>string-buf]) => number",
"license": "(license) => ()\n\nPrints the slope interpreter's software license (for the interpreter itself, not for slope code run with the interpreter)",
"list": "(list [value...]) => list\n\nReturns a list containing the given values",
"list?": "(list? [value]) => bool\n\nChecks whether the given value is a list",
"list-join": "(list-join [list...]) => list\n\nJoins together two or more lists by spreading the contents of the second and later lists after the content of the first list to create one larger list. This is as opposed to `append` which will append a list to a list if two lists are given",
"list-seed": "(list-seed [length: number] [value]) => list\n\nUsing list seed to create lists filled with a certain element is more efficient than recursion for large lists and will not overflow the stack\n\n`length` should be a positive number larger than zero. If a floating point number is given, it will be floored",
"list-sort": "(list-sort [list] [[sub-list-index: number]]) => list\n\nIf `list` is a list of lists, sorting can be done by the index value of a sublist if desired. `list-sort` always sorts the list in ascending order, but can be reversed with `reverse`",
"list->string": "(list->string [list] [[join-on: string]]) => string\n\nConverts the given list to a string, inserting the optional join-on string between each list item. Any non-string items in the list will be converted to strings automatically before joining",
"load": "(load [filepath: symbol|string...]) => symbol\n\n`load` will open the file represented by filepath and evaluate its contents as slope . It will run any within the file. `load` can accept a relative reference, which will be treated as relative to the current working directory. Any `define` statements within the laoded file will result in the symbol getting added to the global environment",
"load-mod": "(load-mod [module-name: symbol|string...] [[alias: symbol|string]]) => symbol\n\n`load-mod` will search the module path for the given module. If found, the file 'main.slo' within the module folder will be parsed. Arbitrary code will not be executed, only `define`, `load-mod`, and `load-mod-file` statements will be evaluated. The resulting symbols can be accessed via `[alias|module-name]::[symbol]`, for example: `mymodule::my-proc`",
"load-mod-file": "(load-mod-file [filepath: symbol|string]) => symbol\n\n`load-mod-file` is used within modules to do a relative load of files that are a part of the module, but are not 'main.slo'. The same evaluation rules as `load-mod` apply to `load-mod-file`",
"macro": "(macro [([[argument: sumbol...]])] [expression...]) => macro\n\n`macro` creates its own scope and any define statements within the macro will be scoped to the macro (available to it and any child scopes). Arguments to the resulting macro will not be evaluated",
"macro?": "(macro? [value]) => bool\n\nChecks if the given value is a macro",
"map": "(map [procedure] [list...]) => list\n\n`map` will pass the values of each list as arguments to the given prcedure and a new list will be created from the results. If multiple lists are given, they should be the same length and their values will be given as arguments such that the first argument of each list will be provided to the given procedure (as two arguments in the case of two lists, three in the case of three etc), then the second argument, and so on",
"max": "(max [number...]) => number\n\nReturns the largest of the given numbers",
"member?": "(member? [list] [value]) => bool\n\nChecks whether the given calue is in the given list",
"min": "(min [number...]) => number\n\nReturns the smallest of the given numbers",
"mkdir": "(mkdir [path: string] [permissions: number] [[make-all: bool]]) => ()\n\nCreates the directory at the given path. Permissions for the directory must be given, usually as an octal number (but any numerical representation that is valid is fine). If the optional argument make-all is #t any parent directories in the given path that do not exist will also be created with the given file permissions",
"mod-path": "(mod-path) => string\n\nReturns the current module path. The module path is calculated each time modpath is called in order to detect changes. As such, this is more expensive than a runtime constant",
"mv": "(mv [from-path: string] [to-path: string]) => ()\n\nMoves the file represented by from-path to to-path",
"negative?": "(negative? [number]) => bool\n\nChecks if the given number is less than zero",
"not": "(not [value]) => bool\n\n`not` will invert the truthiness of the value it is given",
"net-conn": "(net-conn [host: string] [port: string|number] [[use-tls: bool]] [[timeout-seconds: number]]) => io-handle|#f\n\n`net-conn` is the generic way of making a network request in slope. It returns an open io-handle or an exception. The returned io-handle represents a TCP connection to the given host at the given port. If the optional value use-tls is set to #t then TLS will be used. If the optional value timeout-seconds is set to a positive number the io-handle will be returned if a connection happens within the given number of seconds or #f will be returned. If timeout-seconds is not given or is less than 1 there is no timeout and an invalid connection attempt will hang indefinitely",
"net-get": "(net-get [url: string]) => list\n\nAccepts http(s), gopher, and gemini url schemes. `net-get` will request the resource found at the given url and return a list where the first item is a bool indicating that the request was successful or not. The second item in the list is either the response body or the error message (if the first value is false). Gemini requests will also have a third item in the list indicating the mime type of a successful request or an empty string for a failed request",
"net-listen": "(net-listen [network: string|symbol] [address: string|symbol] [callback: proc|macro] [[init-callback: proc|macro]]) => ()\n\nListens for incoming network requests at the given address, which should generally include a port (ex: `127.0.0.1:3000`). The network should be `tcp` (which will accept IPv4 or IPv6), `tcp4`, `tcp6`, `unix`, or `unixsocket`. Any other network will result in an error. The network is not case sensitive.\n\nThe callback should accept one argument: an io-handle. 'net-listen' automatically does an infinite loop to listen for requests, when one is received the callback will be called in its own concurrent coroutine passing the connection as an argument to the callback (as an open io-handle). It is the callback's responsibility to close the io-handle it is given.\n\nThe optional argument `init-callback` will be called once, before the infinite loop starts; it should accept two arguments: a network type string and an address string. The init callback is useful for printing information to the screen.\n\nIf there is an error accepting a connection the error will be returned, ending the infinite loop. Otherwise, 'net-listen' will go on forever and the caller will have to use Ctrl+C to exit the program",
"net-post": "(net-post [url: string] [data: io-handle|string] [[mime: string]]) => list\n\nAccepts http(s), gopher, and gemini url schemes. `net-post` will request the resource found at the given url and return a list where the first item is a bool indicating that the request was successful or not. The second item in the list is either the response body or the error message (if the first value is false). Gemini requests will also have a third item in the list indicating the mime type of a successful request or an empty string for a failed request.\n\nIn addition to making the request to the url the request body will include the data found in the io-handle or string. In the case of an http(s) this will be passed as post data. In the case of gemini or gopher this is just a shorthand for adding the data to the url as a query paramater. When making an http(s) post request via `net-post` the mime type will default to `text/plain` unless an alternate mime type is provided",
"newline": "(newline) => ()\n\nPrints a newline character to stdout",
"ns-create": "(ns-create [ns-name: string|symbol]) => true\n\nCreates an empty namespace with the given ns-name. The ns-name cannot contain an ascii space character, colon, apostrophe, open paren, closing paren, quote, or hash character as these are reserved for use elswhere in the slope system. Once values are defined within the namespace (via `ns-define`) they can by called the same as you would call code from a loaded module using the `namespace::symbol` syntax.\n\nNote: To reference other values within the namespace you must use the aforementioned calling convention. `ns->string` will automatically adjust the references so that its output will use the local global scope and allow the code to be loaded normally",
"ns-define": "(ns-define [ns-name: string|symbol] [identifier: string|symbol] [value: any]) => any\n\nAdds the given identifier/value to the given namespace and returns the value. There is no `set!` equivalent and `ns-define` can be used to update the value of a given identifier for a given namespace.\n\nNote: To reference other values within the namespace you must use the aforementioned calling convention. `ns->string` will automatically adjust the references so that its output will use the local global scope and allow the code to be loaded normally",
"ns-delete": "(ns-delete [ns-name: string|symbol] [[identifier: string|symbol]]) => true\n\nIf an identifier is given, that identifier and its associated value will be removed from the given namespace. If no identifier is given, the given namespace will be removed from the system",
"ns-list": "(ns-list [[ns-name: string|symbol]]) => list\n\nIf no namespace is given `ns-list` will return a list of the namespaces that have been created by `ns-create` within the current slope session. If a namespace is given, a list of the identifiers within that name space will be returned",
"ns->string": "(ns->string [ns-name: string|symbol]) => string\n\nReturns a formatted and stringified version of the namespace suitable for dumping to a file for later use via `load` or, with a little bit of effort setting up a few other module items, `load-mod`.\n\nNote: To reference other values within the namespace you must use the aforementioned calling convention. `ns->string` will automatically adjust the references so that its output will use the local global scope and allow the code to be loaded normally",
"null?": "(null? [value]) => bool\n\nChecks if the given value is an empty list",
"number?": "(number? [value]) => bool\n\nChecks if the given value is a number",
"number->string": "(number->string [number] [[base: number]]) => string\n\nThe value for `base` defaults to 10 (decimal). If a value other than 10 is provided then the number will be parsed as an integer and output in the given base. If the value for `base` is 10 or is not given, the number will be parsed as a floating point number",
"open?": "(open? [io-handle]) => bool\n\nChecks whether or not the given io-handle is currently open",
"or": "(or [expression...]) => bool\n\nEvaluates each expression. If any expression evaluates to a truthy value #t is returned, otherwise #f is returned",
"pair?": "(pair? [value]) => bool\n\nChecks if the given value is a non-empty list",
"path-abs": "(path-abs [filepath: string]) => string\n\nCreates an absolute path from the given filepath by resolving all relative references and expanding ~",
"path-base": "(path-base [filepath: string]) => string\n\nReturns the last element in the given filepath. For example:\n\n\t(path-base \"/home/someuser/.vimrc\") => \".vimrc\"\n\t(path-base \"/home/someuser/.vim/\") => \".vim\"",
"path-dir": "(path-dir [filepath: string]) => string\n\nReturns all of the path minus the element after the last path separator. For example:\n\n\t(path-dir \"/home/someuser/.vim\") => \"/home/someuser\"\n\t(path-dir \"/home/someuser/.vim/\") => \"/home/someuser/.vim\"",
"path-exists?": "(path-exists? [filepath: string]) => bool\n\nCheckes whether the given filepath exists",
"path-extension": "(path-extension [filepath: string] [[replacement-extension: string]]) => string\n\nReturns the file extension for the file the given filepath points to, as defined as everything from the last '.', i.e. '.txt'. If there is no extension an empty string is returned. If a replacement-extension is given it will replace the original extension and the full path, including the new extension, will be returned. The replacement-extension should include a '.' if it is wanted in the new path",
"path-glob": "(path-glob [filepath: string]) => list\n\nReturns a list of filepaths that match the glob pattern provided as filepath. A glob is created by inserting a * character. For example:\n\n\t(path-glob \"/home/*/.bashrc\") => [\"/home/someone/.bashrc\" \"/home/otherperson/.bashrc\"]\n\t(path-glob \"/home/myuser/.*\") => [\"/home/myuser/.bashrc\" \"/home/myuser/.profile\" \"/home/myuser/.vimrc\"]",
"path-is-dir?": "(path-is-dir? [filepath: string]) => bool\n\nChecks whether or not the given filepath represents a directory",
"path-join": "(path-join [path-segment: string...]) => string\n\nJoins the given path-segments by the path separator appropriate for the given system. If no path-segments are passed the root direcotry is returned",
"PHI": "PHI => number",
"PI": "PI => number",
"positive?": "(positive? [number]) => bool\n\nChecks if the given number is greater than zero",
"procedure?": "(procedure? [value]) => bool\n\nChecks if the given value is a procedure",
"pwd": "(pwd) => string\n\nReturns the current working directory as a string",
"quote": "(quote [expression...]) => bool",
"rand": "(rand [[max: number]] [[min: number]]) => number\n\nIf no arguments are given `rand` will produce a floating point number between 0 and 1. If only `max` is given, `rand` will produce a floating point number between 0 and `max`. If `max` and `min` are both given, `rand` will produce a floating point number between `min` and `max`. `max` is always exclusive (the value produced will always be less than `max`), while `min` is inclusize. To obtain integers use `floor`, `ceil`, or `round` on the result or `rand`",
"rand-seed": "(rand-seed [number]) => #t\n\nSets the seed value for generating random numbers. The seed value defaults to the timestamp that is acquired when slope starts up. This procedure will over-ride that behavior to use the given seed",
"range": "(range [[count: number]] [[start: number]] [[step: number]]) => list\n\nIf no arguments are given an empty list will be returned. `count` should be a positive integer and represents the number of list items that will be produced by `range`. If no arguments other than `count` are given then a list from 0 -> count will be produced. To start at a number other than zero `start` can be given. To step by a value other than one, `step` can be given.\n\n\t(range) => ()\n\t(range 5) => (0 1 2 3 4)\n\t(range 5 10) => (10 11 12 13 14)\n\t(range 5 2 2) => (2 4 6 8 10)",
"read-all": "(read-all [[io-handle]]) => string\n\nReads all content from the given io-handle (defaulting to stdin) and returns it as a string",
"read-all-bytes": "(read-all-bytes [[io-handle]]) => list\n\nReads all content from the given io-handle (defaulting to stdin) and returns it as a list of numbers representing bytes",
"read-all-lines": "(read-all-lines [[io-handle]]) => list\n\nReads all content from the given io-handle (defaulting to stdin) and splits it into a list on newline",
"read-bytes": "(read-bytes [[io-handle]] [[count: number]]) => list|#f\n\nReads `count` bytes from the given io-handle and returns them as a list of numbers. If `count` is not given, it will default to 1. `count` must be a positive number larger than 0. If `count` is 1, the byte will be returned as a number, not a list of numbers. If no io-handle is given, stdin will be used. Does not opperate on string buffers",
"read-char": "(read-char [[io-handle]]) => string|#f\n\nReads a rune from the given io-handle (defaulting to stdin). Does not opperate on string buffers",
"read-line": "(read-line [[io-handle]]) => string|#f\n\nReads a line from the given io-handle (defaulting to stdin). Does not opperate on string buffers",
"reduce": "(reduce [procedure] [accumulator: value] [list]) => value\n\nThe given procedure should always take two values: the current list item and the current state of the accumulator. The procedure should return the accumulator state for the next evaluation of the procedure (ie. with the next list item). You do not need to bounds check within the procedure, it will be run once for every item in the list. The accumulator can be of any type and can, in theory, change type over the course of running reduce. The list can contain any values that the procedure is able to work with.\n\nExample:\n\n\t(reduce + 0 [1 2 3 4 5])\n\nReturns:\n\n\t15",
"ref": "(ref [list|string] [index: number|number-list] [[set: value]] [[relative?: bool]]) => value|list|string\n\nList/string indexing starts at 0. When the optional `set` argument is given the list/string will be returned with the value at the given index replaced by the `set` value that was given. Any value given as the optional set argument when working with a string will be coerced to a string, if an empty string is given then the index will be removed from the string. If a list is given along with an index, set value, and relative? is true, the value will be updated in place without need to store the list again with `set!`. `relative?` does not affect strings. If a list of numbers is given as `index` those numbers will be used to access sublists (nested lists) the same as in other languages where you provide a list of indexes (such as `myvar[0][2][1]` or the like in C based languages). If a list is given as `index` for a string only the first index value will be used. If a given index is negative the it will count backward from the end of the list such that an index of -1 will select the last item in a list -2 the second to last. If the absolute value of the negative number is larger than the list, an out of bounds error will occur",
"regex-find": "(regex-find [pattern: string] [string]) => list",
"regex-match?": "(regex-match? [pattern: string] [string]) => bool",
"regex-replace": "(regex-replace [pattern: string] [input: string] [replacement: string]) => string",
"reverse": "(reverse [list|string]) => list|string\n\n`reverse` will reverse a list or a string and return the same type of value it was given, but with the contents reversed",
"rm": "(rm [path: string] [[rm-all: bool]]) => ()\n\nRemoves the file pointed to by path from the filesystem. If rm-all is set to #t all children of the path will be removed as well (a recursive rm)",
"round": "(round [number] [[decimals-to-round-to: number]]) => number\n\nRounds the given number to the nearest whole number. If decimals-to-round-to is provided round will round to that number of decimal places",
"rune->bytes": "(rune->bytes [number]) => list\n\nWill convert the given number representing a rune to a list of numbers representing bytes. Runes in the ascii set will result in a list containing a single byte, larger runes will usually result in a list containing multiple bytes",
"rune->string": "(rune->string [number]) => string\n\nWill floor any value passed to it and attempt to convert it to a single character string represented by the given number",
"set!": "(set! [var-name: symbol] [value|expression|procedure]) => value | expression | procedure\n\nReturns the value that var-name was set to, in addition to creating the var symbol and setting it to the given value. Only an existing variable can be set with `set!`, trying to set a non-existant variable will result in an exception. `set!` will always set the value for the variable in the nearest scope. `var-name` can be an expression that evaluates to a symbol or a raw unquoted symbol",
"signal-catch-sigint": "(signal-catch-sigint [procedure]) => bool\n\nThe procedure passed to signal-catch-sigint should take no arguments and will be run upon sigint being received. This will override all default behavior: if you still want the program to exit you will need to call (exit) from within the procedure. This procedure (signal-catch-sigint) should be called at the root level of your program as it will only have access to the global environment.",
"sleep": "(sleep [[milliseconds: number]]) => ()\n\nHalt further program execution until the given number of milliseconds has passed",
"slice": "(slice [list|string] [start-index: number] [[end-index: number]]) => list|string\n\n`slice` is used to get a subsection of an indexed item (string or list). `end-index` is exclusive (should be one higher than the last character you want included in the slice). If `end-index` is not given then the slice will go from the start position until the end of the list|string",
"slope-version": "(slope-version [[major: number]] [[minor: number]] [[patch: number]]) => list|bool\n\nIf no arguments are given a list will be returned containing the running slope interpreter version as three numbers: major, minor, and patch versions. If one argument is given a check will be performed to see if the current version is >= to the major version given. If so, `#t` will be returned. If not, an exception with information about the versions will be returned. In addition to providing the major version as a number, you may also provide minor and patch versions as arguments. They will each be compared to the minor and patch versions for the given runtime. All arguments given must be >= to their counterparts in the slope runtime version to return '#t'. Any version component not given will default to 0",
"stderr": "stderr => io-handle (file: write-only)\n\nio-handle for stderr",
"stdin": "stdin => io-handle(file: read-only)\n\nio-handle for stdin",
"stdout": "stdout => io-handle (file: write-only)\n\nio-handle for stdout",
"string?": "(string? [value]) => bool\n\nChecks whether the given value is a string",
"string->bytes": "(string->bytes [value]) => list\n\nTakes any value and converts it to string (if it is not already a string), then converts that string to a list of numbers representing bytes",
"string->list": "(string->list [string] [[split-on: value]] [[count: number]]) => list\n\nSplits the string at each instance of `split-on` value. If `split-on` is not a string it will be case to string for the purposes of splitting the string into a list\n\nIf a count is provided the string will be split into no more than _count_ list elements.",
"string->md5": "(string->md5 [string]) => string\n\nProduces an md5 hash string from the given string",
"string->sha256": "(string->sha256 [string]) => string\n\nProduces a sha256 hash string from the given string",
"string->number": "(string->number [string] [[base: number]]) => number|#f\n\nIf a non-decimal base is supplied, the input string will be parsed as an integer and any flaoting point value will be floored. A valid base passed with a string that does not parse to a number will return #f",
"string->rune": "(string->rune [string]) => number\n\nConverts the first character in the given string to a rune number. Any other characters in the string will be ignored. If the string is empty `string->rune` will return 0. The resulting number can be converted back to a string with `rune->string`",
"string-buf?": "(string-buf? [value]) => bool\n\nChecks whether the given value is an io-handle of type 'string buffer'",
"string-buf-clear": "(string-buf-clear [io-handle: string-buf]) => ()\n\nResets/clears the content from the given string buffer io-handle, resulting in the buffer being empty",
"string-fields": "(string-fields [string]) => list\n\nSplits a string around each instance of one or more consecutive white space characters",
"string-format": "(string-format [template: string] [value...]) => string\n\nReplaces values designated by `%v` in the template string with successive values (`value...`).\n\nWidth can be set by adding an integer between the `%` and the `v`: `%10v`, or left aligned: `%-10v`",
"string-index-of": "(string-index-of [string] [search-value: string]) => number\n\nReturns -1 if `search-value` does not appear in `string`",
"string-lower": "(string-lower [string]) => string\n\nConverts all characters in the given string to their lowercase form and returns the new lowercase string",
"string-make-buf": "(string-make-buf) => io-handle: string-buf\n\nReturns a new empty string buffer io-handle",
"string-ref": "(string-ref [string] [index: number]) => string\n\nReturns the character at the given index as a string. Like with lists indexes begin at 0",
"string-replace": "(string-replace [string] [find: string] [replacement: string] [[count: number]]) => string\n\nDoes a straight string replacement `count` number of times, defaulting to all matches found. For more complex/flexible matching use `regex-replace`",
"string-trim-space": "(string-trim-space [string]) => string\n\nRemoves any leading or trailing white-space from `string`",
"string-upper": "(string-upper [string]) => string\n\nConverts all characters in the given string to their uppercase form and returns the new uppercase string",
"subprocess": "(subprocess [list] [[output-redirection: io-handle|#f]] [[error-redirection: io-handle|#f]] [[input-redirection: io-handle|#f]]) => number\n\nPassing `#f` to any of the redirections will have them use the std version of the redirection (stdout, stderr, or stdin). If you want to have something redirect to devnull you must explicitly pass it",
"symbol?": "(symbol? [value]) => bool\n\nChecks if the given value is a symbol",
"sys-args": "sys-args => list\n\nsys-args is the list of arguments that were invoked to run the currently running slope program. The first argument in the list is always the name of the program that was invoked, which will not include the slope interpreter itself (if invoked directly it will be removed from the list).",
"term-char-mode": "(term-char-mode) => ()\n\nChanges the terminal to char mode",
"term-cooked-mode": "(term-cooked-mode) => ()\n\nChanges the terminal to cooked mode",
"term-raw-mode": "(term-raw-mode) => ()\n\nChanges the terminal to raw mode",
"term-restore": "(term-restore) => ()\n\nRestores the terminal to how it was before any mode changes were instituted",
"term-sane-mode": "(term-sane-mode) => ()\n\nChanges the terminal to sane mode",
"term-size": "(term-size) => list\n\nReturns a list containing two numbers that represent the width and height of the terminal",
"timestamp": "(timestamp) => number\n\nReturns a number representing the current date/time in the unix timestamp format",
"timestamp->date": "(timestamp->date [timestamp: string|number] [[format: string]]) => string\n\nDate format/pattern substitutions:\n\t%a - 12 hour segment, lowercase: pm\n\t%A - 12 hour segment, uppercase: PM\n\t%d - Day without leading zero: 4\n\t%D - Day with leading zero: 04\n\t%e - Day without leading zero, but with space padding: 4\n\t%f - Month name, short: Jan\n\t%F - Month name, long: January\n\t%g or %G - Hour, 24 hour format: 15\n\t%h - Hour, 12 hour format without leading zero: 2\n\t%H - Hour, 12 hour format with leading zero: 02\n\t%i - Minutes without leading zero: 4\n\t%I - Minutes with leading zero: 04\n\t%m - Month number, without leading zero: 6\n\t%M - Month number, with leading zero: 06\n\t%o - Timezone offset, without minutes: -07\n\t%O - Timezone offset, with minutes: -0700\n\t%s - Seconds, without leading zero: 5\n\t%S - Seconds, with leading zero: 05\n\t%w - Short weekeday: Wed\n\t%W - Long weekday: Wednesday\n\t%y - Two digit year: 21\n\t%Y - Four digit year: 2021\n\t%Z - Time zone, as three chars: UTC, PST, etc.\n\t%% - Will yield a literal percent sign: %\n\tanything else - A percent followed by an unrecognized char will yield a ?, as will a hanging % as the last char in the string\n\nSo the string: \"%F %e, %Y %h:%I%a\" would be equivalend to something like: \"August 8, 2021 4:03pm\"\n\nWhen a string is converted to a date and no time zone information is present, slope will assume the user's local time.",
"type": "(type [value: any]) => string\n\nReturns a string describing the type of anything passed to it",
"uninspect": "(uninspect)\n\nThis string will exit from a subrepl started via `(inspect)`. It is a special 'magic string' rather than a procedure. See: `(usage inspect)`",
"url-host": "(url-host [url: string] [[new-host: string]]) => string\n\nReturns a string representing the host value of the given url. If a new-host value is provided the whole url will be returned but with the original host replaced by the new-host value",
"url-path": "(url-path [url: string] [[new-path: string]]) => string\n\nReturns a string representing the path portion of the given url, or an empty string if no path is present. If a new-path value is provided the whole url will be returned but with the original path replaced by the new-path value",
"url-port": "(url-port [url: string] [[new-port: string]]) => string\n\nReturns a string representing the port portion of the given url, or an empty string if no port is present. If a new-port value is proviuded the whole url will be returned but with the original port replaced by the new-port value",
"url-query": "(url-query [url: string] [[new-query: string]]) => string\n\nReturns a string representing the query portion of the url, or an empty string if no query segment is present. If a new-query value is provided the whole url will be returned but with the original query segment replaced by the new-query value",
"url-scheme": "(url-scheme [url: string] [[new-scheme: string]]) => string\n\nReturns a string representing the scheme portion of the url. If a new-scheme value is provided the whole url will be returned but with the original scheme replaced by the new-scheme value",
"usage": "(usage [[identifier: string|symbol]]) => ()\n\nIf not passed a value, usage will display a list of all known built-in procedures, as well as a list of loaded modules. When given a value equal to a built-in procedure a description of that value will be output to stdout. When given a module name or alias followed by two colons (`my-mod::`) a list of symbols that the module supplies usage information for will be printed to stdout. When passed the full identifier of a module's symbol that has usage information available (example: `my-mod::my-proc`) that usage information will be displayed. To get usage information for a module or its symbols the module must first be loaded via `load-mod`",
"write": "(write [string] [[io-handle]]) => ()|io-handle\n\nWrites the given string to stdout. If an optional io-handle has been provided the string will be written to the given io-handle. If the io-handle is closed an exception will occur. Returns an empty list if no io-handle is given, or returns the io-handle if one was given",
"write-bytes": "(write-bytes [bytes: list|number] [[io-handle]]) => ()|io-handle\n\nWrites the given list of numbers (or the single byte number) to stdout as bytes. If an optional io-handle has been provided the list (or single byte number) will be written to the given io-handle. If the io-handle is closed an exception will occur. Returns an empty list if no io-handle is given, or returns the io-handle if one was given",
"write-raw": "(write-raw [string] [[io-handle]]) => ()|io-handle\n\nWrites the given string to stdout, or an optionally provided io-handle. Does not process any escape values or special characters within the given string. Returns an empty list if no io-handle is given, or returns the given io-handle",
"zero?": "(zero? [number]) => bool\n\nChecks if the given number is zero",
}