The Conseptizer programming language project
We don't want to bother you too much with our philosophy. Instead, we will directly explain the features of our language.
We use a stack-based computation model, but we evaluate from right to left:
This will push two values on the stack and call a subroutine which leaves a bool on the stack. Identifiers beginning with upper case letters are for values, which will be pushed. All other identifiers are for subroutibes (subs), which will be called. (Literals like ".png" are pushed, of course.)
Closures are wrapped in parens, and you can define something with a colon:
While this allows tacit/point-free programming in the concatenative style, we do not emphasize this too much. It's often useful for refactoring, though. Note that we evaluate right-to-left, but top-to-bottom, i.e. newlines are not handled like spaces. This makes code look more like it consists of statements and expressions. (Instead of a newline, you can also use a comma.)
Putting things from the stack into local vars is easy:
As you can see, lists work like in Lisp. (Well, mostly; we replaced car and cdr with e and z, because they are shorter names.) Of course, for small subs, we often don't need local vars:
You probably guessed it: The percent sign is used to push a sub instead of calling it. As you can see, quoting works like in Lisp. (Well, mostly; we use quote objects instead of lists starting with the symbol 'quote'.)
Local definitions are easy:
(Newlines in argument lists are ignored, so we can split args and code into different lines. If your code itself becomes too long for a single line, refactor it or use the backslash (\) to join lines.) Often you'll want to use the top-of-stack value, but keep it. You can use the dot (.) for duplicating it, and 'drop' for removing it:
Most of the time, we refactor our code if it uses 'drop'. Or any primitive stack operation other than '.' and 'x', really. That's because it's easy to get stack ordering wrong. The stack should be a convenient language feature, not something that makes your life hard.
We have a feature that helps to quickly write recursive subs: The @-sign in front of a closure binds the closure itself to 're':
You can have arguments directly after the @-sign; they will be kept when calling 're'!
Of course, you can have arguments both after the @ and the |. This is useful sugar because often some args to a recursive function stay unchanged. So it makes code shorter, more explicit and faster (less push'n pop).
So far, we ignored memory allocation. Well, we allocated, but we didn't free anything. There's an implicit stack of memory regions, and we always allocate in the one on top. We can run a piece of code with a fresh region and automatically drop+free it afterwards:
We can also copy a value into the previous region. In the following example, we get rid of all garbage (like local bindings) allocated by 'filter'.
It's up to you to find good places for using additional regions, though you can get some profiling help. Memory management with regions is a bit awkward when you want to modify global data structures. However, it can be extremely fast - and it is quite convenient for purely functional code. The implementation also doesn't need to be more complex in presence of multi-threading. So it has its good and bad sides.
Often it's useful to change (i.e. update) the value of a variable. This can be done by creating a pointer to the binding with the circumflex (^) and then changing it via the pointer.
Sometimes it's really useful to just keep throwing values on the stack and then collect them into a list eventually. We've got a feature for that:
The 'unlist' sub basically does the opposite: It pushes all the elements of a list onto the stack. There are various things that can be implemented much easier with '[' and ']'. For example, generating XML/HTML can be done like this:
That's about it. The core language has only about a dozen of features. That's not because we wanted some abstract minimalistic core model. It's because we wanted technology that can be realistically understood in its entirety. The translation to C (or even Assembly) is pretty straightforward. Yet, it has some pretty high-level features (like closures).
As soon as our compiler is somewhat more complete, we will publish it as Free Software, of course.
Copyright (C) 2016 Wolfgang Jaehrling
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is here.