I enjoyed reading your code
I made some minor modifications, would you consider taking a look at them and providing feedback?
#include<stdlib.h> //malloc
#include<stdio.h> //fgets printf
#include<string.h> //strlen
//typedef void _;
typedef char C,*S;
typedef struct a {
long long t, r;
long long d[3];
long long p[2];
}* Array;
static Array st[26];
#define globalArrayStack st
Array reduce(Array x, Array y);
Array scanArray(Array x, Array y);
Array ex(Array * e);
long long WS = 0;
long long * allocMem(long long size) {
size *= 8; // Assuming 8 bytes per long long
WS += size; // Update Workspace Size
return (long long *)malloc(size);
}
void copyMem(long long *dest, long long *src, long long count) {
for (long long i = 0; i < count; i++) {
dest[i] = src[i];
}
}
long long calcSize(long long rank, long long *dimensions) {
long long totalSize = 1;
for (long long i = 0; i < rank; i++) {
totalSize *= dimensions[i];
}
return totalSize;
}
Array indexOfChar(char targetChar, char* str) {
for (long long i = 0; str[i] != '\0'; i++) {
if (str[i] == targetChar) {
return (Array)(i + 1); // +1 because index is 1-based in this context
}
}
return 0;
}
Array createArray(long long type, long long rank, long long *dimensions) {
Array newArray = (Array)allocMem(5 + calcSize(rank, dimensions));
newArray->t = type;
newArray->r = rank;
copyMem(newArray->d, dimensions, rank);
return newArray;
}
Array id(Array x) {
return x;
}
Array sequence(Array x) {
long long length = *x->p;
Array newArray = createArray(0, 1, &length);
for (long long i = 0; i < length; i++) {
newArray->p[i] = i;
}
return newArray;
}
Array plus(Array x, Array y) {
long long rank = y->r;
long long *dimensions = y->d;
long long totalCount = calcSize(rank, dimensions);
Array resultArray = createArray(0, rank, dimensions);
for (long long i = 0; i < totalCount; i++) {
resultArray->p[i] = ((!x->r) ? *x->p : x->p[i]) + ((!y->r) ? *y->p : y->p[i]);
}
return resultArray;
}
Array mul(Array x, Array y) {
long long rank = y->r;
long long *dimensions = y->d;
long long totalCount = calcSize(rank, dimensions);
Array resultArray = createArray(0, rank, dimensions);
for (long long i = 0; i < totalCount; i++) {
resultArray->p[i] = ((!x->r) ? *x->p : x->p[i]) * ((!y->r) ? *y->p : y->p[i]);
}
return resultArray;
}
Array extractFromArray(Array indexArray, Array sourceArray) {
long long reducedRank = sourceArray->r - 1;
long long *reducedDimensions = sourceArray->d + 1;
long long totalElements = calcSize(reducedRank, reducedDimensions);
Array resultArray = createArray(sourceArray->t, reducedRank, reducedDimensions);
copyMem(resultArray->p, sourceArray->p + (totalElements * *indexArray->p), totalElements);
return resultArray;
}
Array boxArray(Array inputArray) {
Array boxedArray = createArray(1, 0, 0);
*boxedArray->p = (long long) inputArray;
return boxedArray;
}
Array concatenateArrays(Array firstArray, Array secondArray) {
long long firstArraySize = calcSize(firstArray->r, firstArray->d);
long long secondArraySize = calcSize(secondArray->r, secondArray->d);
long long totalSize = firstArraySize + secondArraySize;
Array concatenatedArray = createArray(secondArray->t, 1, &totalSize);
copyMem(concatenatedArray->p, firstArray->p, firstArraySize);
copyMem(concatenatedArray->p + firstArraySize, secondArray->p, secondArraySize);
return concatenatedArray;
}
Array findElement(Array x, Array y) {
printf("nyi\n");
return (Array) 0;
}
Array reshapeArray(Array shapeArray, Array inputArray) {
// Determine the rank and total size for the new shape
long long newRank = shapeArray->r ? *shapeArray->d : 1;
long long newSize = calcSize(newRank, shapeArray->p);
// Calculate the total number of elements in the input array
long long inputSize = calcSize(inputArray->r, inputArray->d);
// Create a new array with the desired shape and type
Array reshapedArray = createArray(inputArray->t, newRank, shapeArray->p);
// Determine the number of elements to copy (the smaller of newSize or inputSize)
long long elementsToCopy = newSize > inputSize ? inputSize : newSize;
// Copy elements from the input array to the reshaped array
copyMem(reshapedArray->p, inputArray->p, elementsToCopy);
// If the new size is larger, repeat the input array elements to fill the reshaped array
if (newSize > inputSize) {
copyMem(reshapedArray->p + inputSize, reshapedArray->p, newSize - inputSize);
}
return reshapedArray;
}
Array sha(Array x) {
Array z = createArray(0, 1, & x -> r);
copyMem(z -> p, x -> d, x -> r);
return z;
}
Array size(Array x) {
// Create an array to store the size result
Array resultArray = createArray(0, 0, 0);
// If the array x has a rank (r) greater than 0, use the size of the first dimension.
// Otherwise, the size is considered as 1 (for rank 0 arrays).
long long sizeValue = (x->r > 0) ? x->d[0] : 1;
// Store the calculated size in the result array
*resultArray->p = sizeValue;
return resultArray;
}
Array firstElement(Array x) {
Array z = createArray(0, 0, 0);
*z -> p = * x -> p;
return z;
}
Array reverseArray(Array inputArray) {
long long rank = inputArray->r;
long long *dimensions = inputArray->d;
long long totalElements = calcSize(rank, dimensions);
Array reversedArray = createArray(0, rank, dimensions);
for (long long index = 0; index < totalElements; index++) {
reversedArray->p[index] = inputArray->p[totalElements - index - 1];
}
return reversedArray;
}
char verbTable[] = "+{!<#,*|", at[] = "\\/";
Array( * verbDispatcher[])(Array, Array) = {
0,
plus,
extractFromArray,
findElement,
0,
reshapeArray,
concatenateArrays,
mul,
0
},
( * vm[])(Array) = {
0,
id,
size,
sequence,
boxArray,
sha,
0,
firstElement,
reverseArray
},
( * va[])(Array, Array) = {
0,
scanArray,
reduce
};
Array reduce(Array functionArray, Array inputArray) {
// Check if the input array is empty or has rank 0; return it directly if so
if (!inputArray->r) {
return inputArray;
}
// Create arrays for the reduction process
Array resultArray = createArray(0, 0, 0);
Array tempArray = createArray(0, 0, 0);
// Initialize the first element of resultArray with the first element of inputArray
*resultArray->p = *inputArray->p;
// If inputArray has more than one element, initialize tempArray with the second element
*tempArray->p = inputArray->r > 1 ? inputArray->p[1] : 0;
// Perform the reduction
long long totalIterations = (inputArray->r ? *inputArray->d : 1) - 1;
for (long long i = 0; i < totalIterations; ++i) {
*tempArray->p = inputArray->p[i + 1];
*resultArray->p = *((*verbDispatcher[(long long)functionArray])(resultArray, tempArray))->p;
}
return resultArray;
}
Array scanArray(Array functionArray, Array inputArray) {
// Check if inputArray is empty or has rank 0, in which case return inputArray itself
if (!inputArray->r) {
return inputArray;
}
// Create an array 'result' with the same shape as inputArray
Array result = createArray(0, inputArray->r, inputArray->d);
// Initialize temporary arrays for the scan operation
Array temp1 = createArray(0, 0, 0);
Array temp2 = createArray(0, 0, 0);
// Initialize the first elements of temp1 and result with the first element of inputArray
*temp1->p = *result->p = *inputArray->p;
// If inputArray has more than one element, initialize temp2 with the second element
if (inputArray->r > 1) {
*temp2->p = inputArray->p[1];
}
// Iterate over the elements of the inputArray, applying the functionArray
long long totalIterations = (inputArray->r ? *inputArray->d : 1) - 1;
for (long long i = 0; i < totalIterations; ++i) {
*temp2->p = inputArray->p[i + 1];
result->p[i + 1] = *temp1->p = *((*verbDispatcher[(long long)functionArray])(temp1, temp2))->p;
}
// Return the final result of the scan operation
return result;
}
long long convertStringToLongLong(char* inputString, long long *numberLength) { // convertStringToLongLong
char currentChar;
long long index = 0, result = 0;
*numberLength = 0;
while ((currentChar = *inputString++) && (currentChar >= '0' && currentChar <= '9')) {
index++;
result = result * 10 + (currentChar - '0');
}
*numberLength = index;
return result;
}
void printInt(long long i) {
printf("%lld ", i);
}
void newline() {
printf("\n");
}
void printTab() {
printf(" ");
}
void printArray(Array arrayToPrint) {
long long rank = arrayToPrint->r;
long long *dimensions = arrayToPrint->d;
long long totalElements = calcSize(rank, dimensions);
if (arrayToPrint->t) {
// If the array type is non-zero, handle as a nested (boxed) array
for (long long i = 0; i < totalElements; i++) {
printf("< ");
printArray((Array)arrayToPrint->p[i]);
}
} else {
// Handle as a flat array
for (long long i = 0; i < totalElements; i++) {
printInt(arrayToPrint->p[i]);
}
}
}
char isLowercase(long long a) {
return a >= 'a' && a <= 'z';
}
char qv(long long a) {
return a < 'a';
}
char isUppercase(long long a) { // qv
return a < 'a';
}
Array getAdverb(char c) {
return indexOfChar(c, at);
}
Array verb(char c) {
return indexOfChar(c, verbTable);
}
Array noun(char** inputString) { // createNounFromString
char firstCharacter = **inputString;
long long stringLength;
// Check if the first character is not a digit; return NULL if so
if (firstCharacter < '0' || firstCharacter > '9') {
return NULL;
}
// Create an array to store the converted number
Array numberArray = createArray(0, 0, 0);
// Convert the string to a long long number and store in the array
*numberArray->p = convertStringToLongLong(*inputString, &stringLength);
// Move the input string pointer forward by the length of the numeric part
*inputString += stringLength - 1;
return numberArray;
}
Array executeExpression(Array *expressionElements) {
long long firstElement = (long long)*expressionElements;
long long secondElement = (long long)expressionElements[1];
if (isLowercase(firstElement)) {
if (secondElement == ':') {
return (st[firstElement - 'a'] = executeExpression(expressionElements + 2));
}
firstElement = (long long)st[firstElement - 'a'];
}
if (isUppercase(firstElement) && getAdverb(secondElement)) {
Array (*adverbFunction)(Array, Array) = va[(long long)getAdverb(secondElement)];
return adverbFunction((Array)firstElement, executeExpression(expressionElements + 2));
}
if (isUppercase(firstElement)) {
Array (*monadFunction)(Array) = vm[firstElement];
return monadFunction(executeExpression(expressionElements + 1));
}
if (secondElement) {
Array (*verbFunction)(Array, Array) = verbDispatcher[secondElement];
return verbFunction((Array)firstElement, executeExpression(expressionElements + 2));
}
return (Array)firstElement;
}
Array * parseInput(char* inputStr) {
Array parsedElement;
Array *parsedElements;
char currentChar;
long long strLength = strlen(inputStr);
if (strLength==1) {
printf("Error: Input string is empty\n");
exit(1);
}
if (!strLength) return (Array *)0;
inputStr[--strLength] = 0;
parsedElements = (Array *)allocMem(strLength + 1);
strLength = 0;
while ((currentChar = *inputStr)) {
parsedElement = noun(&inputStr);
if (!parsedElement) {
parsedElement = verb(currentChar);
}
if (!parsedElement) {
parsedElement = (Array)(long long)currentChar;
}
parsedElements[strLength++] = parsedElement;
inputStr++;
}
parsedElements[strLength] = 0;
return parsedElements;
}
int main() {
char inputString[99];
while (1) {
//printf("WS: %llu\n", WS); // Print workspace size
printTab(); // Print a tab or space
// Read input from stdin. If fgets returns NULL, break the loop
if (fgets(inputString, 99, stdin) == NULL) {
break;
}
// Process the input and print the result
printArray(executeExpression(parseInput(inputString)));
newline(); // Print a newline character
}
return 0;
}
// https://codebeautify.org/cpp-formatter-beautifier
@dataf3l thanks for caring to take a look, i'm flattered.
as it follows, the code isn't exactly mine - it is due to a much taller Canadian bear, only he wrote it when his hair wasn't completely grey yet, and i was 8 years old.
i'm short on time to figure out what "codebeautify" exactly is, but the result looks like a job of a fairly well-trained AI model. as it happens, those things still miss out on subtleties, and as it must be evident from your snippet still have a major difficulty maintaining consistent and confident style - which is the whole idea and the central point of this little piece of history.
impressive, though.
does it compile, if you know?
sorry, i missed your point. you've said you've made minor modifications, which i interpret as modifications to the logic of this software, but i have no means of figuring out what you've done. please point me in the right direction.
as for AI hiccups, the simplest example is pt(), which is not really "printTab() // print a tab or space", it stands for prompt, which is a single 0x20.
would you care to try the same approach on a slightly more production-grade piece of software which is written in the same style? i'm really keen to learn what the results may be:
https://github.com/kparc/bcc/blob/master/ec/e.c
another example of where the model successfully got the wind of what was going on, but completely obliterated the context, terminology and consistency of the original code is this:
Array reduce(Array x, Array y);
Array scanArray(Array x, Array y);
as i am responsible for basic implementation of these two staple k adverbs in this toy code, i have the authority to say that their names are cast in stone as over and scan. although reduce() hits the mark to a degree (i'd also accept fold()), scanArray() lost it completely. but what's worse is that the machine-generated code failed to see and reflect the rhyme between scan and over.
on a closing note:
char isUppercase(long long a) { // qv
return a < 'a';
}
on your local terminal, try:
$ ascii -d
(you'd hopefully agree that isUppercase() is a fatally misleading interpretation of what arthur actually tests for in qv())
also, AI tends to use Element everywhere where it sees a member of an array. In some contexts, e.g. in parse function, this is just plain wrong - if the model had any clue of what it is "beautifying", it would use "Token" instead, which I'd gladly accept.
iota is totally not Sequence. I'd accept til instead, but that's far beyond the agency of any code beautifier you can feed this code to.
i gave you honest and direct feedback, and i hope you'll find it useful.
in return, i'd like to hear your honest opinion as to what degree the original code benefitted from your modifications.
I first approached the code not knowing exactly what it did other than it
was some form of interpreter or compiler:
I used “CPP” which can expand all the macros and an online code beautifier to
add line breaks.
Later, I removed every comma I could find.
After this, I asked the AI what new names could be given, since this is a SHORT
program, it gave a lot of new names, I haven’t gotten around to renaming
everything the way the AI likes it.
Then, I asked the AI to refactor every single function, and tested the
interpreter along the way, to make sure the functionality was still there, so
this is bug by bug compatible (it segfaults a lot).
But it can read the same expressions the original one could apparently.
I did this so I could kinda understand the code, I can’t say I totally
understand it now, but I think it’s a step on that direction.
After all the work I googled what is K and J and found these wonderful
languages, perhaps with more time I’ll learn how to use them.
“does it compile, if you know?” => yes, it does build.
Try: gcc -o ./temp filename.c
“you've said you've made minor modifications”
Well, I didn’t add any keywords or free() or things like that.
But I guess one could consider them to be not so minor, perhaps this is a
subjective matter
“pt(), which is not really "printTab()" ”
If the AI likes printTab, I call it printTab(). But then again I totally see
what you mean by the AI misinterpreting the original intent, I’m surprised it
even got whatever was right right, which was probably a lot.
“would you care to try the same approach on a slightly more production-grade piece of software which is written in the same style? i'm really keen to learn what the results may be:“
That’s a lot of code. Challenge accepted.
“over and scan”
I guess one could rename it back the way it was before…
To me, the whole question was, can AI make sense of the code Make it
understandable by mere mortals, who don’t know much And can it make the code not
only readable but also enjoyable?
These are questions on my mind, there is a lot of code out there, But not all
the code is readable, so, one wonders, can AI be used To make code better? Can
we learn to understand other people’s code using the AI?
These are the questions I set out to answer. Can we refactor existing code? Can
the AI make sense of single Letter variables and very terse code somehow?
Sure, we may not Agree with the AI on everything, but surely the work done by it
is perhaps more digestible by the average programmer, than an endless array of
rather mysterious single-letter local variables.
“…isUppercase() is a fatally misleading interpretation...”
I noticed this as well, I didn’t have time to change it to a More meaningful
name, I also don’t think it’s a good interpretation. I have no clear thoughts on
what to call it at this point.
“AI tends to use Element everywhere … this is just plain wrong … it would use "Token" instead, which I'd gladly accept.”
Ok then I change it I guess… We’ll have to wait and see, for the next version.
“in return, i'd like to hear your honest opinion as to what degree the original code benefitted from your modifications.“
I did in fact add a single if statement somewhere manually because I didn’t like
the fact that it segfaults on some edge cases, it still segfaults a lot, because
error checking is non existent, but I figured maybe one can add that “over
time”.
So, the code is basically not yet improved, it needs more work before one can
start improving on it, I noticed a stale PR AFTER I started the work, that had
more features!, too late for that now I guess.
I had fun trying to understand the code, but some aspects of it are still a
mystery to me, the intent isn’t too clear, unfortunately.
I also didn’t get around renaming the names of the fields of the struct like p
or r or d to something different and perhaps more informative, yet, perhaps in a
future session.
I’ve considered adding the original code as “comments at the beginning of each
line” so it’s easier to keep track of the changes.
But I thought that could be considered offensive, so maybe not.
I also thought perhaps one could rewrite this in Rust, but I fear the current
design maybe Rust’ borrow checker won’t like the code very much if at all, I
foresee a lot of weird errors if one is to attempt such a thing.
Thanks for the feedback, and for the prompt response, I’ll review the other
code.
After all the work I googled what is K and J and found these wonderful languages
oh. welcome to the club :)
it segfaults a lot
this is by design. this is a sketch of an interpreter written in one sitting. i dare to assume that back in 1989 Arthur Whitney couldn't afford his own AT&T 3B1, which wasn't exactly a cheap machine.
I’ve considered adding the original code as “comments at the beginning of each line”
so did i, but i opted to leave this piece of code undocumented, and there are good reasons for that. and it keeps on paying off.
at the beginning of each line
usually we annotate this kind of code this way: https://github.com/kparc/pf/blob/master/pf.h it is up to you whether you want to beautify this code any further or read it as it is.
If the AI likes printTab, I call it printTab()
if you're available to take friendly advice - just don't. think for yourself and question "authority".
this were the ideas by the AI:
This program appears to be a minimalistic implementation of some operations common in array programming languages like J. It defines operations on arrays (referred to as K-structures in the code) and includes functions for arithmetic, reshaping, boxing, etc.
Here's a proposed renaming of variables and functions to make the code more readable. The goal is to maintain brevity while improving clarity. The renaming is based on the context in which these entities are used and the typical naming conventions in array programming:
| Original Name | New Name | Description/Notes |
|---|---|---|
_ |
VoidFunction |
Typedef for void function |
C |
Char |
Typedef for char |
S |
String |
Typedef for char pointer (string) |
J |
Long |
Typedef for long long |
K |
Array |
Typedef for main array structure |
st |
globalArrayStack |
Global stack for arrays |
WS |
WorkspaceSize |
Tracks the workspace memory usage |
ma |
allocMem |
Memory allocation function |
mv |
copyMem |
Memory copy function |
tr |
calcSize |
Calculate size of an array |
i |
indexOfChar |
Find index of a character in a string |
ga |
createArray |
Function to create a new array |
id |
identity |
Identity function |
iota |
sequence |
Generate sequence array |
plus |
addArrays |
Add two arrays |
mul |
multiplyArrays |
Multiply two arrays |
from |
extract |
Extract subarray |
box |
boxValue |
Box a value into an array |
cat |
concatenate |
Concatenate two arrays |
find |
findElement |
Find element (not yet implemented) |
rsh |
reshapeArray |
Reshape an array |
sha |
shapeArray |
Get the shape of an array |
size |
arraySize |
Get the size of an array |
fst |
firstElement |
Get the first element of an array |
rev |
reverseArray |
Reverse an array |
vt |
verbTable |
Table for verbs |
at |
adverbTable |
Table for adverbs |
vd |
verbDispatcher |
Dispatcher for verb functions |
vm |
monadDispatcher |
Dispatcher for monadic functions |
va |
adverbDispatcher |
Dispatcher for adverb functions |
over |
reduce |
Reduction operation |
scan |
scanArray |
Scan operation |
sI |
stringToInt |
Convert string to integer |
pi |
printInt |
Print an integer |
nl |
newline |
Print a newline |
pt |
printTab |
Print a tab (or space) |
pr |
printArray |
Print an array |
qp |
isLowercase |
Check if char is lowercase |
qv |
isUppercase |
Check if char is uppercase |
adv |
getAdverb |
Get adverb from character |
verb |
getVerb |
Get verb from character |
noun |
getNoun |
Get noun (number) from string |
ex |
execute |
Execute parsed expressions |
wd |
parseInput |
Parse input string into expressions |
main |
main |
Main function (unchanged) |
Please note that some functions, like find, are marked as "not yet implemented (nyi)". Additionally, the use of single-letter variable names and terse style is typical in languages like J, which this program seems to be inspired by. My renaming aims to strike a balance between maintaining the spirit of the original code and improving readability.
The reason the code is inconsistent is because I didn't apply all the AI suggestions, just some of them.
this were the ideas by the AI [...]
hats off, this part of the footwork the machine got almost flawlessly. only i'm not sure why anyone would want to make use of the middle column. it is superfluous.
endless array of rather mysterious single-letter local variables.
yes, i know - at first, some such code looks like an "endless" soup of someone's exercise in obscurity and pretentiousness.
but local variables are just xyzrf, and they are neither endless nor mysterious.
sometimes you'll see extra locals (s, c, t) but they are rare and easy to infer even without comments.
i assume you discovered this repo via HN post. there's plenty of links there which point to resources which describe rationale and conventions of this particular style of c.
this were the ideas by the AI
i learned something new today. thank you.
this were the ideas by the AI
which AI did you employ, and how did you state the prompt?
@dataf3l fwiw, these are the next evolutions of j you can try and run:
This comes with a paper from the APL90 conference. the only version i got, was a dos binary distributed at that time: https://ktye.github.io/zoo/#j90
The next version from roger where i could get the source is j4.1 or 4.2: this is the first fully working version that you can study the source code of. compiled to wasm: https://ktye.github.io/zoo/#j42