Spin2

17810121318

Comments

  • Wuerfel_21 wrote: »
    I think it is important to distinguish between a variable that can be read from the outside and one that can be both read and written from the outside. The lack of this distinction is one reason why C++ and Java programmers often use getter/setter methods instead of public instance variables. (The other reason is that a setter method can do more than just set the variable (usually to notify some other code about the changed variable))

    Yes, I can see that. We really only NEED methods.
  • cgracey wrote: »
    We really only NEED methods.

    Ruby has an interesting approach: one can never access an object's variables from the outside, but when you define a getter method and a setter method whose name ends in an "=", it can be used like a variable. That obviously looses the speed of directly writing to a variable, unless some optimization to detect trivial getters/setters is performed.
    Here's an example of how it works for clarification:
    class Foo
      def myvar()
        puts("Getter called!")
        return @variable
      end
      def myvar=(new_value)
        puts("Setter called!")
        @variable = new_value
      end
    end
    
    x = Foo.new
    x.myvar = 1 # prints Setter called!
    x.myvar += 2 # prints Getter called!, then Setter called!
    puts(x.myvar)# prints Getter called!, then 3
    
    
  • The syntax should be public/private with private assumed.

    No need to change 30 years of semantics at this point...
  • cgraceycgracey Posts: 13,125
    edited 2020-01-30 - 20:23:36
    I think a firewall of methods is a good way to protect an object's internal variables.

    What Ruby does there is maybe mainly to simplify source code, so that rather than have a method to set myvar, like 'set_myvar(value)', it lets the programmer write simple assignments which are easier to look at and understand at a glance: 'myvar = 0'. It has the same effect as a method, but it's a lot easier to look at.
  • Wuerfel_21Wuerfel_21 Posts: 982
    edited 2020-01-30 - 20:29:19
    cgracey wrote: »
    What Ruby does there is maybe mainly to simplify source code, so that rather than have a method to set myvar, like 'set_myvar(value)', it lets the programmer write simple assignments which are easier to look at and understand at a glance: 'myvar = 0'. It has the same effect as a method, but it's a lot easier to look at.
    Exactly. (It also avoids nonsense like "obj.set_myvar(obj.get_myvar()+2)")
  • Cluso99Cluso99 Posts: 16,921
    edited 2020-01-30 - 20:30:26
    Can we just get a usable release please?

    We will end up with chips and no software. This is gating further software development, for me at least. Do we want to wait another ten years?
  • ersmithersmith Posts: 4,446
    edited 2020-01-30 - 22:11:50
    Cluso99 wrote: »
    Can we just get a usable release please?

    We will end up with chips and no software. This is gating further software development, for me at least. Do we want to wait another ten years?

    At the risk of sounding like a broken record, you can program in Spin for the P2 today, and have been able to for some time. Lots of people have had success with FlexGUI/fastspin. If you stick with the basic Spin1 subset (with P2 assembly code in DAT) then I suspect you'll be able to port it over very quickly to Chip's Spin2 when that's available. I think the main benefit of Chip's Spin2 will be the smaller code size and (when it's available) self-hosted compilation, but for now you can certainly get a lot done with fastspin.

  • David BetzDavid Betz Posts: 14,190
    edited 2020-01-30 - 22:00:42
    ersmith wrote: »
    [I think the main benefit of Chip's Spin2 will be the smaller code size and (when it's available) native compilation, but for now you can certainly get a lot done with fastspin.
    Native compilation? Do you mean a self-hosted development environment? Surely, FastSpin already compiles to native code.

  • Cluso99 wrote: »
    Can we just get a usable release please?

    We will end up with chips and no software. This is gating further software development, for me at least. Do we want to wait another ten years?

    Actually, it was DONE yesterday, but there has been one more thing I've wanted to add for the first release that I'm implementing today. Just a wafer thin mint. I still need to write the documentation, anyway.

    I'm putting a stealth method pointer into the call-stack frame which can be used to vector output anywhere, without the need for passing discrete method pointers around. Methods can set it to whatever they want, as they go, and any methods called inherit the current pointer. It's going to be dreamy.
  • cgracey wrote: »
    I'm putting a stealth method pointer into the call-stack frame which can be used to vector output anywhere, without the need for passing discrete method pointers around. Methods can set it to whatever they want, as they go, and any methods called inherit the current pointer. It's going to be dreamy.

    Could you describe exactly how this works (from a programmer's perspective, not from the compiler's)? The general idea seems promising, but it also seems like there are a lot of ways that it could go wrong and cause a beginner to get hopelessly tangled up...

  • David Betz wrote: »
    ersmith wrote: »
    [I think the main benefit of Chip's Spin2 will be the smaller code size and (when it's available) native compilation, but for now you can certainly get a lot done with fastspin.
    Native compilation? Do you mean a self-hosted development environment? Surely, FastSpin already compiles to native code.

    Yes, I did mean self-hosted, thanks. I've corrected my original post.
  • cgraceycgracey Posts: 13,125
    edited 2020-01-30 - 22:31:46
    It works like this:
    PUB doit
      send := @serial.tx
      send("This is a test... ", hex(x), 13)
    
    PRI hex(num)
      repeat 8
        send(lookup((num ROL= 4) & $F: "0".."9", "A".."F"))
    

    SEND is a method pointer located at long[dbase][-2]. It can be assigned and read. If assigned zero, which is the initial state on cog start, SEND() doesn't do anything.

    SEND() is a special method that the compiler generates which has hidden sub-methods for handling strings of bytes, single values, and multiple return values from methods.
  • What if the user wants to send the result of a function rather than the function itself? That is, what if they want to do:
    PUB doit
      send := @serial.tx
      send("uppercase a is", upper1("a"))
    
    PRI upper1(x) : r
      r := x + "A" - "a"
    
    How is that distinguished from:
    PUB doit
      send := @serial.tx
      send("uppercase a is", upper2("a"))
    
    PUB upper2(x)
      send(x + "A" - "a")
    

  • cgraceycgracey Posts: 13,125
    edited 2020-01-30 - 22:46:26
    I forgot to mention that all return values are sent to the SEND method, as well. So, both of your code examples would do the same thing.
  • cgraceycgracey Posts: 13,125
    edited 2020-01-30 - 22:51:43
    The SEND method must be a method that takes a single parameter.

    This mechanism can be used to vector data streams anywhere.
  • I haven't been closely following the Spin2 development, so excuse me for the following dumb question. Will Spin2 methods be able to handle a variable number of parameters? And how does it distinguish between long parameters and string pointers?
  • Dave Hein wrote: »
    I haven't been closely following the Spin2 development, so excuse me for the following dumb question. Will Spin2 methods be able to handle a variable number of parameters? And how does it distinguish between long parameters and string pointers?

    At this time, Spin2 methods have fixed numbers of parameters. That could change in the future. For your second question, I'm not sure what you mean.
  • Dave, were your questions revolving around strings?
  • Dave HeinDave Hein Posts: 6,204
    edited 2020-01-30 - 23:49:59
    Chip, in your example you show two different calls to send:
      send("This is a test... ", hex(x), 13)
      send(lookup((num ROL= 4) & $F: "0".."9", "A".."F"))
    
    In the first case there appear to be 3 parameters, which are a string, the return value of hex() and 13. In the second case there is one parameter, which is the value returned by lookup. Maybe the first call is actually only one parameter with the values of hex(x) and 13 included in the string. However, in the first call you are passing a string pointer, and the second call passes a long value. How does send, or tx know the difference?
  • cgraceycgracey Posts: 13,125
    edited 2020-01-31 - 00:59:03
    The compiler builds the SEND() instance with three different ingredients. It's much different than a normal method call. For every element, the method pointed to by SEND is called with the element as a single parameter, effectively like 'serial.tx(element)'.

    1) "This is a test..." gets recognized as a byte string and some bytecodes in the interpreter execute to send all those bytes to the SEND method.

    2) 'hex(x)' gets called and then, afterwards, any return values it generated get sent the SEND method. Of course, hex() may do its own SEND() operations.

    3) '13' gets sent as a single value to the SEND method.

    I think these three cases cover everything, although a z-string output could be added. It might need a keyword.
  • I think that it would be useful to have a shorter notation for a string pointer, something like:
      ser.write(@"hello, world")
    
    instead of
      ser.write(string("hello, world"))
    

    I realize that for this particular example you could use the new "send" method instead, but in general strings can be awkward to use in Spin and having an easier way to get pointers to them would be nice.
  • Chip, thanks for the clarification. To me it seems that if you can do send("abc", hex(5), 13) you should also be able to do serial.tx("abc", hex(5), 13). Of course, this would mean that you don't need the hidden send method pointer. You should just be able to use any method pointer or direct call.
  • ersmith wrote: »
    I think that it would be useful to have a shorter notation for a string pointer, something like:
      ser.write(@"hello, world")
    
    instead of
      ser.write(string("hello, world"))
    

    I realize that for this particular example you could use the new "send" method instead, but in general strings can be awkward to use in Spin and having an easier way to get pointers to them would be nice.

    Eric, I like that notation. It's easy to parse, too, because it's just @constant(s). That would be good to replace string() with.
  • Dave Hein wrote: »
    Chip, thanks for the clarification. To me it seems that if you can do send("abc", hex(5), 13) you should also be able to do serial.tx("abc", hex(5), 13). Of course, this would mean that you don't need the hidden send method pointer. You should just be able to use any method pointer or direct call.

    Wouldn't we still need the hidden pointer to give hex() an output method?
  • RaymanRayman Posts: 11,489
    edited 2020-01-31 - 02:12:35
    I like the idea of replacing string() with @, just not sure about the @

    I'm not sure what's better though... Could it be $ or & or maybe > ?
    *?
  • Dave HeinDave Hein Posts: 6,204
    edited 2020-01-31 - 03:30:09
    post deleted.
  • cgracey wrote: »
    whicker wrote: »
    Public / private syntax gets annoying. If it's not explicitly declared public or shared, it's private.

    KISS

    I agree.
    CON	shared	i = 2,	j = 5,	k = 11
    		m = 1,	n = 2
    
    VAR	shared	x,y,x
    		a,b,c
    

    We already have public / private syntax (DAT vs VAR) so if DAT variables were accessible by name we wouldn't need to worry about declaring "shared". CON sections are always accessible via obj#constant syntax but variables are not unless explicitly declaring setter/getters. If you wanted to trigger some OTHER more complex behavior, you could always do it the way we have BUT... The idea of being able to access "variables" (even if the limitation is they must be declared in DAT) is very promising. I wonder if @Rayman thinks this would be a reasonable compromise? I find myself creating a lot of setter/getters and I think the declaration of private vs public is inherent..

    Just my 2c.
  • evanhevanh Posts: 10,088
    edited 2020-01-31 - 08:41:37
    Chip,
    I've just tested Pnut v33p for the LOC bug and found it is still present. Further reading - https://forums.parallax.com/discussion/comment/1457051/#Comment_1457051

    Here's my latest testing source repacked to suit Pnut. And also the correct output of Fastspin along with the buggy output of Pnut.

    Fastspin
     LOC  PA, #label testing
    =========================
    
     CogExec    PC    Op-code  PA-data    Cog-dref Hub-dref LUT-dref
    cog008   00000015 fe9ffff2 00000008   11111111 00000000 fdb00078
    hub110   00000019 fe9000f6 00000110   52525252 22222222 00000000
    hub4a4   0000001d fe8004a4 000004a4   00000000 33333333 fd640200
    lut3a0   00000021 fe8003a0 000003a0   facbeff6 00000000 44444444
    
     HubExec    PC    Op-code  PA-data    Cog-dref Hub-dref LUT-dref
    cog008   00000468 fe800008 00000008   11111111 00000000 fdb00078
    hub110   00000478 fe9ffc94 00000110   52525252 22222222 00000000
    hub4a4   00000488 fe900018 000004a4   00000000 33333333 fd640200
    lut3a0   00000498 fe8003a0 000003a0   facbeff6 00000000 44444444
    
     LutExec    PC    Op-code  PA-data    Cog-dref Hub-dref LUT-dref
    cog008   000003a7 fe800008 00000008   11111111 00000000 fdb00078
    hub110   000003ab fe800110 00000110   52525252 22222222 00000000
    hub4a4   000003af fe8004a4 000004a4   00000000 33333333 fd640200
    lut3a0   000003b3 fe9fffec 000003a0   facbeff6 00000000 44444444
    

    Pnut
     LOC  PA, #label testing
    =========================
    
     CogExec    PC    Op-code  PA-data    Cog-dref Hub-dref LUT-dref
    cog008   00000015 fe9fffc8 000fffde   8607ee30 00000000 fa97fe3e
    hub110   00000019 fe9003d8 000003f2   fd63ec18 00000000 fd6bec18
    hub4a4   0000001d fe8004a4 000004a4   00000000 33333333 fd640200
    lut3a0   00000021 fe900df8 00000e1a   fda004a8 00000000 fd7c002d
    
     HubExec    PC    Op-code  PA-data    Cog-dref Hub-dref LUT-dref
    cog008   00000468 fe800008 00000008   11111111 00000000 fdb00078
    hub110   00000478 fe800110 00000110   52525252 22222222 00000000
    hub4a4   00000488 fe900018 000004a4   00000000 33333333 fd640200
    lut3a0   00000498 fe8003a0 000003a0   facbeff6 00000000 44444444
    
     LutExec    PC    Op-code  PA-data    Cog-dref Hub-dref LUT-dref
    cog008   000003a7 fe9ff180 000ff528   fd8003a1 9e3e3e39 00000000
    hub110   000003ab fe9ff590 000ff93c   2aa3ec03 21030111 00000000
    hub4a4   000003af fe8004a4 000004a4   00000000 33333333 fd640200
    lut3a0   000003b3 fe9fffb0 00000364   6f432020 00000000 00000000
    
  • RaymanRayman Posts: 11,489
    edited 2020-01-31 - 13:51:18
    So, the CONs are all shared already with # to get them...

    There would have to be some way to make var public or private...
    We have PUB and PRI. Maybe we need VAR and something else for public variable section...

    maybe EXT for "external"?

    Things in VAR section would be private, things in EXT would be public...
  • Rayman wrote: »
    Was just thinking that it would be nice if you could change a "VAR" in a sub-object from a higher level object...

    For example, I'm playing around with a VGA driver and would be nice if could do:
    vga.boxcolor := $10
    

    To set the VAR section defined variable, "boxcolor", to the value $10

    Instead, I currently need to create a function like "SetBoxColor" in the VGA driver as a work around....

    +1 for this

    ... and I would also add two events, if possible. To maintain the same example with the VGA driver, it will be nice that if the compiler founds two subs (in the VGA driver object):
    PRI boxcolor_evGet
    
    it execute it before an external read access
    x := vga.boxcolor
    

    and executes this
    PRI boxcolor_evSet
    
    after an external write access
    vga.boxcolor := $10
    

Sign In or Register to comment.