Show Posts
|
|
Pages: [1] 2 3 ... 6
|
|
1
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 29 September, 2012, 18:02:27
|
Clarification of changes: * Namespaces will be allowed to have type-parameters. * Constructors will be as just explained in previous posts. * "Virtual functions" are stored directly in their container as function-pointers. * Functions can be mapped to other functions without relisting arguments (we can discuss syntax: "func x = y" or "func x(args) = y"). * Interface functions may have default bodies; Interfaces may also contain data members (stored "by value"), which may have default values. This might result in the "func" keyword being required in interfaces again though. * CONSIDERATION: I'd like to remove "anonymous members" from structs, and instead (1) Allow datatype names to be used as variable names, and (2) Have structs automatically "inherit" the properties (i.e. members and functions) of ALL their inner members (with any conflicts having to be resolved explicitly): 1 2 3 4 5 6 7 8 9 10 11
| // OLD: struct A { int x, y; } struct B { A; int y; } b := B(A(1,2),3); // b.x == b.A.x == 1 // b.A.y == 2 // b.y == 3
// NEW: struct B { A A; int y; } // ...Everything else is the same |
|
|
|
|
|
3
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 28 September, 2012, 00:39:50
|
I've decided to forego the idea of traits for Antelope. It's a great idea, but I found that most of the same expressiveness is already available from the fact that methods are declared separately, interfaces are applied automatically, and parametric types fill in the rest. Some other changes I may make: * Namespaces may have type-parameters (namespace Foo<T>;) so as to build setups more generically (not only would you not have to do this to every method, but you could also have static/global T values). * Optional Constructors: This would change construction to use parenthesis, and optional constructors (and destructors?) would be declared within structs: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct Foo { int x; int y;
new(int xArg, int yArg = 5) { // Constructor x = xArg; y = yArg + x; // Now you can map values differently }
delete { ... } // Destructor }
f1 := Foo(1,2); // x=1, y=1+2 f2 := Foo(1); // x=1, y=1+(5)
struct Bar { int x,y,z; } // Constructors not required. b := Bar(1,2,3); |
(Another option would be Kotlin's approach) Some other ideas I'm toying with: * Changing lambda syntax from (args)=>code to { args => code } (and from =>code to { code }). I'd only do this so I could allow these to come AFTER function-calls when it is the last argument ( Kotlin does some fancy stuff this way). Call(1,2,=>{code}) would become Call(1,2) { code } * Singleton objects (e.g. "object x { ... }" declares x like a struct, but that x is also the one and only instance of it) * "Class (struct) objects" as in Kotlin - a struct may contain a singleton object, which is referred to externally by the class name itself. This can be used to encapsulate "static members", but be referred to as a variable. This object can compose (inherit) from another struct for added functionality. * Change variable syntax so that types come after a colon (func f(x:int, y:int, z:int = 3) { ... }). This matches how return-values are given, and would make parsing types much easier (which is also true with constructors, since then Foo{1,2} becomes Foo(1,2) and can be parsed as a function-call). Also, this makes "A := B" more sensible for type-inference, since the type is "left out". The only other thing I am debating is whether "virtual" functions should be stored directly within structs, or accumulate in an array as a "vtable" (C++) aka "class descriptor" (Java). I'm wanting to just keep them as pointers, because I want the language to be VERY oriented around using interfaces INSTEAD of using that kind of "inheritance". This allows embedded function-pointers to count as methods, and it is still POSSIBLE to construct a vtable (e.g. to mirror some hypothetically already compiled setup if necessary). Thoughts?
|
|
|
|
|
4
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 28 September, 2012, 00:21:01
|
(I posted this elsewhere a while ago) Potential Major Change:TRAITS seem to solve most of the problems not solved (or caused) by inheritance (single or multiple, or using mixins). Traits are like interfaces, except you can provide default implementations for functions ("methods"). They would keep their original functionality as interfaces without any added bulk or framework, but with the addition that structs can "use" them and gain all their methods for free! This provides a clean model for code reuse without affecting any underlying framework! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| trait Wargler { Foo(int); Bar():int; Wargle() { Foo(Bar()); } Talk() { Print("Wargle!"); } }
trait Fargler { Boo(); Bar() = Moo(5); // { return Moo(5); } Fargle() { Moo(Bar()); } Talk() { Print("Fargle!"); } }
struct A : Wargler { ... } struct B : Fargler { ... } struct C : Wargler, Fargler { ... } |
In this example, structs A, B, and C use above traits, causing the implemented methods (functions) of those traits to be automatically added as (static) methods of those structs. The struct is required to implement the other methods itself (and may also re-implement any of the others). Multiple traits can be combined (as with struct C), and implement each-other's methods (but if more than one trait implements the same method, then it must be redefined in the struct). The following methods are required for this trait usage: 1 2 3 4 5 6 7
| func A.Foo(int) { ... } func A.Bar():int { ... } func B.Boo() { ... } func C.Foo(int) { ... } func C.Bar():int { ... } func C.Boo() { ... } func C.Talk() { ... } // Overlaps both traits |
The following methods are generated automatically from this trait usage: 1 2 3 4 5 6
| func A.Wargle() { Foo(Bar()); } // func Wargle(A* a) { A.Foo(a, A.Bar(a)); } func C.Wargle() { Foo(Bar()); } // ...etc... func B.Fargle() { Moo(Bar()); } func C.Fargle() { Moo(Bar()); } func A.Talk() { Print("Wargle!"); } func B.Talk() { Print("Fargle!"); } |
Traits still work like interfaces: 1 2 3 4 5 6 7 8 9
| func Warg(Wargler w) { w.Wargle(); } func Farg(Fargler f) { f.Fargle(); }
(a,b,c) := (A{...}, B{...}, C{...});
Warg(a); // wa := Wargler{a}; Warg(wa); Farg(b); // ...etc... Warg(c); Farg(c); |
Traits can build off of each other in the same manner (e.g. trait X : traitY { ... }). Trait methods can also be aliased to avoid collisions or link to different methods (e.g. "struct X : Wargle(Other = Foo)" gives "Other" as an alias for Wargle's "Foo" method within struct X). Note: I have also added that syntax as a short-hand for a function body which just returns a value (i.e. "= value" rather than "{ return value; }").
|
|
|
|
|
5
|
Calculator Community / TI Z80 Calculator Projects / Antelope - New Syntax for OOP Idioms!
|
on: 09 August, 2012, 22:19:58
|
NEW SYNTAX FOR OOP IDIOMS!To test the feel of everything, I compared a simple OOP example from Java/C# with the "equivalent" Antelope code, and found it to be bulky and nasty. Therefor, I am wanting to rework some of the syntax relating specifically to "inheritance" idioms, as follows (with "..." indicating "no change"): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| PSUEDO-JAVA/C# | | ANTELOPE (new syntax) | | ANTELOPE (old syntax) ======================================================================== class Fooer { | | struct Fooer { | | ... void Foo() { <1> func Foo() { <1> func(this) Foo = func(this) { Print("Foo "); | | Print("Foo"); | | ... } | | } | | } } | | } | | ... | | | | ... class FooID : Fooer { <2> struct FooID { <2> ... int id; | | int id; | | ... | | | | ... <3> Fooer { <3> Fooer = Fooer { void Foo() { <4> func Foo() { <4> func(this) { Print("Foo "); | | Print("Foo "); | | ... Print(id); | | Print(id); | | ... } | | } | | } | | } | | } } | | } | | ... |
1) I'd previously given the "func(this..." syntax for declaring function-pointers " as methods" (i.e. "f.Foo(f)" shortens to "f.Foo()"), which allowed "virtual" (late-bound) methods to be " simulated". However, letting these be declared directly as " internal methods" looks much cleaner, matches the common OOP idiom/paradigm, and still gives control over where they are stored in the struct. 2) Antelope rejects traditional "inheritance" in favor of composition. Instead, anonymous members provide transparent access to their internals, as if they were "inherited" directly into the containing struct. Thus, the "inherited" Fooer appears in the body of the struct rather than at the top (as in 3). 3) Since anonymous members exist soley to "simulate inheritance", it is just redundant to have to repeat the anonymous type twice when a more compact form will do. This form is suggestive of embedding an " anonymous class" within a struct, which is exactly the intent (as in 6). 4) Since "inheritance" is simulated by composition, the new "Foo" function must be embedded directly in the anonymous Fooer. However, the only safe option is to use a "Fooer" method (since a "FooID" method could have misalignment issues) declared anonymously (to hide it from "Fooers" outside of FooID) and then type-cast to "FooID" within the method. This new form takes a non-method function definition for overriding (replacing) "inherited" methods, which then does these things automatically (using offsets if necessary). These can be given anywhere in the initialization list, and are skipped if omitted. 1 2 3 4 5 6 7 8 9 10 11
| PSUEDO-JAVA/C# | | ANTELOPE (new syntax) | | ANTELOPE (old syntax) ====================================================================== Fooer foo = Fooer(); <5> Fooer foo = Fooer{}; <5> ... FooID fid = FooID(5); | | FooID fid = FooID{5}; | | ... | | | | ... Fooer moo = Fooer() { <6> Fooer moo = Fooer { <6> ... void Foo() { | | func Foo() { | | func(*Fooer f) { super.Foo(); | | Fooer.Foo(this); | | Fooer.Foo(f); Print(" Moo!"); | | Print(" Moo!"); | | ... } | | } | | } } | | } | | ... |
5) Initialization values are given directly "{...}" rather than via a constructor "(...)". The first variable declaration can be simplified to either "Fooer foo = {5};" or "foo := Fooer{5};" for conciseness. 6) "Inner" methods are normally skipped in initialization, but new versions can be defined for them (in any order) (as in 4). This very closely resembles an "abstract class" declaration in Java, which likewise creates an instance with "new" methods (only the Antelope version does not require a "new version" of the struct: this is merely a regular initialization which passes an anonymous function to the function-pointer "Foo"). 1 2 3 4 5 6 7 8 9
| PSUEDO-JAVA/C# | | ANTELOPE (nothing new here) ============================================================= void FooFooer(Foo f) { <7> func FooFooer(*Foo f) { f.Foo(); | | f.Foo(); } | | } | | FooFooer(foo); // "Foo" <8> FooFooer(foo); // "Foo" FooFooer(fid); // "Foo 5" | | FooFooer(fid); // "Foo 5" FooFooer(moo); // "Foo Moo!" | | FooFooer(moo); // "Foo Moo!" |
7) In Java/C#, pointers ("references") are automatic. In Antelope you have to use them specifically (and you should, because they are more efficient). 8) The compiler will automatically pass the address of these variables so as to match the type. Otherwise, this code is exactly the same. Alternatively, FooFooer could have been written as a method ("func Fooer.FooFooer()"). NOTE: Although there were short forms (e.g. "Foo :=" rather than "func(this) Foo =" (at 1), or "Foo := Fooer{}" or "Fooer Foo = {}" (at 5)), I used the fully expanded forms for a more complete comparison of the fully written out forms.
|
|
|
|
|
6
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 29 July, 2012, 23:08:35
|
Cool. I like the preprocessor directives especially since I'm starting to understand their usefulness (remember, I have mostly confined myself to C# where they are largely unnecessary). Do you plan to add #pragma, or would that not be needed? No pragma, because I intend on having my compiler be "the" compiler for Antelope (like how java uses "javac" as the core compiler, but various IDE's can be built off of it). Instead, by exposing the guts of the compiler as an API, other tools can "intercept" code between phases and do whatever else they want (including intercepting a "#pragma" anyway). Also, I will have the compiler strictly allow each file to be used only once, with context of usage not playing any significant role (other than referring to relative file-locations). However, I'd like to allow URL's in #includes too!  I will probably let the compiler take an argument to specify default url paths as well as default file paths, so that other tools (IDE's) can provide environment control separately from the source code. On a side note, "projects" could be managed simply enough by creating one file with an "#include" for everything else; and since this can be done with "#define" as well, perhaps it might be worth providing a "#nodef" or "#ignore" directive to veto specific things beforehand.
|
|
|
|
|
8
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 27 July, 2012, 10:38:48
|
Does this mean we have to use C++ style method declaration? I.e. struct Foo { ... } func Foo::Bar() : byte { ... }Yes, this has always been the case (so that you can add new methods you as needed, without affecting the "definition" of a struct). Allowing them inside of structs was something I had added for familiarity with Java/C# as well. However, since you can embed function pointers inside structs (and make them act as virtual methods as well), I thought it would minimize confusion (and simplify the language) to just stick with one convention: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| struct Foo { func(*Foo,...) fp; // embedded function pointer func(this,...) mp; // just like fp, but counts as a "method" }
func f(*Foo,...) { ... } // function (not embedded in Foo) func Foo.m(...) { ... } // method (same as f, but is a method)
Foo foo, foo2; foo.fp(foo,...); // foo EXPLICITLY passed as 1st arg foo.fp(foo2,...); // same deal (can pass ANY Foo) foo.mp(...); // "method" = foo IMPLICITLY passed as 1st arg ("this") f(foo,...); // same deal as fp, but not a func-pointer foo.m(...); // same deal as mp, but not a func-pointer
// They all STORED as func(*Foo,...) though; // and since fp and mp are pointers, you can do this: foo.fp = foo.mp = f; foo.fp = foo.mp = m; |
I honestly prefer the "inside" form myself; but it's not hard to get used to, and you have the following benefits: * The struct JUST contains what it actually consists of (e.g. it "is" its "constructor"; the end). * This is simpler than in C++: You use "." insteaf of "::", and only declare it once (no "headers"). If enough people are not appeased by these benefits (and my other reasoning), then I might reconsider. I do want to make a language that people like as well.
|
|
|
|
|
10
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 23 July, 2012, 22:56:53
|
*Bump* - Here are some new changes:I have reverted the "cofunc" syntax back so that they cannot be embedded within structs, but they can hold data like structs. This is slightly less flexible (only because bizarre"siamese" cofuncs are no longer possible), but is more intuitive: 1 2 3 4 5 6 7 8 9
| // Old setup: struct Counter { int n; cofunc( ):int { n++; return n; } } c := Counter{5}; c(); c(); c(); // 6, 7, 8
// New setup: cofunc Counter { int n; } ( ):int { n++; return n; } c := Counter{5}; c(); c(); c(); // 6, 7, 8 |
I have also decided to go with the "Templating" form of parametric types (though I still need to choose an unambiguous syntax for multiple parameters), but I will have templated things share the same functions when they are the same. This means that you can still use POINTERS to keep things generic for all types  1 2 3 4 5 6 7 8 9 10 11 12 13 14
| struct Link<T> { T value; *Link<T> next = null; } struct List<T> { *Link<T> head = null, tail = null; length = 0; }
func Tree<T>.Size<T>():int { return length; }
func Tree<T>.add<T>(T val) { l := new Link<T>{val}; if(head == null) { head = tail = l; } else { tail = tail.next = l; } length++; }
List<int> list; // an int list list.add(1); ist.add(1); ist.add(3); |
If it were "*T value" rather than "T value" in Link, then there'd only one version of everything, since pointers are the same size. Also, you can be specific, such that a "func List<int>.Foo()" would take ONLY int-lists (which is why the extra "<T>" is needed in "List<T>.add<T>"). I have also added the option of an extra "this" for cofuncs, removed the empty parenthesis from no-argument lambdas, allowed lambdas to have full {bodies}, ... and in the process, I have discovered that passing a function-pointer is MUCH more efficient than using a cofunc as an "iterator". Here is an example of all (riding off that "List" code): 1 2 3 4 5 6 7 8 9 10 11 12 13 14
| List<int> list;
cofunc List<T>.Iterator<T>():*T { for(l := head; l != null; l = l.next) yield l.value; }
func List<T>.Each<T>(func(*T) process) { for(l := head; l != null; l = l.next) process(l.value); }
// Cofunc iteration (uses two loops!) for(int i: list.Iterator{}) { ...do stuff with i... } // Func-pointer iteration (much more efficient) list.Each(i => {...do stuff with i...}); |
The only thing is that you cannot encapsulate everything into a function; though perhaps I can allow direct access to local variables, but then disallow such funcs from "escaping" the embedded function by restricting their use (e.g. cannot be assigned to external variables or passed into external routines).
|
|
|
|
|
11
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 23 July, 2012, 00:18:48
|
1) you could just make Printer an abstract class with a default method 2) Also, are you sure Foo and Foo<T> would have the same methods? 3) Foo<T>'s type would probably be something like IEnumerable<T>. 1) There are no classes in Antelope, and no "abstract" mechanism. There are just structs, interfaces, and methods, which are all separate features (to be very technical, nothing "has" methods, but there may "be" methods "for" something; they are given additionally). However, you can use embedded function-pointers to simulate some of that behavior: 1 2 3 4 5 6 7
| struct abstract { int x, y; meth := func(this, int a, int b) { x=a; y=b; }; }
a1 = abstract{1, 2}; // uses defualt method a2 = abstract{1, 2, someOtherMethod}; |
2) I am actually leaning toward the "templating" setup, so that if there is a "Foo<T>", then it will be incorrect to have a plain "Foo". The compiler will generate different methods for each type used, but will share methods which would be exactly the same. This means that you can use a pointer to keep everything generic: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| struct Foo<T> { *T value; } // always the same size func Foo<T>.Bar<T>() { ... } // always the same everything func Foo<int>.Bar() { ... } // THIS one is specific to ints.
c := Foo<char>{'5'}; b := Foo<bool>{true}; i := Foo<int>{123};
c.Bar(); // inferred as c.Bar<char>, or Foo<T>.Bar<T> where T = char b.Bar(); // inferred as b.Bar<bool>, or Foo<T>.Bar<T> where T = bool i.Bar(); // inferred as i.Bar<int>, or the separate Foo<int>.Bar
// --- Another just to clarify: --- func A<T>(T arg) { ... } // Different per type-size (and per differing operations on T) func B<T>(*T arg) { ... } // Same per type, so long as nothing type-specific happens |
3) ...Nothing would be quite like "IEnumerable<T>"; though yes, they share the same kind of generic idea. On a side note though, Antelope abandons the concept of "iterators" as they are in Java, C++. Instead, you'd iterate over things directly within a cofunc (which, by the way, was inspired by the "enumerators" or C# and the coroutines of Lua): 1 2 3 4 5 6 7 8 9 10 11 12 13
| struct Tree<T> { T value; *Tree<T> left, right; }
cofunc Tree<T>.InOrder<T>():T { if(left != null) for(int i : new left.InOrder{}) { yield i; } yield value; if(right != null) for(int i : new right.InOrder{}) { yield i; } }
Tree<int> tree; // Tree of int values ... for(int i : tree.InOrder{}) { Print(i); } |
EDIT: I actually found that using function-pointers as "visitors" is a MUCH more efficient pattern: 1 2 3 4 5 6 7 8 9 10 11 12
| struct Tree<T> { T value; *Tree<T> left, right; }
func Tree<T>.InOrder<T>(func(T) process) { if(left != null) { left.InOrder(process); } process(value); if(right != null) { right.InOrder(process); } }
Tree<int> tree; ... tree.InOrder(Print); // Or: tree.InOrder(i => somethingElse(i)); |
(SEE MY NEXT POST)
|
|
|
|
|
12
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 20 July, 2012, 22:56:54
|
If it's my understanding, then "struct Foo<T:Printer> { []T data; }" and "struct Foo<Printer> { []Printer data; }" are basically the same. ... Not every Print() method would be the same for every Printer. Sort of... The intention is that, where the Print() method is the same, you would only have to store one of them. For example, a Foo<Bar> would be a "Foo" containing "Bar" values, and therefor each using the same "Bar.Print" method. If you want a structure of mixed "Printers", then you would just use the Interface directly: 1 2 3 4 5 6 7 8 9 10 11 12 13
| interface Printer { Print(); }
// Using "mixed" Printers ("<>" is unnecessary) struct Mixed { []Printer data; } Mixed m = {{someBar, someCar, someDar}}; //m is: {{Printer{someBar,Bar.Print}, Printer{someCar,Car.Print}, Printer{someDar,Dar.Print}}} m.data[i].Print(); // Calls the "Print" of data[i] on the Printer-object of data[i]
// Using only same-typed Printers: struct Same<T:Printer> { []T data; } Same<Bar> s = {{bar1, bar2, bar3}}; // s is: {{bar1,bar2,bar3}, Bar.Print} s.data[i].Print(); // Calls the single embedded Bar.Print on data[i] |
The idea with this style is that the parameterization ( <T:Printer>) just acts as a way to base a structure on some type which qualifies as some interface, but to store it as is and use the relevant method. The method must still be stored (since the structure does not know which type it ever since, being a generic structure based on a pointer to "something"), but is stored separately for efficiency. However, if you want a bunch of Printers (not things based on Printers, but actual Printers), then just use the Printer type directly! The conundrum again is as follows: This "generic" style has the "win" of providing a more efficient "generic" version. You could even have a plain "Foo" as a function argument, and pass it either a "Foo<Bar>" or a "Foo<int>", and it will work all the same using the same function, because the relevant method is embedded. Besides, a "templated" form is a convenience which CAN be coded by hand. That is, if you want a structure of int values, then go ahead and make one. HOWEVER, without a "template"-based setup, it is impossible to make a standard library for such. For example, someone could make a very nice tree structure based on a "generic" type, or even just on an int; but if you wanted one made JUST for strings, you would have to code it all over again just for strings. So a "templated" setup has a win there; but would then not allow for the generic behavior I just described, result in multiple vesions of everything (bloat), and it would be impossible to have a runtime (precompiled) library of such, since every possible type cannot be anticipated that way. C# actually uses something between the two forms (it uses one definition of everything, but the "reifies" them at runtime ... something like that) ... I am going to go read more on how C# does it and see if I get any ideas; though Antelope does not use a virtual environment and JIT is not applicable. By the way, that " <A B>" syntax might not be a bad idea; though it would be the only thing in the language which does not use a separator (comma or semicolon) between arguments...
|
|
|
|
|
13
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 19 July, 2012, 07:13:09
|
Because I took a couple pages to say it in detail (and because I've been told not to link to other forums), I will just sum it up: With the switch from a C# style to a Go style, I figured that interfaces provide enough dynamic behavior to be alright without providing parametric types ("templates" in C++, "generics" in Java), since you can just use an interface to store data of any type. The empty interface ("interface any{ }") acts as a pointer to anything without any method requirements (like "void*" in C++ or "Object" in Java). The problem is that using a more complete interface means that each entry is larger, since the relevant function pointers are stored alongside them. For example, each item in "data" below contains a pointer to some object, AND pointers to all the methods of the interface: 1 2
| interface Printer { Print(); } struct Foo { []Printer data; } |
One solution would be to use Parametric types as a convention for storing the method-pointers separately. It would look something like this, with "<T:Printer>" designating that the struct separately stores ONE copy of method-pointers for the "Printer" interface, and designating "T" as a stand-in for something that counts as a "Printer" (i.e. something with a Print method, but not an actual "Printer" itself): 1 2
| interface Printer { Print(); } struct Foo<T:Printer> { []T data; } |
This would require each "T" to be stored as a pointer (rather than a whole "Printer") so that the same struct (or any parameterized entity)can be used for anything. Otherwise there would have to be a different "version" for each type, and it would be pointless to actually store implicit method pointers for the interface, since each version could have the "right one" hard-coded in. In either case though, this struct would be used with a datatype plugged into it, which the compiler would use to restrict what can be stored in it: 1 2
| Foo<int> list; // list may only work with ints (and stores an int.Print pointer) Foo<Bar> list2; // list2 only works with Bars (stores a single Bar.Print pointer) |
The issue is two things (and a couple of trivial things that I will leave out): The first is that it does not parse well ("x<y,z>w" could be a tuple of two boolean comparisons, it it could be saying that "w" is somthing of type "x<y,z>"). This can be solved by using an unconventional syntax, or by requiring the "tuple version" to use extra parenthesis to work properly, or by reparameterizing each parametric type (e.g. "x<y><z>"). The second is just whether to use the "generic" form (all parametric types are pointers) or the "template" form (the compiler makes a copy of everything for EACH type used). The first only uses one version of everything and provides true dynamic entities. The second makes multiple copies which are hard-coded per type, but each is more efficient; however, the copies depend on what is used per compile, and there cannot be one precompiled version (set of methods), so it could not exist as a runtime library unless the "generics" setup is used.
|
|
|
|
|
14
|
Calculator Community / TI Z80 Calculator Projects / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
|
on: 18 July, 2012, 21:17:36
|
Woah, looks like I missed a few posts. I don't know why, but I wasn't receiving emails. Anywho, the grammar looks good! I'll have to take a gander at the source sometime soon  Awesome, you guys! Thanks! By the way, the source will need some redoing; but feel free to see it anyway. I will have to make sure the Token class matches everything now, and there are other changes (reserved words do not currently "count" as identifiers, which might result in confusing error messages). Also, the Tokenizer class (previously "Preprocessor") is going to be highly revised so that it can spit out one thing at a time (like the Token.nextToken()) rather than messing with Lists (so as to make it play nice as an API). I am planning on perhaps making a "TokenSource" interface so that things OTHER than Token.nextToken() can be used to get tokens, with Tokenizer acting as an intermediary which uses that and preprocesses at the same time. ...I have an interesting conundrum to resolve as well, and posted about it in Cemetech; but it's REALLY long...
|
|
|
|
|
|