├── .gitignore ├── LICENSE ├── README.md ├── a.c ├── a.h ├── makefile ├── ref ├── a.c ├── a.h └── readme.md └── t.k /.gitignore: -------------------------------------------------------------------------------- 1 | a 2 | m 3 | mm 4 | *.dSYM 5 | a.m* 6 | ref/t 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 atw & regents of kparc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # k/simple 2 | 3 | a tiny k interpreter for educational purposes by arthur whitney 4 | 5 | ## target audience 6 | 7 | the contents of this repository assume readership who have read and 8 | understood [k&r](https://en.wikipedia.org/wiki/The_C_Programming_Language) 9 | at some point in their careers. any experience with using interpreters of 10 | vector languages is beneficial but not necessary. any experience with 11 | implementing language interpreters would definitely be of help. 12 | 13 | ## background 14 | 15 | in early january 2024, a group of motivated individuals asked [arthur whitney](https://en.wikipedia.org/wiki/Arthur_Whitney_(computer_scientist)) 16 | to demonstrate the fundamentals of the design, organization and style of his 17 | method of writing interpreters in c in the most succinct and approachable 18 | way. 19 | 20 | about a day later, arthur offered a tiny interpreter of a toy vector 21 | language, implemented in about 25 lines of c with a small header file. 22 | his code is published in this repository under [/ref](/ref). 23 | 24 | a few days later, the regents of kparc were invited to share their 25 | impressions on this codebase, and optionally provide some additional 26 | commentary. 27 | 28 | our commentary is offered in form of two files `a.[ch]` in the root of 29 | this repository, which serve a number of purposes: 30 | 31 | 1. both files can be seen as two chapters of a small "essay", header 32 | file [a.h](/a.h) being the first chapter, [`a.c`](/a.c) source file being the second. 33 | not only the text seeks to explain the code in reasonable detail, 34 | which is strictly line by line, but also takes a few liberties and 35 | diversions in a bid to elucidate the "bigger picture" behind this tiny piece of code. 36 | 37 | 3. for the ease of comprehension, the narrative has been made 38 | **completely linear**, and hopefully makes for an easy, entertaining and 39 | useful read. to make this possible, the original code had seen some 40 | very minor restructuring and regrouping; however, no refactoring, 41 | no changes to logic or naming conventions have taken place, except 42 | for a handful cosmetic enhancements which have been discussed with 43 | atw and are seen as beneficial to the cause. a number of less trivial one-line 44 | functions (e.g. `e()`) are presented in "exploded view" to simplify discussion 45 | of their control flow and role of individual components. 46 | 47 | although the c code of `k/simple` is formatted to fit on mobile phone screens 48 | in portrait mode, for a more comfortable reading experience we recommend 49 | using professional equipment. 50 | 51 | 5. with help of the included [makefile](/makefile), kparc's "essay" can 52 | be built to any available architecture, e.g. riscv, arm, wasm32, xtensa 53 | or even x86. default make target assumes presence of a recent gcc, clang 54 | or tinyc compiler. 55 | 56 | ## quick start 57 | 58 | ``` 59 | $ cd && git clone https://github.com/kparc/ksimple 60 | $ cd ksimple && make 61 | $ ./a 62 | k/simple (c) 2024 atw/kpc 63 | 2+2 64 | 4 65 | x:!9 66 | y:2+x 67 | x-y 68 | -2 -2 -2 -2 -2 -2 -2 -2 -2 69 | z:x,y 70 | #z 71 | 18 72 | x+!3 73 | Add:59 length 74 | \w 75 | 36 76 | x:y:z:0 77 | \w 78 | 0 79 | ^C 80 | $ vi k.c 81 | ``` 82 | 83 | ## language specification / disclaimer 84 | 85 | *caveat emptor*, manage your expectations. this interpreter is useless for any practical purpose except the stated one, which is education. the language is as follows: 86 | 87 | * although k/simple is a computer language, its grammar is defined in terms we normally use to denote parts of human speech. that is, k expression is composed of **nouns**, **verbs** and **adverbs**. 88 | 89 | * the definition of **verb** is something in between definitions of **operation** and "operator" in mathematics. to make things simple, we define *verb* as an instruction to the interpreter to 90 | do something with exactly one or exactly two operands, or **nouns**. a verb that takes one noun to operate on is called monadic, and verb that takes two nouns is called dyadic. consider the following two 91 | sentences: 92 | 93 | * *shuffle this deck of cards* 94 | * *take three random cards from this deck* 95 | 96 | in linguistics, the structure of the first sentence is known as verb-only predicate, while the second is a verb-plus-direct-object predicate. 97 | in k, we recognize the verb "shuffle" in the first sentence to be monadic, while the second is a dyadic verb "take". the semantics of an action 98 | of a verb is defined by the number of operands given to it, also called "rank" or "valence". this means that monadic `+x` and dyadic `x+y` have 99 | two entirely different meanings. 100 | 101 | > and this precisely why we avoid using terms "operator" and especially "operator overloading" to define what a "verb" is. indeed, two verbs denoted by monadic `+` and dyadic `+` are not overloads 102 | of one another, and although *optically* they use the same **operator** (aimply put, the same _symbol_) they perform two completely different **operations** based on their rank. 103 | and the definition of what an **operation** is in k/simple is mostly identical to its mathematical cousin: it is a well-defined, unambiguous **map** of some inputs to some outputs, 104 | aka *domain* and *range*. while it is tempting to use a more common term "function" here (which is the same as "map" in mathematics), k/simple has ita reasons to define operations 105 | performed by its verbs as **maps**, and not just because it is shorter to type. the main reason is that definitions of "function" can be wildly differing (if not contradicting outright) depending 106 | on whom you ask. with **maps** it proves much easier to get on the same page. 107 | 108 | * a **noun** in k/simple can be either an **atom** (aka scalar value) or a **vector** (aka ordered list of atoms). 109 | 110 | * the one and only type of **atom** formally supported by k/simple is a signed 8-bit integer `-128..127`. 111 | 112 | * a vector of atoms is limited in length to unsigned 8-bit integer, that is no more than 255 items. 113 | 114 | * k/simple supports 6 verbs, `+`, `-`, `!`, `#`, `,` `@` and `|`, all of which have different meanings depending on their *rank*. these meanings are very well documented in [a.c](/a.c) where they are also declared and defined. 115 | 116 | * in fact, k/simple goes an extra mile to implement a number of additional dyadic ops, namely `=`, `~`, `&`, `|` and `*`, which are implemented all at once in one motion by exploring and then generalizing the implementation of `+` verb. the authors hope for the reader will find the accompanying prose particularly useful. 117 | 118 | * an **adverb** is a higher-order function: it takes a verb and modifies its action in some desirable way. k/simple implements one adverb `over`, also known as `fold` and `reduce` in 119 | functional speak. that is, `over` folds a given vector of values and reduces it into a scalar using a given verb. for example, `+/` reads as "plus over" and computes a sum elements of a given vector: 120 | 121 | ``` 122 | +/1,2,3,4,5,6 123 | 21 124 | ``` 125 | 126 | * once `over` is implemented, k/simple can't stand the temptation to show how to implement another staple k adverb, which is `scan`: 127 | 128 | ``` 129 | /vector of factorials from 1 to 5 130 | *\1+!5 131 | 1 2 6 24 120 132 | ``` 133 | 134 | * k expressions are evaluated in a way you're probably not familiar with, which is *right to left*, or, in math speak, *left of right*. this only sounds nonsensical until you get lit: 135 | 136 | ``` 137 | 3+2+1 /take 1, add 2, add 3 138 | 6 139 | 140 | 3*2+1 /take 1, add 2, multiply by 3 141 | 9 142 | ``` 143 | 144 | * This also means that there is only one operator precedence rule to be learned in k/simple: there are no precedence rules to be learned in k/simple. all operators have the same binding strength, including arithmetic. 145 | for example, multiplication doesn't bind stronger than addition, and doesn't get computed first. the lack of precedence is not what we've all learned very early in our careers, but it actually works pretty well. 146 | 147 | * k/simple is simple enough to get away without tokenizer and parser. instead, it accepts user input as a string of up to 99 tokens, and evaluates it token by token strictly left of right (see above). a token in k/simple 148 | is a single character, which can be either a verb `+-!#,@`, a noun `0..9` or a name of a global variable `abc..xyz` which are also treated as nouns. 149 | 150 | * assignment of a value to a global variable is not a verb, and it is not `=`. instead, it is a special case and is `:`. for example: 151 | 152 | ``` 153 | x:4 154 | x:x+1 155 | x 156 | 5 157 | ``` 158 | 159 | * global namespace `a..z` is very useful, and has an unusual feature: you can assign a value to a global in a traditional way `x:2`, 160 | or do it *inline*, as in `x:1+y:2`, which is the same as `y:2` followed by `x:1+y`. one expression, two assignments. 161 | 162 | * k/simple implements simple memory management by refcounting. two handy system commands are implemented, which are useful for refcount debugging: 163 | 164 | * `\w` print current workspace size 165 | * `\v` print global namespace: varname, refcount, length 166 | 167 | * `\\` exits the process. 168 | 169 | * k/simple can run in two modes: interactive or batch. batch mode expects a filename of a k script, e.g.: 170 | 171 | ``` 172 | $ ./a t.k 173 | -1 174 | 0 175 | 0 176 | -1 177 | 1 2 3 4 5 6 7 8 9 178 | 2 179 | 0 2 4 6 8 10 12 14 16 180 | 0 -1 -2 -3 -4 -5 -6 -7 -8 181 | ... 182 | ``` 183 | 184 | ## remarks on style 185 | 186 | * once the basic principles of the presented coding style become apparent, which takes little time for an experienced C programmer (and often even less for an inexperienced one), things like general tendency to avoid explicit `return`, total absence of `switch/case` construct in favour of of ternary cascades and the `$()` macro is likely to raise some eyebrows. however, these decisions are intentional, because k/simple is considered simple enough to be implemented in simpler C. contrary to popular belief, C isn't always so [simple](https://wordsandbuttons.online/so_you_think_you_know_c.html). 187 | 188 | * no less intentional is the hardline policy of keeping vector operands strictly immutable unless they are ready to be disposed of, which is most always the responsibility of a _callee_. indeed, _immutability_ is often seen as a very alien concept by those who are trained to talk to modern computers in imperative mood, even more so to modern compilers. but although it is a valid observation that k/simple sometimes operates on arrays in a less efficient way than it could have, it deliberately omits such optimizations to better emphasize the fact that immutability is an elegant, efficient, and mighty fool-proof idea in a lot of practical scenarios. 189 | 190 | * on related note, `x:y:!9` creates two identical objects in memory, which is not the best idea in real life. a much less naive implementation would be `copy-on-write`, which basically means that `x` and `y` would be referring to the same physical object in memory until one of them gets modified _in place_. this optimization is not implemented since the language provides no means to mutate vectors. 191 | 192 | * it goes without saying that excessive use of C preprocessor amounts to its abuse and results in unportable, "write-only" code ridden with deadly bugs. that said, k/simple also agrees that disciplined and consistent use of `cpp` results in safer, simpler and more idiomatic code compared to a mound of equivalent vanilla C. 193 | 194 | * **"less code less bug"**. this means that a possible knee-jerk reaction to fork this repository and refactor it in the key of `N() -> NEW()` is hardly *new* and is considered by many to be highly counterproductive: indeed, three strokes are definitely *worse* than one under small condition that `N()` always means exactly the same thing, is simpler to memorize, and harder to typo. At the same time, we also acknowledge the fact that tastes differ, and accept the reality in which the excess stroking has become an acquired taste for the vast majority of keyboard operators, which is now inherited and perpetuated by various automated stroke generators. 195 | 196 | ## suggested exercise 197 | 198 | the authors hope that this material enables and inspires further experimentation on reader's own, which can be a very rewarding and fun pastime. for example, one might consider the following toy problems of various degrees of difficulty: 199 | 200 | **i'm too young to die:** 201 | 202 | * inspect verb implementations provided by atw for edge cases, and add checks (which are intentionally omitted) 203 | * implement a few more simple verbs, e.g. dyadic `divide f%x`. 204 | 205 | **hurt me plenty:** 206 | 207 | * extend maximum vector length to `MAX_UINT` 208 | * find and fix at least three segfaults 209 | 210 | **ultraviolence:** 211 | 212 | * change base type from 8bit integer to `long` 213 | * fix tokenizer to support integer numerals greater than 9 and less than 0 214 | * fix tokenizer to support efficient direct vector input (e.g. `42 57 120` instead of `1,2,3,4`) 215 | * ~implement adverb `scan`~ 216 | 217 | **nightmare:** 218 | 219 | * implement a simple parser (e.g. to support quoted strings and parens) 220 | * implement nested vectors, and monadic verb `flip` aka `transpose` 221 | * make vector arithmetic penetrating 222 | * implement copy-on-write 223 | * implement floating point type 224 | * implement functions and local scope 225 | * implement a test suite for your `k` 226 | 227 | 228 | progress takes sacrifice. 229 | 230 | `//:~` 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /a.c: -------------------------------------------------------------------------------- 1 | //!\file k.c \brief bare minimum atw-style k interpreter for learning purposes \copyright (c) 2024 arthur whitney and the regents of kparc \license mit 2 | #include"a.h" //fF[+-!#,@|] atom/vector 1byteint 1bytetoken no(parser tokens ..) cc -Os -oa a.c -w 3 | 4 | //! above is a brief description of k/simple system by atw: 5 | //! he says: he means: 6 | //! fF[+-!#,@] we have 6 verbs [+ - ! # , @ |] in both monadic and dyadic contexts, total of 14 7 | //! (since monadic + and dyadic | are nyi, we actually have 12, feel free to implement f[+]/F[|]) 8 | //! atom/vector k/simple sports atoms and vectors! 9 | //! 1byteint the only supported atom/vector type is 8bit integer, so beware of overflows 10 | //! 1bytetoken input tokenizer is spartan: a token can't be longer than one char 11 | //! no(...) no parser and multichar tokenizer are implemented 12 | //! cc -w .. minimal build instructions (which are much more stringent in provided makefile) 13 | 14 | //!debug 15 | f(wu,O("%lu\n",x)) //!< (w)rite (u)ll: print unsigned long long (e.g. total memory allocation), useful for refcount debugging. 16 | void wg(){i(26,x(U[i],$(!ax,O("%c[%d] %d\n",i+97,nx,rx))))} //!< dump global namespace: varname, refcount, length (also useful for refcount debugging). 17 | 18 | //!printing facilities 19 | f(w,write(1,ax?(c*)&x:sx,ax?1:strlen(sx))) //!< (w)rite to stdout: if x is an atom, print its decimal value, otherwise print x as ascii string. 20 | static c pb[12]; //!< temporary string (b)uffer for formatting vector items. it's ok to declare it globally, since we only have one thread. 21 | f(si,sprintf(pb,"%d ",(int)(128>x?x:x-256));pb) //!< (s)tring from (i)nteger: format a given atom x as decimal in range (-128..127) into buffer b using sprintf(3). 22 | f(wi,w(si(x))) //!< (w)rite (i)nteger: format x and (w)rite it to stdout. 23 | f(W,Q(x)$(ax,wi(x))i(nx,wi(xi))w(10)) //!< pretty print x: if x is an atom, format and print it, otherwise print all items of vector x, 24 | //!< separated by space. terminate output by a newline aka ascii 10. 25 | G(err,w(f);w(58);wi(x);w(y);w(10);Q) //!< (err)or: print name of the c (f)unction where error occured, line number and error msg, return Q. 26 | 27 | //!malloc 28 | f(a,y(x+2,WS+=x;c*s=malloc(y);*s++=0;*s++=x;s)) //!< (a)llocate x bytes of memory for a vector of length x plus two extra bytes for preamble, set refcount to 0 29 | //!< and vector length to x in the preamble, and return pointer to the 0'th element of a new vector \see a.h type system 30 | f(_a,WS-=nx;free(sx-2);0) //!< release memory allocated for vector x. 31 | G(m,(u)memcpy((c*)x,(c*)y,f)) //!< (m)ove: x and y are pointers to source and destination, f is number of bytes to be copied from x to y. 32 | //!< \note memcpy(3) assumes that x/y don't overlap in ram, which in k/simple they can't, but \see memmove(3) 33 | //!memory management 34 | f(r_,ax?x:(++rx,x)) //!< increment refcount: if x is an atom, return x. if x is a vector, increment its refcount and return x. 35 | f(_r,ax?x //!< decrement refcount: if x is an atom, return x. 36 | :rx?(--rx,x) //!< if x is a vector and its refcount is greater than 0, decrement it and return x. 37 | :_a(x)) //!< if refcount is 0, release memory occupied by x and return 0. 38 | 39 | //!monadic verbs 40 | f(foo,_r(x);Qz(1);Q)F(Foo,_r(x);Qz(1);Q) //!< (foo)bar is a dummy monadic verb: for any x, throw nyi error and return error code Q. 41 | 42 | f(sub,ax?(c)-x:_x(N(nx,-xi))) //!< monadic (sub)tract is also known as (neg)ation, or -x: if x is atom, return its additive inverse. 43 | //!< if x is a vector, return a new vector same as x only with sign of its every element flipped. 44 | 45 | f(til,Qr(!ax)(N(x,i))) //!< monadic til is !x aka her majesty apl iota. for a given atom x, it returns a vector 46 | //!< of x integers from 0 to x-1. if x is not an atom, til throws a rank error. 47 | 48 | f(cnt,Qr(ax)nx) //!< monadic (c)ou(nt) is #x. it returns the length of a given vector and throws rank error for atoms. 49 | 50 | f(cat,Qr(!ax)N(1,x)) //!< monadic (cat)enate is (enl)ist, or comma-x: wraps a given atom x into a new vector of length 1 whose 51 | //!< only item holds the value of that atom. if x is a vector, enlist will throw a rank error. 52 | 53 | f(rev,Qr(ax)_x(N(nx,sx[nx-i-1]))) //!< monadic (rev)erse is |x and simply returns a mirror copy of vector x. 54 | 55 | //!dyadic verbs 56 | F(Add, //!< dyadic f+y is add. operands can be both atoms and verbs, ie. a+a, a+v, v+a, v+v are all valid. 57 | ax?af?(c)(f+x) //!< case a+a: if (f,x) are atoms, compute their sum and handle possible overflows by downcasting it to c. 58 | :Add(x,f) //!< case v+a: if f is a vector and x is an atom, make a recursive call with operands swapped, i.e. a+v. 59 | :af?_x(N(nx,f+xi)) //!< case a+v: if f is an atom, return a new vector constructed by adding f to every element of x. 60 | :nx-nf?(_x(_f(Ql()))) //!< case v+v: if (f,x) are vectors, first make sure they are of the same length, throw length error if not. 61 | :_f(_x(N(nx,xi+fi)))) //!< if lengths are the same, return a new vector holding their pairwise sum. 62 | //!< \note by convention, atwc uses x-y for inequality test, which has the same effect as nx!=nf. 63 | 64 | F(Sub,Add(f,sub(x))) //!< dyadic f-x is subtract. since we already have Add() and sub(), we get Sub() for free by negating x. 65 | F(Mod,Qr(!f||!af)ax?x%f:_x(N(nx,xi%f))) //!< dyadic f!x is x (mod)ulo f, aka remainder operation. f must be an non-zero atom, x can be anything. 66 | F(Tak,Qr(!af)_f(N(f,ax?x:sx[i%nx]))) //!< dyadic f#x is (tak)e, which has two variants based on the type of right operand (left must be atom): 67 | //!< if x is a vector, return first f items of x. if f exceeds the size of x, wrap around from the start. 68 | //!< if x is an atom, return a vector of length f filled with x. 69 | 70 | F(Cat, //!< dyadic f,x is (cat)enate: a) join two vectors b) join an atom to vector c) make a vector from two atoms. 71 | f=af?cat(f):f; //!< if f is an atom, enlist it \see cat() 72 | x=ax?cat(x):x; //!< ditto for x 73 | u r=a(nf+nx); //!< (a)llocate array r long enough to hold f and x. 74 | m(nx,r+nf,x); //!< (m)ove contents of x to the end of r. 75 | m(nf,r,f);_r(x);_r(f);r) //!< (m)ove contents of f to the beginning of r, try to release f and x, and return pointer to r. 76 | 77 | F(At,Qr(af) //!< dyadic f@x is "needle at x in the haystack f" and has two modes based on the type of x (f must be a vector): 78 | ax?x>nf?Ql():sf[x] //!< if x is an atom, return the x'th item of f. 79 | :_x(_f(N(nx,sf[xi])))) //!< if x is a vector, return a vector containg items from f at indices listed in x. 80 | //!< \note that the second mode currently doesn't perform the boundary check, fell free to implement it! 81 | 82 | f(at,At(x,0)) //!< monadic @x is simply (f)ir(st): return the head element of x, or throw a rank error if x is an atom. 83 | 84 | //! note how Sub() and at() are implemented in terms of other verbs, and especially how Add() cuts corners by calling itself with operands swapped. 85 | //! in fact, we can use Add() as a template to implement of a whole bunch of additional dyadic verbs, provided that they also hold commutative property. 86 | //! so let's generalize Add(), first in pseudocode: 87 | 88 | //! function fn(f,x) implementing a commutative OP: 89 | //! 1. if both operands f and x are atoms, return (f) OP (x) 90 | //! 2. if f is an atom and x is a vector, return fn(x,f) 91 | //! 3. if both operands are vectors, ensure they are the same length. 92 | //! 4. allocate a (r)esult vector of the same length as x, then: 93 | //! 5. depending on type of x, each i'th element of r becomes either: 94 | //! 5.1 (atom x) OP (i'th element of f) 95 | //! 5.2 (i'th element of x) OP (i'th element of f) 96 | //! 6. finally, attempt to release memory of f and x, and return r. 97 | 98 | #define op(fn,OP) F(fn,ax?af?(c)(f OP x):fn(x,f):af?_x(N(nx,f OP xi)):_f(_x(nx-nf?Ql():N(nx,sx[i] OP sf[i])))) //!< above pseudocode expressed as a C macro. 99 | op(Eql,==)op(Not,!=)op(And,&)op(Or,|)op(Prd,*) //!< et voila, we have definitions of dyadic equal, not equal, and, or and product for free. 100 | 101 | //!verb dispatch 102 | char*V=" +-!#,@=~&|*"; //!< V is an array of tokens of all supported k verbs. 0'th item (space) stands for "not a verb". 103 | u(*f[])(u )={0,foo,sub,til,cnt,cat,at,foo,foo,foo,rev,foo}, //!< f[] is an array of pointers to c functions which implement monadic versions of k verbs listed in V. 104 | (*F[])(u,u)={0,Add,Sub,Mod,Tak,Cat,At,Eql,Not,And,Or, Prd}; //!< F[] is ditto for dyadic versions of verbs listed in V. 105 | // V: + - ! # , @ = ~ & | * 106 | 107 | //!adverbs 108 | F(Ovr,ax?x:_x(r(*sx,i(nx-1,r=F[f](r,sx[i+1]))))) //!< adverb over: recursively fold all elements of vector x using dyadic verb f going left to right. 109 | F(Scn,ax?x:_x(r(a(nx),*sr=*sx;i(nx-1,sr[i+1]=F[f](sr[i],sx[i+1]))))) //!< adverb scan: same as over, but produces a vector of intermediate results. 110 | 111 | //!adverb dispatch 112 | char*AV=" /\\";u(*D[])(u,u)={0,Ovr,Scn}; //!< AV[]/D[] is the same as V[]/F[], only for adverbs. 113 | 114 | //!globals, verbs, nouns, adverbs 115 | f(g,x>='a'&&x<='z') //!< is x a valid (g)lobal variable identifier? 116 | F(ag,y(U[f],!ay?_a(y):x;r_(U[f]=x))) //!< (a)ssign (g)lobal: release no longer referenced global object at U[f], and replace it with object x. 117 | f(v,(strchr(V,x)?:V)-V) //!< is x a valid (v)erb from V? if so, return its index, otherwise return 0. 118 | //!< \note rarely seen ternary form x?:y, which is just a shortcut for x?x:y in c. 119 | f(d,(strchr(AV,x)?:AV)-AV) //!< same as v() for a(d)verbs. 120 | f(n,10>x-48 //!< is x a (n)oun? valid nouns are digits 0..9 and lowercase varnames a..z. 121 | ?x-48 //!< if x is a digit, e.g. '7', return its decimal value. 122 | :g(x)?r_(U[x-97]) //!< if x is a varname, e.g. 'a', return its value from U[26] and increment its refcount. 123 | :Q) //!< ..anything else is an error. 124 | 125 | //!fio 126 | static char*l;u mx=99;FILE*t; //!< l is a line buffer, mx is its max length, t is input stream handle. 127 | us(rl,l=l?:malloc(mx); //!< (r)ead(l)ine: reset mx to max line length, allocate buffer l of size mx if not yet allocated. 128 | P(!s,l[read(0,l,mx)-1]=0) //!< (r)ead: if no filename s is given, read line from stdin up to mx bytes, clamp trailing \n and return 0. 129 | t=t?:fopen(s,"r");Qs(!t,s) //!< open file s for reading if not yet open, throw error in case of problems. 130 | r(getline(&l,&mx,t), //!< read next line from stream t into l up to mx bytes. 131 | r=r //write(2) 4 | #include //strlen memcpy strchr 5 | #include //malloc free 6 | #include //(s)printf 7 | #include //open 8 | 9 | //!minimal type system 10 | typedef unsigned char c;typedef unsigned long u; //!< type c is a shorthand for byte, but type u requires more words: 11 | //!< although u is formally defined as unsigned 64bit integer, 12 | //!< it is in fact an *opaque type*, i.e. k/simple uses it for everything, 13 | //!< e.g. u can be: an atom (-128..127), a vector pointer or a function pointer. 14 | //!< since k/simple only supports a single atom type (8bit integer), 15 | //!< atoms are expensive, since 7 out of 8 bytes of u are wasted. however, 16 | //!< vectors of atoms do not incur this tax, because they are allocated with 17 | //!< a small 2-byte preamble: 18 | 19 | //!< k/simple memory layout for vectors: 20 | //!< vector x: |r|n|aaaaaaaaaaa.. 21 | //!< ^ ^ ^ 22 | //!< | | pointer to x points to its 0'th item, all a's are 8bit wide. 23 | //!< | byte prior to 0'th item of x holds the length of x (max 255 items). 24 | //!< byte prior to vector length holds the (r)eference count. 25 | 26 | //!minimal atwc 27 | #define R return //!< unclutter 28 | #define O printf //!< classic atw debug 29 | #define $(a,b) if(a)b;else //!< handy shorthand for if-then-else. beware of dangling else! 30 | #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} //!< (i)terate: execute (e)xpression n times, loop counter i is accessible from e 31 | #define P(b,e) if(b)return _(e); //!< predicate aka panic: if b evaluates to true, return result of e. 32 | 33 | #define _(e) ({e;}) //!< isolate expression e in its own lexical scope and clamp it with ; 34 | //!< note the outer parens, which is a very powerful c trick: they turn _(e) into a so called 35 | //!< r-value, which basically means we can do x=_(e) for as long as e evaluates to or returns 36 | //!< at least anything at all, i.e. not void. this macro is fundamental to k/simple implementation. 37 | //!< l-/r-values are fundamental to c, good mnemonic is (l)eft/(r)ight although not 100% precise. 38 | 39 | //!functions 40 | #define _u(f,e,x...) u f(x){R(u)_(e);} //!< generic function definition template: f name, x args, e body, all functions return some u 41 | #define f(g,e) _u(g,e,u x) //!< define a monadic function g: takes arg x of type u and returns some u, e is body 42 | #define F(g,e) _u(g,e,u f,u x) //!< define a dyadic function g: takes args f and x of type u, returns some u, or: 43 | #define G(g,e) _u(g,e,u f,u x,u y) //!< define an adverb g: takes a pointer to a verb f, x and y are operands, returns some u (nyi) 44 | #define us(f,e) _u(f,e,c*s) //!< define a function f which takes a string s as its only argument, e is body 45 | 46 | //!accessors for x 47 | #define ax (256>x) //!< is x an atom? (atoms are 0..255, x presumed to be of type u) 48 | #define sx ((c*)x) //!< reinterpret x as char pointer (ie as string or byte vector) 49 | #define xi (nx>i?sx[i]:0) //!< return i'th element of vector x or 0 if i is out of bounds 50 | #define nx sx[-1] //!< length of vector x (second byte of the preamble preceding the actual data \see typedef u) 51 | #define rx sx[-2] //!< refcount of vector x (first byte of the preamble preceding the actual data \see typedef u) 52 | #define x(a,e) _(u x=a;e) //!< whatever a is, call it x and then execute some expression e in a separate lexical scope 53 | #define y(a,e) _(u y=a;e) //!< (ditto for y) idea of x()/y() is to make use of standard accessors eg ax xi etc 54 | #define r(a,e) _(u r=a;e;r) //!< (r)esult: same as x()/y(), but returns r. 55 | 56 | //!accessors for r 57 | #define sr x(r,sx) //!< reinterpret r as char pointer (ie as string or byte vector) \see sx 58 | #define nr x(r,nx) //!< length of vector r 59 | #define ri sr[i] //!< get i'th element of vector r 60 | 61 | //!accessors for y 62 | #define ay x(y,ax) //!< is y an atom? 63 | 64 | //!accessors for f 65 | #define af x(f,ax) //!< is f an atom? 66 | #define nf x(f,nx) //!< length of vector f 67 | #define rf x(f,rx) //!< refcount of vector f 68 | #define sf x(f,sx) //!< reinterpret r as char pointer (ie as string or byte vector) 69 | #define fi x(f,xi) //!< return i'th element of vector f or 0 if i is out of bounds 70 | 71 | //!error handling 72 | static const u Q=128; //!< magic numer for error 73 | #define Q(e) if(Q==(e))R Q; //!< if some e evaluates to Q, return error 74 | #define Qe(s) err((u)__FUNCTION__,(u)__LINE__,(u)s) //!< shortcut for err(): pass function name and error string s 75 | #define Qs(e,s) if(e)R Qe(s); //!< error template: if some e evaluates to true, throw an error 76 | #define Qr(e) Qs(e,"rank") //!< if some e evaluates to true, throw rank error 77 | #define Qz(e) Qs(e,"nyi") //!< ..not yet implemented 78 | #define Qd(e) Qs(e,"domain") //!< domain error 79 | #define Qp() Qs(Q==x,"parse") //!< parse error 80 | #define Ql() Qe("length") //!< length error 81 | 82 | //!all of the above coming together: 83 | #define N(n,e) r(a(n),i(n,ri=e)) //!< this macro is the foundation of k/simple and is ubiquitous in a.c. it reads: 84 | //!< (a)llocate a (N)ew vector r of le(n)gth n, then apply some (e)xpression to it, then return r. 85 | //!< here are few basic examples to illustrate what "apply" means: 86 | 87 | //!< u x=N(8,2*i); //!< x is (0,2,4,6,8,10,12,14) 88 | //!< u f=N(8,pow(2,i)); //!< f is (1,2,4,8,16,32,64,128,256) 89 | //!< u y=N(8,xi==fi); //!< y is (0,1,1,0,0,0,0,0) 90 | 91 | #define _f(e) r(e,_r(f)) //!< execute some (e)xpression, then decrement refcount of f, potentially releasing it, and (r)eturn f. 92 | #define _x(e) r(e,_r(x)) //!< ..same for x. 93 | 94 | //!< \note it would make sense to redefine u as an alias for uintptr_t to reduce 95 | //!< pressure on 32bit targets, e.g. for wasm32 there is no reason for u to 96 | //!< be 8 bytes wide. uintptr_t is basically sizeof(void*) and requires . 97 | //!< for simplicity, we keep u as ull for now. 98 | 99 | static u WS=0; //!< WS workspace size (current number of bytes allocated). 100 | static u U[26]; //!< global namespace: array of values of variables abc..xyz. in c, global arrays are initalized with zeroes. 101 | 102 | #define BA "k/simple (c) 2024 atw/kpc\n" //!< startup banner 103 | 104 | 105 | //:~ 106 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | O=-O0 -g -Werror -Wno-pointer-sign 2 | all: 3 | @cc $O -oa a.c 4 | 5 | t: 6 | @tcc $O -oa a.c 7 | -------------------------------------------------------------------------------- /ref/a.c: -------------------------------------------------------------------------------- 1 | #include"a.h"//fF[+-!#,@] atom/vector 1byteint 1bytetoken no(memmanage parser tokens ..) clang-13 -Os -oa a.c -w 2 | #define r(n,e) _(u r=a(n);i(n,ri=e)r) 3 | f(w,write(1,ax?(u)&x:x,ax?1:strlen(x))) 4 | c b[12]; 5 | f(si,sprintf(b,"%d ",128>x?x:x-256);b) 6 | f(wi,w(si(x))) 7 | f(w_,$(ax,wi(x))i(nx,wi(xi))w(10)) 8 | 9 | F(err,w(f);w(58);w(x);w(10);96) 10 | G(m,(u)memcpy((void*)x,(void*)y,f)) 11 | f(a,c*s=malloc(x+1);*s++=x;(u)s) 12 | f(foo,Qz(1)0) 13 | f(sub,ax?(c)-x:r(nx,-xi)) 14 | f(ind,Qr(!ax)r(x,i)) 15 | f(cnt,Qr(ax)nx) 16 | f(cat,Qr(!ax)r(1,x)) 17 | F(Add,ax?af?f+x:Add(x,f):r(nx,(af?f:fi)+xi)) 18 | F(Sub,Add(f,sub(x))) 19 | F(Ind,Qr(!f)r(nx,xi%f)) 20 | F(Cnt,Qr(!af)r(f,ax?x:xi)) 21 | F(Cat,f=af?cat(f):f;x=ax?cat(x):x;u r=a(nf+nx);m(nx,r+nf,x);m(nf,r,f)) 22 | F(At,Qr(af)ax?sf[x]:r(nx,sf[xi]))f(at,At(x,0)) 23 | char*V=" +-!#,@";u(*f[])()={0,foo,sub,ind,cnt,cat,at},(*F[])()={0,Add,Sub,Ind,Cnt,Cat,At},U[26]; 24 | f(v,(strchr(V,x)?:V)-V) 25 | f(n,10>x-48?x-48:U[x-97]) 26 | us(e,u i=*s++;v(i)?x(e(s),Q(x)f[v(i)](x)):x(n(i),*s?y(e(s+1),Q(y)F[v(*s)](x,y)):x)) 27 | int main(){char s[99];while(1)if(w(32),s[read(0,s,99)-1]=0,*s)w_(e(s));} 28 | -------------------------------------------------------------------------------- /ref/a.h: -------------------------------------------------------------------------------- 1 | typedef unsigned char c;typedef unsigned long u;u Q=96; 2 | #define $(a,b) if(a)b;else 3 | #define ax (256>x) 4 | #define xi (nx>i?sx[i]:0) 5 | #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} 6 | #define _(e) ({e;}) 7 | #define _u(f,e,x...) u f(x){return({e;});} 8 | #define nx sx[-1] 9 | #define sx ((c*)x) 10 | #define x(a,e) _(u x=a;e) 11 | #define y(a,e) _(u y=a;e) 12 | #define f(g,e) _u(g,e,u x) 13 | #define F(g,e) _u(g,e,u f,u x) 14 | #define G(g,e) _u(g,e,u f,u x,u y) 15 | #define us(f,e) _u(f,e,c*s) 16 | #define Q(e) if(Q==(e))return Q; 17 | #define Qr(e) if(e){return err((u)__func__,(u)"rank");} 18 | #define Qz(e) if(e){return err((u)__func__,(u)"nyi");} 19 | #define sr x(r,sx) 20 | #define ri sr[i] 21 | 22 | #define af x(f,ax) 23 | #define nf x(f,nx) 24 | #define sf x(f,sx) 25 | #define fi x(f,xi) 26 | 27 | -------------------------------------------------------------------------------- /ref/readme.md: -------------------------------------------------------------------------------- 1 | # 2024.01.18 (#3) 2 | ## a.h 3 | ```c 4 | typedef char*s,c;s Q=(s)128; 5 | #define _(e...) ({e;}) 6 | #define x(a,e...) _(s x=a;e) 7 | #define $(a,b) if(a)b;else 8 | #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} 9 | 10 | #define Q(e) if(Q==(e))return Q; 11 | #define Qs(e,s) if(e)return err(__func__,s); 12 | #define Qz(e) Qs(e,"nyi") 13 | #define Qr(e) Qs(e,"rank") 14 | #define Qd(e) Qs(e,"domain") 15 | 16 | #define _s(f,e,x...) s f(x){return _(e);} 17 | #define _i(f,e) _s(f,e,c x) 18 | #define f(f,e) _s(f,e,s x) 19 | #define F(f,e) _s(f,e,s a,s x) 20 | 21 | #define ax (256>x) 22 | #define ix (c)x 23 | #define rx x[-2] 24 | #define nx x[-1] 25 | #define xi x[i] 26 | 27 | #define aa x(a,ax) 28 | #define ia x(a,ix) 29 | #define na x(a,nx) 30 | 31 | #define _a(e) r(e,_r(a)) 32 | #define _x(e) r(e,_r(x)) 33 | #define r(a,e) _(s r=a;e;r) 34 | #define s(e) (s)(255&(e)) 35 | 36 | #define oo w("oo\n") 37 | ``` 38 | ## a.c 39 | ```c 40 | long write(),read(),strlen();void*malloc(),*memchr(),*memcpy(),*strchr();void free(); 41 | #define A(c) ((s)memchr(a,c,na)?:a+na)-a 42 | #include"a.h"//fF[+-!#,@] atom/vector 1byte(int/token) clang-13 -Os -oa a.c -w 43 | _i(m,s a=malloc(2+x);*a++=0;*a++=x;a)f(_m,free(x-2);0)f(r_,ax?x:(++rx,x))f(_r,ax?x:rx?(--rx,x):_m(x)) 44 | f(w,write(1,ax?&x:x,ax?1:strlen(x));x)F(err,w(a);w((s)58);w(x);w((s)10);Q) 45 | #define n(n,e) r(m(n),i(n,r[i]=e)) 46 | f(z,Qr(!ax)0>ix?n(-ix,-ix-1-i):n(ix,i)) 47 | c b[6]="???? ";_i(wi,c n=0>x;x=n?-x:x;s s=b+4;do*--s=48+x%10;while(x/=10);w(n?(*--s='-',s):s))f(W,Q(x)$(ax,wi(ix))i(nx,wi(xi))w((s)10);x) 48 | f(qz,Qz(1)x) 49 | f(srt,Qz(1)x) 50 | f(uni,Qz(1)x) 51 | F(Cut,Qz(1)x) 52 | F(Drp,Qz(1)x) 53 | #define g(a,v) ax?s(a):_x(n(nx,v)) 54 | f(not,g(!ix,!xi))f(sub,g(-ix,-xi)) 55 | F(At,Qr(aa)_a(g(a[ix],a[xi])))F(_A,Qr(aa)_a(g(A(ix),A(xi))))F(Z,Qr(!aa)Qd(1>ia)g(ix%ia,xi%ia)) 56 | #define G(f,o) F(f,ax?aa?s(ia o ix):Ltn==f?f(sub(x),sub(a)):f(x,a):_a(_x(n(nx,(aa?ia:a[i])o xi)))) 57 | G(Ltn,<)G(Eql,==)G(Not,!=)G(Sum,+)G(Prd,*)G(And,&)G(Or,|)f(cat,Qr(!ax)n(1,ix)) 58 | F(Cat,a=aa?cat(a):a;x=ax?cat(x):x;s r=m(na+nx);_x(memcpy(r+na,x,nx));_a(memcpy(r,a,na))) 59 | f(at,At(x,0))f(rev,Qr(ax)At(x,z(s(-nx))))f(cnt,Qr(ax)_x(s(nx))) 60 | F(Tak,Qr(!aa||ax)Qd(0>ia||ia>nx)At(x,z(a)))F(Sub,Sum(a,sub(x)))F(Mtn,Ltn(x,a)) 61 | #define v(e) (((s)strchr(V,e)?:V)-V) 62 | s U[26],V=" +-*&|<>=~!@?#_^,", 63 | // + - * & | < > = ~ ! @ ? # _ ^ , 64 | (*f[])()={0,qz ,sub,qz ,qz,rev,qz ,qz, qz ,not,z,at,uni,cnt,qz ,srt,cat}, 65 | (*F[])()={0,Sum,Sub,Prd,And,Or,Ltn,Mtn,Eql,Not,Z,At,_A ,Tak,Drp,Cut,Cat}; 66 | _i(u,10u>x-48?x-48:26u>x-97?r_(U[x-97]):0) 67 | f(e,s z=x;c i=*z++;!*z?u(i):v(i)?x(e(z),Q(x)f[v(i)](x)):x(e(z+1),Q(x)58==*z?U[i-97]=r_(x):_(c f=v(*z);Qd(!f)F[f](u(i),x)))) 68 | int main(){c b[99];while(1)if(w((s)32),b[read(0,b,99)-1]=0,*b)58==b[1]?e(b):W(e(b));} 69 | ``` 70 | # 2024.01.18 (#2) 71 | ## a.h 72 | ```c 73 | typedef char*s,c;s Q=(s)128; 74 | #define _(e...) ({e;}) 75 | #define x(a,e...) _(s x=a;e) 76 | #define $(a,b) if(a)b;else 77 | #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} 78 | 79 | #define Q(e) if(Q==(e))return Q; 80 | #define Qs(e,s) if(e)return err(__func__,s); 81 | #define Qr(e) Qs(e,"rank") 82 | #define Qd(e) Qs(e,"domain") 83 | #define Qz(e) Qs(e,"nyi") 84 | 85 | #define _s(f,e,x...) s f(x){return _(e);} 86 | #define _i(f,e) _s(f,e,c x) 87 | #define f(f,e) _s(f,e,s x) 88 | #define F(f,e) _s(f,e,s a,s x) 89 | 90 | #define ax (256>x) 91 | #define ix (c)x 92 | #define rx x[-2] 93 | #define nx x[-1] 94 | #define xi x[i] 95 | 96 | #define aa x(a,ax) 97 | #define ia x(a,ix) 98 | #define na x(a,nx) 99 | 100 | #define oo w("oo\n") 101 | ``` 102 | ## a.c 103 | ```c 104 | #define _a(e) r(e,_r(a)) 105 | #define _x(e) r(e,_r(x)) 106 | #define r(a,e) _(s r=a;e;r) 107 | #define n(n,e) r(m(n),i(n,r[i]=e)) 108 | #define s(e) (s)(255&(e)) 109 | long write(),read(),strlen();void*malloc(),*memchr(),*memcpy(),*strchr();void free(); 110 | #define A(c) ((s)memchr(a,c,na)?:a+na)-a 111 | #include"a.h"//fF[+-!#,@] atom/vector 1byte(int/token) clang-13 -Os -oa a.c -w 112 | _i(m,s a=malloc(2+x);*a++=0;*a++=x;a)f(_m,free(x-2);0)f(r_,ax?x:(++rx,x))f(_r,ax?x:rx?(--rx,x):_m(x)) 113 | f(w,write(1,ax?&x:x,ax?1:strlen(x));x)F(err,w(a);w((s)58);w(x);w((s)10);Q) 114 | f(ind,Qr(!ax)0>ix?n(-ix,-ix-1-i):n(ix,i)) 115 | c b[6]="???? ";_i(wi,c n=0>x;x=n?-x:x;s s=b+4;do*--s=48+x%10;while(x/=10);w(n?(*--s='-',s):s))f(W,Q(x)$(ax,wi(ix))i(nx,wi(xi))w((s)10);x) 116 | f(qz,Qz(1)x) 117 | f(srt,Qz(1)x) 118 | f(uni,Qz(1)x) 119 | F(Cut,Qz(1)x) 120 | F(Drp,Qz(1)x) 121 | #define g(a,v) ax?s(a):_x(n(nx,v)) 122 | f(not,g(!ix,!xi))f(sub,g(-ix,-xi)) 123 | F(At,Qr(aa)_a(g(a[ix],a[xi])))F(_A,Qr(aa)_a(g(A(ix),A(xi))))F(Ind,Qr(!aa)Qd(1>ia)g(ix%ia,xi%ia)) 124 | #define G(f,o) F(f,ax?aa?s(ia o ix):Ltn==f?f(sub(x),sub(a)):f(x,a):_a(_x(n(nx,(aa?ia:a[i])o xi)))) 125 | G(Ltn,<)G(Eql,==)G(Not,!=)G(Sum,+)G(Prd,*)G(And,&)G(Or,|)f(cat,Qr(!ax)n(1,ix)) 126 | F(Cat,a=aa?cat(a):a;x=ax?cat(x):x;s r=m(na+nx);_x(memcpy(r+na,x,nx));_a(memcpy(r,a,na))) 127 | f(at,At(x,0))f(rev,Qr(ax)At(x,ind(s(-nx))))f(cnt,Qr(ax)_x(s(nx))) 128 | F(Tak,Qr(!aa||ax)Qd(0>ia||ia>nx)At(x,ind(a)))F(Sub,Sum(a,sub(x)))F(Mtn,Ltn(x,a)) 129 | #define v(e) (((s)strchr(V,e)?:V)-V) 130 | s U[26],V=" +-*&|<>=~!@?#_^,", 131 | (*f[])()={0,qz ,sub,qz ,qz,rev,qz ,qz, qz ,not,ind,at,uni,cnt,qz ,srt,cat}, 132 | (*F[])()={0,Sum,Sub,Prd,And,Or,Ltn,Mtn,Eql,Not,Ind,At,_A ,Tak,Drp,Cut,Cat}; 133 | _i(u,10u>x-48?x-48:26u>x-97?r_(U[x-97]):0) 134 | f(e,s z=x;c i=*z++;!*z?u(i):v(i)?x(e(z),Q(x)f[v(i)](x)):x(e(z+1),Q(x)58==*z?U[i-97]=r_(x):_(c f=v(*z);Qd(!f)F[f](u(i),x)))) 135 | int main(){c b[99];while(1)if(w((s)32),b[read(0,b,99)-1]=0,*b)58==b[1]?e(b):W(e(b));} 136 | ``` 137 | # 2024.01.18 138 | ## a.h 139 | ```c 140 | typedef char*s,c;s Q=(s)128; 141 | #define _(e...) ({e;}) 142 | #define x(a,e...) _(s x=a;e) 143 | #define $(a,b) if(a)b;else 144 | #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} 145 | 146 | #define Q(e) if(Q==(e))return Q; 147 | #define Qs(e,s) if(e)return err(__func__,s); 148 | #define Qr(e) Qs(e,"rank") 149 | #define Qd(e) Qs(e,"domain") 150 | #define Qz(e) Qs(e,"nyi") 151 | 152 | #define _s(f,e,x...) s f(x){return _(e);} 153 | #define _i(f,e) _s(f,e,c x) 154 | #define f(f,e) _s(f,e,s x) 155 | #define F(f,e) _s(f,e,s a,s x) 156 | 157 | #define ax (256>x) 158 | #define ix (c)x 159 | #define nx x[-1] 160 | #define xi x[i] 161 | 162 | #define aa x(a,ax) 163 | #define ia x(a,ix) 164 | #define na x(a,nx) 165 | 166 | #define oo w("oo\n") 167 | ``` 168 | ## a.c 169 | ```c 170 | #define s(e) (s)(255&(e)) 171 | #include"a.h"//fF[+-!#,@] atom/vector 1byte(int/token) clang-13 -Os -oa a.c -w 172 | long write(),read(),strlen();void*malloc(),*memchr(),*memcpy(),*strchr(); 173 | f(w,write(1,ax?&x:x,ax?1:strlen(x));x)F(err,w(a);w((s)58);w(x);w((s)10);Q) 174 | c b[6]="???? ";_i(wi,c n=0>x;x=n?-x:x;s s=b+4;do*--s=48+x%10;while(x/=10);w(n?(*--s='-',s):s))f(W,Q(x)$(ax,wi(ix))i(nx,wi(xi))w((s)10);x) 175 | #define r(n,e) _(s r=m(n);i(n,r[i]=e)r) 176 | _i(m,s a=malloc(1+x);*a++=x;a)f(ind,Qr(!ax)0>ix?r(-ix,-ix-1-i):r(ix,i)) 177 | f(qz,Qz(1)x) 178 | f(srt,Qz(1)x) 179 | f(uni,Qz(1)x) 180 | F(Cut,Qz(1)x) 181 | F(Drp,Qz(1)x) 182 | #define A(c) ((s)memchr(a,c,na)?:a+na)-a 183 | #define g(a,v) ax?s(a):r(nx,v) 184 | f(not,g(!ix,!xi))f(sub,g(-ix,-xi))F(At,Qr(aa)g(a[ix],a[xi]))F(_A,Qr(aa)g(A(ix),A(xi)))F(Ind,Qr(!aa)Qd(1>ia)g(ix%ia,xi%ia)) 185 | #define G(f,o) F(f,ax?aa?s(ia o ix):Ltn==f?f(sub(x),sub(a)):f(x,a):r(nx,(aa?ia:a[i])o xi)) 186 | G(Ltn,<)G(Eql,==)G(Not,!=)G(Sum,+)G(Prd,*)G(And,&)G(Or,|) 187 | f(cat,Qr(!ax)r(1,ix))F(Cat,a=aa?cat(a):a;x=ax?cat(x):x;s r=m(na+nx);memcpy(r+na,x,nx);memcpy(r,a,na)) 188 | f(at,At(x,0))f(rev,Qr(ax)At(x,ind(s(-nx))))f(cnt,Qr(ax)s(nx)) 189 | F(Tak,Qr(!aa||ax)Qd(0>ia||ia>nx)At(x,ind(a)))F(Sub,Sum(a,sub(x)))F(Mtn,Ltn(x,a)) 190 | #define v(e) (((s)strchr(V,e)?:V)-V) 191 | s U[26],V=" +-*&|<>=~!@?#_^,", 192 | (*f[])()={0,qz ,sub,qz ,qz,rev,qz ,qz, qz ,not,ind,at,uni,cnt,qz ,srt,cat}, 193 | (*F[])()={0,Sum,Sub,Prd,And,Or,Ltn,Mtn,Eql,Not,Ind,At,_A ,Tak,Drp,Cut,Cat}; 194 | _i(n,10u>x-48?x-48:26u>x-97?U[x-97]:0) 195 | f(e,s z=x;c i=*z++;!*z?n(i):v(i)?x(e(z),Q(x)f[v(i)](x)):x(e(z+1),Q(x)58==*z?U[i-97]=x:_(c f=v(*z);Qd(!f)F[f](n(i),x)))) 196 | int main(){c b[99];while(1)if(w((s)32),b[read(0,b,99)-1]=0,*b)58==b[1]?e(b):W(e(b));} 197 | ``` 198 | # 2024.01.16 (#2) 199 | ## a.h 200 | ```c 201 | typedef char*s,c;s Q=(s)128; 202 | #define _(e...) ({e;}) 203 | #define x(a,e...) _(s x=a;e) 204 | #define $(a,b) if(a)b;else 205 | #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} 206 | 207 | #define Q(e) if(Q==(e))return Q; 208 | #define Qs(e,s) if(e)return err(__func__,s); 209 | #define Qr(e) Qs(e,"rank") 210 | #define Qd(e) Qs(e,"domain") 211 | #define Qz(e) Qs(e,"nyi") 212 | 213 | #define _s(f,e,x...) s f(x){return _(e);} 214 | #define _i(f,e) _s(f,e,c x) 215 | #define f(f,e) _s(f,e,s x) 216 | #define F(f,e) _s(f,e,s a,s x) 217 | 218 | #define ax (256>x) 219 | #define ix (c)x 220 | #define nx x[-1] 221 | #define xi x[i] 222 | 223 | #define aa x(a,ax) 224 | #define ia x(a,ix) 225 | #define na x(a,nx) 226 | 227 | #define oo w("oo\n") 228 | ``` 229 | ## a.c 230 | ```c 231 | #include"a.h"//fF[+-!#,@] atom/vector 1byte(int/token) clang-13 -Os -oa a.c -w 232 | #define r(n,e) _(s r=m(n);i(n,r[i]=e)r) 233 | f(w,write(1,ax?&x:x,ax?1:strlen(x));x)F(err,w(a);w((s)58);w(x);w((s)10);Q) 234 | _i(wi,s b[5];sprintf(b,"%d ",x);w(b);0) 235 | f(W,Q(x)$(ax,wi(ix))i(nx,wi(xi))w(10);x) 236 | 237 | f(srt,Qz(1)0)f(uni,Qz(1)0)F(Cut,Qz(1)0)F(Drp,Qz(1)0)_i(m,s a=malloc(1+x);*a++=x;a) 238 | #define A(c) ((s)memchr(a,c,na)?:a+na)-a 239 | #define g(a,v) ax?255&a:r(nx,v) 240 | f(not,g(!ix,!xi))f(sub,g(-ix,-xi))F(At,Qr(aa)g(a[ix],a[xi]))F(_A,Qr(aa)g(A(ix),A(xi))) 241 | f(ind,Qr(!ax)0>ix?r(-ix,-ix-1-i):r(ix,i))F(Ind,Qr(!aa)Qd(1>ia)g(ix%ia,xi%ia)) 242 | #define G(f,o) F(f,ax?aa?255&ia o ix:Ltn==f?f(sub(x),sub(a)):f(x,a):r(nx,(aa?ia:a[i])o xi)) 243 | G(Ltn,<)G(Eql,==)G(Not,!=)G(Sum,+)G(Prd,*)G(And,&)G(Or,|) 244 | f(cat,Qr(!ax)r(1,ix))F(Cat,a=aa?cat(a):a;x=ax?cat(x):x;s r=m(na+nx);memcpy(r+na,x,nx);memcpy(r,a,na)) 245 | f(at,At(x,0))f(rev,Qr(ax)At(x,ind(255&-nx)))f(cnt,Qr(ax)nx) 246 | F(Tak,Qr(!aa||ax)Qd(0>ia||ia>nx)At(x,ind(a)))F(Sub,Sum(a,sub(x)))F(Mtn,Ltn(x,a))f(qz,Qz(1)0) 247 | #define v(e) ((strchr(V,e)?:V)-V) 248 | s U[26],V=" +-*&|<>=~!@?#_^,", 249 | (*f[])()={0,abs,sub,qz ,qz,rev,qz ,qz, qz ,not,ind,at,uni,cnt,qz ,srt,cat}, 250 | (*F[])()={0,Sum,Sub,Prd,And,Or,Ltn,Mtn,Eql,Not,Ind,At,_A ,Tak,Drp,Cut,Cat}; 251 | _i(n,10u>x-48?x-48:26u>x-97?U[x-97]:0) 252 | f(e,s z=x;c i=*z++;!*z?n(i):v(i)?x(e(z),Q(x)f[v(i)](x)):x(e(z+1),Q(x)58==*z?U[i-97]=x:_(c f=v(*z);Qd(!f)F[f](n(i),x)))) 253 | int main(){c b[99];while(1)if(w(32),b[read(0,b,99)-1]=0,*b)58==b[1]?e(b):W(e(b));} 254 | ``` 255 | 256 | # 2024.01.16 257 | ## a.h 258 | ```c 259 | typedef char*s,c;s Q=(s)128; 260 | #define _(e...) ({e;}) 261 | #define x(a,e...) _(s x=a;e) 262 | #define $(a,b) if(a)b;else 263 | #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} 264 | 265 | #define Q(e) if(Q==(e))return Q; 266 | #define Qs(e,s) if(e)return err(__func__,s); 267 | #define Qr(e) Qs(e,"rank") 268 | #define Qd(e) Qs(e,"domain") 269 | #define Qz(e) Qs(e,"nyi") 270 | 271 | #define _s(f,e,x...) s f(x){return _(e);} 272 | #define f(g,e) _s(g,e,s x) 273 | #define F(g,e) _s(g,e,s a,s x) 274 | 275 | #define ax (256>x) 276 | #define ix (c)x 277 | #define nx x[-1] 278 | #define xi x[i] 279 | 280 | #define aa x(a,ax) 281 | #define ia x(a,ix) 282 | #define na x(a,nx) 283 | 284 | #define oo w("oo\n") 285 | ``` 286 | ## a.c 287 | ```c 288 | #define v(e) ((strchr(V,e)?:V)-V) 289 | #include"a.h"//fF[+-!#,@] atom/vector 1byteint 1bytetoken no(memmanage parser tokens ..) clang-13 -Os -oa a.c -w 290 | #define _i(f,e) s f(c x){return _(e);} 291 | f(w,write(1,ax?&x:x,ax?1:strlen(x));x)_i(wi,s b[5];sprintf(b,"%d ",x);w(b);0)F(err,w(a);w((s)58);w(x);w((s)10);Q) 292 | #define r(n,e) _(s r=m(n);i(n,r[i]=e)r) 293 | f(W,Q(x)$(ax,wi(ix))i(nx,wi(xi))w(10);x)_i(m,s a=malloc(1+x);*a++=x;a)f(ind,Qr(!ax)0>ix?r(-ix,-ix-1-i):r(ix,i)) 294 | #define g(a,v) ax?255&a:r(nx,v) 295 | f(sub,g(-ix,-xi))F(At,Qr(aa)g(a[ix],a[xi]))F(Ind,Qr(!aa)Qd(1>ia)g(ix%ia,xi%ia))f(rev,Qr(ax)At(x,ind(255&-nx))) 296 | #define G(f,o) F(f,ax?aa?255&ia o ix:Ltn==f?f(sub(x),sub(a)):f(x,a):r(nx,(aa?ia:a[i])o xi)) 297 | G(Ltn,<)G(Eql,==)G(Sum,+)G(Prd,*) 298 | 299 | f(cat,Qr(!ax)r(1,ix))F(Cat,a=aa?cat(a):a;x=ax?cat(x):x;s r=m(na+nx);memcpy(r+na,x,nx);memcpy(r,a,na)) 300 | f(at,At(x,0))f(cnt,Qr(ax)nx)F(Cnt,Q(a=ind(a))At(x,a))F(Sub,Sum(a,sub(x)))F(Mtn,Ltn(x,a))f(qz,Qz(1)0) 301 | s U[26],V=" +-*|<>=!#,@", 302 | (*f[])()={0,qz, sub,qz, rev,qz ,qz, qz ,ind,cnt,cat,at}, 303 | (*F[])()={0,Sum,Sub,Prd,qz ,Ltn,Mtn,Eql,Ind,Cnt,Cat,At}; 304 | 305 | _i(n,10u>x-48?x-48:26u>x-97?U[x-97]:0) 306 | f(e,s z=x;c i=*z++;!*z?n(i):v(i)?x(e(z),Q(x)f[v(i)](x)):x(e(z+1),Q(x)58==*z?U[i-97]=x:_(c f=v(*z);Qd(!f)F[f](n(i),x)))) 307 | int main(){c b[99];while(1)if(w(32),b[read(0,b,99)-1]=0,*b)58==b[1]?e(b):W(e(b));} 308 | ``` 309 | 310 | # 2024.01.12 311 | ## a.h 312 | ```c 313 | typedef unsigned char c;typedef unsigned long u;u Q=96; 314 | #define $(a,b) if(a)b;else 315 | #define ax (256>x) 316 | #define xi (nx>i?sx[i]:0) 317 | #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} 318 | #define _(e) ({e;}) 319 | #define _u(f,e,x...) u f(x){return({e;});} 320 | #define nx sx[-1] 321 | #define sx ((c*)x) 322 | #define x(a,e) _(u x=a;e) 323 | #define y(a,e) _(u y=a;e) 324 | #define f(g,e) _u(g,e,u x) 325 | #define F(g,e) _u(g,e,u f,u x) 326 | #define G(g,e) _u(g,e,u f,u x,u y) 327 | #define us(f,e) _u(f,e,c*s) 328 | #define Q(e) if(Q==(e))return Q; 329 | #define Qr(e) if(e){return err((u)__func__,(u)"rank");} 330 | #define Qz(e) if(e){return err((u)__func__,(u)"nyi");} 331 | #define sr x(r,sx) 332 | #define ri sr[i] 333 | 334 | #define af x(f,ax) 335 | #define nf x(f,nx) 336 | #define sf x(f,sx) 337 | #define fi x(f,xi) 338 | ``` 339 | ## a.c 340 | ```c 341 | #include"a.h"//fF[+-!#,@] atom/vector 1byteint 1bytetoken no(memmanage parser tokens ..) clang-13 -Os -oa a.c -w 342 | #define r(n,e) _(u r=a(n);i(n,ri=e)r) 343 | f(w,write(1,ax?(u)&x:x,ax?1:strlen(x))) 344 | c b[12]; 345 | f(si,sprintf(b,"%d ",128>x?x:x-256);b) 346 | f(wi,w(si(x))) 347 | f(w_,$(ax,wi(x))i(nx,wi(xi))w(10)) 348 | 349 | F(err,w(f);w(58);w(x);w(10);96) 350 | G(m,(u)memcpy((void*)x,(void*)y,f)) 351 | f(a,c*s=malloc(x+1);*s++=x;(u)s) 352 | f(foo,Qz(1)0) 353 | f(sub,ax?(c)-x:r(nx,-xi)) 354 | f(ind,Qr(!ax)r(x,i)) 355 | f(cnt,Qr(ax)nx) 356 | f(cat,Qr(!ax)r(1,x)) 357 | F(Add,ax?af?f+x:Add(x,f):r(nx,(af?f:fi)+xi)) 358 | F(Sub,Add(f,sub(x))) 359 | F(Ind,Qr(!f)r(nx,xi%f)) 360 | F(Cnt,Qr(!af)r(f,ax?x:xi)) 361 | F(Cat,f=af?cat(f):f;x=ax?cat(x):x;u r=a(nf+nx);m(nx,r+nf,x);m(nf,r,f)) 362 | F(At,Qr(af)ax?sf[x]:r(nx,sf[xi]))f(at,At(x,0)) 363 | char*V=" +-!#,@";u(*f[])()={0,foo,sub,ind,cnt,cat,at},(*F[])()={0,Add,Sub,Ind,Cnt,Cat,At},U[26]; 364 | f(v,(strchr(V,x)?:V)-V) 365 | f(n,10>x-48?x-48:U[x-97]) 366 | us(e,u i=*s++;v(i)?x(e(s),Q(x)f[v(i)](x)):x(n(i),*s?y(e(s+1),Q(y)F[v(*s)](x,y)):x)) 367 | int main(){char s[99];while(1)if(w(32),s[read(0,s,99)-1]=0,*s)w_(e(s));} 368 | ``` 369 | # 2024.01.10 370 | ## a.h 371 | ```c 372 | typedef unsigned long u; 373 | #define _u(f,e,x...) u f(x){return _(e);} 374 | #define ax (10>x) 375 | #define nx sx[-1] 376 | #define sx ((char*)x) 377 | #define xi sx[i] 378 | #define _(e) ({e;}) 379 | #define r(a,e) _(u r=a;e;r) 380 | #define x(a,e) _(u x=a;e) 381 | #define y(a,e) _(u y=a;e) 382 | #define i(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} 383 | #define f(f,e) _u(f,e,u x) 384 | #define F(f,e) _u(f,e,u x,u y) 385 | #define G(g,e) _u(g,e,u f,u x,u y) 386 | #define us(f,e) _u(f,e,char*s) 387 | #define Q(e) if(96==(e))return 96; 388 | #define Qr(e) if(e){return err((u)__func__,(u)"rank");} 389 | #define Qz(e) if(e){return err((u)__func__,(u)"todo");} 390 | #define ri sr[i] 391 | #define yi x(y,xi) 392 | #define nr x(r,nx) 393 | #define ny x(y,nx) 394 | #define sr x(r,sx) 395 | #define sy x(y,sx) 396 | #define ay x(y,ax) 397 | 398 | ``` 399 | ## a.c 400 | ```c 401 | #include"a.h"//cc -Os -oa a.c -w 402 | F(w_,write(1,y,x))f(w,127>x?w_(1,(u)&x):w_(strlen(x),x))F(err,w(x);w(58);w(y);w(10);96)f(qz,Qz(1)0) 403 | G(m,(u)memcpy((void*)x,(void*)y,f)) 404 | f(a,char*s=malloc(x+1);*s++=x;(u)s) 405 | f(ind,Qr(!ax)x=a(x);i(nx,xi=i)x) 406 | f(cat,Qr(!ax)u r=a(1);*sr=x;r) 407 | F(Cat,x=ax?cat(x):x;y=ay?cat(y):y;u r=a(nx+ny);m(ny,r+nx,y);m(nx,r,x)) 408 | f(at,Qr(ax)*sx) 409 | F(At,Qr(ax||!ay)sx[y]) 410 | f(cnt,Qr(ax)nx) 411 | F(Cnt,Qr(!ax||ay)x=a(x);m(nx,x,y)) 412 | F(Add,ax?ay?x+y:Add(y,x):r(a(ny),i(nr,ri=(ax?x:xi)+yi))) 413 | u(*f[])()={0,qz,ind,cnt,cat,at},(*F[])()={0,Add,qz,Cnt,Cat,At}; 414 | u U[26];f(n,10>x-48?x-48:U[x-97]) 415 | char*V=" +!#,@";f(v,(strchr(V,x)?:V)-V) 416 | us(e,u i=*s++;v(i)?x(e(s),Q(x)f[v(i)](x)):x(n(i),*s?y(e(s+1),Q(y)F[v(*s)](x,y)):x)) 417 | us(ws,u x=*s?e(s):0;Q(x)ax?w(48+x):_(i(nx,w(48+xi);w(32))0);w(10)) 418 | int main(){char s[99];while(1)w(32),s[read(0,s,99)-1]=0,ws(s);} 419 | 420 | char *kinit(){char s[99];return s;} 421 | ``` 422 | # initial 2024.01.10 423 | ```c 424 | typedef char c;typedef unsigned long(*_)(),u; 425 | #define _(e) ({e;}) 426 | #define s(f,e,x...) c*f(x){return _(e);} 427 | #define u(f,e,x...) u f(x){return _(e);} 428 | #define I(n,e) {int $n=n;int i=0;for(;i<$n;++i){e;}} 429 | #define f(f,e) u(f,e,u x) 430 | #define F(f,e) u(f,e,u x,u y) 431 | #define sr ((c*)r) 432 | #define sx ((c*)x) 433 | #define sy ((c*)y) 434 | #define nr sr[-1] 435 | #define nx sx[-1] 436 | #define ny sy[-1] 437 | #define rc sr[i] 438 | #define xc sx[i] 439 | #define yc sy[i] 440 | #define ax (10>x) 441 | #define ay (10>y) 442 | #define oo w_(3,"oo\n") 443 | #define r(a,e) _(u r=a;e;r) 444 | u(m,(u)memcpy((void*)x,(void*)y,n),u n,u x,u y)u(w_,write(1,s,n),u n,c*s)f(w,w_(1,&x)) 445 | f(a,c*s=malloc(x+1);*s++=x;(u)s)f(ind,x=a(x);I(nx,xc=i)x) 446 | f(cat,u r=a(1);*sr=x;r)F(Cat,x=ax?cat(x):x;y=ay?cat(y):y;u r=a(nx+ny);m(ny,r+nx,y);m(ny,r,x)) 447 | f(at,*sx)F(At,sx[y])f(cnt,nx)F(Cnt,x=a(x);m(nx,x,y)) 448 | F(Add,ax?ay?x+y:Add(y,x):r(a(ny),I(nr,rc=(ax?x:xc)+yc))) 449 | f(q,0)_ f[]={0,q,ind,cnt,cat,at},F[]={0,Add,q,Cnt,Cat,At}; 450 | u U[26];f(n,10>x-48?x-48:U[x-97])c*V=" +!#,@";f(v,(strchr(V,x)?:V)-V) 451 | u(e,c i=*s++;v(i)?f[v(i)](e(s)):*s?F[v(*s)](n(i),e(s+1)):n(i),c*s) 452 | u(ws,u x=*s?e(s):0;ax?w(48+x):_(I(nx,w(48+xc);w(32))0);w(10),c*s) 453 | int main(){c s[99];while(1)w(32),s[read(0,s,99)-1]=0,ws(s);} 454 | ``` 455 | -------------------------------------------------------------------------------- /t.k: -------------------------------------------------------------------------------- 1 | /basic smoke test 2 | -1 3 | -0 4 | 0-0 5 | 1-2 6 | 7 | /basic vector ops 8 | a:!9 9 | a+1 10 | 1+1 11 | a+a 12 | a-a+a 13 | a 14 | b:-a 15 | b 16 | c:2!a 17 | c 18 | c:c,c 19 | c 20 | #c 21 | \w 22 | \v 23 | 24 | /ws should be zero 25 | a:b:c:0 26 | \w 27 | \v 28 | 29 | /enlist 30 | x:,2 31 | x:x,x,x,x 32 | x 33 | \w 34 | @x 35 | x@1 36 | x:0 37 | \w 38 | 39 | /scan 40 | +/!9 41 | -/!9 42 | 43 | /over 44 | +\|!5 45 | *\1+!5 46 | 47 | \w 48 | \\ 49 | 50 | /:~ 51 | --------------------------------------------------------------------------------