NICKLE(1)
πΈ NICKLE(1) πΈ
okay bestie, sit down, hydrate, because we're about to talk about nickle β the desk calculator language from the General Commands Manual, and she is so much more than a calculator. π
β¨ NAME β¨
nickle β a desk calculator language. that's it, that's the whole name section, but the way she's a language and a calculator at the same time?? main character energy fr fr. π
π SYNOPSIS π
nickle [--help|--usage] [-f file] [-l library] [-e expr] [-V] [ script ]
[--] [arg ...]
π¦ USAGE π¦
so here's the vibe: any argument that doesn't start with a flag gets treated as a Nickle script, and it replaces standard input entirely. like, script comes in, stdin steps aside. π
Any remaining arguments that come after the script get dropped into the Nickle string array argv so your program can peek at them programmatically. yes bestie, you get real argv. iconic.
When you invoke Nickle with no expression and no script argument at all? She just reads from standard input and writes to standard output. cozy. π
Options are as follows:
--help,--usageβ print a help/usage message and exit. This is a built-in feature of Nickle's ParseArgs module, which means it's also true of any Nickle scripts that use this library. slay, inherited behaviour. β¨-f,--filefile β load file into Nickle before beginning execution. π-l,--librarylibrary β load library into Nickle before beginning execution. (there's a description of the library facility further down, keep reading bestie π«Ά)-e,--exprexpr β evaluate expr before beginning execution. π₯-V,--versionβ print the Nickle version and exit. π«--β quit parsing arguments and pass the remainder, unevaluated, toargv. no take-backs, no interpretation, just raw vibes.
π SYNTAX π
okay so, to make the input language actually usable in an interactive setting, a newline only terminates statements at ``reasonable'' times. A newline terminates either expressions or single statements typed by the user β except for a few statements that require lookahead, most notably if() and twixt(), which have an optional else part. π Inside compound statements or function definitions, only a ; terminates statements. This approach is convenient and does not appear to cause problems in normal use. no cap. β
The syntax of Nickle programs is as follows. In this description, name denotes any sequence of letters, digits and _ characters not starting with a digit; E denotes any expression; S denotes any statement; and T denotes any type. The syntax X,X,...,X denotes one or more comma-separated Xs, unless otherwise indicated. π
π Comments
C-style comments are enclosed in /* and */, and shell-style comments are denoted by a leading # at the start of a line.
π§ Operands
- real number β can include an exponent, and doesn't need to include a decimal point or sign. Will be treated as exact rationals. If a trailing decimal part contains an opening curly brace, the brace is silently ignored; if it contains a curly-bracketed trailing portion, it's treated as a repeating decimal.
Floating point'' constants are currently represented internally as rationals β for floating constants with a given precision (and an infinite-precision exponent), use theimprecise()` builtin function described below. β¨ - octal number β starts with a
0(e.g.,014is the same as12). - hexidecimal number β starts with
"0x"(e.g.,0x1ais the same as26). - string β as in C. String constants are surrounded by double-quotes. Backslashed characters (including double-quotes) stand for themselves, except
"\n"stands for newline,"\r"for carriage return,"\b"for backspace,"\t"for tab and"\f"for formfeed. π - name β a variable reference.
name(),name(E,E,...,E)β a function call with zero or more arguments. Functions are fully call-by-value: arrays and structures are copied rather than being referenced as in C. yes really, copied. π«Άdesc name,T name = valueβ definition expressions: a new name is made available, with the value of the definition being the value of the initializer in the second form, and uninitialized in the first form. The descriptor desc is not optional: it consists of any combination of visibility, storage class or type (in that order). See QUALIFIERS immediately below for a description of these qualifiers. A structured value expression is also possible: see VALUES below. π
In addition to being able to initialize a definition with a Nickle value, C-style array, structure, and union definitions are also allowed. For example, the following
int[*,*] name = {{0,1},{2,3}}
int[2,2] name = {{0...}...}
are permitted with the obvious semantics. This is the context in which the dimensions in a type may be expressions: see the discussion of array types above. See the discussion of array and structure values for array and structure initializer syntax. π
π QUALIFIERS π
A declaration or definition may be qualified, as in C, to indicate details of programmatic behavior. But unlike in C, these qualifiers, while optional, must appear in the given order. order matters bestie, don't test her. π
π Visibility
publicβ any definition expression (function definition, variable definition, type definition) can be qualified withpublicto indicate that the name being defined should be visible outside the current namespace, and should be automatically imported. See Namespaces below for further info. β¨protectedβ any definition expression (function definition, variable definition, type definition) can be qualified withprotectedto indicate that the name being defined should be visible outside the current namespace, but should not be made available by import declarations. See Namespaces below for further info. π
β³ Lifetime
autoβ anautoobject is local to a particular block: its lifetime is at least the lifetime of that block. Anautoobject with an initializer will be re-initialized each time it is evaluated. This is the default lifetime for local objects. πstaticβ astaticobject is local to a particular function definition: its lifetime is at least the lifetime of that definition. A new static object will be created each time its enclosing function definition is evaluated.
In Nickle, the keyword static has to do only with lifetime (like the use of static inside C functions), not with visibility (which is handled by separate qualifiers as described above, not like the use of static in global scope in C). iykyk. π§
globalβ aglobalobject is global to the entire program: its lifetime is the lifetime of the program. Aglobalobject will be created and initialized when its definition is first seen. This is the default lifetime for global objects.
The distinction between static and global lifetime in Nickle is not possible in C, because C functions are not first class objects with nested scope. When deciding which to use in a Nickle program, think about what should happen if a definition is re-evaluated. π€
π₯ OPERATORS π₯
Here are the basic Nickle operators, grouped in order of decreasing precedence. buckle up bestie, it's a long list but every single one is here. π
A[E,E,...,E]β refers to the E'th element of the array expression A, or the E1'th/E2'th/etc element of a multi-dimensional array. Both arrays of arrays ala C and multidimensional arrays ala NAWK are possible. πstruct.tagβ structure dereference.struct->tagβ structure pointer dereference ala C.
++--β unary increment/decrement. May be either postfix or prefix. π-β unary negate.! Eβ logical negation.E !β factorial. Requires a non-negative integer argument.* Eβ pointer dereference.& Eβ reference construction.
(U) Eβ construct a value of union type with tag U and value E.
**β exponentiation. Both operands may be fractional. The left operand must be non-negative unless the right operand is integer. The result type is the type of the left operand if the right operand is integer, and real otherwise.
okay this one's important. This is the only known type-unsound feature of Nickle: an expression like2 ** -3will statically be of typeinteger, but dynamically will generate arationalresult. This may cause a runtime type error later on β considerint x = 2 ** -3;she's giving chaos, be careful. π
*///%β times, divide, integer divide, and remainder. The right operand of the last three operators must be nonzero. π¨ The result type of the division operator will always be at least rational: the result type of the integer division operator will always be int. This is a notable departure from C, where integer division is implied by integer operands. Integer division is defined by
x // y == y > 0 ? floor (x / y) : ceil(x / y)
The remainder is always non-negative and is defined by:
x % y = x - (x // y) * y
+-β addition and subtraction.
<<>>β bitwise left and right shift with integer operands. Negative right operands work as expected. These operators are defined by
x << y = x * 2 ** y
x >> y = x // 2 ** y
Another way to look at this is that negative left operands are considered to be in an infinite twos-complement representation (i.e., sign-extended to infinity), with right shift sign-extending its left operand. β¨
<=>=<>β relational operators.
==!=β equality operators.
Finally, in order of decreasing precedence:
&β bitwise AND. Negative operands are considered to be in an infinite twos-complement representation (i.e., sign-extended to infinity). π^β bitwise XOR. Negative operands as in bitwise AND.|β bitwise OR. Negative operands as in bitwise AND.&&β short-circuit logical AND.||β short-circuit logical OR.E ? E : Eβ conditional expression: if the first expression is logical true, the value is the second expression, else the third. πfork Eβ create (and return) a thread. See Thread below for details. π§΅=+=-=*=/=//=%=**=<<=>>=^=&=|=β assignment operators. Left-hand-side must be assignable.x <op>= yis equivalent tox = x <op> y. π₯E , Eβ returns the right-hand expression.
phew, and that's just part one bestie β there's more coming, don't go anywhere. ππ¦
π TYPES π
okay so bestie, here's the tea on Nickle types: the type declaration syntax is way more like the "left" variant of the Java syntax than the C syntax. and essentially, a type is made up of:
polyintegerrationalrealstringcontinuationvoidβ these are the base types of the language.<pink>void</pink>is actually only usable in certain contexts, notably function returns. it's currently implemented as a "unit" type ala ML, and thus has slightly different behavior than in C. and<purple>poly</purple>? that one is the supertype of all other types (i.e., it can be used to inhibit static type checking), and it's the default type in most situations where a type need not appear. iconic behavior honestly.filesemaphorethreadβ also builtin base types, but integral to the File and Thread ADTs: see below. π«Ά
π More About Types π
Nickle supports polymorphic data: as an expression is evaluated, a data type is chosen to fit the result. any Nickle object may be statically typed, in which case bounds violations will be flagged as errors at compile time. polymorphic variables and functions do not place restrictions on the assigned data type; this is the default type for all objects. no cap.
polyβ this describes the union of all datatypes. a variable with this type can contain any data value.intβ arbitrary precision integers.rationalβ arbitrary precision rational numbers.realβ arbitrary exponent precision floating point numbers. as many computations cannot be carried out exactly as rational numbers, Nickle implements non-precise arithmetic using its own machine-independent representation for floating point numbers. the builtin functionimprecise(n)generates a real number with 256 bits of precision from the numbern, whileimprecise(n,p)generates a real number withpbits of precision. β¨T[]β an array of typeT, of one or more dimensions. there are no zero-dimensional arrays in Nickle. periodt.T[*]β a one-dimensional array of typeT. unlike in C, the dimension of an array is never part of its type in Nickle. further, arrays and pointers are unrelated types in Nickle.T[*,*,...,*]β a two or more dimensional array of typeT. the stars*are not optional. and as the previous paragraphs make clear,T[]is not a zero-dimensional array, bestie. πT[E,E,...,E]β in definition contexts, integer values may be given for each dimension of an array context. these are strictly for value-creation purposes, and are not part of the type. an array type is determined only by the base type and number of dimensions of the array.T0()T0(T,T,...,T)β a function returning typeT0. a function accepts 0 or more arguments.T0()T0(T,T,...,T ...)β a function accepting zero or more required arguments, plus an arbitrary number of optional arguments. the second sequence of three dots (ellipsis) is syntax, not metasyntax: see the description of varargs functions for details.*Tβ a pointer to a location of typeT. pointer arithmetic in Nickle operates only upon pointers to arrays: the pointer must be of the correct type, and may never stray out of bounds. a pointer may either point to some location or be null (0). as in C, the precedence of*is lower than the precedence of[]or(): use parenthesis as needed.struct {T name; T name; ...}β a structure with fields of the given name and type. the typesTare optional: in their absence, the type of the field ispoly.union {T name; T name; ...}β a "disjoint union" of the given types. this is more like the variant record type of Pascal or the datatype of ML than the C union type: the names are tags of the given type, exactly one of which applies to a given value at a given time.(T)β parentheses for grouping. yes queen, that's the whole thing.
π·οΈ Typedef π·οΈ
as in C, new type names may be created with the typedef statement. the syntax is
typedef T typename;
where T is a Nickle type. the resulting typename may be used anywhere a type is expected. so clean, so iconic. π
πΈ VALUES πΈ
values of the base types of Nickle are as expected. see the syntax for constants above. values of type file, semaphore, and continuation may currently be created only by calls to builtin functions: no Nickle constants of these types exist. π
as noted in TYPES above, Nickle has several kinds of "structured value": arrays, functions, pointers, structures and disjoint unions. all of these have some common properties. when created, all of the component values are uninitialized (unless otherwise specified). attempts to use an uninitialized value will result in either a compile-time error or a runtime exception. so like... don't. π
π Arrays π
[E]β creates a (zero-based) array withEelements.Emust be non-negative.[E]{V,V,...,V}β creates an array withEelements, initialized to theVs. if there are too few initializers, remaining elements will remain uninitialized.[E]{V,V,...,V...}β the second ellipsis (three dots) is syntax, not metasyntax. create an array withEelements. the first elements in the array will be initialized according to theVs, with any remaining elements receiving the same value as the lastV. this syntax may be used in the obvious fashion with any of the array initializers below.[*]{V,V,...,V}β creates an initialized array with exactly as many elements as initializers. there must be at least one initializer.[E,E,...,E][*,*,...,*]β creates multidimensional arrays. integer expressions and*cannot be mixed: an array's dimensions are entirely either specified or unspecified by the definition. these arrays may also be created initialized: see next paragraph for initializer syntax.(T[E])(T[E,E,...,E])(T[E]){E,E,...,E}(T[E,E,...,E]){{E,...},...,{E,...}}β alternate syntax for creating arrays of typeT. the initializers, in curly braces, are optional. the number of initializers must be less than or equal to the given number of elements in each dimension. for multidimensional arrays, the extra curly braces per dimension in the initializer are required; this is unlike C, where they are optional.(T[*]){E,E,...,E}(T[*,*,...,*]){{E,...},...,{E,...}}β creates arrays of typeT, with each dimension's size given by the maximum number of initializers in any subarray in that dimension.
π Pointers π
0β the null pointer, in contexts where a pointer is required.&V&A[E,E,...,E]&S.Nβ creates a pointer to the given variable, array element, or structure member. the type of the pointer will be*T, whereTis the type of the object pointed to.*Pβ the value pointed to by pointerP. this can be viewed or modified as in C.
π¦ Functions π¦
(T func(){S;S;...S;})(T func(T name,T name,...T name){S;S;...S;})β function expression: denotes a function of zero or more formal parameters with the given types and names, returning the given result type. the function body is given by the curly-brace-enclosed statement list. all types are optional, and default topoly. as noted above, functions are strictly call-by-value: in particular, arrays and structures are copied rather than referenced. yes bestie, that's a whole thing.T function name(T name,T name,...,T name){S;S;...S;}β defines a function of zero or more arguments. syntactic sugar for
T(T,T,...T) name = (T func(T name,T name,...T name){S;S;...S;});
T function name(T name, T name ...)β the ellipsis here is syntax, not metasyntax: if the last formal argument to a function is followed by three dots, the function may be called with more actuals than formals. all "extra" actuals are packaged into the array formal of the given name, and typechecked against the optional typeTof the last argument (defaultpoly).
π§ Structures π§
(struct { T name; T name; ...T name; }){name = E; name = E; ...name=E;}β create a value of a structured type. the named fields are initialized to the given values, with the remainder uninitialized. as indicated, initialization is by label rather than positional as in C.
π Unions π
(union { T name; T name; ...T name; }.name) Eβ create a value of the given union type, the variant given by.name, and the value given byE.Emust be type-compatible withname.
π STATEMENTS π
okay bestie, the statement syntax is very closely related to C's β like, siblings energy β but we sprinkled in some extra syntax so Nickle's extra functionality can actually do its thing. let's gooo β¨
E;β just evaluates the expression. that's it, that's the whole statement, she's low maintenance π{S ... S}β executes all the enclosed statements, in order, one after another. no skipping, no chaos.if (E) Sβ the basic conditional, babes. classic.if (E) Sβ conditional execution. (yes, she's listed twice up in here. the original said it twice and we respect her.)else Sβ Else is allowed, with the usual syntax and semantics you already know and love. the important tea: anelsebinds to the most recent applicableif()ortwixt(). she's clingy like that π«Άwhile (E) Sβ C-style while loop, periodt.do S while (E);β C-style do loop.for (opt-E; opt-E; opt-E) Sβ C-style for loop, with all three expressions optional.switch (E) { case E: S-list case E: S-list ... default: S-list }β C-style case statement. here's the glow-up: the case expressions are not required to be constant expressions, they may be arbitrary. the first case that evaluates to the switch argument is the one that gets taken; else thedefaultif it's present; else the whole switch body just gets skipped. bye πtwixt(opt-E; opt-E) Stwixt(opt-E; opt-E) S else Sβ if the first argument expression evaluates to true, the body of thetwixt()gets evaluated and then the second argument expression. if the first argument expression evaluates to false, theelsestatement runs if it's there. otherwise? the entiretwixt()statement is skipped. simple as.
the twixt() statement guarantees that all of those events happen in the specified order, no matter how the twixt() is entered (from outside) or exited β and that includes exceptions, continuations, and break. (think of it like Java's finally clause, if Java were cooler.)
try S;try S catch name (T name, ...) { S; ... };try S catch name (T name, ...) { S; ... } ... ;β executes that first statementS. if an exception gets raised during execution, and thenamematches the name in a catch block, then we bind the formal parameters in the catch block to the actual parameters of the exception, and execute the body of the catch block. you can have multiple catch blocks pertry. zero catches is legal but, and I quote, relatively useless π. after a catch block finishes, execution continues after thetryclause. and same aselse: acatchbinds to the most recent applicabletry-catchblock.raise name(name, name, ..., name)β raises the named exception with zero or more arguments. make some noise π£;β the null statement. does nothing, serves looks. πbreak;β discontinue execution of the nearest enclosingfor/do/while/switch/twixtstatement. and heads up: the leave expression will be executed as thetwixtstatement is exited.continue;β branches directly to the conditional test of the nearest enclosingfor/do/whilestatement.return E;β return valueEfrom the nearest enclosing function.
π§ Namespaces π§
like Java and C++, Nickle has a notion of a namespace: a collection of names with partially restricted visibility. in Nickle, namespaces get created with the namespace command. let's talk about it.
opt-P namespace N { S ... }β places all names defined in the statementsSinto a namespace namedN. the optional qualifierPmay be the keywordpublic, but beware bestie: this merely indicates that the nameNitself is visible elsewhere in the current scope, and has nothing to do with the visibility of the items inside the namespace. don't let the name fool you πextend namespace N { S ... }β reopens the given namespaceN, and extends it with the names defined aspublicin the given statementsS.
names defined inside the namespace are invisible outside the namespace unless they're qualified with the keyword public. public names may be referred to using path notation:
namespace::namespace::...::namespace::name
which refers to the given name as defined inside the given set of namespaces. the double-colon syntax is unfortunate, as it's slightly different in meaning than it is in C++ β but all the good symbols were taken, and it's believed to be a feature that the namespace separator is syntactically different from the structure operator. in Java, for example, the phrase
name.name.name
is syntactically ambiguous: that middle name could be either a structure or a namespace. not here. slay π
import N;β the nameNmust refer to a namespace: all public names in this namespace are brought into the current scope (scoping out conflicting names). come on in, the water's fine π
π οΈ BUILTINS π₯
Nickle comes with a whole collection of standard functions built in. some of these are written in C, but many are written in Nickle itself. several collections of functions have associated builtin datatypes: their namespaces, together with their types, should be viewed as ADTs. yes queen, abstract data types β¨
π Top-Level Builtins π
int printf(string fmt, poly args...)β callsFile::fprintf(stdout, fmt, args ...)and returns its result.string function gets ()β callsFile::fgets(stdin)and returns its result.string function scanf (string fmt, *poly args...)β callsFile::vfscanf(stdin, fmt, args)and returns its result.string function vscanf (string fmt, (*poly)[*] args)β callsFile::vfscanf(stdin, fmt, args)and returns its result.real imprecise(rational value)β see the discussion of typerealabove.real imprecise(rational value, int prec)β see the discussion of typerealabove.int string_to_integer(string s)int atoi(string s)β the argumentsis a signed digit string, and the result is the integer it represents. if the stringsis syntactically a hexadecimal, octal, binary, or explicit base-10 constant, treat it as such.int string_to_integer(string s, int base)int atoi(string s, int base)β treatsas a string of digits in the given base. a base of0acts as with no base argument. otherwise, base specification syntax in the string is ignored.int putchar(int c)β places the given character on the standard output usingFile::putc(c, stdout), and returns its result.int sleep(int msecs)β tries to suspend the current thread for at leastmsecsmilliseconds. returns1on early return, and0otherwise.int exit(int status)β exits Nickle with the given status code. does not return anything. bye bestie πint dim(poly[*] a)β given a one-dimensional arraya,dim()returns the number of elements ofa.int[] dims(poly[] a)β given an arbitrary arraya,dims()returns an array of integers giving the size of each dimension ofa. so,dim(dims(a))is the number of dimensions ofa. it's giving recursion but make it cute π¦*poly reference(poly v)β given an arbitrary valuev, "box" that value into storage and return a pointer to the box.rational string_to_real(string s)rational atof(string s)β converts the real constant stringsinto its associated real number.number abs(real v)β returns the absolute value ofv. the result type chosen will match the given context.int floor(real v)β returns the largest integer less than or equal tov. this will fail ifvis a real and the precision is too low.int ceil(real v)β returns the smallest integer greater than or equal tov. this will fail ifvis a real and the precision is too low.int exponent(real v)β returns the exponent of the imprecise realv.rational mantissa(real v)β returns the mantissa of the imprecise realv, as a rationalmwith0 <= m <= 0.5.int numerator(rational v)β returns the numerator of the rational numberv: i.e., ifv = n/din reduced form, returnn.int denominator(rational v)β returns the denominator of the rational numberv: i.e., ifv = n/din reduced form, returnd.int precision(real v)β returns the number of bits of precision of the mantissa of the imprecise real numberv.int sign(real v)β returns-1or1asvis negative or nonnegative.int bit_width(int v)β returns the number of bits required to representabs(v)internally.int is_int(poly v)β type predicate.int is_rational(poly v)β numeric type predicates are inclusive: e.g.,is_rational(1)returns1.int is_number(poly v)β type predicate.int is_string(poly v)β type predicate.int is_file(poly v)β type predicate.int is_thread(poly v)β type predicate.int is_semaphore(poly v)β type predicate.int is_continuation(poly v)β type predicate.int is_array(poly v)β type predicate.int is_ref(poly v)β type predicate: checks for pointer type. this is arguably a misfeature, and may change. β οΈint is_struct(poly v)β type predicate.int is_func(poly v)β type predicate.int is_void(poly v)β type predicate.int gcd(int p, int q)β returns the GCD ofpandq. the result is always positive. πͺint xor(int a, int b)β returnsa ^ b. this is mostly a holdover from before Nickle had an xor operator.poly setjmp(continuation *c, poly retval)β thesetjmp()andlongjmp()primitives, together with the continuation type, form an ADT useful for nearly arbitrary transfers of flow-of-control. thesetjmp()andlongjmp()builtins are like those of C, except that the restriction thatlongjmp()always jump upwards is removed(!): a continuation saved viasetjmp()never becomes invalid during the program lifetime. thesetjmp()builtin saves the current location and context into its continuation pointer argument, and then returns its second argument.void longjmp(continuation c, poly retval)β thelongjmp()builtin never returns to the call site, but instead returns from thesetjmp()that created the continuation, with return value equal to the second argument oflongjmp().string promptβ the prompt printed during interactive use when at top-level. default"> ". when waiting for the rest of a statement or expression, and when debugging, respectively. default values are"> ","+ ", and"- ".string prompt2β the prompt printed during interactive use when waiting for the rest of a statement or expression. default"+ ".string prompt3β the prompt printed during interactive use when debugging. default"- ".string formatβ theprintf()format for printing top-level values. default"%g".string versionβ the version number of the Nickle implementation you're literally running right now. π¦string buildβ when that Nickle build happened, formatted like"yyyy/mm/dd", or just"?"if the build date is somehow a mystery.file stdinβ bound to the standard input stream, bestie. π«Άfile stdoutβ bound to the standard output stream. πfile stderrβ bound to the standard error stream. π
π Exceptions π
A few standard exceptions are predeclared and used internally by Nickle. So iconic of them.
exception uninitialized_value(string msg)β thrown when you try to use an uninitialized value. we've all been there. πexception invalid_argument(string msg, int arg, poly val)β thearg-th argument passed to a builtin function had an invalid value,val.exception readonly_box(string msg, poly val)β attempt to change the value of a read-only quantity toval. no touching. π«exception invalid_array_bounds(string msg, poly a, poly i)β attempt to access arrayaat indexiand it's out of bounds.exception divide_by_zero(string msg, real num, real den)β attempt to dividenumbydenwhereden == 0. math said no. πexception invalid_struct_member(string msg, poly struct, string name)β attempt to refer to membernameof the objectstruct, which does not exist.exception invalid_binop_values(string msg, poly arg1, poly arg2)β attempt to evaluate a binary operator with argsarg1andarg2, where at least one of those values is invalid.exception invalid_unop_values(string msg, poly arg)β attempt to evaluate a unary operator with an invalid argumentarg.
π§ Builtin Namespaces π§
Math
The math functions available in the Math namespace are implemented in a fashion intended to be compatible with the C library. Please consult the appropriate manuals for further details. (iykyk π)
real piβ imprecise constant giving the value of the circumference/diameter ratio of the circle, to the default precision of 256 bits.protected real eβ imprecise constant giving the value of the base of natural logarithms, to the default precision of 256 bits. Sinceeis protected, you must reference it viaMath::e, so you don't run into problems using the fifth letter of the alphabet at top level. yes fr fr, that single letter is a menace. πreal function sqrt(real v)β returns the square root ofv.real function cbrt(real v)β returns the cube root ofv.real function exp(real v)β returnse**v. β¨real function log(real a)β returnsvsuch thate**v == a. Throws aninvalid_argumentexception ifais non-positive.real function log10(real a)β returnsvsuch that10**v == a. Throws aninvalid_argumentexception ifais non-positive.real function log2(real a)β returnsvsuch that2**v == a. Throws aninvalid_argumentexception ifais non-positive.real function pi_value(int prec)β returns the ratio of the circumference of a circle to the diameter, withprecbits of precision.real function sin(real a)β returns the ratio of the opposite side to the hypotenuse of angleaof a right triangle, given in radians.real function cos(real a)β returns the ratio of the adjacent side to the hypotenuse of angleaof a right triangle, given in radians.void function sin_cos(real a, *real sinp, *real cosp)β returns withsin(a)andcos(a)stored in the locations pointed to bysinpandcosprespectively. If either pointer is0, don't store into that location. May be slightly faster than calling both trig functions independently. two-for-one, we love efficiency. πreal function tan(real a)β returns the ratio of the opposite side to the adjacent side of angleaof a right triangle, given in radians. Note thattan(pi/2)is not currently an error: it will return a very large number dependent on the precision of its input. β οΈreal function asin(real v)β returnsasuch thatsin(a) == v.real function acos(real v)β returnsasuch thatcos(a) == v.real function atan(real v)β returnsasuch thattan(a) == v.real function atan2(real x, y)β returnsasuch thattan(a) == x / y. Deals correctly withy == 0. slay. πreal function pow(real a, real b)β the implementation of the**operator.
File
The namespace File provides operations on file values. π
int function fprintf(file f, string s, ....)β print formatted values to a file, as with the UNIX stdio libraryfprintf().fprintf()andprintf()accept a reasonable sub-set of the stdio library version:%c,%d,%e,%x,%o,%f,%s,%gwork as expected, as does%vto smart-print a value. Format modifiers may be placed between the percent-sign and the format letter to modify formatting. There are a lot of known bugs with input and output formatting. π
Format Letters:
%cβ requires a small integer argument (0..255), and formats as an ASCII character.%dβ requires an integer argument, and formats as an integer.%xβ requires an integer argument, and formats as a base-16 (hexadecimal) integer.%oβ requires an integer argument, and formats as a base-8 (octal) integer.%eβ requires a number argument, and formats in scientific notation.%fβ requires a number argument, and formats in fixed-point notation.%sβ requires a string argument, and emits the string literally.%gβ requires a number, and tries to pick a precise and readable representation to format it.
Format Modifiers:
- digits β all format characters will take an integer format modifier indicating the number of blanks in the format field for the data to be formatted. The value will be printed right-justified in this space.
- digits.digits β the real formats will take a pair of integer format modifiers indicating the field width and precision (number of chars after decimal point) of the formatted value. Either integer may be omitted.
-β a precision value indicating infinite precision.*β the next argument tofprintf()is an integer indicating the field width or precision of the formatted value.
file function string_write()β return a file which collects written values into a string.int function close(file f)β close filefand return an indication of success.int function flush(file f)β flush the buffers of filefand return an indication of success.int function getc(file f)β get the next character from filefand return it.int function end(file f)β returns true if filefis at EOF, else false.int function error(file f)β returns true if an error is pending on filef, else false.int function clear_error(file f)β clears pending errors on filef, and returns an indication of success.file function string_read(string s)β returns a virtual file whose contents are the strings.string function string_string(file f)β return the string previously written into the filef, which should have been created bystring_read()orstring_write(). Behavior on other files is currently undefined. β οΈfile function open(string path, string mode)β open the file at the givenpathwith the givenmodestring, ala UNIX stdiofopen(). Permissible modes are as in stdio:"r","w","x","r+","w+"and"x+".integer function fputc(integer c, file f)β output the charactercto the output filef, and return an indication of success.integer function ungetc(integer c, file f)β push the charactercback onto the input filef, and return an indication of success.integer function setbuf(file f, integer n)β set the size of the buffer associated with filefton, and returnn.string function fgets (file f)β get a line of input from filef, and return the resulting string.file function pipe(string path, string[*] argv, string mode)β start up the program at the givenpath, returning a file which is one end of a "pipe" to the given process. Themodeargument can be"r"to read from the pipe or"w"to write to the pipe. Theargvargument is an array of strings giving the arguments to be passed to the program, withargv[0]conventionally being the program name.int function print (file f, poly v, string fmt, int base, int width, int prec, string fill)β print valuevto filefin formatfmtwith the givenbase,width,prec, andfill. Used internally byFile::fprintf().int function fscanf(file f, string fmt, *poly args...)β fill the locations pointed to by the arrayargswith values taken from filefaccording to stringfmt. The format specifiers are much as in UNIX stdioscanf(): the"%d","%e","%f","%c"and"%s"specifiers are supported with the expected modifiers.int function vfscanf (file f, string fmt, (*poly)[*] args)β given the filef, the formatfmt, and the array of argumentsargs,fscanf()appropriately.
Thread
The namespace Thread supports various operations useful for programming with threads, which provide concurrent flow of control in the shared address space. There is one piece of special syntax associated with threads. π
fork(E)β accepts an arbitrary expression, and evaluates it in a new child thread. The parent thread receives the thread as the value of theforkexpression.
The remainder of the Thread functions are fairly standard.
int function kill(thread list...)β kills every running thread in the arraylist. With no arguments, kills the currently running thread. Returns the number of threads killed. πint function trace(poly list...)β shows the state of every running thread in the arraylist. With no arguments, traces the default continuation. Returns the number of threads traced.int function cont()β continues execution of any interrupted threads, and returns the number of continued threads.thread function current()β return the current thread.int function list()β reports the currently running thread to stdout.int function get_priority(thread t)β tells you the priority of threadt. gossip about the thread, basically. πint function id_to_thread(int id)β hands you back the thread that matchesidif it exists, and0if it doesn't. no thread, no vibe.poly function join(thread t)β sits there waiting for threadtto finish, then gives you back whatever it returned. patient bestie energy π«Άint function set_priority(thread t, int i)β tries to set threadt's priority to leveli, and returns the new priority. higher priority = more runtime, periodt: a task with a higher priority will always run instead of a lower priority task. threads tied at the highest priority get pre-emptively multitasked. it's giving main character gets the CPU π
β¨ Semaphore β¨
The Semaphore namespace wraps up all the operations on the semaphore built-in ADT. semaphores are for thread synchronization, bestie. every signal() on the semaphore wakes up the least-recent thread that called wait() on it. the ``count'' of waiting processes can be set when you make the semaphore.
semaphore function new(int c)β makes a fresh semaphore with an initial countcof waiting processes. ifcis positive, that meanscthreads can wait on it before one blocks. ifcis negative, it sets a count of threads that must be waiting on the semaphore before further waits won't block. negative count is the weird girl of this API, fr.semaphore function new()β just callssemaphore(0)and returns the result.int signal(semaphore s)β increments semaphores. ifsis non-positive and some thread is blocked ons, it releases the least-recently-blocked thread. returns1on success. slay.int wait(semaphore s)β decrements semaphores. ifsis negative, it blocks until released. returns1on success. the waiting is the hardest part πint test(semaphore s)β checks whetherwait()on semaphoreswould make the current thread block. if it would, returns0. otherwise it attempts to decrements, and returns1if that worked.
πΈ String πΈ
The String namespace has a few basic operations on the string ADT. lowkey essential.
int function length(string s)β returns how many characters are ins.string function new(int c)β returns the single charactercas a string.string function new(int cv[*])β returns a string made out of the characters incv.int function index(string t, string p)β returns the integer index of pattern stringpinside target stringt, or-1ifpisn't a substring oft. finding your people in the crowd π¦string function substr(string s, int i, int l)β returns the substring ofsthat starts at offseti(zero-based) and runs forlcharacters total. iflis negative, the substring is made of the characters precedingiinstead of the ones succeeding it. yes, backwards. iconic behaviour, honestly.
π² PRNG π²
The PRNG namespace does pseudo-random number generation and manipulation. the core generator is the RC4 stream cipher generator, properly bootstrapped. this gives you a stream of cryptographically-secure pseudo-random bits at a reasonable amortized cost. (but beware β initialization is somewhat expensive. it's giving luxury β¨)
void function srandom(int s)β initializes the generator using the (arbitrarily-large) integersas a seed.void function dev_srandom(int nbits)β initializes the generator usingnbitsbits of entropy grabbed from some reasonable entropy source. on UNIX systems, that source is/dev/urandom. asking for more initial entropy than the system actually has may lead either to bootstrapping (as on UNIX) or to hanging, so use cautiously. β οΈ do not yolo this one.int function randbits(int n)β returns ann-bit pseudo-random number, in the range0..(2**n)-1. useful for stuff like RSA. math girlies rise πint function randint(int n)β returns a pseudo-random number in the range0..n-1.void function shuffle(*(poly[*]) a)β does an efficient in-place true shuffle (c.f. Knuth) of the arraya. no cap, an actual shuffle.
π Command π
The Command namespace is what the top-level commands use, as described below. it's also occasionally useful all on its own.
string library_pathβ holds the current library search path: a colon-separated list of directories that get searched for library files.int function undefine(string name)β this is the top-levelundefinecommand. removes the name denoted by stringnamefrom the namespace. this removes all visible definitions of that name.int function undefine(string[*] names)β removes each of the names in the arraynamesfrom the namespace. this removes all visible definitions of each one.int function delete(string name)β tries to remove the command with the given stringnamefrom the top-level command list, and returns1if it worked.int function lex_file(string path)β tries to make the file atpaththe current source of Nickle code, and returns1if it worked. note that this effectively ``includes'' the file by pushing it onto a stack of files to be processed.int function lex_library(string filename)β same aslex_file(), except it searches the directories listed in thelibrary_pathvariable for the first file with the givenfilename.int function lex_string(string code)β tries to make the Nickle code contained in the stringcodethe next input.int function edit(string[*] names)β this is the top-leveleditcommand. the names in the array are a path of namespace names leading down to the symbol name, which comes last.int function new(string name, poly func)β binds functionfuncto the top-level command stringname, i.e. it makes it part of the top-level command vocabulary.int function new_names(string name, poly func)β binds functionfuncto the top-level command stringname, i.e. makes it part of the top-level command vocabulary. unlikenew(), the string names handed tofuncat the top level are passed unevaluated, either as an array of string names or as a single string name.int function pretty_print(file f, string[*] names)β this is the top-levelprintcommand. each passed name string gets looked up and the corresponding code gets printed to filef.int function display(string fmt, poly val)β usesprintf()to display the valuevalin formatfmt.
π°οΈ History π°οΈ
Nickle keeps a top-level value history, which is super useful alongside command-line editing when you're calculating. the History namespace has the functions that reach into it.
int function show(string fmt)β the history top-level command with no arguments. shows the most recent history values with formatfmt.int function show(string fmt, int count)β the history top-level command with one argument. shows the lastcounthistory values with formatfmt.int function show(string fmt, int first, int last)β the history top-level command with two arguments.poly function insert(poly val)β insertsvalinto the history list and returns it.
π Environ π
A lot of operating systems have some notion of ``environment variables.'' the Environ namespace has the functions to mess with them.
int function check(string name)β returns1if a variable with the givennameis in the environment, and0otherwise.string function get(string name)β tries to fetch and return the value of the environment variable with the givenname. throws aninvalid_argumentexception if that variable isn't available. rude, honestly π€int function unset(string name)β tries to unset the environment variable with the givenname, and returns an indication of success.string function set(string name, string value)β tries to set the environment variable with the givennameto the givenvalue, and returns an indication of success.
π» COMMANDS π»
Nickle has a whole set of commands you can drop at the top level. here's the menu, bestie:
quitβ exit Nickle. bye girl πquit Eβ exit Nickle with integer status codeE.undefine NAME {,NAME}β remove these names from the system.load Eβ load a file given by the string nameE.library Eβ load a library given by the string nameE. see the discussion of theNICKLEPATHenvironment variable in ENVIRONMENT below, and the discussion ofCommand::library_pathabove.E # Eβ print expr1 in base expr2 .print NAMEβ display a formatted version of the object denoted byNAME. comments and original formatting are lost. ifNAMEis a variable, the type gets printed too, along with the value. sacrifice the formatting for the glow-up β¨edit NAMEβ invoke$EDITORon the named object, and re-incorporate the results of the edit. this is most useful with functions.historyβ display the 10 most recently printed values. you can grab them with$n, wherenis the number shown to the right of the value in this list.history Eβ display theEmost recent history values.history E,Eβ display history values from the first integerEthrough the second.
π DEBUGGER π
When an unhandled exception reaches top level during execution, you get a dash prompt, which means debug mode is active. in this mode, the command-line environment is the one where the unhandled exception got raised. plus you get a bunch of debugging commands:
traceβ get a stack backtrace showing the current state, like the GDBwherecommand.upβ move up the stack (i.e., toward the top-level expression) ala GDB.downβ move down the stack (i.e., toward the current context) ala GDB.doneβ leave debugging mode, abandoning execution. it's over π
In addition, the Debug namespace is scoped in in debugging mode. this is primarily of use in debugging Nickle itself.
collect()β run the garbage collector. take out the trash, bestie ποΈβ¨dump(function)β print the compiled byte code forfunction.
πΏ ENVIRONMENT πΏ
bestie, these are the little environment variables nickle sniffs around for when it wakes up. set them, don't set them, it's your world π
EDITORβ the editor that theeditcommand reaches for, and yes that's the sameeditwe already talked about back in COMMANDS β¨NICKLERCβ where your.nicklercfile lives. nickle will load it at the very beginning of execution, if it can π«ΆHOMEβ ifNICKLERCisn't set, nickle uses this to go find your.nicklercπNICKLEPATHβ a colon-separated path, and every element in it is a directory full of Nickle code. thelibrarycommand and the-lflag (both described above, iykyk) search this path for a filename that matches the one you gave. if this variable isn't set at all, the default library path is/usr/share/nickleπNICKLESTARTβ the filename of the file that gets loaded as a bootstrap when nickle starts up. if this variable isn't set, the default is to load/usr/share/nickle/builtin.5cπ
β¨ EXAMPLES β¨
ok so here's a whole lil demo, taken from the bc manual:
real function exponent(real x) {
real a = 1;
int b = 1;
real s = 1;
int i = 1;
while (1) {
a = a * x;
b = b * i;
real c = a / b;
if (abs(c) < 1e-6)
return s;
s = s + c;
i++;
}
}
that right there defines a function that computes an approximate value of the exponential function e ** x. and then:
for (i = 1; i < 10; i++)
printf ("%g\n", exponent (i));
which prints approximate values of the exponential function for the first ten integers. chef's kiss, honestly π
π VERSION π
this document describes version 1.99.2 of nickle, plus some newer features that snuck in. it was distributed with version "2.108" of nickle. yes, both numbers, they're different things, don't @ me π
π BUGS π
buckle up bestie, this is the part where we're honest:
- go read the discussion of the type of the exponentiation operator
**above. that's the first thing π - due to a difficult-to-remove grammar ambiguity, you cannot use a bare assignment expression inside an array initializer β it gets confused with a structure initializer. like, watch:
> int x = 0;
> (int[*]){x = 1}
-> (int[*]) { x = 1 }
Non array initializer
the workaround is to parenthesize the assignment expression:
> (int[*]){(x = 1)}
[1]{1}
and because this bug is so rare, so hard to fix, and so easy to work around, it is unlikely to be fixed anytime soon. so, coping mechanisms only π
- there are a lot of known bugs with input and output formatting. the obvious stuff works. the other stuff does not. that's it, that's the bug report π
- the semantics of division are unfortunately different from those of C. this is arguably because C is broken in this area β and no, we cannot currently see any obvious fix. C allows automatic implicit coercion of floating to integral types, but this implementation considers that a misfeature π«
- and, uh, the implementation has not been thoroughly tested. no cap. β οΈ
π AUTHOR π
Nickle is the work of Keith Packard <keithp@keithp.com> and Bart Massey <bart_massey@iname.com>. legends, honestly π
Nickle is Copyright 1988-2006 Keith Packard and Bart Massey. All Rights Reserved.
and now the legal bit, because we respect the paperwork π β 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 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.
Except as contained in this notice, the names of the authors or their institutions shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the authors.
and that's a wrap on the whole page!! π this one's the NICKLE(1) man page, dated 2026-03-14, and you made it all the way to the end β obsessed with you fr fr π¦β¨ go write something cute in nickle, bestie, you've got this π
π¦ where this page comes from
- alpine 3.24: nickle-doc 2.108-r0 Β· MIT
- arch rolling: nickle 2.108-1 Β· custom
- epel 10: nickle 2.108-1.el10_3 Β· MIT
- fedora 43: nickle 2.108-1.fc43 Β· MIT
- fedora 43: nickle 2.108-1.fc43 Β· MIT
- fedora 44: nickle 2.108-1.fc44 Β· MIT
- fedora 44: nickle 2.108-1.fc44 Β· MIT
- ubuntu stonking: nickle 2.108 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms