3 <TITLE>Rune Language Overview</TITLE>
6 <CENTER><H2><B>Rune Language Overview</B></H2></CENTER>
7 <CENTER><H3><B>(c)Copyright 1996-2015, Matthew Dillon</B></H3></CENTER>
9 <H2><B>(I) History</B></H2>
12 Well, the story of Rune is both short and long. It's short because I've
13 rarely discussed the language in detail outside of a few friends, and long
14 because my desire to write a language began in the late 1980's after I
15 had learned C and started looking for something better. Many years later
16 and people will note that I am still a hard-core C programmer, working
17 mainly on the DragonFly kernel code base. You might think (if you
18 don't know me) that I'd be able to find *something* out there that
19 would satisfy me language-wise, other than C. But I haven't.
21 For various reasons I have very little love for most of the programming
22 languages out in the world today. I hate the design-by-committee mess
23 people call C++, I hate the kitchen-sink-control-the-programmer approach
24 taken by Eiffel, the cacophony that is perl, the limited Objective-C,
25 the ever-changing typeless lets-break-everyone-again TCL (though I will
26 admit that, of late, it seems to have settled down. Still, TCL isn't
27 for me!). I'm not interested in the politics associated with Java
28 that has destroyed it as a viable programming language, or the wordy
29 semantics and constructs. (Java is a toy that should never have seen
30 the light of day IMHO). I don't mind some of the newer mostly interpreted
31 languages but I feel that many are missing things.
32 I have been unenthused with nearly every language I've come across.
33 Languages like Scheme, Python, and Ruby are better, but still don't
34 have the level of flexibility of abstraction that I desire and don't
35 handle locking or threading the way I'd like to see either.
37 I'm a great believer in the concept of a class hierarchy and in
38 object-oriented (or at least object-centric) design, yet as you can
39 see I have done little but write in C in all the intervening time.
41 Of course, another reason I have not switched to a more object-oriented
42 language is simply because every time I try I find myself constantly
43 comparing it to my ideal.
45 Rune is culmination of almost everything I want in a language.
47 Throughout this period, I began thinking of my dream language
48 as "D" in my head, but I was ambivalent about trying to name it that
49 in public. I finally came up with a great name, "Rune", after running
50 through literally a thousand different possibilities. I don't care if
51 it's already being used for other things... so is virtually every other
52 interesting word in the dictionary.
54 So why has it taken so long to write Rune? Well, there are many
55 reasons. Some of you might remember the 'DICE' system I wrote for
56 the Amiga and sold as shareware in the early 90's. DICE is a full
57 ANSI-C compiler. And a preprocessor. And an assembler. And a linker.
58 And a full set of standard C libraries. I wrote it all, for the 68000.
59 Unfortunately this points to a severe character
60 flaw of mine when it comes to me and writing programs... I am a bit of a
61 perfectionist when it comes to my own private work, and other people's
62 work rarely achieves the level of perfection I am comfortable with.
63 I was able to build DICE
64 quickly because the C language standard already existed, as well
65 as standards for all the other necessary pieces, so I just had to
66 start coding it up. But Rune is a different matter. With Rune I am
67 designing the language as well as implementing it, and that amplifies
68 the problem when one has my particular character flaw.
70 Over these many years my language has evolved. I have added many
71 features and removed many more. I have written over two dozen drafts
72 of the grammer but it has only been the last five or so years that
73 it's settled down into something I consider to be reasonable. Another
74 time factor is what I call the 'Dillon' factor. When I set out to
75 design something I expect to be able to grasp and hold the entirety
76 of the project in my head without external references. Rune is complex
77 enough that it has taken quite a while to achieve this state of mind.
78 I want to be able to scale simple concepts into extremely sophisticated
79 capabilities. I hate special cases, so when I set out to write the
80 lexical scanner, the parser, the semantic search code, and the other
81 pieces that make up a language, I expected to get the core written
82 and proven out in a week or less or it was no good. Needless to say I
83 have had many false starts. A false start for me typically means wiping
84 the code (rm -rf) in disgust. The only thing left after a false start is
85 the memory of what went right and what went wrong, and I used that
86 to begin the next iteration.
88 The reason I do things like this is actually quite simple.
89 If I've done the low level pieces correctly
90 the sophisticated pieces wind up being trivial to implement. I believe,
91 strongly, that getting the core right greatly increases the flexibility
92 of a project and I knew that flexibility would be key in further
93 development of the language once the initial release occured.
94 Whatever time I've apparently wasted getting this far is going to be
95 repaid ten fold over in the future.
97 <B>Well, I finally did it! I got over the hump and now have something
100 mark when I looked up from the keyboard one day and realized that I
101 had just implemented variable arguments, constant procedure and operator
102 call optimizations, and default value aggregation for types all in one
103 day without realizing it. A week later I could actually start
104 writing code in Rune, and a week after that I could actually start
105 writing *sophisticated* code in Rune. And, as a bonus, the unoptimized
106 interpreter has wound up being pretty damn fast just on its own.
107 It's good enough now that I can simply not worry about it and focus
108 on other issues. After spending a month coding I set it all aside to
109 catch up on other things, and now I've dusted it off again,
110 found that I still understand all the concepts and all the
111 code (which must mean the code must be pretty good in my view), and I'm
112 going to move forward with the work necessary for the first release.
114 So that's the history in a nutshell. I've conveniently left out what
115 few discussions I've had with friends and my many attempts to kickstart
116 the process (multiple times, over the years). In the end it's only the
117 final draft that matters, and this is it. This is the end of the
118 beginning and the beginning of a new beginning.
120 <H2><B>(II) Language Features and 'why I did it that way'</B></H2>
123 Right off the bat I will say that I absolutely can't stand languages
124 that try to implement <I>everything</I>. Kitchen sink languages tend
125 to be so complex that they wind up being unusable or unreadable or
126 unlearnable or unmaintainable.
127 When a programmer is able to implement some concept
128 ten thousand different ways, you will wind up with ten thousand different
129 implementations and you can shuck any efficient, collaborative
130 development right out the window.
131 I have thought long and hard in regards to which
132 concepts I should include. Should I have inheritance,
133 multiple-inheritance, subclassing, organization of class hierarchies,
135 I've thrown out a lot of concepts as simply not meshing well with the
137 For example, you will not find traditional multiple-inheritance in Rune.
138 Why? It's overkill to the point of confusion (kinda like C++ actually).
140 You will find relatively robust in-band exception handling with
141 return-value bypassing, but it is explicitly not supposed to be used
142 for nominal status returns as might be handled when doing non-blocking
145 You will not find a formal single-root class hierarchy.
146 Been there, done that, stupid idea.
147 Nor will you find complex overloading features. Rune is designed
148 in a manner that avoids most potential namespace conflicts without
149 over-burdening the programmer with long dotted identifier sequences
150 or complex specifications. Since most conflicts are naturally avoided
151 in the first place, Rune doesn't need sophisticated overloading or
152 conflict handling features. What will you find in Rune? Read on!
154 * Import mechanism. Rune uses an import mechanism to collect project
155 files and libraries together into a program. There is NO include
156 mechanism. If you import a directory then Rune will locate
158 and import that. "main.d" then typically imports whatever libraries are
159 required as well as the other project files and provides a <B>main()</B>
160 to start execution of the program. library-library dependancies are
161 typically resolved simply by having the library itself import whatever
162 other libraries it needs (and as a welcome side effect this makes
163 libraries self contained). Duplicate imports of self-contained entities
164 are allowed and do not result in any significant extra overhead unless
165 you are really, really stupid.
167 * Semantic search is not class-hierarchical. Rune locates an identifier
168 through a semantic search based on how you stack and name imports.
169 Within a procedure the semantic search begins in a manner similar to C,
170 where the search checks local declarations within the procedure
171 and then within the current file. If the identifier cannot be found
172 the semantic search progresses upward through the import hierarchy
173 until it hits the root (of the import hierarchy) -- the original file
174 that kicked the whole thing off in the first place. There is no object
175 root. Identifiers are searched level-by-level and can be forward
176 referenced (Rune makes no distinction between backward and forward
177 references except in one parse-time convenience case). You can push down
178 into an import, type, or class, by using a dotted sequence of identifiers.
179 For example, if "main.d" imports
180 "File" (file support) and "a.d", "b.d", and "c.d", then code sitting
181 in "c.d" can access the file support library simply with a
182 construct like <B>File.FILE *fi = ....;</B>. Rune also includes
183 a mechanism which allows you to push into an import without
184 specifying the import's id, called the
185 <B>import ... as self</B> mechanism.
187 The <B>import ... as self</B> mechanism is typically used only to
188 collect tightly integrated source files together... for example,
189 source files in a subdirectory. The main system library is typically
190 imported by a project <I>as self</I>, but library imports in general
191 are almost universally named entities rather then <I>self</I>
192 entities in order to avoid namespace conflicts.
194 * Typedefs. Rune supports typedefs. Typedefs are used to 'alias'
195 classes, with or without modification, making them available
196 at mulitple semantic points. A typedef can be placed anywhere
197 a declaration can be placed: At the top level, in a <B>class</B>
198 definition, in a procedure... even inside a compound type if you
199 want (though I might call you crazy). Typedefs are extremely
200 convenient constructs which allow you to short-cut common types
201 to avoid having to specify class paths with lots of dots. For
202 example, if your project uses <B>File.FILE</B> a lot, you could
203 simply <B>typedef File.FILE FILE;</B> in your project's "main.d"
204 source file and just use <B>FILE</B> in all the project source
207 Typedefs can also be used to change default assignments. For
208 example, <B>typedef myint int a = 4;</B> creates a new type
209 <B>myint</B>. When you instantiate <B>myint</B> the object will
210 default to a contents of 4 unless you specifically give it another
213 * Classes. Rune supports a class mechanism. All types except compound
214 types are based on a class somewhere. This includes core Rune types
215 such as <B>int</B>. What you know as an <B>int</B> in C serves the
216 same function in Rune but <B>int</B> is actually a class in Rune.
217 Classes contain declarations: typedefs, storage declarations,
218 operators, procedures, and constants. <B>It is important to note
219 that Rune uses a C-like pass-by-value model. Rune also supports bounded
220 pointers and so can pass objects by reference, and also suppors passing
221 and returning an object by <I>lvalue</I>. An <I>lvalue</I> is effectively
222 an object which appears to be passed by value but is actually passed
223 by reference, allowing the procedure to modify it. <I>lvalue</I> is
224 necessary to implement operators like "++" and "+=".
225 Rune makes no distinction between core classes and
226 user-defined classes.</B>
228 * Subclasses. Rune supports a subclassing mechanism. A subclass is
229 basically a merge of its superclass plus additional declarations
230 and/or refinements of declarations inherited from the superclass.
231 Namespace overloading is explicit.. if you declare something using
232 an identifier that conflicts with a declaration in the superclass
233 and you do not explicitly say that you are refining the superclass
234 declaration, then your declaration will be used by anything you
235 define in your subclass but the 'hidden' declaration in the superclass
236 will be used by anything defined by the superclass. Rune allows you
237 to refine methods, typedefs, and storage (changing the size of
238 preexisting storage elements in an object). However, because I have
239 no wish to force the entire language to be dynamically typed at run
240 time only subclasses which refine procedures and extend existing storage
241 are compatible with dynamically bound reference pointers (see later).
243 * Refinement. When you subclass a class you can refine declarations
244 made in the superclass. You can only refine declarations by making
245 them more specific. For example, if the declaration in the
246 superclass is <B>Integer x = 4;</B> then you could refine it to
247 be <B>int8 x;</B> but you could not refine it to be <B>float x;</B>.
248 You can refine types and default values and you can partially
249 resolve procedure arguments (and/or refine argument and return types).
250 <B>Refinement is an extremely powerful mechanism. The core library
251 uses refinement to declare all of Numeric's operators in the Numeric
252 superclass using a typedef'd type called 'T'. The core library then
253 simply refines the typedef T in various subclasses in order to
254 automatically re-form the supeclass operators and procedures into
255 more specific operators -- without having to redeclare the operators
256 or procedures at all.</B> This means, in effect, that you can write
257 generic procedures in the superclass that operate on declarations
258 defined by the superclass in the context of the subclass.
259 <FONT COLOR="darkgreen">Subclas refinement is one of the most
260 sophisticated and important features of the Rune language</FONT>.
262 Refinement also works well with <B>method</B> procedures. These are
263 procedures which execute with an object context called <B>this</B>.
264 If you create a subclass which refines a superclass declaration,
265 and then call an unrefined superclass method, the superclass method
266 will use your refined declaration(s). Superclass procedures which
267 are overriden by subclass refinement are not automatically called.
268 Instead your refined procedure must use the
269 <B>super.<I>funcname(...)</I></B> mechanism to chain the
272 * Pass-by-value and embedded objects. Rune uses the C pass-by-value
273 and embedded-type model. In C you can embed one structure inside
274 another. You can do the same with classes in Rune. Rune also has what
275 we call an <I>lvalue</I> extension which allows a procedure to
276 enforce a pass-by-reference and/or return-by-reference for an object
277 that is passed to it literally, which Rune operators use to implement
278 variable-modifying behavior (such as <B>++x</B>) and which you will
279 be able to use to for the same purpose.
281 * Forward referenced identifiers. Rune does not impose many requirements
282 on the ordering of declarations within a file or between files and
283 libraries. The only thing you cannot do is directly embed classes
284 to form a loop (class A into class B and class B into class A).
285 You can, of course, embed mutual pointers. Certain identifiers cannot
286 be forward referenced due to being handled at parse time or being used
287 before being initialized in a procedure, but most do not have that
290 * Bounded pointers. Rune's interpreter uses bounded pointers. For
291 example, a pointer into an array can be manipulated with addition
292 and subtraction, just like C. But Rune will not allow the pointer
293 to be accessed if it goes out of bounds. Rune also does not allow you
294 to arbitrarily cast pointers from one type to another (XXX ok, it does
295 at the moment, but soon it wont). You can think of this as
296 C with 'safe' pointers. Pointers have been given a bad name by C,
297 but pointers are not inherently a bad concept.
299 * Dynamically bound pointers (known as reference types). In Rune something
300 like 'Frame *x;' represents a pointer to a frame. A dynamically bound
301 reference is something like 'Frame @x;'. This creates a pointer object
302 that can be bound to the specified class or any compatible subclass of
303 the specified class at run-time. You can only access the fields
304 defined by the superclass (Frame in this case) through a reference type,
305 but that's why we have refinement. Your method calls will call into the
306 version of the procedures refined by the associated subclass, for
307 example. <B>Reference types are most often used to allow a superclass
308 to define procedures to manage linked lists of the object which mix
309 various subclassed versions of the object.</B>
311 * Automatic zeroing. Declarations are automatically initialized to zero
312 if not otherwise assigned. Allocations are always zerod unless
313 explicitly told not to.
315 * There is no union. Believe me, this is a feature. I might still
316 wind up adding a union feature later but, so far, I have found that
317 the subclassing mechanism is sufficient to handle situations that
318 would normally use a union in C. And if the subclassing mechanism
319 is not sufficient, I will <I>make</I> it sufficient.
321 * User-defined operators. All operators in Rune are physically declared.
322 Even Rune's core operators on integers and other types are declared
323 in the core Rune "sys" library. You can define your own unary and binary
324 operators and you can use almost any sequence of operatic characters
325 to do it. Rune imposes one bit of sanity, however. You cannot specify
326 the precedence. The precedence of an operator is fixed based on
327 the characters making up the operator. All C-like operators wind
328 up with C-like precedences. Your own operators will wind up with
329 C-like precedences too, using simple rules defined in the Rune grammer.
332 User-defined operators can be emplaced in any class belonging to the
333 arguments you supply to the operator when you use it, and may also be
334 emplaced in your main semantic search path. So, for example, you
335 cannot add a new operator to the core <B>Integer</B> class but you
336 can certainly add a new operator that operates on various kinds of
337 ints semantically, such as in your <B>main.d</B> module. </FONT>
339 * LValue operators and procedures (deserves separate mention). An
340 LValue operator is capable of assigning the
341 left hand side as well as returning a result. The left hand
342 side must be an lvalue. For example, the C "+=" and "++" operators
343 are LValue operators, while "+" is a normal RValue operator which
344 returns a temporary result. LValue operators allow Rune to implement
345 almost the full complement of C operators and additionally allows you to
346 build your own user operators with similar characteristics.
348 * User-defined casts. All casts in Rune are physically declared, including
349 core castings such as int8->int16. The Rune class library implements
350 the more common casts but does not implement all casts. For example,
351 you cannot cast <B>int8</B> directly to <B>uint32</B>. You would
352 have to do something like <B>(uint32)(uint8)int8_value</B>.
354 * Method calls. A method call is something like <B>file->setMode(10)</B>.
355 That is, making a call through an object. In Rune a method declarations
356 or call is similar to a procedure call passing the object as an lvalue
357 as the first argument to the procedure and, in fact, the Rune resolver
358 converts method declarations and calls to exactly that. You can also
359 create <I>global</I> method calls. These can be called through the
360 object or through the object's type or class, but rather then passing
361 an object as the first argument the type is instead passed. Something
363 <B>FILE *fi = FILE.fopen(...)</B> is an example of a global method
366 Rune normally constructs the first argument for a method call silently and
367 automatically. The argument is named <B>this</B> and will be the object
368 the method is acting upon as an lvalue. However, there are cases where
369 you might want to declare this argument yourself. Specifically
370 there are cases where you may wish to pass an lvalue pointer to an object
371 rather then the lvalue object itself.
372 Constructors are typically used to initialize objects
373 in-place, while method calls are typically used to create new objects.
374 Having a method call create an object can get complex if the caller really
375 wants the method to create a subclass instance of the object. While
376 the caller could certainly refine the creation method to allocate the
377 subclass, it is almost always easier to simply have the original method
378 procedure take a reference pointer lvalue as the <B>this</B> argument
379 and allocate the correct subclass object itself. For example,
380 the <B>createFrame</B> method in "classes/gfx/window.d" needs to be passed
381 a reference pointer as an lvalue so it can allocate a new Frame object
382 and then assign it to the pointer. The reference pointer is typically
385 * Rune does not implement multiple-inheritance, on purpose. The main
386 reason is that implementations of multiple inheritance abstract what
387 is actually going on so much that they often make code unreadable
388 and unmaintainable. In Runeland we would prefer that code be relatively
389 easy to understand and maintain.
391 * Declaration/Class merging. You do not have to build all the declarations
392 making up a class in the <B>class</B> definition itself. You can place
393 the declarations outside the class definition and even place them in
394 other source files as long as <B>library</B> scope is adhered to and
395 the declarations do not forward-reference the class (this is because
396 the merge occurs at parse time rather then resolve-time). This allows
397 very large classes to be built without ruining the readability of the
398 source code. You cannot merge a declaration into a class outside
399 of the library defining the class, but you can of course create a
400 subclass and add elements to the subclass (remember the part where I
401 said being able to do something a thousand different ways is not a good
402 idea? Well, this is one of those).
404 * No magic type promotion. It should be noted that Rune does
405 not automatically promote integer types for standard operators.
406 If you add two <B>int8</B>'s together the result is going to be
407 another <B>int8</B>. Rune will not allow you to compare an <B>int8</B>
408 against an <B>int32</B>, at least not unless you create a specific
410 My intent is to remove the single largest problem
411 C has when porting between architectures (including tiny 8-bit
412 architectures). If you want something else you need to cast the
413 arguments beforehand or create your own operator (which you can do,
414 in fact). This allows Rune to be used across the entire range
415 of platforms, from 8 bit cpu's to 128 bit cpu's, without having to
416 change the language specification or create special cases.
418 Automatic type promotion is not the convience you might think it is.
419 I have occassionally tried to compare or assign integers with characters
420 while programming in Rune but it has not been a burden. In fact I
421 believe requiring the use of a specific cast or integer constant suffix
422 in these cases has actually clarified the code in question.
424 * Inherent casts. If Rune knows the type an expression needs to be,
425 for example an argument to a procedure, it will attempt to
426 cast your expression to that type. Rune can only cast based on
427 cast operators in the class hierarchy, so Rune can automatically cast,
428 say, an <B>int8</B> to an <B>int32</B> but cannot automatically
429 cast an <B>int8</B> to a <B>uint32</B> without a little help from
430 the programmer. <B>Keep in mind the such casting occurs after
431 the expression has been evaluated. If you add two int8's together,
432 you will get an int8 result and that result might then be cast to
433 something larger. The cast will not change the fact that the
434 "+" operator in this case still returns an int8 result.</B>
436 * Object-oriented. Everything is an object and eventually bases at
437 some class or compound type. Oh wait, I said that. Ok, I'll say
438 it again. Even the lowly <B>int</B> is a class.
441 * Partial procedure argument resolution. This allows you to make a
442 more complex procedure compatible with a less complex call interface
443 by pre-evaluating and pre-supplying missing arguments. For example,
444 a library that takes a procedure callback does not need to know about
445 additional information you supply with the procedure, such as
446 application-specific structures and pointers. Partial resolution
447 looks like a procedure call in C but you take the address of the call.
448 For example, if you have a function that takes two arguments you can
449 turn it into a function taking one argument by partially resolving
450 the other, like '<B>&func(a:23)</B>'. <B>alias</B> can also be used
451 to partially resolve a procedure.
454 * Memory and code efficient. I stick to a model that does not require
455 structural or support bloat. The run-time is separated from the
456 relatively static information (such as statement and expression
457 trees) in order to allow the relatively static info to be cached in
458 a file (e.g. so it can be shared across multiple instances of the
459 program and to avoid unnecessary copy-on-writes). This will also
460 allow us to implement run-time threading fairly trivially. I've
461 only written an interpreter so far but I fully intend to take advantage
462 of the compartmentalization I've built into the language to produce
463 an intermediate (pre-parsed) form as well as assembly/object code.
465 * Built-in Interpreter. My intention with Rune is to be able to
466 interpret it, compile it, and even do an on-the-fly hybrid of the
467 two. I also intend to eventually allow dynamic compilation...
468 loading and unloading source modules on the fly. At the moment
469 I am concentrating on the interpreter. I cannot really start work
470 on the compiler until the interpreter is nearly finished because the
471 compiler is going to rely on the interpreter's constant expression
472 resolution/optimization code.
474 * No #includes. No preprocessor, but a few very simple preprocessor
475 directives to block-out debugging and testing code, or non-operational
476 musings. Yes, this is a feature.
477 The language implements an import mechanism. You pull everything.
478 There is no export mechanism but there are three levels of semantic
479 scope: <B>private</B>, <B>library</B>, and <B>public</B>.
480 <FONT color="red">My intention is to give the lexer a simple
481 preprocessing capability, similar to what you might find in
482 <B>make</B>, but I have no intention of having a full
483 fledged C-like preprocessor (even though I could just borrow
484 the one from DICE) because I believe that preprocessing, and most
485 especially #include files, take what could be a nice modular
486 object-oriented application and library set and turns it into a
487 nightmare of semantic collisions.</FONT>.
490 * Compartmentalized lexer, parser, interpreter, compiler, and library
491 manager. At each stage the intention is to be able to save an image
492 to the file that can be mmap()'d and accessed directly in a later
493 stage, eventually allowing whole projects to be partially pre-parsed,
494 pre-compiled, etc. The features will also be necessary when we
495 allow code modification on the fly.
498 * Threads. Very cool threads. The traditional problem one has
499 with threading is that one must explicitly declare all sorts
500 of infrastructure to create and manage multiple threads and
501 related data structures. Rune simplifies this greatly by introducing
502 the concept of a threaded procedure call. If thread A calls the
503 threaded procedure X, thread A will be suspended until the threaded
504 procedure X issues a <B>result(exp);</B> statement. The value is
505 returned to A and A's thread resumes execution. The threaded procedure
506 also continues to run. <B>For example, a threaded procedure could
507 allocate and return an object representing the thread to the caller, then
508 continue running as the new thread.</B>
510 Rune threads are synchronous by default, meaning they share the same
511 machine context of their caller and are thus very low-overhead and very
512 fast-switching. A Rune thread may be qualified <B>async</B> to
513 generate an actual new machine thread (similar to a posix thread).
514 A good example of the use of synchronous threads is for, say, a GUI
515 gadget library where each gadget is implemented as a Rune thread.
516 Since only one gadget is typically being manipulated at any given
517 moment it is a serious waste of resources to give each gadget a full
518 machine thread. Instead you would run all the gadgets as synchronous
519 threads within a single async gadget-handling thread.
521 * Compound types and expressions. Rune implements compound types and
522 expressions via a clever use of parenthesis and commas. <B>There is
523 no comma-operator in Rune. Also, note that the arguments you supply
524 to a procedure are, in fact, interpreted as one big compound type.</B>
525 Instead, there is the concept of the compound expression. A compound
526 expression is something like this: <B>(1, 2, 3)</B>. Additionally,
527 the elements of a compound type can be named in a manner similar
528 to the declaration of a procedure's arguments. For example,
531 <P>(int a = 2, int b, int c) x = (b:23, c:44);
534 <P>Now is that cool or what? As with any declaration you can specify
535 default values. When you make a call to a procedure which
536 returns a compound type you can either store the result into
537 a compatible compound type (throwing away elements you don't
538 request), or you can store the result into a normal type
539 in which case only the first element of the returned compound
540 type is retained. When building a compound type any elements
541 which have defaults are optional, and since a procedure's
542 argument set is a compound type you can extend a procedure in a
543 backwards compatible fashion by adding a new argument to it and
544 giving it a default. 'Older' users of the procedure who don't
545 supply the argument are still ok.
547 * Multi-valued returns. If you haven't guessed yet, a multi-valued
548 return is simply a compound type. I've flip-flopped a number of
549 times on whether to implement critical error handling via a multi-valued
550 return mechanism or via exception handling, but ultimately came to the
551 conclusion that trying to implement it via the multi-valued return
552 mechanism would lead to code that is just too messy.
554 * Exception-handling. Rune implements exception handling through a
555 <B>raise</B>/<B>catch</B> mechanism, but distinguishes between
556 nominal status returns such as a file EOF or non-blocking status
557 and more serious error conditions or aborts that warrant an exception.
558 The exception-handling mechanism gives subsystems a relatively painless
559 semi in-band way to back-out of situations without having to process
560 error codes from every single procedure call made. The mechanism is
561 similar to what TCL uses in some respects.
563 * Constructors and Destructors. Classes and compound types may
564 have any number of constructors and a destructors. A constructor
565 or destructor is a method procedure that takes no arguments.
566 Constructors are called when an object is instantiated, after any
567 type and declarative defaults are set. Destructors are called
568 when the last reference to an object goes away. The constructors
569 and destructors associated with the superclass will be called
570 before the constructors and destructors associated with a subclass.
571 Constructors and destructors are called in order, first to last.
573 Destructors can resurrect an object. When an object's ref count
574 reaches 0 Rune will set the ref count back to 1 and call the
575 destructors. Rune then checks the ref count. If the ref count
576 is still 1 Rune blows the object away. If the ref count is greater
577 then 1 then the object is considered to have been resurrected.
579 * Heap storage via pointers. Heap storage is nothing more then a
580 bounded pointer that is always ref-counted. To create storage on
581 the heap you simply declare a pointer and execute
582 the <B>new</B> method on it. For example, <B>MyObject *a; a.new();</B>.
583 It's that simple. Rune will
584 free the storage when the last reference goes away. <B>Note that
585 Rune does not garbage-collect. Creating an unreachable cycle is
586 considered a run-time error though, at the moment, Rune does not detect
588 I consider creating an unreachable cycle to be sloppy programming. Also
589 note that while Rune will recursively free chains, it is not a good idea
590 to depend on the feature to delete very long chains because you might
591 run the thread out of stack space (XXX this has to be fixed). Instead
592 we recommend that you NULL-out the chain pointers manually.
595 * Scripting. Rune supplies a nifty little scripting mechanism which makes
596 writing a program in Rune and executing it via the interpreter trivial.
598 * No version control. Rune does not attempt to integrate version control
599 mechanisms into the language. Folks, the plain fact of the matter is
600 that even the most robust Eiffel-like version control mechanism does not
601 make up for actually testing a project with the latest set of so-and-so
602 library. In fact, I strongly believe that integrating such features into
603 a language create even more problems then they solves because, like
604 exceptions, it results in code paths which are virtually untestable
605 and as likely to blow up as to work the first time they occur in real
606 life. In balancing one evil against the other I would much prefer
607 documentation over enforced language-level compatibility. Rune provides
608 mechanisms, such as argument defaulting, that allow you to create
609 backwards compatible libraries but does not require that you use them.
610 There is also nothing preventing you from including a major version
611 number in the names of the libraries you import, nor is there anything
612 preventing you from importing different versions of the same library
613 (though the usability of doing so is suspect). Rune is designed to
614 facilitiate, but not enforce, backwards compatibility.
616 * Automatic object locking and ref-counting is fully integrated into
617 the language and threading system.
618 Rune obtains a lock and a ref-count on the storage related to any
619 object it is actively working on or has a local stored pointer for.
621 shared or exclusive lock depending on the use case. The lock
622 structure itself is associated with the objects storage, not embedded
623 in the object itself so the sizeof() an object is always as expected.
624 Object locking is mandatory and handled by Rune itself in order to
625 ensure stability in a multi-threaded environment.
627 Rune utilizes a <B>soft</B> object locking model by default. Procedures
628 and statement blocks can be coded to use a <B>hard</B> locking model.
629 Deadlocks are not possible in the soft model but locks are allowed to
630 be temporarily lost (and then reacquired) while a thread is blocked,
631 including if it blocks obtaining a nominal object lock.
632 Hard code sections do not allow this temporary release.
634 Thus programmers can depend on a certain degree of automatic locking
635 at all times, but are expected to use <B>hard</B> code sections when
636 lock atomicy over long sections of code is required.
640 <H2><B>(III) Native types and operators</B></H2>
643 Please refer to the Rune core class module,
644 <A HREF="../classes/sys/class.d">../classes/sys/class.d</A>.
646 Generally speaking core types are as you might expect coming from
647 a C environment, with some exceptions. Types in Rune do not have
648 the same kind of wiggle room as you see in C:
651 * char's are always unsigned 8 bit quantities regardless of the
652 architecture. This is different from C where char is typically
653 signed. In Rune char is <I>always</I> unsigned and the application
654 must use something like <B>int8_t</B> if a signed 8 bit quantity
657 * int's are always signed 32 bit quantities regardless of the
660 * long's are always signed 64 bit quantities regardless of the
663 * bool's are a subclass of Numeric, NOT a subclass of Integer.
664 They can be cast to or from an integer, however. This is done
665 on purpose. Special-case boolean logic makes certain optimizations
671 <H2><B>(IV) Operational Stages</B></H2>
677 The Rune lexer converts a Rune source file into a stream of tokens. The
678 lexer is responsible for detecting and skipping comments and
679 whitespace, and for separting tokens out.
681 The Rune lexer is writen directly in C and is extremely fast.
682 Tokens are tracked through a token_t structure which is typically
683 manipulated by reference, and an integer identifier is returned
684 along with the new token. The parser switches on this identifier
685 almost universally. The lexer has a look-ahead capability which
686 the parser uses on occassion.
692 The Rune parser takes a stream of tokens from the lexer and (well Duhhh!)
693 parses the language. The Rune parser will also recursively open and
694 parse lexical streams related to <B>import</B> statements.
696 The Rune parser is recursive-descent, written directly in C. Parser
697 elements are simply procedures. However, the language is LALR(1)
698 (or LALR(2) depending on how you look at it) and the parser never
699 has to back up. This makes the parser extremely fast.
701 The Rune parser constructs a hierarchy of statements, expressions, and
702 declarations on the fly. The primary hierarchical component that
703 glues everything together is the statement. For example,
704 the statement representing an import will have a reference to
707 However, declarations are used as the cornerstone of the semantic
708 identifier search and management subsystem. Any identifier that
709 can be looked up will have an associated declaration somewhere.
710 Declarations also earmark storage offsets within structures,
711 compound objects, procedures, and procedure arguments.
713 <B>The Rune parser does not attempt resolve identifiers at parse-time.
714 This is one reason why Rune can have forward-referenced identifiers.
715 Identifiers representing definitions are hashed at parse time,
722 The resolver is responsible for taking a parse set and turning it
723 into something that can be interpreted, compiled, and/or saved
724 and restored. The resolver takes three passes on the parse-set:
726 <B>* Pass 1 -</B> Resolve subclass hierarchy. The resolver
727 must first integrate declarations from the superclass into the
728 subclass. Refinement issues are handled at this time. Note
729 that the statement, expression, and declaration subhierarchies
730 are duplicated for each subclass in order to allow Rune to optimize
731 the refined procedures on a subclass-by-subclass basis. This
732 greatly simplifies the job of the interpreter and compiler.
733 <FONT color="red">I intend to have Rune figure out which elements
734 in the superclass are not used at all by the subclass and to
735 delete them / not generate them. For now though Rune will
736 generate them.</FONT>
738 Resolving the subclass hierarchy is a recursive affair. For
739 example, the declarations in <B>Numeric</B> must be integrated
740 with the declarations in <B>Integer</B> and from there integrated
741 with the declarations in, say, <B>int8</B>.
743 <B>* Pass 2 -</B> Resolve types, expressions, and declarations.
744 This pass is responsible for assigning an intermediate type to
745 each expression, resolving the types (figuring out actual
746 storage and alignment requirements for a type), and resolving
747 non-procedural declarations (figuring out the actual storage
748 requirements for a declaration). This includes completely
749 resolving compound types, the top-level, and classes, all of
750 which are really just a list of declarations.
752 This pass is responsible for looking up and resolving operators,
753 procedures, and for enforcing casts. All of these operations
754 involve manipulation of the expression hierarchy.
756 <B>* Pass 3 -</B> Resolve temporary storage requirements. The
757 interpreter and compiler needs to know about temporary
758 storage requirements for expressions and aggregate
759 storage requirements for procedures. For example, if you
760 do something like <B>int a = (b + c) * d;</B> then we need
761 to store the intermediate result from (b + c) somewhere.
762 This pass figures out what those requirements are so the
763 interpreter can allocate the necessary space.
765 This pass allows the interpreter or compiler to know how much
766 space a procedure requires to operate inclusive of variable
767 declarations and temporary space. The Rune intepreter will
768 reserve the space for the entire procedure up-front, on the
769 stack, when the procedure is called. The resolver is also able
770 to figure out legal overlappings for storage so when you
771 have multiple executable blocks in a procedure, like this:
772 <B>{ int a; ... } { int b; ... }</B>, Rune will only
773 need to allocate 4 bytes of actual stack space to represent
774 both <B>a</B> and <B>b</B>. The same goes for temporary
775 space used by an expression. The resolver is aware of the
776 difference between lvalue and non-lvalue intermediate storage
777 and only needs to allocate actual space for non-lvalue storage.
780 * Interpreter (selected resolver backend)
783 The interpreter will execute a Rune program. It provides internal
784 tie-ins to core classes, operators, and system calls, which are
785 implemented by the interpreter itself rather then in Rune code.
786 For example, adding two integers together ("+" on two ints)
787 requires tie-ins to a native in-interpreter implementation of the
790 The Rune interpreter will do further on-the-fly optimization of
791 the statement and expression hierarchy. For example, if you
792 have a constant-producing procedure and you call it, the
793 interpreter will interpret the procedure, recognize the
794 constant result, and then shortcut the expression hierarchy so
795 the constant can simply be supplied directly the next time that
796 particular call is made. Another example is our internal
797 bindings. When the interpreter sees an operator bound
798 to an internal function it will resolve the binding and from then
799 on simply call the function directly.
801 Other common optimizations are things like <B>variable + constant</B>
802 operations, which Rune will do for certain selected types. In most
803 cases the interpreter simply changes the ex_Func function
804 vector in the expression or st_Func function vector in the statement
805 to achieve the optimization.
807 The Rune interpreter holds all state necessary to make a procedure
808 call on the stack, allowing us to create pthreads threads
809 for Rune threads on a 1:1 basis.
812 * Compiler (selected resolver backend)
817 * Assembler (selected resolver backend)
822 * Linker (selected resolver backend)