Welcome to the official (and likely only) 3code programming language page!
3code came about as I pondered over some of the reasons that software tends to be hard build and understand. A theory I came up with was that perhaps one factor that contributes to the complexity of software is the nearly infinite number of options available to a programmer at any time. For instance, there are usually countless ways to implement a given functionality. Some ways are better than others, but the fact remains that the programmer has an almost limitless supply of resources to use when constructing. This is not true of physical disciplines. To use an example, consider the construction of a house: There are all sorts of constraints that control the final shape of the house. The most obvious one is money. The budget will limit how much can be spent on materials and labor. This, in turn, places limits on the types of materials that can be used. A reasonable option might be wood. That in turn will put limits on the styles of construction. The list goes on and on.. Eventually, after settling each style and material constraint, the shape of the entire building sort of emerges from those decisions.
With 3code, I decided to take one of the more limitless aspects of most programming languages and place a restriction on it. In 3code, you may only have three local variables and three global variables. Those are the fundamental limitations in the language. Originally I had also planned on limiting the number of function calls per function to three, but I never got around to implementing that limitation. Perhaps I will try it some day. In my very limited tests, I have come to a feeling (not a conclusion) that it may not be necessary to place a function call limitation in the language simply because the local variable limitations and the structure of the language itself sort of forces the idea of small, succinct functions that do one thing and one thing well.
One other aspect of 3code that is notable (I think) is that variable assignment is reversed from what we have generally come to think of as "normal." With most languages, you assign values like this:
When I first learned to program back when I was around 7 or 8 years old, I remember being very confused by this seemingly backwards syntax. Up to that point I had always been introduced to math equations with this form:
variable := 42;
Everything started to make sense when I realized that you could switch the side of the equals and still get the same result. However, that event stuck in my mind and I resolved one day to see if I could make it right. :-) In 3code, I have reversed the standard assignment order back to the "proper" way. This is the same assignment in 3code:
2 + 7 = 9
This one simple change influenced a significant portion of the resulting syntax for 3code. And, in my opinion, it worked out pretty well.
42 = i
There are only functions, variables, and numbers in 3code. The structure of the language could, I think, easily support other data types such as characters and strings, but I did not get to implementing them. Variables have fixed predefined names.
The current implementation of 3code is far from complete, however it has been sitting on my hard drive for quite awhile and I decided it was time to post it to the web so that I could get it out there. Perhaps it will inspire someone. Hopefully in a good way...
|start parameter group
|end parameter group
|end if statement
|begin function definition
|global variable 1
|global variable 2
|global variable 3
|local variable 1
|local variable 2
|local variable 3
|used in if/then
|used in if/then/else
|Currently Built-in Functions
|greater than or equal to
|less than or equal to
|output new line
|output number and new line
|output ASCII character with given value
3code programs are made up of sequences of statements. In many languages, statements require a separator token of some sort (usually a semicolon). 3code does not require such separators thanks to the structure of the language. With 3code, a single token is a valid statement that executes and the result of that execution in copied into the result buffer. Assignments and function returns are always done using the value in the result buffer. To make this slightly more clear, consider the following perfectly legitimate 3code program:
This program places the value 42 into the result buffer. Any assignment or function return that were to happen now would use the value 42. To illustrate this, let's assign global variable 1 the value of 42. Since we have 42 in the result buffer already, all that is required is this:
x is now equal to 42. We can see this easily enough:
The value 42 would be printed on the terminal. But wait.. we're not done with that 42 yet! It is still in the result buffer. Which means even after all this, we can assign y to be 42 as well very easily:
This works because the print function did not change the value of the result buffer. Now let's see the complete program all run together:
Other spaces can be inserted there, but this is the minimal number of spaces required. Note how there is no need for semicolons or any other statement terminators using this very simple syntax.
42=x println[x]=y println[y]
Functions are the backbone of 3code. Defining a new function is easy. Here's a sample function that returns the square of the number that is passed into it:
The F indicates that what follows is a function definition. The sq is the function's name. A function name can be just about any character string as long as it isn't a reversed word or character. The 1 following the function name is the number of arguments the function takes. This number can be 0, 1, 2, or 3. Everything after this until a newline is the function's body. In this case, the body is *[i i]. Since you are only allowed three local variables in 3code, the passed in values are assigned to those local variables in the order in which they are passed in. So this function's body is saying to multiply the first argument by itself. The result is left in the result buffer to be used later. Let's print out the square of 7:
F sq 1 *[i i]
Another notable attribute of 3code is that functions are defined by both their name and number of arguments. You could define another function named sq that took two arguments instead of one and both would be allowed to exist in the system. Which you got would depend of course on how many arguments you passed to the function when you made the call.
The final piece of syntax is branching. Every language needs some way to make decisions and usually that manifests itself in the form of if/then/else. In 3code, it takes the following form:
The condition is the value in the result buffer. The else and branch2 are optional. The ? at the end is required. An example might be easier to see. The following function recursively computes factorials:
condition then branch1 else branch2?
Note that the function's name is a non alpha-numeric character (!). This is perfectly acceptable to 3code. Here's an example of how we might use the function:
F ! 1 i then *[i ![-[i 1]]] else 1?
That would print 5040.
That covers the basics of 3code as it is now. Looping is done using recursion as is common in functional languages. My current interpreter does not support tail recursion and has issues with hitting stack limits due to how it is implemented. It isn't the best and I have long wanted to rewrite it, but time and motivation have not been forthcoming. Just keep that in mind when playing with large numbers and recursive functions--especially factorial!
F fib 2 println[i] +[i j]=k j=i k=j fib[i j]
|Count down from 100
F counter 1 i then println[i] counter[-[i 1]]?
|Power (for positive powers only)
F ^ 2 j then *[i ^[i -[j 1]]] else 1?