Learn ~ATH turned 1 today!
yaaaay!
So I think that my explanations of things hasn't been very clear so far, and while I can't promise that they will improve, I do intend to teach programming in person at my school, which might help me to know how to make this tutorial better. Maybe, maybe not.
May things go well!
~ATH is a programming language. A programming language is a language that can be used to write programs in, that can afterwards be run. ~ATH appears in Homestuck, and drocta ~ATH is my attempt at making it a real thing.
drocta ~ATH is limited to what is physically possible of course, because it actually exists. It is also limited to fit well with the comic, and how much of it I have created so far.
As such, so far all the programs that can be written in drocta ~ATH are text only, do not yet accept input (input in progress), and tend to be stupidly long.
It is however capable of computing anything a computer can given enough time and memory.
~ATH is based on objects, and the lives thereof.
For example, in homestuck, there are programs that relate things to the lifespan of a universe, or a person, etc.
Of course, in order to make an interpreter possible, we have to limit ourselves to virtual objects. Ones that do not actually exist.
References to these objects are stored in things called variables. You probably know what these are already, but if you don't, think of a variable as a box that can have a thing in it. Each box has a name. You can say "do something with whatever is in the box called "apple".
In ~ATH, every object is either "alive" or "dead". Note that it is the object that is alive or dead, not the variable that refers to the object.
Now onto the actual syntax!:
Currently(as of build 7), drocta ~ATH has 5 different commands: import, ~ATH(){}, .DIE();, print, and BIFURCATE.
First we will go over the import statement. The import statement has the purpose of creating a new variable and a new object. Unlike other operations that can create a new object, the object created by import is initially unrelated to all of the other objects.
The syntax of the import command is:
import anything other than semicolons here VARIABLENAME;
The things between import and the last space before the semicolon are ignored.
In future versions, the import statement might also do additional things, such as using things from other files. But for now it just initializes variables.
EXAMPLES:
If you wanted to make a new variable called BANANA, and you wanted the reader to know that BANANA is a fruit, you would say:
import FRUIT BANANA;
or you could say
import YELLOW FRUIT BANANA;
The things that go before the variable name don't actually matter, you can say
import ghsdgh hgsdkhg hgksdhg hgskdjg BANANA;
if you wanted.
Now for the next command: The eponymous ~ATH loop!
The syntax of it is as follows:
~ATH(VARNAME){ Some other code goes here }
What this does, is each time the code execution gets to the ~, it checks what the variable in the parentheses is, and then checks the object the variable points to. If the object is alive, it continues. If the object is dead, it skips to after the }. When the Code execution reaches the }, (that is, if it did not skip to after it), it will jump back to the corresponding ~. Code execution will keep going around in this loop until the object pointed to by the variable is not alive. In later versions, there may be an additional requirement that the } be followed by EXECUTE(code here); Where code here can be replaced with ~ATH code, NULL, or possibly a file name. But currently, this is not the case. Now onto the command BIFURCATE!: Bufurcate actually has 2 forms, the standard bifurcate, And the reverse bifurcate. The first of the two has syntax as follows:
BIFURCATE VARNAME1[VARNAME2,VARNAME3];
What this does, is it takes the object pointed to by the first variable, and determines two objects, which are stored in the other two variables. The two objects determined will always be the same for a particular object. If you for example say:
BIFURCATE V1[V2,V3]; V2.DIE(); BIFURCATE V1[V4,V5];
then V4 and V2 will point to the same object, which will be dead.
The other form of the BIFURCATE command has syntax as follows:
BIFURCATE [VAR1,VAR2]VAR3;
This is pretty much the inverse operation. That is, it undoes the other one. If you say for example:
BIFURCATE A[B,C]; BIFURCATE [B,C]D;
then A and D will point to the same object. To be clear, undoing the other is not the only time you can use it. It will take any two objects, and determine an object from those two. If there is already an object for the combination of those two, then that object is the resultant object. If none has been created yet (and the two arent split from something in that order), it will create a new object, which it will put in VAR3.
To be clear, if you say:
BIFURCATE A[B,C]; BIFURCATE [C,B]D;
A and D will NOT point to the same object, unless you create A such that B and C are the same. That is, if you say:
BIFURCATE [Y,Y]A; BIFURCATE A[B,C]; BIFURCATE [C,B]D;
A and D WILL point to the same object, and B and C will both point to the same object as Y (and as each other).
The Final command is print.
print is pretty simple, but its syntax might change somewhat.
Currently the syntax is:
print the text you want to output;
Note the semicolon. the semicolon is important.
So hello world would be
print Hello, World.;
SYNTAX EXPLANATION COMPLETE! PLEASE SUGGEST CLARIFICATIONS IF NECESSARY.
MESSAGE END.
Ok, if you tried running a script with the interpreter, it might not have run properly. The part of the code that handles what part of the code is executed next is buggy. It is in the process of being fixed. Also, if python gives you a syntax error, it is probably because you are using a different version of python. Change raw_input to input And change print stuff to print(stuff) I will post an introduction to the syntax soon.
So recently I started kind of working on this again for a bit. I have fixed some bugs with the parser that I haven't pushed yet. I am also writing an improved interpreter that will use the parser instead of the hacky thing that just goes through strings.
However, for the time being, even after I release this version, I would recommend maybe using the older version for a while if anyone is using it, because this version is probably even more buggy.
However, you know how a few posts ago (but more than a year ago (wow) ) I posted that post where I said that I didn't think bifurcate can be used to split values into more than 2 values?
Well I still kind of think that, but on the map page for homestuck on act 6, it says split Act_6[Act_1,Act_2,Act_3,<etc>];
So this is something I intend to implement, and something I am implementing.
And like I said before I would like it to be done with repeated bifurcation, as a sort of syntactic sugar.
And I am thinking I want it to be like
[a,b,c] means the same thing as [a,[b,c]]
so split Z[A,B,C];
would be the same as
BIFURCATE Z[A,BCTEMP]; BIFURCATE BCTEMP[B,C];
and that split [A,B,C]Z;
would be the same as
BIFURCATE [B,C]BCTEMP; BIFURCATE [A,BCTEMP]Z;
But the way the splits would be done could also be backwards
so [a,b,c] could be the same as [[a,b],c]
I'm pretty sure I prefer the first way, but the second way is actually easier to implement.
or at least cleaner looking to implement.
Why doesn't my code look clean ever?
Anyway, my reason for this post is this:
Does anyone have any opinions about how split is implemented?
How do you use this?
Just to be super clear, though you probably understand this, “drocta ~ath” is not for practical purpose. It is purely an amusement.
That being said, to run this, you need to have python 2 installed. (Yes, currently most new python projects are in python 3. Unfortunately I haven’t gotten around to making any of the updates I’ve wanted to on this project, and it has been years since I’ve worked on it.)
To run it: download the github repository from https://github.com/drocta/TILDE-ATH
then, (probably from the command line, though running it in other ways may also work) navigate into the folder where you put all those files, use python 2 in order to run interp_2.py . [1] Then, it will allow you to type something in. It would be good if it gave some sort of prompt saying that it is accepting input, but it currently does not. What you have to type in is the file name of the drocta ~ath program that you want to run.
for example, you might type:
python interp_2.py looptest.~ATH
in order to run the program looptest.~ATH , and then you would see the output:that alternates between “APPLE” and “ORANGE” a number of times (like, 5 times I think).
If it isn’t working for you, let me know and I can try and help you troubleshoot what’s going on.
If you are asking, not “how do I run the programs in this language” but “how do I write programs in this language”, uh, read through the rest of this blog I guess. It isn’t complete, but the point of this blog was meant to be a tutorial for how the language works. If you have any particular questions about how to do a particular thing in the language, then ask that. But I don’t currently have time to re-do the whole project of this blog and put a tutorial for the language as a whole in one response to an ask.
P.S. I am currently in grad school for math (I made this language while in high school). I haven’t been doing all that much programming lately unfortunately.
([1] What’s that? “interp_2.py” is a weird name for the main file? Indeed it is. Originally I had “interp.py” and then before I started using git I made a new version which I called interp_2.py, and then, for basically no good reason, I kept that name for the file. If I go back to this at all, I suspect that I will change that to just “interp.py” or maybe “main.py” or something. idk.)
"computationalalchemist answered: You could use Prolog-style lists for split."
I'm not totally sure what you mean, but my best guess as to what you meant is probably pretty similar to how I making it.
I implemented it and I think it works, I need to double check though.
With what I have now when I use
split [THIS,THIS,THIS,THIS,THIS,THIS,NULL]COUNTER; ~ATH(COUNTER){ print "bananas"; BIFURCATE COUNTER[JUNK,COUNTER]; }EXECUTE(print "ok";); print "whee!";
it yields
bananas bananas bananas bananas bananas bananas ok whee!
Which seems to make sense to me, and also, fits with how lisp lists work, and apparently also prolog lists.
also where it says split it will also accept bifurcate. they are actually treated as the same command.
import statements aren't fully implemented yet though.
I think I will put this version on github pretty soon.
Thank you for the advice.
I am planning two features to add.
one us user defined functions.
the second is, uh, it would let you make an object that would have itself as one of its components or one of the component of one of its components etc.?
the third one I am not sure if I think it is dumb yet, but I was thinking maybe functions being objects that you can bifurcate together to make functions.
which would allow for macros kind of?
which of these three features are good ideas, (as in, for each, it is a good idea)?
I made a parser for ~ATH in addition to the interpreter. I might make the interpreter use the parser at some point in the future, or I might not. If I made the interpreter use the parser, the code for the interpreter would probably be a little cleaner, and possibly a little faster.
The parser is available from my github.
To use it, call tokenize on the text, and then read_all_from on the result of tokenize.
The output will be a list of lists
https://github.com/drocta/TILDE-ATH-Parser
(haven't updated lately because of other unrelated projects, and also other reasons that aren't necessary to describe)
In drocta ~ATH, the control flow (what part of the program is run when) is almost entirely determined by ~ATH loops.
The only case where it isn't is when the object initially pointed to by THIS dies. That ends the program immediately.
This post will show in more detail the basics of its usage.(also introduces the variable NULL)
Is there anything in this post that needs to be clarified?
In a program, you might want something to only happen if something else is true, or you might want it to happen provided that something had not happened.
In many programming languages you would use a command called if.
drocta ~ATH does not have an if statement.
Instead, you create a loop on an object, and inside the loop kill the object.
A little morbid, but isn't ~ATH always?
for example:
SOMECODEHERE ~ATH(SOMEVAR){ SOMEVAR.DIE(); SOMEOTHERSTUFF } SOMEOTHERSTUFF
However, this isn't the only way. There is another way that is likely preferable in most situations. I just thought this way was the most obvious, and would be an easy way to explain the other method, which is quite similar.
The loop doesn't exactly wait for the object to die, but rather repeats so long as the variable points to an object that is alive. The variable can be made to point to an object that is not alive, either by make the object it points to DIE(), or by making it so it points to a different object that is ALREADY DEAD.
(How can you expect to kill it, when it is ALREADY DEAD?!? haha)
So if the object you wanted the part of the script to run if it was alive was important, and you didn't want to kill it, you could just do this:
BIFURCATE [IMPORTANTOBJECTVAR,IMPORTANTOBJECTVAR]BLAH; BIFURCATE BLAH[V,V]; ~ATH(V){ BIFURCATE [V,NULL]V; BIFURCATE V[JUNK,V]; OTHER CODE TO ONLY EXECUTE IF IMPORTHATOBJECT IS ALIVE }
Thats nice to be able to do, isn't it.
In fact, it leads nicely into how to make loops that go around a fixed number of times.
suppose if you had
BIFURCATE [BLAH,NULL]V; ~ATH(V){ BIFURCATE V[BLAH,V]; DO OTHER STUFF, POSSIBLY WITH BLAH } MORE STUFF
V would initially be alive, so it would go into the loop, but then V would be made to point to the NULL object, which is dead, so it wouldn't loop the second time.(it would skip to after the loop
that would do the stuff in the loop once.
now what if you wanted to do it twice?
Just make it so it has to bifurcate the thing twice before moving on! Like so:
BIFURCATE [BLAH,NULL]V; BIFURCATE [BLAH,V]V; ~ATH(V){ BIFURCATE V[BLAH,V]; DO OTHER STUFF, POSSIBLY WITH BLAH } MORE STUFF
this way it will go through the loop, pop off the one side of V, doing the other stuff in the loop. V will still point to something alive after this, so it will do it again, but this time, when it pops off one side of V, the result will be the NULL object, so it will stop.
Thats a loop that goes around twice!
This can be extended to as many number of repetitions as follows:
BIFURCATE [BLAH,NULL]V; BIFURCATE [BLAH,V]V; BIFURCATE [BLAH,V]V; BIFURCATE [BLAH,V]V; ETCETERA BIFURCATE [BLAH,V]V; ~ATH(V){ BIFURCATE V[BLAH,V]; DO OTHER STUFF, POSSIBLY WITH BLAH } MORE STUFF
where the loop will go around however many blahs there are attached to the NULL, because it will keep popping the BLAHs of until it reaches NULL.
The variable V is being used to store how many more loops need to be executed, Which is a number.
V is being used to store a number.
You might be thinking something along the lines of
"Well this is all very nice, but that seems to only allow for loops that loop a predetermined amount of times."
NOT SO!
You can have another loop that makes a variable point to a number, which would then be in another loop! For example, the following will add two numbers in A and B, store the result in C, copy that result to CTEMP, and then print "some text" the ammount of times in CTEMP!
SOME CODE TO GET A AND B HERE import bluh BLAH; BIFURCATE [BLAH,A]ATEMP; BIFURCATE [BLAH,B]BTEMP; BIFURCATE ATEMP[JUNK,ATEMP]; BIFURCATE BTEMP[JUNK,BTEMP]; BIFURCATE [BLAH,NULL]C; BIFURCATE C[JUNK,C]; ~ATH(ATEMP){ BIFURCATE ATEMP[JUNK,ATEMP]; BIFURCATE [BLAH,C]C; } ~ATH(BTEMP){ BIFURCATE BTEMP[JUNK,BTEMP]; BIFURCATE [BLAH,C]C; } BIFURCATE [BLAH,C]CTEMP; BIFURCATE CTEMP[JUNK,CTEMP]; ~ATH(CTEMP){ BIFURCATE CTEMP[JUNK,CTEMP]; print some text; } print DONE!;
YAY! We just added two numbers together! In similar ways, we can also subtract, multiply, divide, etcetera!
if some of this was unclear I would appreciate requests for what needs to be clarified.
Is there anything in this post that needs clarification?
I have completed the syntax explanation post, and I think it is fairly clear now.
If you find any part of the post to be unclear, and that it requires further explanation, please say so so that I can improve it.
Now that that post is in an acceptable form, We can get on to writing our first few ~ATH programs.
print Hello, World.; THIS.DIE();
That wasn't so hard, was it?
I feel I should note that the syntax of the print command is not entirely finalized, it might change sometime later.
EXPLANATION:
The first line of the program says to output the text "Hello, World."
The 2nd line of the program says to end the program. This is because the variable THIS initially points to a object that is only alive when the program is running. If the object is ever not alive, the program stops.
oh gosh why is this a bullet-ed list I am not good with tumblr
To run a drocta ~ATH program, you need the interpreter (available from the github), and python 2.7 (the interpreter can be easily modified to run with python 3, but it is written to work in 2.7)
Open interp_2.py with python, and when the program comes up, type in the filename of the ~ATH program, and hit enter, this will run the ~ATH program.
With this example, it should output the text
Hello, World.
Now, of course, this is a relatively simple program, but at least its something.
Depending on how you are running the interpreter, the program might close immediately after outputting the text.
That might not be what we want.
To make the program not close until we specifically tell it to, we use the following:
print Hello, World.; ~ATH(THIS){ } THIS.DIE();
EXPLANATION:
The first line is the same as in the first program. It outputs "Hello, World."
The second line is different, this says to start a loop, which will continue until the object pointed to by the variable THIS is dead.
The third line says to go back to the corresponding ~ATH statement.
The fourth line is never executed, because the loop above it will continue until the object pointed to by THIS dies, and if that object dies the program ends. If it were to be executed it would make the object pointed to by THIS die, and the program would end as a result.
This program should be the same, except that it will keep running until you close it manually.
Now you have hello world written in two different ways!
PRINT2 prints a string object as a string. If you give it something that is not a string object it will probably crash, or output the empty string.
Now, to import user defined functions use
importf filename as FUNCTIONNAME;
Why didn't I put this up earlier?
because I have other projects and because sometimes I am busy/lazy.
OK!
So to make a function to import, you pretty much make a ~ATH file like normal, except the object passed into the function is put into the variable ARGS, and when you want to pass the output object of the function out, you have it as an argument to the DIE method.
It can either be when saying THIS.DIE(RETURNOBJ); or ANYOTHERVARIABLE.DIE(RETURNOBJ);
The object your killing doesn't have to be the one tied to the life of the function or anything.
Oh I should clarify, the THIS variable/object will refer to the current function, not the file that called it.
It CAN be returned, and will be dead when you exit the function.
PROGRESS!
FUNCTIONS WORK.
LESS SPAGHETTI CODE.
although this starts to make me wonder if maybe this strays too far from what ~ATH is like in comic,
but w/e.
News and tutorials on drocta ~ATH by drocta. interpreter here A brief summary of how to write code in the language (but also see the table of contents)
38 posts