Shop OBEX P1 Docs P2 Docs Learn Events
Spin language extensions — Parallax Forums

Spin language extensions

I've been working again on spin2cpp (a compiler for Spin), adding the remaining parts of Spin that hadn't been implemented yet. All that's left now is implementing COGINIT/COGNEW for Spin methods (for PASM those functions have pretty much always worked), and the _FREE and _STACK variables; the latter already can be assigned to but don't do anything (and may never in spin2cpp).

I'm starting to think about going beyond the current Spin language and adding a few (optional) new features that would be controlled by a command line flag or some kind of language extension keywords. Things I'd like to add are:

(1) Optional variable typing; this would allow us to treat floats and strings as first class objects (so x + y where x and y are both floating point could be converted into a call to something like F32.add). I think the type of variables should be deduced from their uses, with optional type annotations at the declaration point. The default would have to be to ignore type mismatches (or issue only warnings) but it should be possible with a switch to make the typing strict to help catch errors.

(2) The ability to pass objects as parameters to methods. This would require that we have some way to specify interfaces that objects can implement, but I can see this as being useful for implementing generic formatting functions that work on TV, VGA, serial, and so on.

This is pretty blue sky right now, but what would you like to see added to a Spin++ language?

Eric
«1

Comments

  • Sounds very cool.

    I think it is quite relavent to link your related post from another thread.
    ersmith wrote: »
    Brett Weir wrote: »
    Also, OpenSpin is largely without a maintainer, and to my knowledge, there are no new Spin compilers being developed. Thus, this library is to be limited to the current capabilities of OpenSpin unless a viable alternative Spin compiler emerges.

    spin2cpp is still under development. I admit that it's been sporadic, but it is absolutely my intention to make it possible to use spin2cpp (+ propgcc) as a drop in replacement for OpenSpin in tools like PropellerIDE, and perhaps eventually to provide some Spin language extensions. Which is a whole other topic and probably needs a new thread.

    Eric
  • Are we trading code size for speed by generating cpp (LMM/CMM/COG?) instead of Spin VM tokens? I like the idea but at least for the P1, openspin will need to stay around for those applications that just fit into 32K as Spin program. The P2 opens up all kinds of new possibilities/opportunities/worm cans. :)

    Support for the "infamous" @@@ operator provided to us in BST??

    From the BST manual:
    5.2
    @@@ - The absolute address operator
    In SPIN methods, there are the @ & @@ operators. In PASM you have @. In all contexts they mean
    different things.
    In SPIN, @ means “Give me the HUB address of that variable”. The interpreter does this at runtime as it only
    knows the absolute address when it know where it is in the Propeller. This applies to global (VAR), Local
    (PUB/PRI) and PASM (DAT) variables. The @ always returns the address of that variable in the HUB.
    In SPIN, @@ means give me the compile-time offset (It's only used when referencing variables in a DAT
    block) plus the object base address (this results in the correct absolute HUB address). Again, its a runtime
    operator only.
    In PASM, @ means “Give me the offset of this symbol into the DAT section”. It is a relative value with its
    base as the start of the objects DAT block.
    The @@@ operator results in a compile-time constant that gives the absolute hub address of the symbol in
    question. It's a special use symbol and it's not widely used. If you know you need it, you know what it is.

    There is code around that uses this (in OBEX, I believe) but the reliability of being able to run BST on more recent OS levels is at times a concern. (It is still working for me on latest Win/OSX/Linux but I may just be lucky). Support for @@@ in spin2cpp (and openspin) would remove some future headaches.

    It certainly sounds like an interesting project and a nice face lift for Spin.
  • Heater.Heater. Posts: 21,230
    Arghhh that @@@ monstrosity.

    ZiCog uses it.

    If I recall it only exists because @ and or @@ are broken when used in DAT sections.

    Can't we just fix @@ to work properly there. Would that even break any existing code?
  • Hi Eric!

    I'm glad to hear you're still working on spin2cpp. Would there be any advantage to creating a spin2llvm? I've started looking at what is involved in writing an LLVM backend for the Propeller, both P1 and P2.
  • mindrobots wrote: »
    Are we trading code size for speed by generating cpp (LMM/CMM/COG?) instead of Spin VM tokens? I like the idea but at least for the P1, openspin will need to stay around for those applications that just fit into 32K as Spin program.
    Yes, the idea is that the user could have control over the speed/space tradeoff. For smallest code they'd use openspin, for the fastest code they'd use spin2cpp with LMM. In between there's spin2cpp with CMM (and various compiler options).

    I certainly expect openspin to stick around -- spin2cpp is an option in addition to openspin, not really in place of it. Besides, other people are developing and constantly improving openspin, and I'm sure that with P2 coming the Spin language will be supported there as well. I guess in this thread I'm just trying to open up ideas of how Spin could be improved. Parallax (and everyone else) is free to take or leave what they want from this!
    Support for the "infamous" @@@ operator provided to us in BST??
    I'd forgotten about that one. Sounds like a good idea. As Heater said it would be even better to make @ work as expected in PASM, but that would break backwards compatibility so it probably won't fly right now. @@@ is only for PASM right? In Spin code it would do the same thing as @ (I guess there could be a difference in implementation underneath, but for spin2cpp @ is evaluated at compile time anyway so it's not an issue there).

    Eric
  • David Betz wrote: »
    Hi Eric!

    I'm glad to hear you're still working on spin2cpp. Would there be any advantage to creating a spin2llvm? I've started looking at what is involved in writing an LLVM backend for the Propeller, both P1 and P2.

    An LLVM backend would be cool! I hope you can get that working, and if you could use any help on it let me know. It should be possible to retarget spin2cpp to emit LLVM directly (rather than having to go through C/C++).

    Eric
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2016-01-04 17:24
    Eric,

    I like both of your extension ideas. Have you figured out a way with number 2 to check for parameter-count -- and now, type -- agreement during runtime, since it can no longer be verified at compile time?

    -Phil
  • Heater.Heater. Posts: 21,230
    An LLVM back end means....Spin running in the browser after passing through Emscripten :)

  • Heater. wrote: »
    An LLVM back end means....Spin running in the browser after passing through Emscripten :)
    Is that good? :-)

  • Heater.Heater. Posts: 21,230
    David,
    Is that good? :-)
    No idea.

    Might be cool to be able to test and debug your Spin code in a browser. No IDE or compiler to install, no Propeller required.

    Kind of leaves all that PASM and hardware emulation to deal with.....



  • jmgjmg Posts: 15,173
    ersmith wrote: »
    As Heater said it would be even better to make @ work as expected in PASM, but that would break backwards compatibility so it probably won't fly right now. @@@ is only for PASM right?
    Where there is a mutually exclusive case, many compilers offer a user mode switch.
    That way, you can choose 'compatible' or 'works properly', depending on the project.
    Call the old spin Spin 1.0 and this Spin 2.0 for example.

    I'm guessing conditional compile features are already in Spin2cpp ?

  • ersmith wrote: »
    ...
    Support for the "infamous" @@@ operator provided to us in BST??

    I'd forgotten about that one. Sounds like a good idea. As Heater said it would be even better to make @ work as expected in PASM, but that would break backwards compatibility so it probably won't fly right now. @@@ is only for PASM right? In Spin code it would do the same thing as @ (I guess there could be a difference in implementation underneath, but for spin2cpp @ is evaluated at compile time anyway so it's not an issue there).

    Eric

    wrong.

    @@@ gives the actual hub address of something at compile time to use to build tables with pointers or such.

    In Spin you can't do that without @@@ because @ is resolved a runtime and @@ just gives you the offset in the module.

    If you resolve @ at compile time then @@@ is just an alias for it.

    Enjoy!

    Mike.

  • Heater.Heater. Posts: 21,230
    msrobots,

    I have to read the Prop manual again. But the last hundred times I did that it implied that @@ "gives the actual hub address of something"

    Problem is it does not.
    DAT
    x long @@something
    

    Does not give the actual hub address of something.

    Hence the introduction of @@@.

    When is @@ ever actually useful? Have people ever used it? Can't we just fix it?
  • Only the @ operator is allowed in a DAT section. It gives the object offset of the variable that is referenced. When used within a method the @ operator gives the absolute address of a variable. The @@ operator is used to add the object address to an object offset to generate the absolute address. The value of "@@0" is the starting address of the object.

    The @@@ operator was introduced in BST to provide the absolute address of a variable within a DAT section.

    Why does it work that way? You would have to ask Chip. I would guess it's just a natural outcome of the object structure used in Spin. Object offsets are useful if you want the program to be relocateable.
  • Dave Hein wrote:
    Why does it work that way?
    Probably because the post-compile object linker does not include a fix-up pass to compute and insert relocated address values.

    -Phil
  • Heater.Heater. Posts: 21,230
    Dave Hein,

    Perhaps I'm remembering this "@" thing incorrectly. Hardly surprising given that it is so confusing.
    Only the @ operator is allowed in a DAT section. It gives the object offset of the variable that is referenced. When used within a method the @ operator gives the absolute address of a variable.
    Straight away there we have two different meanings of the same operator. Eeek.
    The @@ operator is used to add the object address to an object offset to generate the absolute address.
    Great but we cannot use it in DAT.
    Why does it work that way?
    Can't imagine.

    A DAT section is shared across all instances of an object. Unlike VAR. All we want is the actual real address of something in DAT.

    Surely one of these uses of @ or @@ is actually useless and never used and can be fixed without breaking anything.

    May be?





  • kwinnkwinn Posts: 8,697
    Heater. wrote: »
    Dave Hein,

    Perhaps I'm remembering this "@" thing incorrectly. Hardly surprising given that it is so confusing.
    Only the @ operator is allowed in a DAT section. It gives the object offset of the variable that is referenced. When used within a method the @ operator gives the absolute address of a variable.
    Straight away there we have two different meanings of the same operator. Eeek.
    The @@ operator is used to add the object address to an object offset to generate the absolute address.
    Great but we cannot use it in DAT.
    Why does it work that way?
    Can't imagine.

    A DAT section is shared across all instances of an object. Unlike VAR. All we want is the actual real address of something in DAT.

    Surely one of these uses of @ or @@ is actually useless and never used and can be fixed without breaking anything.

    May be?





    May be time to start from scratch and not worry so much about backwards compatibility? Perhaps a unique one or two character symbol for each of the current use cases?
  • Heater.Heater. Posts: 21,230
    Yep. Spin works with unicode. We have a gazillion symbols to use as operators for whatever we like.

    How about:
    ¤something
    
    For that "give met the the real actual frikken address of something"

    For example.
  • kwinnkwinn Posts: 8,697
    Well, I was hoping for something that was already on the keyboard...and optimistically without multiple keypresses.
  • Roy ElthamRoy Eltham Posts: 3,000
    edited 2016-01-04 22:59
    Here's roughly how Chip's original Spin compiler (and OpenSpin) works at the highest object level (this leaves out a LOT of details):

    1. First pass compile the top object, among other things, this produces a list of any other spin files referenced in the OBJ section(s).
    2a. If it has referenced objects, then loop over all the referenced objects:
    2b. Recurse to step 1 (essentially compile the referenced object as if it is the top object).
    2c. Loop back to 2a until all the referenced objects are compiled.
    3. Redo the first pass compile of the top object.
    4. Second pass compile the top object. At this stage any referenced objects are copied onto the end of this object from the heap (note: those objects can contain other objects that they reference).
    5. Copy the object onto the heap.
    6. This is where we return from the recursion that happens in step 2b. If we are done recursing and finished the top object compile then we fall through to step 7.
    7. We now have a compiled object that has all of it's referenced objects tacked onto the end of itself (and each of those has their referenced objects tack on to them, and so on).
    8. Distill all the objects down, eliminating duplicate objects and fixing up tables used for calling into objects.
    9. Finalize the binary, sticking on the beginning portion and doing the checksum calc.

    So when it's compiling a given object it only has the context of that object and it's directly referenced objects, and for those directly referenced objects it only knows the CON values and the PUB indices of it. This is why the @ operator in DAT sections is an offset from the start of the given object and not absolute. It doesn't know what the absolute address is going to be yet.

    So what needs to happen to get @@@ working (or change @@) is to add another pass after the distill step has been done to go over the objects and figure out the actual end result address and fixup that places where @@@ (or @@) where used. However, because they can be used in many different ways and contexts it's difficult to fixup without being able to recompile expressions and do that could change the bytecode which could move the address.

    BST likely compiles things in a completely different way, and probably also forces the compiling of expressions with @@@ involved to always go a certain way so as to not change the bytecode when fixing up (just needing changing a value used with the load constant bytecode). This would be pretty difficult to do with the compile method used by Chip (and OpenSpin).

    Roy
  • Heater.Heater. Posts: 21,230
    It's on my keyboard. Shift-4. What's the problem?

    That is less key presses than "@@@"

    At the end of the day readability is preferable to typabily. Code is read more often than it is written.

    But hey, it was only a "for instance"
  • I like both of your extension ideas. Have you figured out a way with number 2 to check for parameter-count -- and now, type -- agreement during runtime, since it can no longer be verified at compile time?

    It would be verified at compile time. Variables would now have types (at least in the "extended spin" mode) and so the type of the parameter would have to be compatible with the type of the object being passed in; and that type would determine the method signatures. I guess we'd have several options for specifying how objects are compatible, e.g.:

    (1) Object X is compatible with object Y if they provide the exact same methods and variables.
    (2) Object X is compatible with object Y if X contains a subobject that matches Y in the sense of (1) above.
    (3) Object X is compatible with object Y if Y is an abstract class (interface) and X implements Y

    (1) is really simple, but very limiting. I think (2) might work, but is still pretty rigid. (3) is more flexible, and I think is the "right" solution, but finding a simple implementation (in the spirit of Spin) may be tricky.

    Any other ideas, thoughts?

    Eric
  • jmgjmg Posts: 15,173
    Heater. wrote: »
    At the end of the day readability is preferable to typabily. Code is read more often than it is written.

    Yup, Which is exactly why hieroglyphs should be avoided like the plague.

    Google finds already in use, the clear operators like ADR(), ADDR() and AddressOf()

    There is no need to invent new symbols, in what is already a niche language.

  • Heater,
    Chip's compiler and OpenSpin do not "work" with Unicode. The wrapper code converts the Unicode to PASCII (an 8bit per character remapping where the bottom 127 chars are ASCII, and the upper 128 chars are all the circuit symbol things and what not from the parallax font). The guts of the compiler pretty much just uses ASCII and ignores the extra stuff.
  • jmgjmg Posts: 15,173
    kwinn wrote: »
    May be time to start from scratch and not worry so much about backwards compatibility? Perhaps a unique one or two character symbol for each of the current use cases?
    ? If the idea is to accept Spin, surely backward compatible is rather a key & cornerstone feature ?

    It is fine to clean up errors, and use of Compile mode switches is a common way to deprecate features, but you cannot discard backwards compatibility.

  • msrobots wrote: »
    ersmith wrote: »
    ...
    Support for the "infamous" @@@ operator provided to us in BST??

    I'd forgotten about that one. Sounds like a good idea. As Heater said it would be even better to make @ work as expected in PASM, but that would break backwards compatibility so it probably won't fly right now. @@@ is only for PASM right? In Spin code it would do the same thing as @ (I guess there could be a difference in implementation underneath, but for spin2cpp @ is evaluated at compile time anyway so it's not an issue there).

    wrong.

    @@@ gives the actual hub address of something at compile time to use to build tables with pointers or such.

    Sorry, yeah, I meant to write "in DAT" rather than "PASM". That is, inside Spin PUB and PRI methods @@@ and @ seem to be equivalent in meaning, although @ may be slower because it requires runtime computation. But inside DAT @ means just the offset from the object base, so it differs from @@@.

    I think I've actually got @@@ working in the git version of spin2cpp, but only when the --gas switch is given (in which case DAT sections are output as inline assembly rather than being assembled by spin2cpp into a C array). My test case (exec12.spin) produces the same result as bstc.

    Deciphering this also reveals what @@ is: it's an operator that adds the base address of the object to the thing after it. Normally that "thing" will be something fetched from DAT which itself has an @ in front of it. So in a sense @@(@foo) == absolute address of foo, although this is true only when the @foo part comes from DAT (if you write "@foo" in a PUB or PRI you already get the absolute address of foo, and then @@(@foo) will add the base address twice).
    ''
    '' test of address operators
    ''
    
    CON
      _clkmode = xtal1 + pll16x
      _clkfreq = 80_000_000
      
    OBJ
      fds : "FullDuplexSerial.spin"
    
    PUB main | start,elapsed,idx
      '' start up the serial port
      fds.start(31, 30, 0, 115200)
    
      fds.str(string("Str1 is: "))
      fds.str(@Str1)
      fds.str(string("Str2 is: "))
      fds.str(@Str2)
      fds.str(string("Str3 is: "))
      fds.str(@Str3)
      fds.str(string("indirect with @@:", 13, 10))
      repeat idx from 0 to 2
        fds.str(@@strAddr[idx])
      fds.str(string("indirect with @@@:", 13, 10))
      repeat idx from 0 to 2
        fds.str(strAddr2[idx])
      exit
    
    PUB exit
      fds.txflush
      fds.stop
    
    DAT
      Str1 byte "Hello.", 13, 10, 0
      Str2 byte "This is an example ", 0
      Str3 byte "of strings in a DAT block.", 13, 10, 0
    
    DAT
      StrAddr word @Str1, @Str2, @Str3
      StrAddr2 long @@@Str1, @@@Str2, @@@Str3
    
    

  • Is there an optimizer? Dead code removal? Inliner? I realize that's not an extension, but those things would allow squeezing more from a Prop.
  • JasonDorie wrote: »
    Is there an optimizer? Dead code removal? Inliner? I realize that's not an extension, but those things would allow squeezing more from a Prop.

    spin2cpp converts Spin to C++ (or C) and then compiles it with a C compiler, normally PropGCC. So all of those optimizations are available.
  • Rather than passing objects to functions, perhaps we could get away with just passing methods. It's easier to specify compatibility then (the method signature has to match). Internally at run time we would have to pass 2 pointers, the object and the method pointer, but that shouldn't be hard to implement, and gives us a lot of flexibility without too much conceptual baggage.

    I'm imagining something like:
    PUB putfloat( PUB.putstr(x), f )
      stringformat(f, buf) // convert f to a string in buf
      putstr(buf)              // print buf
    
    which would be used like:
      printer.putfloat(fds.str, pi) '' print pi on the serial port
      printer.putfloat(vga,string, pi) '' print pi on the vga screen
    
    The PUB. notation in the declaration would distinguish the parameter as being a method parameter, and at run time the call to putfloat would actually have to pass the source object (fds or vga in our examples), a method pointer (str or string), and then the other parameters.
  • JasonDorie wrote: »
    Is there an optimizer? Dead code removal? Inliner? I realize that's not an extension, but those things would allow squeezing more from a Prop.
    OpenSpin has dead code removal as an option now.

Sign In or Register to comment.