So I guess the next steps would be:
1) Figure out some hypothetical/potential system of packages and attributes to make sure it's suitable as it sounds. This will also help decide if any "built-in" features should be expressed as packages/attributes, etc.
2) Fix up the grammar (again)
3) Rework the compiler to match these changes so far.
EDIT: Adding a couple other item(s) to the list:
Traits may contain state: (
Stateful Traits)
Traits may contain non-function members, which by default are "hidden" within the context of the trait, but can be composed (mapped) into a class explicitly:
Code: trait T1 { int x; func Do1() { x++; } }
trait T2 { int x; func Do2() { x++; } }
class Foo : T1, T2 { ... } // Foo has both traits
// Foo is equivalent to this:
class Foo {
int x_T1; // only visible inside Do1
int x_T2; // only visible inside Do2
func Do1() { x_T1++; }
func Do2() { x_T2++; }
}
Trait fields can be made visible by mapping them to a name to use:
Code: class Foo : T1(x), T2(x) // Use both x's:
{ int x; func Do1(){x++;} func Do2(){x++;} } // <-- equivalent
class Foo : T1(x), T2(x->y) // use T1.x as "x" T2.x as "y"
{ int x, y; func Do1(){x++;} func Do2(){y++;} } // <-- equivalent
EDIT (A bit more on traits): traits also serve as interfaces, and anything that has the same properties of a trait can be passed/assigned as that type of trait (this is true whether the item explicitly uses the trait or not). Also, a trait list members which are required, but are not part of the "interface" of the trait. These are listed in parenthesis after the trait name:
Code: trait Square(int side) { // "side" required, but not part of the "type"
func Area() { return side*side; } // "Area" is part of the "type"
}
class Foo { int side = 5; } // Counts as a Square (has "side")
class Bar { func Area() { ... } } // Counts (has its own "Area")
func(Square s) { // can pass a Foo (gets default Area) or a Bar
s.x; // ERROR: x is not part of the "type"
s.Area(); // ok
}
Trait members that ARE part of the type are stored as stated into classes (or traits) that use them (e.g. class C uses trait T, and T has a "val Foo", so C has a "val Foo"). However, the "type" of the trait (i.e. when it is passed as an interface) contains these members by reference; though some "const" members may be passed by value when it is more efficient:
Code: trait T { // Members passed to takes_a_T as...
Foo f; // passed as ref Foo (Foo** in C++)
val Foo vf; // passed as Foo ("val" means store by value)
const Foo cf; // passed as Foo
int x; // passed as ref int
const int cx; // passed as int
}
class C : T { ... } // C gets members of T as they are declared
func takes_a_T(T t) { ... }
I've also updated Original post about traits in Antelope.
"ref" and "out" parameters:
(There are already "ref" arguments, so I'm just adding "out" ones as well)
Code: func(ref int x) // x is passed by reference. x must be pre-initialized
func(out int x) // x is passed by reference to be initialized by this function
Contextual functions:
Functions can be defined for specific objects within a context (this provides better support for DCI programming, as a means of "role-casting"):
Code: trait Doer { func Do(); } // A "Doer" is anything with a Do method
class Context {
Foo f;
func f.Do() { ... } // In this Context, f "has" a Do method
func UseCase1() { // perform use-case-1 in this context
...
DoStuff(f); // f is a "Doer" in this context, by virtue of "Do"
}
}
func DoStuff(Doer d) { d.Do(); }
This can also be used to give "val" members access to the containing class. This allow any inheritance model to be (structurally) mimicked:
Code: class A { vfunc F() { ... } }
class B {
val A myA; // "val" means that myA is stored by value
vfunc myA.F() { ... } // overriding F for myA
}
Essentially, if a class (B) contains member (myA) stored by value, then the that class can override its virtual functions (F), and because the member is stored directly within the class (stored by value), that function has direct access to all other members of the class! For the example above, this equivalent to using inheritance directly:
class B : A { vfunc F() { ... } } // inherit from A, and override F
The gain here is that these overrides can apply to multiple "by value" members, and they can be positioned where-ever you want in the class. This does not create an "is-a" relationship (X has Y and Z, but is not a Y or a Z). Further-more, you can even override functions of members of members (A has a B has a C; A overrides a function for C). This behavior provides functionality that is not possible otherwise, and that inheritance can only partially provide.
Though this project has essentially become dormant, I still (occasionally) think about what needs to be done; more so recently, but this still does not take a front-seat in my mind or my life anymore.
... This will be a bit of a ramble and have tangents, but I don't have time to edit much ...
All that aside, I have found myself casually revisiting this project in my head, and I think that one (project-related) thing that gets in the way of going further (assuming time ever allows) is the vagueness/complexity of the direct I decided to take this with packages and attributes (instead of preprocessor commands).
The really tricky thing here is actually "reflection"; that is, if attributes can be somehow used to "program the program", there needs to be a way to refer to parts of a program directly. For example, referring to the compile-time properties of a class rather than it's runtime representation. This is partly due to that I am borrowing a lot of ideas from C#, which does all the reflection and attribute "magic" at runtime, whereas I need a system to do the same at compile-time.
More recently (but not terribly recent), I've been influenced by the D language, which seems to do what I'm talking about very nicely. I had dismissed this language before (as many have, I am sure) because it seems TOO feature-rich, and has lots of unique ideas that are unfamiliar to many languages and therefor a lot of unfamiliar syntax .. blah! ... However, after trying to really understand it (with some encouragement from a D-enthusiast coworker), I've found that D has some brilliant unique approaches to things like meta-programming, collections, etc.
...Anyway, a D program can have a lot of compile-time evaluated code without resorting to a separate syntax for such. Familiar constructs such as classes, templates, functions, etc. can be used in a compile-time manner the same as for runtime use. The very opposite would be a preprocessor, which is a separate language that reformats the text of your code to result in altered/new code.
So anyway, still sticking with the more C#-like approach, the tricky part has been finding how to refer the actual components of a program as first-class objects; how do you "get" the attributes used, or the compile-time objects behind those attributes, or vice versa?
I have lost a lot of time just mulling over this kind of stuff in my head; but after becoming more familiar with how C# handles this (by necessity of my job), I may have something: C# has a typeof syntax that gives you a an object containing the meta-data for a class (or of any other type). From there, you can get the properties of the class (or type) such as the fields, methods, attributes, etc.; and from there, you can access the meta-properties of those fields, methods, etc., and even get the value or even the constant-initialization value (e.g. the "5" in "int x = 5"), and even go as far as creating expression-tree objects and dynamically create & compile code at runtime.
I can have Antelope do something similar for getting the meta-representation of things (only at compile-time instead of at runtime), except (unlike C#) this does not have to be limited to types only: I can have a meta-operator for this, like the $ (which would look a bit like JQuery, and have actually a lot of the same idea). (Yes, this would mean not having an "interpret this at compile-time" operator, but D gets by just fine without one, and I can make a compiler smart enough to know what can be evaluated ahead of time anyway ... honestly it would be a mess to have to mark those things manually anyway). So for example, $(foo).getAttributes, or $(bar).parentClass, or $(x).getTheFileAndLineNumberWhereItsDefined. ... And I suppose that still could act as an "interpret" operator to an extent: $(x).value, or something.
Also, one other thing that ties this together is that I've already come up with a way to treat packages (or "namespaces") and classes as first-class objects already, such that a package can inherit from a class to get it's methods and fields as "static" members of the package, and you can even pass a package or class around as a first-class object. As a re-cap from earlier posts, this would be accomplished by reserving the IX register just for the "this" parameter of methods, so that all functions can be treated as methods of their containing package or namespace. Calling a "static" function still does not pass a "this" into IX (no need to), but on the other hand, this allows "static" containers (packages, namespaces, singleton objects) to implement an interface and be passed as an interface object, which consists of a pointer to the object and a table of pointers to the methods; but the object reference can be null (or anything), because when that is passed as "this" into IX and then the "method" (function) is called, it is not looking for "this" anyway, because it can refer to the members of the static entity statically! ... Also, this gives me an alternate approach to D's "mixins" (which essentially act as copy-paste code snippets that can take arguments referring to types or aliases of other variables, so no textual manipulation) by allowing static entities (classes, singleton objects, namespaces, packages) to actually inherit from classes or to "compose" from traits (which are like classes, but their contents just get mixed in statically instead of using an "inheritance" model).
Also, I can remove that "explicit-address" operator and have that use a built-in attribute instead:
Code: //OLD:
MyType myObject @ "asmAddress"
//NEW:
[Address("asmAddress")]
MyType myObject;
Yes that is longer, but it's not meant for typical use anyway, and that gets rid of an extra syntax (and one less special-character operator).
More changes to simplify syntax:
* Remove the @ operator altogether. One example usage is in the previous post, and the other is a short-hand for mapping a function-argument directly to another variable. The short-hand is not worth the added syntax, and it's better to have optimizations done by a smart compiler rather than clever syntax
* Allow more type-interefence: func Foo(x, y) // types not needed here
* Make type-parameter constraints always implicit:
Code: //OLD:
trait Contract { func Bar1(); func Bar2(); }
func Foo<T:Contract,Bar3()>(T arg); // arg must implement Contract AND have a Bar3() method
//NEW:
func Foo<T>(T arg) { ... arg.Bar1(); } // it is inferred that T is some type with a Bar1 method
//Even better (type-inference):
func Foo(arg) { ... } // everything is inferred
Planned changes with the $ operator:
* Using $ before a construct name means that the entity is interpreted (no change here).
* Using $ to capture an entity now requires parenthesis, and returns a compile-time object which gives the user access to meta-information about the entity.
* Using $ as a datatype to refer to compile-time meta-objects.
Code: $while(...) { ... } // this loop is compile-time evaluated
func $foo(...) { ... } // this function is compile-time evaluated (inline)
int $value; // value is an int that is always compile-time evaluated
x + $(y+z); // If y and z are 1 and 2, then this compiles to x + 3
$(y+z).someProperty // access "someProperty" of the compile-time representation of an expression (y+z)
$(int[]) // capture compile-time representation of the int[] datatype
func Foo($ obj) // Foo is a function that takes a compile-time meta-object as an argument
*BUMP* - Current List of updates being made to the grammar to bring things current (I'll be updating this):
* Singleton objects (done)
* Base-types for enums (done)
* Type in/out parameters (done)
* Function ref/out arguments (done)
* Nullable types (int? myNullInt) (done)
* Package-level "if" (replaces #if) (done)
* Non-nullable Types (Foo! myFoo) (done)
* Update syntax: do goto label { ... } (done)
* (re-)Add method-pointer type syntax (done)
* D-style asm syntax (replaces #assembly) (done)
* Package system (replaces namespaces and #include) (done)
* Contextual methods (func someMember.Foo(...){...}) (done)
* "is" operator (tests for Type or sub-type: if(foo is Bar)) (done)
* Wildcard type-parameters (Foo<?>) and "ref" type-parameters (Foo<ref T>) (done)
* Argument-type inference (func(x,y) // infered types) (not going to do this; can use "var" instead)
* Packages taking arguments / args in "using" statements (not going to do this; too much complexity)
* Traits with requirements (trait Foo(int x) // x is a pre-req) (not going to do this; can implement later using attributes)
EDIT: All Done!
I want to allow functions to be declared without types on the arguments (the types would be inferred), but this conflicts with the current rule that a type can be specified once for several arguments:
Code: func Add(int a, b) => a + b; // a and b are both int
Add(100, 200); // 300
Add("A", "B"); // INVALID
func Sum(m, n) => m + n; // same as func Sum(var m, var n)
Sum(100, 200); // 300
Sum("A", "B"); // "AB"
func NotClear(x, int y, z) ... // Does z have to be an int?
I need to choose one of the following ways for the rules work:
(1) Types stop being inferred once a type is given (in the example above, x is inferred, but b and z must be an int)
(2) Types are always inferred if not given per argument (in the example above, the types of b, x, and z are inferred)
(3) Types are inferred only when the first argument does not have a type (a and b are both int, but x and z are inferred)
EDIT: I've decided not to implement this feature. Instead, you can use "var" like this: func Foo(var x)
The Grammar is finally up to date again, to match all the new setup of
packages and attributes introduced in January.
Next I'll update the specification for the Syntax Tree Classes, and then update the actual code for those classes.
EDIT: The syntax tree classes (specs) are now up to date. Also, I've allowed functions to be declared within functions, and inline within expressions (anonymous or named), though lambda syntax is still valid. Next I'll update the actual compiler classes to match.
I haven't done any Java on my new computer yet, so I'm currently looking for a good (free) lightweight Java IDE to use (I used to love JCreator, but now it's always marked as malware). So far, my interest was piqued by Geany, tIDE, Navicoder, and Bluej. Navicoder and tIDE had issues on my computer, so I'll see how Geany does (Bluej is nice for ad-hoc testing), otherwise I'll look into a nice (free) text editor.
I've also considered other JVM languages, but I've found none that are (1) similar enough to Java for my liking (if I want to invest my project in our, I want to be fairly familiar), (2) are actively supported or officially released, and (3) do not add to much complexity (*cough * SCALA).
I've also considered other "cross-platform" options (well, really just .NET - C# is so nice to code in), but I think Java still wins out on that front while still being easy to develop for (lightweight tools, etc.).
... Java, why couldn't you just have first-class functions!? (Aka delegates/lambdas/function-pointers)
Many many years ago I used Dr. Java. It was very lightweight, and would probably suit your needs.
I think C# is a better option, but that's mostly just personal taste. There are lightweight tools for it (you can just use Notepad and the compiler, if you really want), but honestly, VS is the best IDE experience I've had.
Java 8 has lambdas, FYI.
Java has lambdas and "function-pointers" (Method objects, invokable), and delegates seem similar to lambdas.
AHelper wrote:
Java has lambdas and "function-pointers" (Method objects, invokable), and delegates seem similar to lambdas.
Java 8 has lambda syntax, which is a shorthand for writing out an anonymous class instance. I don't know if that's quite as efficient, and these "lambdas" cannot be mapped directly to other functions etc. ... however, the "method references" syntax (e.g. System.out::println is short for x -> Systemt.out.println(x)) offers a work around, so I guess all the expressiveness is there; and maybe that's all that matters, so long as the size/speed aren't affected too poorly. There is still limited use of this feature within the existing Java API though, and the same limitations as to what variables can be "captured" by an anonymous class instance still apply.
Foo(System.out::println);
Foo(new SomeInterface() { public void theMethod(string s) { System.out.println(s); } });
class _Temp implements SomeInterface { public void theMethod(string s) { System.out.println(s); } }
Foo(new _Temp());
...Anywho, since I'm sticking with Java anyway, maybe I'll find them more convenient than the alternative. (For .Net, I'd have to code for Mono and worry about what that means for what code is supported, I'd probably need VS or deal with something else, and not know what OS's are supported ... it might be worth it though, just to use C#, especially if basically everyone is covered ... still sticking with Java for now).
I'll take a look at Dr. Java too; I had used that a couple years ago, and it got the job done.
Update: I've begun updating the syntax tree classes code. Fortunately, the changes to make are minimal here. I expect the file processing to be more work, since I am ripping out the preprocessor; however, since the pipeline is modularized, this should be fairly easy
I'm using Dr. Java, which I found more suitable than Geany (Geany comes with some nice color presets, but you can't customize them; Dr. Java let's you customize, but has only the default scheme).
it would be nice if some of the coding-convenience features were more automatic and easier to get to though. Has Visual Studio spoiled me? ... Bah! Maybe I'll give NetBeans another try...
Use Eclipse or NetBeans. It's upsetting, but there isn't really anything to compete with them.
Java 7 compatible JVMs add invokedynamic instructions, which makes runtime efficiency of functional behavior much better.
If you want a JVM language that isn't Java (and don't want to deal with Scala), try Clojure. It's very much unlike Java, but it's pretty easy to learn. But I really do recommend Scala. It's easy to dip your toes in without hitting too much of the complexity, and it gets rid of soooo much boilerplate. Plus it has real lambdas.
... Hmm, maybe I will give Scala a try. It is supposed to be more convenient and have better performance than Java. I had dismissed it before when I was reading about "traits" (as defined by Schärli) and I found that Scala's "traits" use inheritance-red-tape to compose them into classes, rather than using static inline composition (which does not prevent a language from still being able to use them like interface types). ... However, some of my recent study of DCI is leading me to think this may be a beneficial design choice.
Thanks for the suggestion! And I'll also give NetBeans another go.
Work and home have been busy lately, but when I get time I'm going to give
IntelliJ a try; and if I don't like that, then I'll go for NetBeans.
On a side note, I've been toying with the idea of making a Java back-end for the compiler, and then bootstrapping the compiler onto the JVM (that is, re-write the compiler IN the Antelope language and then compile it, and repeat). That way the compiler can be written and updated in Antelope, and Antelope could be used as a JVM language.
I suppose I could do the same thing to make an on-calc Antelope compiler; but I worry about size constraints. Either way, this would not require me to rework the code structure of the compiler, because I'm already designing it modular-ly such that portions of the compiler can be replaced or manipulated separately.
Do you use any ROMcall when compiling ? Because if you don't, then it'll be easy to compile KnightOS programs with it, which would be awesome
You should migrate the code repo to github, I think google code is closing