Author Topic: Antelope (formerly "OPIA") - A Polymorphic z80 language  (Read 48597 times)

0 Members and 3 Guests are viewing this topic.

Offline BlakPilar

  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 734
  • Rating: +44/-1
    • View Profile
Re: OPIA - A full OOP language for z80
« Reply #30 on: January 02, 2012, 04:52:08 pm »
Quote
BOOLEANS - Booleans are a whole byte because it takes extra instructions to extract/insert just one bit, which adds more than just another byte to the program just for using it; so it actually saves space that way.
Hmm... So, for Boolean values, would it be faster on-calc if we just used single bytes and checked if they were (non-)zero ourselves?

As for the name, I like OPIA. I see nothing wrong with it. Everything else sounds good :D

Offline shkaboinka

  • LV3 Member (Next: 100)
  • ***
  • Posts: 86
  • Rating: +10/-1
    • View Profile
    • Antelope (polymorphic z80 language)
Re: OPIA - A full OOP language for z80
« Reply #31 on: January 02, 2012, 05:03:46 pm »
REGARDING OBJECT-CONSTRUCTION, INTERPRETED ASPECTS, TYPE-CASTING:

At one point, I provided 3 mechanisms for object (class instance) construction. Given some class called Foo:

Foo f; // No construction used (can be assigned later)
f = Foo(...); // Constructed into a static (anonymous) allocation and then assigned to f
f = new Foo(...); // Constructed into a dynamically allocated location and then assigned to f
f = static Foo(...); // A Pre-constructed (embedded directly into the program) instance is assigned to f
static Foo f = ... // NOT the same as previous statement (static var vs. static instance assigned to var)

This is long gone, because I decided that the compiler can determine when to embed and when/how to allocate as needed. I had also had a $ and a $$ operator, which both told the compiler to interpret certain things, and how strictly. For example, a construct (e.g. a while-loop) marked with $ would be interpreted, but a $$ would mean to also interpret everything (e.g. inner loops) inside of it. A $ function would be inlined, and a $$ function would be inlined AND completely interpreted ($$ was a "deep"/recursive command) ... However, that is a mess! ... Most of the code is pre-interpreted and optimized automatically by tracing values through the code, skipping variables when the same value is known to reside elsewhere, etc. One can still use $ to state that something MUST be interpreted (and with a higher priority), or else an error is given if it is not possible (e.g. values cannot be predicted as needed).

For Type-Casting, I restorted to (expression -> type) syntax. Eventually I replaced all uses of the -> operator ("returns" or "cast") with a colon. Type-casting is not an operation in itself, but more of a guideline to the compiler such that the expression or value is to be TREATED as the specified type. The cast applies to all code previous to the colon. For example, (a+b:byte) performs the addition in such a way that it results in a byte value (i.e. rather than performing it however, and then converting the result to a byte); and (a+(b:byte)) performs the addition is such a way that b is REGARDED as a byte (i.e. only the lower-byte of b is used).

Offline shkaboinka

  • LV3 Member (Next: 100)
  • ***
  • Posts: 86
  • Rating: +10/-1
    • View Profile
    • Antelope (polymorphic z80 language)
Re: OPIA - A full OOP language for z80
« Reply #32 on: January 02, 2012, 05:07:50 pm »
REGARDING STRINGS (and a good sample of code prior to the Go-style reformation of the language):

There is no "string" type, but a character array (char[]) is used instead. String literals automatically convert to character arrays followed by a null value (zero). I wanted to have the + operator concatenate strings cleanly, and cleanly means not allocating a whole new array for EVERY concatenation. Java solves this using a "StringBuilder", which I could code (using OPIA code) as follows:

class StringBuilder {
    private class Node {
        public char[] string;
        public Node next;
        public Node(char[] s) { string = s; next = null; }
    }
     
    private Node head, tail;
    private uword length;

    public StringBuilder() { head = tail = null; length = 0; }

    public void concatenate(char[] string) {
        Node n = new Node(string);
        if(head == null) { head = tail = n; }
        else { tail = tail.next = n; }
        for(uword i = 0; string[0] != 0; i++) { }
        length += i;
    }

    public void merge(StringBuilder s) {
        if(s.head == null) { return; }
        if(head == null) { head = s.head; }
        else { tail.next = s.head; }
        tail = s.tail;
        s.head = s.tail = null;
    }

    public char[] toString() {
        uword pos = 0;
        char[] string = new char[length];
        for(Node n = head; n != null; n = n.next) {
            for(uword i = 0; n.string != 0; i++) {
                string[pos++] = n.string;
            }
        }
        return string;
    }
}

abstract class Object {
    public abstract void printTo([char[]] handler);

    public StringBuilder toString() { // intentionally misleading
        StringBuilder s = new StringBuilder();
        printTo(s.concatenate);
        return s;
    }
}

interface Comparable<T> where T : Comparable<T> {
    byte compareTo(T other); // interface members are inherently public abstract


// The compiler then makes these conversions (and pretends that the resulting StringBuilder is "returned" by each):

char[] str;
StringBuilder sb;
...
sb  + sb;  // => sb.merge(sb);
sb  + str; // => sb.concatenate(str);
str + str; // => str.toString().concatenate(str);
str + sb;  // => str.toString().merge(sb);
str = sb;  // => str = sb.toString();
sb  = str; // => sb = new StringBuilder(); sb.concatenate(str);

Offline shkaboinka

  • LV3 Member (Next: 100)
  • ***
  • Posts: 86
  • Rating: +10/-1
    • View Profile
    • Antelope (polymorphic z80 language)
Re: OPIA - A full OOP language for z80
« Reply #33 on: January 02, 2012, 05:11:09 pm »
REGARDING PASSING VALUES (to functions):

Previously, function parameters were to be stored (in reverse order) in a static block of memory just before the function body / entry point, so as to be locate-able even from a function-pointer. However, I found that it would be more efficient to allocate the arguments per call and pass the address of the entire allocation. In other words, here is the difference in the old and new process of passing values:

old method:
- Store arguments 1,2,...,n in (func_address - 1), (... - 2), ..., (... - n)
- Call the function
- Arguments are accessed within the function directly, since they are stored in predetermined addresses

new method:
- Store arguments in some static memory location (unique to each call), and store the address of this in the IX register
- Call the function
- Arguments are accessed as offsets of the address stored in IX

This might seem like it takes up more memory because the arguments are allocated per call. The reason this is efficient is that (1) only one value needs to actually be passed, (2) arguments which can be resolved to predetermined values can be preloaded at compile time, (3) once the values are extracted from IX, they can live in registers (i.e. the compiler will convert expressions to SSA form anyway), and (4) this means that when a function is called via a function-pointer, the arguments can be loaded statically rather than determining their location as an offset of the function-address.

Originally, I decided that function arguments should go into static memory addresses (like other variables) because I was under the assumption that the compiler must pick a location for a variable and stick with it. However, the compiler will let the "address" of a variable shift around as it moves from register to register (and to memory as needed). I avoid the loop-hole of "what if a variable is pointed-to by a pointer?" conundrum by not allowing variables to be pointed-to, except when passed to "ref" or "out" parameters (i.e. passed "by reference", which assumes a saved state until the function returns). To clarify, pointers are allowed, and they can point to new allocations and allocations referenced by other pointers; but not to other variables of the same base-type (i.e. without the "pointer" part), unless that variable is declared "final".

I have to credit Ben Ryves for suggesting to use IX rather than HL for passing an address (to allow for offset access), which he says is the way that BBC BASIC passes values.

I do plan to actually pass values within registers, but that is a bit complex, and I want to get it working before I hammer out a complex way of determining which registers to use for which scenarios ... for now though, I can easily let functions which take a single byte or word to use A or HL though (since this is how values are returned anyway).

Offline shkaboinka

  • LV3 Member (Next: 100)
  • ***
  • Posts: 86
  • Rating: +10/-1
    • View Profile
    • Antelope (polymorphic z80 language)
Re: OPIA - A full OOP language for z80
« Reply #34 on: January 02, 2012, 05:53:24 pm »
=== AND NOW FOR A MAJOR CHANGE TO THE LANGUAGE ===

THIS IS WHERE I CHANGED THE LANGUAGE SIGNIFICANTLY TO BE
MORE LIKE GOOGLE'S "GO" LANGUAGE AND LESS LIKE JAVA/C#:

Basically I replaced classes & inheritance with a more powerful, flexible, light-weight model for interfaces & composition. This is a what makes Google's Go language a "fast, statically typed, compiled language that feels like a dynamically typed, interpreted language." It's a different approach to polymorphism, involving these changes:

(1) Replace classes with plain structs, but allow them to embed "anonymous" types which simulate a some inheritance using composition.
(2) Methods are declared separately (externally), and can thus be declared as needed and on ANY type.
(3) A datatype automatically implements an interface simply by having the required methods. For example, a function could taking a "Writer" interface could be given ANYTHING that has a "write()" method (like "duck typing"). This allows datatypes (structs) to implement interfaces without even "knowing" it, and without any overhead added to the type (struct).

It would have been silly NOT to do (2) and (3), since they simplify and add power to the language without any major underlying changes. The argument for (1) is that the "class" is removed because it becomes redundant: If you remove the method table from a class and store it separately, you have an "interface" (see chart below).

Class-based model (* denotes a pointer):

struct := [all the data of the struct]
table := [*method, *method, *method, ... ]
class := [*table, struct]
interface := [*table, *class]

New model: (same, but without "class", and *class becomes *object)

This new model provides better access to the underlying mechanisms, and results in simpler code! At the most fundamental level, polymorphism simply involves the use of method-pointers, which is what "virtual" methods are anyway. Thus, one could do it all manually just by storing or passing method-pointers directly (and given (2), method pointers would just be function-pointers, since the "this" argument would be explicit). However, passing an object along with it's methods is EXACTLY what the "interface" is already!

Here is some example code, with some obvious syntax changes made:

   byte x,y,z = a,b,c; // assignment in parallel
   *byte ptr; // ptr is a pointer-to-byte variable
   [5]byte arr; // arr is a static array of 5 elements (not a reference)
   []byte arrP; // arrP is a reference to an array (unassigned)
   *[]byte p2a; // pointer to array
   []*byte aop; // array of pointers
   a,b,c := 1,"yo",ptr; // declaration with type-inference (byte, []char, *byte).

   func f1(byte a, b : char) { ... } // function f1 takes bytes a & b, and returns a char
   func f2( : byte, char) { ... } // function f2 takes nothing, returns a byte and char
   func f3(byte a, b) { ... } // function f3 takes bytes a & b, returns nothing
   func(byte,byte,byte) x; // pointer to some func(byte a,b,c)
   func(byte a,b,c) y; // Same as above (using name-holders)
   x = func(byte a,b,c) { ... }; // anonymous function is declared and assigned to x

   struct A { byte x; } // an A has an x (someA.x)
   struct B { A; byte y; } // Composure: someB.x is short for someB.A.x

   interface foo { // interface foo defined as anything containing:
      foo1(:byte); // a method called foo1 which returns a byte
      foo2(byte); // ...foo2 which takes a byte and returns nothing
   }

   func A.foo1(:byte) { ... } // method for A implementing foo.foo1
   func A.foo2(byte b) { ... } // ... implementing foo.foo2

   A a; ... foo f = foo(a); // interface-instances are constructed around valid vars
   B b; ... f = foo(b); // Invalid, because b.foo1 is really b.A.foo1
   f = foo(blah,blahX,blahY); // overloaded interface instance:
   f.foo1(); f.foo2(5); // calls blah.blahX() and blah.blahY(5)

I later decided that I could allow functions to be declared within structs to designate "virtual functions". By doing so, a function-pointer is actually stored within the struct, with a default value pointing to the provided method body. Virtual methods CAN be used to fulfill interface requirements:

   struct Thing {
      byte x, y;
      func act(); // no method body (in list)
      func compute(:byte) {
         return x*y;
      }
   }

   func Thing.add(:byte) { return x+y; }
   func Thing.sub(:byte) { return x-y; }

   a := Thing{1,2,Thing.add};
   b := Thing(3,4,Thing.sub};

   a.act();   // calls a.add()
   b.act();    // calls b.sub()
   a.compute(); // calls Thing.compute() on a
   a.compute = Thing.add;
   a.compute(); // now calls Thing.add() on a
   b.act = a.compute;
   b.act();   // calls Thing.add on b

Offline shkaboinka

  • LV3 Member (Next: 100)
  • ***
  • Posts: 86
  • Rating: +10/-1
    • View Profile
    • Antelope (polymorphic z80 language)
Re: OPIA - A full OOP language for z80
« Reply #35 on: January 02, 2012, 05:56:39 pm »
COFUNCTIONS (Closures, Iterators, Coroutines):

Cofunctions are function-objects which are stored like objects (structs), but invoked (called) like functions. The yield command is used to return values, but continue from the same spot on the next call. Cofunctions are declared with colons separating the initializer arguments (for creating instances), invokation arguments (for passing values on each call), and return type(s):

   // simulating a closure on x
   cofunc counter(byte x : : byte) {
      x = x+1;
      return x;
   }

   counter c1, c2 = counter(3), counter(1);
   c1(); c1(); c1(); // 4,5,6
   c2(); c2(); c2(); // 2,3,4

   // using yield to simulate a generator
   cofunc rotatingSeq( : byte add : byte) {
      byte last = add; // last will be stored internally
      yield add;
      yield add+last;
      return add-last; // next call starts back at top
   }

   rotatingSeq r = rotatingSeq();
   r(1); r(2); r(3); // 1,3,2
   r(4); r(5); r(6); // 4,9,2
   r(7); // 7, (see f3 below)

   // Cofunctions are valid as function-pointers:

   func(:byte) f1,f2 = c1,counter(0);
   f1(); f1(); f1(); // 7,8,9
   f2(); f2(); f2(); // 1,2,3

   func(byte:byte) f3,f4 = r,rotatingSeq();
   f3(1); f3(2); f3(3); // continuing from r: 8,-5,3
   f4(1); f4(2); f4(3); // starting fresh: 1,3,2

The compiler accomplishes all of this by making the following modifications:
(1) The underlying function is modified to take a pointer to a cofunction-instance in addition to its per-call arguments.
(2) Each confunction-instance is stored as a tail-call (a "goto") to the underlying function (thus allowing the cofunction to be called as if it was the function itself), followed by any information needing to be stored between calls.
(3) When the yield command is used, the underlying function directly modifies the tail-call in the confunction to jump to where the function left off (rather than to the start of the function).

Offline shkaboinka

  • LV3 Member (Next: 100)
  • ***
  • Posts: 86
  • Rating: +10/-1
    • View Profile
    • Antelope (polymorphic z80 language)
Re: OPIA - A full OOP language for z80
« Reply #36 on: January 02, 2012, 06:06:51 pm »
...And there it is. This forum should now be caught up (though much discussion is missing, I provided all the key points). The changes from Java/C# style (btw, I think C# is the best OOP language yet...) to a more Go-ish style (..but Go seemed more simplified and flexible, and more powerful at the low-level; and therefor more suitable for a z80 language) results in less overhead, MUCH less keywords, and NO TYPE HIERARCHY (as inheritance imposes)! In keeping with this simplification, I removed the public/private/protected mechanisms, and simply provide that lowercase entities are only visible within the namespace they are declared in (files may start with "namespace BLAH;"), structs & interfaces etc. may not be declared within eachother, nor may they contain "static" variables/entities (instead, these are declared directly within the same namespace). I was going to disallow nested namespaces and remove the "using [someNamespace]" mechanisms, but I think I can allow that anyway, since it would be the only real way to nest "static" things in a modular way ... but without having to use the "static" keyword :)

As always, all current documentation can be found at http://tinyurl.com/z80opia , but I will try to keep up to date here as well. You can view the source there and test it as you please (which would be much appreciated). Thanks everyone for participating :)

Offline DJ Omnimaga

  • Clacualters are teh gr33t
  • CoT Emeritus
  • LV15 Omnimagician (Next: --)
  • *
  • Posts: 55943
  • Rating: +3154/-232
  • CodeWalrus founder & retired Omnimaga founder
    • View Profile
    • Dream of Omnimaga Music
Re: OPIA - A full OOP language for z80
« Reply #37 on: January 02, 2012, 06:34:42 pm »
Sorry DJ ... I began posting about it in both places, and got a decent initial response in both. However, it only REALLY took off at Cemetech, and thus decided to keep most of the posts there (they are time consuming, because they are thought out). Rather than just abandon [these people] altogether, I left notifications ... but I see how that could be a problem. I don't want to "steal" followers from one site to another, and I suppose I could have just copied posts and reposted them here. So much information was interchanged at Cemetech that I found it would be an overwhelming task to keep it all in both places; however, to give SOMETHING of substance, perhaps I will go back and take the key points and put them in a post here. That way there is a glimpse of what's going on, and some of the key discussion.

However, my project is hosted at google code ( http://code.google.com/p/opia/ ), and even when I discuss it anywhere, I leave links there regularly, because that is where one must go to see the language documentation and source etc. (I am not going to host it in multiple places). I will, however, go back and put a lot of the key discussion here as well.

Sorry for the misunderstanding! ... I promise a lot of information HERE soon.
Aaah ok, I guess I kinda misinterpreted it then. I thought your Google site had a discussion board and it seemed that on Cemetech you constantly encouraged people to go there, so I thought it was to move the entire discussion there instead of an established board. It's always good to have some major updates on big sites too (even if not all of them) so it rejoins as many community members as possible. Of course since on Omnimaga the language is in direct competition with two on-calc languages the audience might be a bit harder to convince, though, although we never know.

Anyway nice updates and I'm glad this is still progressing. :)

Also holy sixtuple-post batman! O.O

Offline BlakPilar

  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 734
  • Rating: +44/-1
    • View Profile
Re: OPIA - A full OOP language for z80
« Reply #38 on: January 03, 2012, 04:42:00 pm »
I haven't read all of this yet because I haven't had time, but I skimmed over the cofunctions section because I didn't understand them from the Wiki. From what I'm getting, they're the same as anonymous functions?

EDIT: I'm looking at the source, and in the Token file, should "volitale" be "volatile"?
« Last Edit: January 03, 2012, 04:54:41 pm by BlakPilar »

Offline shkaboinka

  • LV3 Member (Next: 100)
  • ***
  • Posts: 86
  • Rating: +10/-1
    • View Profile
    • Antelope (polymorphic z80 language)
Re: OPIA - A full OOP language for z80
« Reply #39 on: January 03, 2012, 05:25:50 pm »
I have corrected the spelling of "volatile", thanks :) (I really thought it was spelled that way!)

As for my "cofunctions", you will actually get a more accurate explanation if you look up "coroutine". The reason I chose "cofunctions" anyway was that "cofunc" looked more intuitive to me than "coro", since they can be used a "funcs". ... But it is more accurately a coroutine. A coroutine is a function which can do something, pause and return a value, and then continue where it left off when it is called again. It's like having a subprogram which you can switch between! ...and there are many other uses, one of which is that of an iterator/generator. This is a superior approach to making an iterator object as a class, because those have to store information as well, but also reenter the same methods and check the same things again, etc. The reason that coroutines have to be created as variables is that this allows for multiple "instances" of them to be in use at one time (and the same mechanisms are required anyway; so might as well just attach the data to a variable).

A "closure" is when a function is declared inside of another function, and it uses some of the local variables that were declared inside of the outer function. Languages that support this can do one of two things: (1) allow the variables to be modified directly, (2) make a copy of those variables, and that function gets it's own copy of them to modify as it pleases. Closures are used in class-less languages like JavaScript to simulate OOP-ish structures, or to create specific functions (e.g. a function-making function).

Because coroutines and closures both require additional storage, I combined them into what I call "cofunctions" (which are really just coroutines). These can be used as closures by, instead of capturing variables in the outer-context automatically, you just pass those variables (or values) to the "initialization arguments" of the cofunction. The "initialization" arguments are stored as part of the cofunction object, and can be used and modified each time (and they are used to create an instance variable of the confunctions); and the per-call arugments are the arguments that are passed each time the cofunction is called. Rather than have a stange { a,b = yield stuff; } syntax, I just have the per-call arguments have variable names attached to them (just like how return values do NOT have names attached to them).

I hope that helps; but if not, look up coroutines, generators, and closures. There is also a very nice (but very in depth) analysis of coroutines ("Revisiting Coroutines") which convinced me to allow them and helped me decide the best way to implement them, which I made a short-link for as: http://tinyurl.com/revcor

Anonymous functions are just nameless functions which are immediately assigned to function-pointers. It's the equivalent to creating a "new" function, in the same way someone would create a "new" anything else in Java/C#/C++. This is convenient because you can pass a literal function declaration to a function-pointer argument, rather than having to declare a function elsewhere and then reference it. This is why I am allowing anonymous functions to be declared in other functions (where else are they going to go?), but I am on the fence as to whether or not I will allow this for other functions as well, or whether or not either of them can refer to local variables declared in their surrounding functions (if they CAN, then they will modify them directly, since (1) that is useful and most efficient in some cases, and (2) variables can already be "captured" by passing them to the initialization arguments of a "cofunc").

SIDE NOTE/QUESTION: What are people's feelings about my changes from the Java/C# style to the Go-ish style? (Compare the old and current overviews from http://code.google.com/p/opia/w/list )
« Last Edit: January 03, 2012, 05:38:34 pm by shkaboinka »

Offline BlakPilar

  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 734
  • Rating: +44/-1
    • View Profile
Re: OPIA - A full OOP language for z80
« Reply #40 on: January 03, 2012, 05:33:07 pm »
Ahh, okay, now I understand. I was beginning to think that cofunctions and anonymous functions were synonymous, but now I see what they're for. That's really cool, actually. I'll definitely have to try something soon! :D

Offline shkaboinka

  • LV3 Member (Next: 100)
  • ***
  • Posts: 86
  • Rating: +10/-1
    • View Profile
    • Antelope (polymorphic z80 language)
Re: OPIA - A full OOP language for z80
« Reply #41 on: January 05, 2012, 04:24:37 pm »
EDIT: All you really need to look at is this table (and yes, I did just totally scrap the old post for this):
   switch(value) { case 1: ... 2: ... 3: ... }

   ---Jump Table---   --Look-Up-Table--   --Direct_Address--
    ld a,(value) ;3    ld hl,cases   ;3    ld hl,(value)  ;3
    ld ($+4),a   ;3    ld bc,(value) ;4    jp (hl)        ;1
    jr N         ;2    add hl,bc     ;1
    jp case_1          jp (hl)       ;1
    jp case_2         cases:
    jp case_3          .dw case_1
                       .dw case_2
                       .dw case_3
   ----------------   -----------------   ------------------
   = 8 (+3 per case)  = 9 (+2 per case)   = 4 bytes used (+0)
Since a jump-table is clearly the worst choice, the only real choice now is whether I want uber-efficiency (using direct addresses) or flexibility (using look up tables). I either I provide enums with even values (and hidden) so that they are efficient to use with switches, or I provide "selectors" which are each to be used with exactly ONE switch each, and contain actual case-addresses as values. The big difference is that an enum could be USED in any switch, but a selector IS the control for a particular switch. ... Perhaps I could provide both? Enums are flexible and better as values (being one byte each), while selectors are uber-efficient when used as case-selectors. A selector would be declared somewhat like an inline enum:
enum eFoo {X,Y,Z} ... eFoo a; a = eFoo.X;
selector(X,Y,Z) sFoo; sFoo = X;

(My previous suggestion about modeling a switch as a function with multiple entry points comes from the idea that modifying an address directly and jumping to it sounds just like a function-pointer; but since switch-cases can fall through to the next case, perhaps a function with multiple entry-points, being several functions smashed together but with an entry-table as well (stored as a function-pointer-array) ... but I resolved that by just using a look-up-table instead).

EDIT-EDIT: Here is a possible solution: I could provide both simply by providing the enum, and "switch on an enum" variables:
   // enum usage
   enum Foo {X,Y,Z}
   Foo f1 = Foo.X;
   f2 := Foo.X;

   // switch "on Foo"
   switch(Foo) g1 = g1.X; // "g1.X" because g1 uses specific case-addresses
   g2 := switch(Foo).X; // g2 uses case-addresses that DIFFER from g1...
   g1 = g2; // ...which means that THIS IS NOT ALLOWED

   // switch on anonymous enum
   switch{X,Y,Z} h1 = h1.X;
   h2 := switch{X,Y,Z}.X;

   // Later on, g1 is used in a switch statement:
   switch(g1) {
     case X: ...
     case Y: ...
     case Z: ...
     // NO default code (all cases are explicit)
   }

The switch "on enum" variables only use the values of the specified enum as labels for addresses of a specific switch-construct. Therefor, each switch variable has its own set of values, and thus they cannot be assigned each other's values (hence the "varA = varA.value", and the type is singular to each variable). Each switch variable may only be used in ONE switch-statement.

Note: The parser can tell the difference between each use of "switch" by the placement of parenthesis, brackets, and identifiers:
 - switch(ident) ident
 - switch{list} ident
 - switch(ident) {
« Last Edit: January 07, 2012, 06:53:24 am by shkaboinka »

Offline BlakPilar

  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 734
  • Rating: +44/-1
    • View Profile
Re: OPIA - A full OOP language for z80
« Reply #42 on: January 07, 2012, 12:53:24 pm »
Personally I would think look-up tables are the best (I normally go for flexibility), but seeing as this is on the calculator, I would say direct addresses.

Offline shkaboinka

  • LV3 Member (Next: 100)
  • ***
  • Posts: 86
  • Rating: +10/-1
    • View Profile
    • Antelope (polymorphic z80 language)
Re: OPIA - A full OOP language for z80
« Reply #43 on: January 07, 2012, 01:20:28 pm »
How do you feel about using both though (i.e. the "EDIT-EDIT" section)? That way, an enum can be used as an enum, but also for a switch (so long as it only receives values from its own value-set). By stating a variable as a "switch" rather than an enum, you are saying very specifically "this is for a switch" (or that the selection part of a particular switch is being treated as a directly-modifiable variable). The cool thing is that this functionality exists on the functional level in the form of function-pointers :)
« Last Edit: January 07, 2012, 01:20:43 pm by shkaboinka »

Offline BlakPilar

  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 734
  • Rating: +44/-1
    • View Profile
Re: OPIA - A full OOP language for z80
« Reply #44 on: January 07, 2012, 01:31:00 pm »
I was a little confused about that part (using switch statements as variables). If I had something like switch(Foo) g1 = g1.X; is that essentially initializing g1 as the type Foo while setting its value to Foo.X (i.e. Foo g1(Foo.X); in C++)? Then if I did switch(g1), case X would be the one executed?
« Last Edit: January 07, 2012, 01:31:52 pm by BlakPilar »