Brian Connors
25 September 1999
A note on the syntax examples: They're not in BNF. <Angle-bracketed> items identify lexical tokens, while clauses between |bars| are optional parts of the specification. This applies to the original reference as well, but was not specified.
The feedthebear() example was probably the first code ever written in Magenta, but you'll notice that it doesn't have an obvious return value (for that, check the rewritten version in the ARM. The function syntax in Magenta is very bizarre, and I don't think I ever understood it for more than a week.
You can see a number of oddities in the syntax that likely wouldn't have been implemented:
function <id> ( <argdeclist> ) returns <typename> | punts <expr> <namedblock> |As for a call... that's a little sticky, but it generally looks as you'd expect (but see Entry below). Arguments may be passed by value, by location (i.e. pointers), or by reference; the syntax is based on C++.
Okay, I'll make this quick. Do it either C-style ( int [] []
) or Basic-style ( int (x,y)
). Whatever works.
I found out later that I was stealing liberally from Perl long before I'd ever learned it. This section replaces the original control flow section in the definition, since it's incomplete and begs rewriting anyway.
The curious thing about the control structures of Magenta is that they throw the block syntax into complete disarray. Named blocks are actually possible, though, and someone might have a use for a named for loop.
For the record, I'm redefining all control structures in terms of C/Perl syntax. Magenta was heading in that direction anyway.
for ( <initcond> ; <finalcond> ; <stepcond> | ; except <condition> |) <block>;I should just apologize to the language police right now. Trying to create a Basic-type for loop with C syntax was cute, but even in a joke language anyone can agree that the C form is the Right Thing, being more flexible. Magenta was intended to be perverse and pointless, but one thing it wasn't was weak. This is my final offer and I'm sticking by it.
The new thing is the except clause. If there is an except clause, the clause's condition is examined and if met, the loop is advanced without executing the block. Could this be useful? I dunno...
while ( <condition> ) <block>;
until ( <condition> ) <block>;These two structures evaluate the condition at the top of the loop. They should be self-explanatory.
do <block> while ( <condition> );
do <block> until ( <condition> );Evaluate at the bottom of the loop. Again, self-explanatory.
if ( <condition> ) <block> | else <block> |if/else construction, just like C (though ideally the keyword elseif should be recognized, and maybe cut in half by the lexer for the parser's convenience).
unless ( <condition> ) <block> | else <block> |This is actually Perl's unless statement. Perl is a language hack's dream because there's a lot of random moderately useful garbage in there, and I wish it had been more prominent at the time so I could have stolen a few of its features for Magenta. Slipped in when I wasn't looking, it did.
switch <condition_variable> { case ( <condition> ) <block> ; ... default <block> ; };This is a very interesting idea of what a switch or select construct could look like. C treats it as a sort of computed goto farm, allowing oddities such as Duff's Device. Magenta takes a different tack, allowing each case to have its own block and therefore its own scope. Interestingly, it would seem that we'd have to add ranges to the language as well, but that comes under expression syntax.
Magenta had two control structures directly related to its IPC capabilities as well.
during <lcondition> <block> ;During watches a specific condition (theoretically anything, but in practice there'd be no point in using a during unless it was watching another task in the same program) and evaluates that condition, then executes its body until it tests the condition and finds it false. (There is no opposite construction paralelling while/until and if/unless; the only name I could come up with for it is despite and that would be really confusing because it doesn't mean much of anything.)
when <condition> <block> ;I bet you didn't know Logo had concurrency features.
That (specifically, the Atari 8-bit implementation) is where when came from. When essentially blocks until the condition happens, then executes its block.
finally <block> ;One after-the-fact feature, generalized from Java, was the finally clause. It was part of the exception system in Java, but in Magenta it becomes a block of statements executed whenever a loop construct is exited. Interestingly, at the time it seemed like syntactic saccharine (i.e. cute but useless); looking back, however, it makes some sense to make anything in the finally clause share scope with the loop body that just exited, so you can play with something that was declared inside the block. Good for cleanup purposes, I suppose (you could maybe dump the contents of a variable local to the loop to something outside its scope for postmortem purposes).
The following table is all the operators in Magenta, arranged by precedence. I've added a few. Where necessary I've added commentary, tagged with footnotes; I've also revised the table to make it more Java-like. There will be differences; fortunately, nobody has implemented it yet so there shouldn't be problems.
operator default meaning direction ------------------------------------------------------------------ () force precedence : member lr -(unary) negative lr !(unary) not lr ~(unary) compl lr * pointer to (address) rl & contents of rl <id>& reference to lr sizeof size of an object rl ++(unary) increment operand->operator --(unary) decrement operand->operator & bitwise AND lr | bitwise OR lr ^ bitwise XOR lr ~ binary select(1) lr $ binary mingle(2) lr ^: exponentiation lr * multiplication lr / division lr % modulo lr + addition lr -(binary) subtraction lr << shift left lr >> shift right lr >>> unsigned shift right lr < less than lr <= less than/equal to lr > greater than lr >= greater than/equal to lr == comparison lr != not equal to lr && logical and lr || logical or lr ^^ logical xor lr := or = assignment rl += add to variable rl -= subtract from variable rl (also *=, /=, %=, &=, |=, ^=, ^^=, >>=, <<=, >>>, and so on) , comma lr
[ <start> .. <finish> ]
syntax, though I'm not quite sure how it would work, except to say that comparing a value against a range would return a true value if it was in the range and a false value if it wasn't. Precedence? I don't know.
What the hell is a variagent, anyway? Well, I dumped the reference manual through a program called MacTravesty one day and came up with one interesting word in the whole garbled mess that came out. I knew that Magenta would have to support variagents, but I didn't actually know what the bloody things were.
I finally decided that a variagent was simply an object method that was called as its own thread within the program. The syntax would look something like this:
class bar extending foo is: <yada yada yada> variagent baz (int num) { <more yada yada yada> }; <continuing in the yada vein> } end bar;And then would be called somewhere in a program with
baz(num);Treating a variagent as a function would probably result in the parent task blocking until it got an answer, therefore negating the main reason for having the things in there in the first place. The convoluted priority syntax applies.
This turned into a prime example of not knowing when to quit. Magenta packages were a (supposedly) slightly more elaborate version of Java packages, with interface/implementation distinctions and recursive cascading subpackage visibility (I'll explain later). It was pretty evil.
Basically, a Magenta package could stand by itself or be part of another package. The interesting thing about Magenta subpackages that Java doesn't have is that the contents of a subpackage would be visible to the parent package, but not to sibling packages. The syntax would look something like
package <id> | of package <id> | is: <various and sundry declarations> end <id>;Alternately, there was a specific subpackage symbol that could be used to declare something in the x.y format, but looking back it's silly.
Packages never made it out to the public, at least not in this form. It seems that if you create this sort of cascading access format, you drastically complicate privacy/visibility issues. Suddenly there's no more protected class; you have to create individual protection levels for each level of nested packages whereby the children can see into the parents but not their siblings (but what if you want a child to be visible from a grandparent?). This gets extremely nasty after a while, and I don't even want to speculate what the syntax or semantics of that would look like. This is what I meant by recursive cascading subpackage visibility; think of an ice cube tray, I guess. You can fill the tray by pouring water into one cube slot (though it takes a while), but one cube is separate from another.
To top it all off, programs would be declared using the same syntax (with no nesting, obviously; that would be COBOL), only using the program keyword and requiring a main() function. The whole thing is unspeakably nasty, and I never finished a usable spec. The concept is intuitive enough in principle, but godawful ugly to put down in syntax.
There are a few control flow tricks I've thought of for Magenta. Some are new, some are old. Some are cool, but don't belong in the language.
I suppose it's significant that Dennis Ritchie could never figure out what to do with the entry
keyword in C. Here's a solution:
... r = foo:bar(42) entry baz; //calls foo:bar()... ... class foo is: ... function bar(int n) returns int { ... baz: dosomething() //and jumps to this label after //initializing variables ... } end foo;What's going on here is that you have multiple entry points into one function. They're just labels, so they fall through. The clever bit is that they initialize any variables that should be in scope at the label; the probem is that you can't guarantee a variable will look as you expect it. Very ALTERish, in my opinion.
alter <label> to <label> ;Should be in there, should be deprecated. Labels are intended as specific (though retargetable) address references, an interesting side-effect of which is that alter could probably be implemented as a function that takes only pointers. However, it more likely requires some attention from the compiler. This is definitely syntactic saccharin, though; even so, though you might never see a goto in a Magenta program, you could do some very weird things with alter anyway.
Oh, and to clarify: what alter does, for those of you who don't know Cobol, is to alter the destination of a goto (or a comefrom, one might assume) to another label. In Magenta, the label that's altered is actually the destination/origination of a goto or comefrom at that point.
comefrom <label> ; resume;I think comefrom might have one or two legitimate uses, but it would require some very bizarre parsing tricks to work. There was some call for both comefrom and alter in the original discussions; this is the syntax. Come From in Magenta is intended not as a regular control construct but more a debugging aid:
function fred() is: ... trapdoor: console:printf("Not here\n"); ... /* [?_DEBUG] //remember the conditional compilation directives? comefrom trapdoor; //just to separate report code, really stdout:printf("but here\n"); resume; //pop the stack and go back from whence //you came */ end fred;More precisely, the semantics are like this:
Why not? Granted it's rather silly, but it can't hurt to have some way of remembering the source of a goto. The semantics for resume are the same as listed under comefrom.
No way, no how. And I tried, too, before I realized that a) they were pretty much useless except for stack unwinds anyway and b) the semantics of doing a goto between functions were literally impossible.
I threw a storage qualifier called persist into the language definition without any particular idea of how it was supposed to be handled. So here's how...
persistent <id> <filepath> ;
any persistent variable used within that block is stored to that file. If another persistent file is created/accessed, it becomes the default until it passes out of scope, at which point it is closed automatically.
As it happens, there was a lot of call for a more dynamic object model. In the grand tradition of design-by-committee, the dynamic folks get the isa and becomesa keyword.
if (food isa zookeeper:arm) { bear:spit(food); food becomesa trash; }The beauty of this is that you can see something to play with, but it won't quite work the way you want it to. It's very rudimentary, it's clumsy, it works with a considerable embarrassment factor; in short, it's a kludge, and it's perfect for Magenta.
I would love to see more features in here. Send them to me and the best will be posted on these pages; I'd especially like to see someone try creating a usable reference manual.