Shop OBEX P1 Docs P2 Docs Learn Events
New Spin - Page 30 — Parallax Forums

New Spin

1272830323336

Comments

  • Roy ElthamRoy Eltham Posts: 3,000
    edited 2017-03-05 10:06
    Chip,
    Agreed. Structures can solve a lot of things. The main issue is that you need to have types on your parameters to methods and on the methods themselves (for their return type). I don't have a problem with this, but some folks here do.

    Currently all parameters to methods and locals are LONGs, we could retain the idea that if they don't have a type then they are assumed to be LONGs. I think it makes sense, in this case, to have the type be a suffix decoration on the var name. Like: MyLocalVar#type, so if it's not there then it's a LONG.

    Also, It's probably okay to say that structures are always passed/returned by reference/pointer and not value (so the actual param being passed is a pointer to the structure instance.

    Did you see my post about how to define a structure? It could be a new block type like this:
    STRUCT  myStruct
       LONG  member1
       WORD member2
       LONG  member3
       BYTE  member4
    
    Then you would instance one in a VAR
    VAR
      myStruct  aStructInstance
    
    And use it...
    PUB main
      aStructInstance.member1 := 1
      ' etc.
    
    PUB SomeMethod( structParam#myStruct )
      if structParam.member1 = 1
        ' do something
    
  • cgraceycgracey Posts: 14,208
    Yes, passing by reference would be the way to go.
  • cgraceycgracey Posts: 14,208
    That would mean that all structure variables would automatically get passed as addresses. But, we'd still need to be able to have structures in local variable space, right?
  • cgraceycgracey Posts: 14,208
    edited 2017-03-05 10:21
    Roy Eltham wrote: »
    Chip,
    Agreed. Structures can solve a lot of things. The main issue is that you need to have types on your parameters to methods and on the methods themselves (for their return type). I don't have a problem with this, but some folks here do.

    Currently all parameters to methods and locals are LONGs, we could retain the idea that if they don't have a type then they are assumed to be LONGs. I think it makes sense, in this case, to have the type be a suffix decoration on the var name. Like: MyLocalVar#type, so if it's not there then it's a LONG.

    Also, It's probably okay to say that structures are always passed/returned by reference/pointer and not value (so the actual param being passed is a pointer to the structure instance.

    Did you see my post about how to define a structure? It could be a new block type like this:
    STRUCT  myStruct
       LONG  member1
       WORD member2
       LONG  member3
       BYTE  member4
    
    Then you would instance one in a VAR
    VAR
      myStruct  aStructInstance
    
    And use it...
    PUB main
      aStructInstance.member1 := 1
      ' etc.
    
    PUB SomeMethod( structParam#myStruct )
      if structParam.member1 = 1
        ' do something
    

    That looks good. I was just hashing out some syntax, but yours is way better.

    Wait... That 'structParam' keyword would not be necessary, because 'myStruct' is known to be a structure type, already. You could just use 'myStruct' and the compiler would pass the address, as if '@myStruct' had been used.

    Hold on... There'd still be a need in SomeMethod to know what it was dealing with. That's why you'd use the keyword.

    This could get hairy, passing structures between object levels. Everybody would need to know the structures in use.
  • If you want to use a structure from a different object then do something like this
    VAR
      objWithStruct.myStruct aStructInstance
    
  • cgraceycgracey Posts: 14,208
    If you want to use a structure from a different object then do something like this
    VAR
      objWithStruct.myStruct aStructInstance
    

    Good idea.
  • Roy ElthamRoy Eltham Posts: 3,000
    edited 2017-03-05 10:47
    Chip,
    You misunderstood my example. structParam was the parameter var name not a keyword, and the #myStruct was the type information. myStruct is the type. aStructureInstance is the VAR instance of that type.
    local variables can be typed just like the parameter was...
    PUB someMethod() | localStructVar#myStruct
      localStructVar.member1 := 2
    
  • Also, a struct defined in a child object needs to be able to be used as a "type" in the parent. The compiler can handle that, STRUCT blocks can be public like CON stuff and PUBs are.
    This allows objects to pass structures into child objects. Spin doesn't allow child objects to know anything about the parents, and that should remain true.
  • I'm not convinced we need structs. A struct is just an object that happens not to have any methods. We already have syntax for that. If we add types to parameters and local variables (so that they can be objects just like globals can) then that would give us everything we need. In fact it would allow for a simplification of the language. If VARs can be objects, then the OBJ section could be removed or re-purposed. Perhaps OBJ could be for type declaractions only, and re-named TYPE, like so:
    TYPE
       SerialType : "FullDuplexSerial"  '' declares SerialType to be an alias for a class
    VAR
       SerialType  fds ' declares an instance of FullDuplexSerial
     
    PUB SendChar(port#SerialType, c)  ' port is a pointer to a FullDuplexSerial
      port.tx(c)
    

    Maybe the syntax could be re-arranged or simplified further, but you get the drift. Rather than having two conceptual items for the user and compiler to deal with (structs and objects) we just have one (objects).

    Eric
  • cgraceycgracey Posts: 14,208
    Structures are neat, in that you can drill down as deep as they go, while objects are now one-level down, only. Would the solution be to increase object depth?

    I'm all for keeping things as simple as possible.
  • cgraceycgracey Posts: 14,208
    edited 2017-03-05 12:38
    ersmith wrote: »
    I'm not convinced we need structs. A struct is just an object that happens not to have any methods. We already have syntax for that. If we add types to parameters and local variables (so that they can be objects just like globals can) then that would give us everything we need. In fact it would allow for a simplification of the language. If VARs can be objects, then the OBJ section could be removed or re-purposed. Perhaps OBJ could be for type declaractions only, and re-named TYPE, like so:
    TYPE
       SerialType : "FullDuplexSerial"  '' declares SerialType to be an alias for a class
    VAR
       SerialType  fds ' declares an instance of FullDuplexSerial
     
    PUB SendChar(port#SerialType, c)  ' port is a pointer to a FullDuplexSerial
      port.tx(c)
    

    Maybe the syntax could be re-arranged or simplified further, but you get the drift. Rather than having two conceptual items for the user and compiler to deal with (structs and objects) we just have one (objects).

    Eric
    I understand your example, but man!!! There are a lot of things going on there that are complicated for a newbie to get his head around. This is inside-out compared to what I think is "normal" programming. The only way I can imagine this becoming readily understandable, is if some graphic analogy were synthesized from every code example, so people could grasp the concepts in play.

    I'm thinking, for now, that maybe just going back to simple things is the way to go, along with object and method pointers, but using only LONG variable sets for them.
  • Dave HeinDave Hein Posts: 6,347
    edited 2017-03-05 12:44
    Look at how C/C++ does structures, and just implement the same thing in Spin without braces. Done.

    The same suggestion for method pointers. The same suggestion for floats/doubles. The same suggestion for types. Etc.

    You just have to change the syntax to match Spin's syntax.

    Of course, then Spin becomes as complex as C/C++. Is that what you really want?
  • cgraceycgracey Posts: 14,208
    edited 2017-03-05 13:14
    Dave Hein wrote: »
    Look at how C/C++ does structures, and just implement the same thing in Spin without braces. Done.

    The same suggestion for method pointers. The same suggestion for floats/doubles. The same suggestion for types. Etc.

    You just have to change the syntax to match Spin's syntax.

    Of course, then Spin becomes as complex as C/C++. Is that what you really want?

    Yes, I think some early limits have to be chosen to keep things from getting way out of hand. By the time we'd implement everything, it would have been better to just make C++. If people are going to program in such a complex way, they might as well use the standard method (C++). Spin needs to be simple to grasp, in order to overcome its status as an oddball language.
  • cgraceycgracey Posts: 14,208
    edited 2017-03-05 13:12
    Keeping things about as simple as possible for method pointers:
    VAR long MyMethod[4]			' ptr to next long, vpbase, pbase, index
    
    
    PUB ReturnMethodPointer
    
      MethodPtr(MyMethod, SomeMethod)	' "MethodPtr" builds method pointer structure
    
      #MyMethod(Params)			' #AddressOfMethodPointerStructure calls method
    
      return MyMethod			' address of method pointer structure can be passed
    

    I prefer a built-in 'MethodPtr()' operation to overloading the ":=" assignment operator, in order to keep the assignment concept pure and simple.
  • cgraceycgracey Posts: 14,208
    And simple object pointers:
    OBJ
    
      Obj1  : "objectfile1"		' object instance
      Obj2  = "objectfile2"		' object type (no VAR block)
    
    VAR
    
      long vbase
    
    PUB SetObjPtr(ptr)
    
      vbase := ptr
    
    PUB UseObj(param)
    
      Obj2[vbase].method(param)
    
  • I've just been lurking here, and not commenting at all. But I feel that the example given by @ersmith is MUCH easier to follow then what you're doing here: too 'bare metal', which makes it more complex for a newbie in my eyes.

    And I agree with his sentiment on structs: just an object without methods.
  • Seconded - I really don't like the user having to know that a method pointer is 4 longs - Ersmiths version where it's just an MPTR and the details are hidden from you is much cleaner, and allows them to be put it arrays.
  • Maybe we should shelve method pointers for now -- that sounds like the more complicated feature, and perhaps we can get by with just object pointers.

    I still think (optional) types on parameters would be useful. Consider a report writing function. Without parameter types it would look something like:
    OBJ
      Ser = "SerialTerminal"
    PUB report(s, a, b)
      Ser[s].str("the values are ")
      Ser[s].dec(a)
      Ser[s].str(" and ")
      Ser[s].dec(b)
    
    With types it could be:
    OBJ
      Ser = "SerialTerminal"
    PUB report(s = Ser, a, b)
      s.str("the values are ")
      s.dec(a)
      s.str(" and ")
      s.dec(b)
    
    The second version is more concise, and also allows the compiler to do type checking on the first parameter of report. I don't know what the best syntax is for declaring the type of a parameter: I think any of the below could work:
    PUB report(Ser[s], a, b)
    PUB report(s = Ser, a, b)
    PUB report(s#Ser)
    PUB report(s@Ser)
    

  • JasonDorie wrote: »
    Seconded - I really don't like the user having to know that a method pointer is 4 longs - Ersmiths version where it's just an MPTR and the details are hidden from you is much cleaner, and allows them to be put it arrays.
    Actually that one was Chip's. I raised a (weak) objection that if we're adding another data type that DLONG would be more useful than MPTR. I'm kind of reluctant to add a type > 32 bits to the language, it will complicate things.

    OTOH if we have object pointers maybe MPTR could just be an object? We could have something like:
    OBJ
      mptr: "MethodPointer"
    PUB returnMethod
      mptr.MethodPointer(@theMethod)
      return mptr
    PUB theMethod(x, y)
      OUTA := x & y
    PUB useMptr(x, y)
      mptr.Invoke(x, y)
    
  • cgraceycgracey Posts: 14,208
    ersmith wrote: »
    JasonDorie wrote: »
    Seconded - I really don't like the user having to know that a method pointer is 4 longs - Ersmiths version where it's just an MPTR and the details are hidden from you is much cleaner, and allows them to be put it arrays.
    Actually that one was Chip's. I raised a (weak) objection that if we're adding another data type that DLONG would be more useful than MPTR. I'm kind of reluctant to add a type > 32 bits to the language, it will complicate things.

    OTOH if we have object pointers maybe MPTR could just be an object? We could have something like:
    OBJ
      mptr: "MethodPointer"
    PUB returnMethod
      mptr.MethodPointer(@theMethod)
      return mptr
    PUB theMethod(x, y)
      OUTA := x & y
    PUB useMptr(x, y)
      mptr.Invoke(x, y)
    

    That's neat!

    We could even make objects work so that if no .method is present, it just calls the first PUB method.
  • I think the structure and syntax of SPIN relates to how we each visualize how the machine digests our code and how we navigate the editor.

    "..if some graphic analogy were synthesized from every code example, so people could grasp the concepts in play."

    Oh how many times have I longed for a step by step graphic to peer under the hood of that beautiful machine. If I understood the tokenizer and interpreter maybe I could write in assembly, or at least modify it. I think much of our struggle and inability to contribute to this amazing project is our individual lack of understanding or outright misunderstanding of how the P1 works, let alone how the p2 should work. Investing in graphic aids, and explaining in detail how it works would not be wasted.

    I have a bunch of feedback from my years with the Propeller Tool but I don't want to wear out my welcome just yet.
  • cgraceycgracey Posts: 14,208
    edited 2017-03-06 10:05
    4given wrote: »
    I think the structure and syntax of SPIN relates to how we each visualize how the machine digests our code and how we navigate the editor.

    "..if some graphic analogy were synthesized from every code example, so people could grasp the concepts in play."

    Oh how many times have I longed for a step by step graphic...

    If the inner workings can be fully unveiled, a Lego-minded child could make inferences and design decisions that, otherwise, only an expert could make. I really want to have good code visualization graphics for Spin2. They should be able to remove just about every ambiguity.
  • I've updated fastspin / spin2cpp to support object pointers using the syntax proposed above (no types on parameters though, you have to add explicit ObjName[p] casts). There's also preliminary support for the v16a instruction set. Binaries are posted in the "fastspin for P2" thread, and sources are on github in the "spin2" branch of spin2cpp.
  • .portability is not the goal, then anything goes. Just keep inventing keywords to do whatever.

    While I think just making a bunch of keywords is extreme, the case for a robust set is reasonable.

    On portability, SPIN will employ PASM. It's not portable online, though maybe on a procedural basis it could sort of be. Depends on the target.

    I say we should not consider portability as a design goal.

    Strings could go either way. I have no opinion on the byte shuffle.

  • Heater.Heater. Posts: 21,230
    Wow, Spud,

    You have managed to quote me here from a totally different thread!

    I actually agree. Spin, as I know it, includes PASM. Ergo, it's totally machine specific. Anything goes.
  • An implied library which is an object, but does not need the object.method() syntax, just the method() syntax. If any method uses an unknown keyword, the implied object's methods get checked for a name match. Any object that uses any of those methods will have that implied object included, which is just a 2-long cost. The top-level file can specify this implied object. That way, Spin can get extended without any tool changes.
    I like that. But why not make it universal:
    OBJ
    
      pst : "Parallax Serial Terminal" 
        .dec as printdec, .char as printchar
        .str as printstr, #CR as CR
    
    PUB start
    
      pst.start(9600)
      printdec(1234)
      printchar(CR)
    

    BTW, I used indentation instead of putting the entire declaration on one line, since the alias list could be quite long.

    -Phil
  • Also, Chip, I think we talked about this before. Is there any value in requiring method calls always to include the parens, even when there is no argument list? IOW, are there any ambiguities that this might overcome?

    -Phil
  • cgraceycgracey Posts: 14,208
    An implied library which is an object, but does not need the object.method() syntax, just the method() syntax. If any method uses an unknown keyword, the implied object's methods get checked for a name match. Any object that uses any of those methods will have that implied object included, which is just a 2-long cost. The top-level file can specify this implied object. That way, Spin can get extended without any tool changes.
    I like that. But why not make it universal:
    OBJ
    
      pst : "Parallax Serial Terminal" 
        .dec as printdec, .char as printchar
        .str as printstr, #CR as CR
    
    PUB start
    
      pst.start(9600)
      printdec(1234)
      printchar(CR)
    

    BTW, I used indentation instead of putting the entire declaration on one line, since the alias list could be quite long.

    -Phil

    That's a good way of doing it.
  • Phil,
    Excellent idea! I was trying to think of a good way to make it "generic and universal" so everyone could make "library" like stuff. I was thinking a new type of object declaration and then some kind of syntax on the objects PUBs to define the library names. Your idea is simpler and easier.
  • Only thing I wonder about...

    Do we need to have those larger OBJ declarations in all of our objects in order to have all the "standard library" stuff available?
Sign In or Register to comment.