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?
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!
I've realized that recently, my tutorial posts have been too much code and not enough explanation. That was a mistake on my part, so I am going to go back now and give better explanations.
Also, I have started to make a table of contents page.
I figured that if someone wanted to read the tutorial after a significant part of it were finished, that they might have trouble, because it would kind of be in reverse order.
So TABLE OF CONTENTS AHOY!
Yeah.
So uh, any suggested changes to the format of the TOC page?
Why cant I mark this as a post that can be answered? I can let people photo reply, but once it goes in drafts I can't let people answer this question? Or is it too long?
What is with this interface?
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)
Most programs people use have some form of user input. A calculator isn't much use if it always uses the same numbers after all!
~ATH of course accepts user input and output as shown in the file Roxy sent Jane.
Also I just found out you can put more than one read more line in one post.
The input command has the syntax:
INPUT VARNAME;
What this does is when program execution meets this line, the program pauses execution, allowing the user to input text. When the user hits enter, program execution will continue and the variable VARNAME will be made to point to an object corresponding to the text the user entered.
This object is such that the left half is the object that corresponds to the first character. If there is no character after that, the right half will be the NULL object. Otherwise the right half will be the object corresponding to the input without the first character. If they hit enter without inputting any characters the object will just be the NULL object.
Now that we have that all explained, we can start to make programs that actually take user input!
As you might have guessed from the title, the thing we will be making is a very basic calculator. All it does is add two numbers, like in the last example.
But in this, it will get the numbers from the user!
One simple way to do this is to use the length of the input text as the number: The way we define what we call numbers just so happens (heh) to be such that if we interpret the object for the input string as a number, the number will be the same as the length of the input!
This isn't the greatest solution, but it is easier than other methods. We will use this method first and then move on to other methods that are harder to write, but will be nicer when using the end program.
HERE WE GO:
ok, so like I said, much of it is pretty much the same as that previous program, so we might as well just include said here:
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!;
so pretty much what we need to do it put the code to get A and B where that goes(at the beginning), as well as stuff to tell the user how to print stuff.
Like I said, the objects from the input commands can be interpreted as numbers.
so this becomes:
print INPUT SOMETHING WITH THE NUMBER OF CHARACTERS AS THE FIRST NUMBER YOU WANT TO ADD; INPUT A; print INPUT SOMETHING WITH THE NUMBER OF CHARACTERS AS THE SECOND NUMBER YOU WANT TO ADD; IMPORT B; 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!;
So yeah. That should work. I still need to test this, but I am pretty dang sure that this works.(have to go do homework now) In the next post I will explain how to make it so that the user can type in the number as an actual number!
(no features added, but cleaned up some code and added example programs)
yup.
it has the basis for how I am going to make functions if I make them, and cleaned up the code for BIFURCATEion, and removed some lines that were commented out.
if you want the new version it is on the github.
no real difference when it runs though.
(if something you wrote doesn't work with the new version, try using BIFFURCATE instead of BIFURCATE to use the old code. You probably won't need to, though. It works fine for me. and you probably haven't written anything in it anyway, because you kind of have to uh, want to, I guess)
Hi, I haven’t posted much here in quite some time.
So, is this blog abandoned? I wouldn’t say so. I intend to post more ~ATH content here at some point. I have other projects and obligations, which is why I’ve been not doing as much with it recently (”recently”: understatement of the year), but if anyone has any questions about drocta ~ATH, or would like to request that I do something in particular with it, I think I’ll probably respond within a reasonable amount of time.
Right now though I thought I would link to two blogs that I think you are likely to appreciate if you like this blog.
The first is @sbahjsic http://sbahjsic.tumblr.com/ which is for a programming language and assorted connected software meant to be, well, sbahjsic . like sweet bro and h---- jeff. (Warning though, that blog has some javascript alerts when you view it. Also it has moving parts which might be bad if you get nauseous easily or something? idk.) The blog theme there is a work of art to behold. This is likely to appeal because it is also a homestuck related programming language, and also it is great.
The second is @tilde-he which is where I post most of my non ~ATH related tumblr posts. This is somewhat likely to maybe appeal because it is by the same person as this blog (me).
Again, if you have any questions or comments about my version of ~ATH, or, really, any version that you can point out, feel free to send them, and I’ll try to respond within a reasonable amount of time.
Alright, cheers
I am still here, and I am wondering what my update schedule for this tutorial should be.
Its been a week or two since I last posted part of the tutorial, so I feel like I probably should have posted during that time, but I haven't.
I will probably post one today.
Do you have any suggestions for what type of update schedule I should keep on this tutorial?
yeah, can i make ~ATH continue to generate variables? like if one part of the program dies it generates another variable? because i've been trying to do that lmfao
Hi. Not sure what you mean by continue to generate variables. If you mean like how in lisp you can like, automatically generate a new variable name and then use that variable name, then no, drocta ~ATH doesn't have a feature like that, but I'm guessing that's not what you mean.
Variables are just like, names in your program. If you want more objects, you can just continue bifurcating objects to get more objects.
drocta ~ATH does not support parallelism or concurrency or whatever (though presumably canonical ~ATH would, in some sense. At least, it appears to handle circumstantial simultaneity.), So, if you are referring to drocta ~ATH, then I'm not sure what you mean when you refer to part of the program dying. Though, given that in canonical ~ATH, there is the expression
[THIS,THIS].DIE();
then it seems like, presumably, it should be possible to have something like
THIS.DIE();
in order to describe part of the program dying (ending), without another part doing so.
But, again, drocta ~ATH does not support anything like that. Feel free to make a fork of it which does though. Please let me know if you do!
Do you know of any ~ATH implementations or dialects besides yours and mine?
Actually, yes kinda. Sorta anyway. There is a compiler of sorts that is intended to be ~ATH based, though I don't think it matched canon all that well.
It did have one feature which I found interesting though. It had a command which would create a variable thing which was truthy iff there was a process currently running with a given name, so you could have in a ~ATH loop something that loops until another program is closed. It was a pretty cool feature, but the syntax for it was odd. The author misinterpreted the panel where Sollux deletes the different virus folders he had, so the command was called rm -rf or something like that.
It also had a command to run an executable (by file name) iirc.(which allowed for easy implementation of the robin hood and friar tuck programs)
It also was an editor of sorts, but it had a small window that I don't think was resizeable. It worked (iirc) by doing some string replacement to turn the program into a c(++?) program, which it then compiled.
I'll try to find it so I can link it. I don't think it represents ~ATH all that accurately, but the using another program as one of the objects seemed like a neat and probably accurate feature (given the mobius double reacharound)
tl;dr yes
ok, so last time I didn't actually get to converting from unary to binary again.
So I guess I will do that here. (HAHA I JUST GOT IT TO DIVIDE WITH REMAINDER. APPLYING THAT TO THE CONVERSION IN PART 2)
so to do this we have to find the largest power of two not greater than the number, then the largest power of two not greater then what is remaining of the number, and repeat until we reach zero.
OR we can
find the largest power of two not greater than the number, then for each power of two less than the number, if it is not greater than it, use a 1 and subtract it from the remaining number.
and then for both of these, of course, reverse the result at the end so it is in the right order.
I think so far the second one of these sounds better.
but come to think of it, repeated integer division by two might work well.
that is, instead of repeated doubling, and then checking which one is larger, it might be faster to do repeated halving. (with a remained of either zero or one)
this could be then used to make a integer base 2 logarithm, with a remainder thing.
and instead of checking each power of two less than it, we could just find the integer base 2 log, and the base 2 log of the "remainder" and the base 2 log of THAT remainder and so on.
that seems like its the best way to me so far, but how do we take the integer logarithm AND get the log remainder thing?
getting the logarithm is fairly simple, just keep dividing by the number until you reach 1.
but how do you get the remainder part? and also how do you do the integer division in the first place with these number like things that we have created?
well lets answer the first of those questions first.
It seems like the remainder of the logarithm is probably related to the remainders of the divisions that make it up.
so lets look at taking the integer base 2 logarithm of 9:
9 4 (rem 1) 2 1
that took 3 halvings, and there was one remainder of one.
what if we try on 10 or something though?
10 5 2 (rem 1) 1
that also took 3 steps and one remainder, but the remainder was on a different step.
now lets try 11
11
5 (rem 1) 2 (rem 1) 1 (rem 0)
now wait, theres a pattern here, which might allow for converting it even faster.
the remainders of the divisions are the remainders of the logs expressed in binary!
we COULD turn them back into unary, so we could take the log of it again,
but what we are doing it converting it into binary anyway! (so that would be a silly step)
so if we look at what we were doing with the repeated division again, but looking at the numbers in binary, it will be pretty evident how to convert it in a better way.:
lets try 9 again (which is 1001)
1001
100 (rem 1) 10 (rem 0) 1 (rem 0) 0 (rem 1)
whats that? the remainders are the number we want in binary?
and now that I think of that it was kind of obvious?
yes. I for some reason did not think of that earlier.
so yeah, thats what we will do, we will repeatedly integer divide the number by 2 until we reach zero, and the remainders will be the number in binary.
but wait, we haven't even written out how to divide the number by two in the first place!
well turns out its not actually that hard to do.
in a loop (that keeps looping provided the number is not zero) subtract one, and if its still not zero, subtract one again. if it is zero after the first subtracting, the remainder of that division is 1. (only the last loop will cause the remainder to be 1) count the number of times where 2 was subtracted.
so like for 5 that would be
5 3 1 1 2 0 2 (remainder 1)
so that gives the correct answer (2 with remainder zero)
ok, now to implement this integer division by 2 in drocta ~ATH (wow! already? that was fast! /sarcasm)
//given that NUM is the variable that has the name we are halving BIFURCATE [NUM,NUM]2NUM; BIFURCATE 2NUM[NUMCPY,JUNK]; BIFURCATE [BLAH,BLAH]2BLAH; BIFURCATE 2NULL[SUBCOUNT,REMAINDER]; ~ATH(NUMCPY){ BIFURCATE 2BLAH[UNEVEN,JUNK]; BIFURCATE NUMCPY[JUNK,NUMCPY]; BIFURCATE [NUMCPY,NUMCPY]2NUMCPY; BIFURCATE 2NUMCPY[NUMCPYCPY,JUNK]; ~ATH(NUMCPYCPY){ BIFURCATE [BLAH,SUBCOUNT]SUBCOUNT; BIFURCATE NUMCPY[JUNK,NUMCPY]; BIFURCATE 2NULL[UNEVEN,NUMCPYCPY]; } ~ATH(UNEVEN){ BIFURCATE [BLAH,REMAINDER]REMAINDER; BIFURCATE 2NULL[UNEVEN,JUNK]; } } BIFURCATE [NUMCPY,NUMCPY]2NUMCPY; BIFURCATE 2NUMCPY[NUMDIV2,JUNK];
ok, that should divide the number by 2, put the result in NUMDIV2, and put the remainder in REMAINDER.
this takes linear time based on the size of the unary number. (provided I didn't make a mistake)
so now it is time to make sure it works, hold on a second while I check it. (I mean, im not going to post this until after I check it, so it doesn't really make sense to tell you to wait, because you just keep reading, but as I am writing this, I am about to test it.)
ok, I tested it. and it didn't seem to work, but then I realized that I made a mistake in making the test. (I checked the wrong variable)
but yeah, turns out that works...
ok, so now we need to repeatedly divide the number by two to get the remainders.
and we need to store these remainders in a list or something.
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