UNIQUE! PreProcessing-free

MultiStage Execution

Eddon does one thing that is really unique.  It has no control statements or other syntax.  This means that when you define things like classes, you are actually writing code.  When you see “subclass”, it’s not a keyword, but rather a method that generates a class!  Here’s how that works, and why.

The compiler will read through your file, generate the EDDON code, and then execute it!  It then dumps the objects that were just created to a cache file.  As long as the original source is not modified, we can skip the compilation.  We run the code by creating an instance of an application object, which calls one of the methods we’ve compiled.

You have probably used languages that have preprocessors or definition formats like XML that generate the static portions of the code.  Eddon generates static objects wherever it can and also lets you build objects through real code rather than using a separate language to accomplish this task.  The EDDON syntax is specifically designed to make it this process very clean.  You’ll see your actual variable and function names along the left edge of your editor rather than special keywords or types, and object creation is terse and simple. Keeping your code in a single language makes it cleaner and easier to maintain and the ability to use every feature of the language at compile time basically means you can control the compiler yourself.  This can be done in other languages, but is usually not as straightforward.

Labels

Eddon also has a feature called “labels”.  Normally a literal will be at the right-most of your statement with only statement arguments following it.  If any token follows a literal, then Eddon takes this to be a label.  A label is a simple method without arguments.  The compiler takes the label to be a method and passes this method to the literal, replacing the literal with the result of the method call.  The method used must not have side-effects and must guarantee that the same result is returned given a particular literal.  A number of these are built-in, such as “ms”, “hz”, and “dB”.  Multiple labels are interpreted from left to right.  The general rule is that you always start at the direct object and work your way out.

Strings

Every double-quoted string in your code is placed into a file that is loaded at run-time.  The index of the string is the Eddon hash of the string itself.  The file may be translated and modified.  The compiler will attempt to recognize when text content changes, and adjust the index hashes of translations if the default is changed.

 

Lesson 5: AddArray

OK – this one is gonna be quick.  Let’s say you have an array where you are keeping track of disk accesses.  In the array is the number of accesses in a given second, where the array index is the second since the Epoch.

First, this has to be a sparse array since we’re gonna have a lot of zeros because we haven’t been keeping track of disk accesses since then!  Luckily, most arrays in Eddon are sparse, and this is the default.

Second, we likely need to do be able to manipulate these values very quickly.  Let’s say we want to graph accesses for the past year, with monthly totals.  That’s about 2.6M seconds we need to total, * 12 for the whole year.  Start adding and you have over 30 million adds.  No problem for modern computers, but is there a faster way?

Eddon arrays are based on Judy Arrays, and one of the objects available is a wrap of the JudyBitMap that can add 2.6M entries in just 190 operations.  How?  When we record an entry to the object, we split the integer to be written into its individual bits and insert each bit into a Judy bitmap.  The Judy knows how many items are between any two indices, without counting them.  So to get a total, we ask each Judy bitmap for a count of bits, then shift the total and add the count from the next judy bitmap.  There will be 64 “get index count” calls, 63 adds, and 63 shifts.  Obviously, getting the count is a function call, not an assembly operation, but its still a value who’s total is obtained using a variety of shortcuts.

What’s the point?   It’s just cool.  And polymorphism is maintained.

Examples:

sum myArray

sum myArray from: 600 to: 62300

sum myArray to:16320

Each example works with any kind of container with a numeric index.  The first sums the whole array, the last shows that the indexes are optional and will default to first and last.  If the container is really large and not an AddArray, it will be split apart into chunks that are summed by separate threads and then totalled at the end.

Eddon makes it easy to write methods that automatically divide and conquer large containers and you can swap out the underlying container type with no code changes, not even a recompile.

Bash The Objects!

Likely the most common shell around, BASH! And there are plenty of extensions to give you Object Oriented features. However, most of these have a horribly complex API, are severely bloated, or lack basic reflection capability (or all 3). So, I wrote my own.

It lacks the verb-first syntax of Eddon and Eddon’s powerful parallel event system and such, but it uses a similar parameter passing system and ease of use. Check it out and see what you think at http://www.github.com/uudruid74/bashTheObjects/

Eddon will have an "immediate-mode", also known as an eval-print-loop, capable of common shell tasks. This will basically be a class that implements a basic shell and debugger with a few extensions to make normal shell tasks easier, such as execution of external binaries when a method isn’t found or using the current directory when a direct object isn’t given, etc.

Lesson 4: Locking

Example 1
sum = 0
for myArray each: [ sum += x ]

Example 2
mymethod sync myArray nameParam: mydata error: myhandler

Eddon is parallel, so object locking is pretty important. Forcing the user to lock things themselves leads to bugs and a more difficult programming environment. Trying to lock everything results is too much locking and lower performance.

Eddon locks are a simple bit in mutable objects that is manipulated through atomic operations. The locks may be controlled through lock and unlock methods (and trylock which locks if it can or returns null if already locked) . You can also add a sync to any method call and the passed parameter will be locked until the method returns (this locks the top of stack and sets a flag in the context to unlock it when the context next runs or is exited via an exception). This is the preferred method since it is safe as far as thrown exceptions. Simple operations, like adding to a linked list, are NOT atomic. If you might access this from another event, you should sync the access or wrap it in lock/unlock calls. Also, inherently parallel operations (using each: for example) will lock the direct object until all child events complete. This is the most commonly used locking.

Eddon also has some subtle tweaks which it uses in certain situations. In the first example above, myArray is locked by the for method and then the array is broken into multiple subarrays. These are processed sequentially (while loop) but no further locking is done. Since data outside the block is being written to, each block will write its own copy. Otherwise, you’d need to lock the sum value and you’d get no benefit at all from threading. The loop is repeated once all the subarrays have been processed. These sorts of tweaks can be turned off and are only done when it would be logical to do so.

Attempting to lock a previously locked object causes the task to be placed back into the run queue with at least one other event ahead of it. When Eddon loads an event that starts with a lock instruction, and the lock is already locked, it knows this lock is contended. This causes the event to be placed into a lock queue. When you call unlock on an object, the system will first check the lock queue for waiting events before unlocking the bit. This turns lock contention into simple serial (FIFO) access and the thread benefits from data locality (keeping the thread working with data from the same object) and the thread ignores the priority queue during this time, effectively making lock sequencing a higher priority operation.

At no time does a thread block. Additionally, events that stay on the lock queue too long or when the system is idle, trigger a deadlock detector and the system aborts. The Mutex class uses system-level semaphores with all the trappings, but they still wont put a thread to sleep.

Lesson 3: Conditionals

Eddon has a wide collection of literals, including the ability to define literal objects.  One of the object types is called a LogicNode.  While other conditionals are possible, the LogicNode is one of the most common due to its flexibility.

Example:
 Repeat = (< /Yy/ ? userinput>
     true: < restartActions>
     false: < endActions >
     null: [ 
         userinput = read `Terminal
         call Repeat
      ]
   )

The LogicNode is surrounded by parenthesis, like most literal objects.  It begins with a selector, which is surrounded by angle brackets.   This particular example uses a comparison operator. The rest of the lines define attributes for the object.  When you take the value of a LogicNode, normally by calling it like a block or using the if method, the first selector is called and the result is used to index the object.

Here, the compare method is called on  a regular expression.   The results of the comparison determine which branch is taken.

The node can test multiple conditions such as less,  greater,  equality, else, similarity,  null,  exceptions,  and more.   You can even add a “case:” attribute which lets you test specific values,  or a “within:” attribute that tests ranges of values.   The branches can be changed at run-time because this is just an object.   Each branch is simply another object assigned to one of the attribute slots of the object.  Since the branches are executed,  they can be more LogicNodes.

 


			

Lesson 2: Basic Syntax

Example

 play audio file "background.mp3"

That is object-oriented EDDON code. Let’s see how it works.

* The left-most object is a string literal

Actually, the system creates an XML file of your strings behind your back so that you can translate them or modify them without changing the source.

We now start passing messages using the selectors from right to left. The output of one method is the input to the next.

* File is passed to the string. It gives you a file object from a string.  It will be opened on demand.

* Audio is passed to the file.  It finds a playable audio stream and returns it.

* Play is a method that plays a media stream.  It sets up events that wait on your OS to tell it when new data is available.  It returns an activity you can kill or wait on.

Lesson 1: Systems Architecture

Sendmail had a nice feature where it would fork off a new process for each incoming mail message. This was fine until the system gets busy. The last thing you want to do is spawn off more processes when you can’t handle the load you have. Ever see a server that is so busy that root can’t log in to fix it?

EDDON uses a fixed thread pool based on your number of processor cores. Every thread picks a ready event from a priority queue and executes it. If an event would block, it is placed on an internal waiting list and the thread picks a new task from the queue. When the IO or mutex is available, the event is moved back to the ready list.

This is just like how your OS works except that your OS will attempt to round-robin available tasks because these tasks are coded as long “linear” processes rather than short events. Eddon never keeps more tasks running than the system can handle. The “listen/accept” code is at a lower priority than normal socket processing, so you need to have a thread with nothing to do in order to accept new connections. This may seem like it would slow down your system, but its just the opposite.