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 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?
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.
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...
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).
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 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.
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.
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.
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)
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:
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:
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.
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.
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.
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?
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,
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.
Comments
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: Then you would instance one in a VAR And use it...
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.
Good idea.
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...
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.
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'm all for keeping things as simple as possible.
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.
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.
I prefer a built-in 'MethodPtr()' operation to overloading the ":=" assignment operator, in order to keep the assignment concept pure and simple.
And I agree with his sentiment on structs: just an object without methods.
I still think (optional) types on parameters would be useful. Consider a report writing function. Without parameter types it would look something like: With types it could be: 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:
OTOH if we have object pointers maybe MPTR could just be an object? We could have something like:
That's neat!
We could even make objects work so that if no .method is present, it just calls the first PUB method.
"..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.
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.
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.
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.
BTW, I used indentation instead of putting the entire declaration on one line, since the alias list could be quite long.
-Phil
-Phil
That's a good way of doing it.
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.
Do we need to have those larger OBJ declarations in all of our objects in order to have all the "standard library" stuff available?