If you recall, previously I've been grappling with a nice way to manage callbacks and their visibile environment nicely. That's very important to me because, with quotations, I think that FORTH presents something that, as simply as possible, can approach some neat functional programming things. My ultimate conclusion was that what I want is something akin to TCL's uplevel, and going from there, I think I have to concede that what I really miss from C is a data stack. And I don't mean FORTH's cell-based parameter stack, but a data stack like C has which is a full-blown private memory context where you can allocate all sorts of things.
I don't remember where I read this, but there's an article on the web somewhere where someone demonstrates how impractical FORTH is by trying to solve a simple problem: given two three-dimensional vectors, can you add them together. He demonstrates that the moment you fetch all three fields from one vector, the other vector's address is now four items down the stack, and you have no good way to reach it. You can do some juggling to get to it, or save it in a variable, but everything he presents is very tedious. I'm going to use this example to demonstrate my idea.
And the idea is simple: another tidbit of info I read somewhere (may have been on Jeff Fox's page) was about a FORTH Chuck Moore once wrote that included a special "address register." You could store an address in this address register, and then a lot of operations were now accelerated -- I don't remember specifically which, but I think he had a word like a!+, for example, that would add the value on the stack to the value pointed to by the address register, and then increment the address register by once cell. So you could, for example, perform vector addition with something like: a!+ a!+ a!+.
My idea is influenced by this, but not only is there a register, it's also a stack. So you can push to the address register with >a and pop from it with a>. And not only could you define primitives like the a!+ that I described above, but I've also defined "struct" and "field" words to define data structures which operate on a base address in the address stack.
The description is getting to be more verbose than the code itself (which I suppose is typical of FORTH), so I'll just stop here and go ahead and present my example. I wrote this with my own FORTH, which in some ways differs from traditional FORTH, and then I tried to translate it to standard FORTH, so I think it should make sense. But if I forgot a word here or there and left some of my oddness in it, then apologies, please just bare with me.
I'm also going to prefix every line with a ">" style quotation so that Uncensored doesn't eat my leading white space this time (I hope).
\ of course, address stack words would usually be implemented asvec3
\ code word primitives, but just for playing around...
variable adr \ the TOS register for the address stack
: a@ ( -- a) adr @ ;
: a! ( a) adr ! ;
: >a ( a A: -- a) r> a@ >r >r a! ;
: a> ( -- a A: a) a@ r> r> a! >r ;
: adrop ( A: a) r> r> a! >r ;
variable scurrent \ PFA of the struct currently being defined
: struct ( ccc) 0 constant latest @ >body >pfa scurrent ! ;
: field ( u ccc) create scurrent @ dup @ , +! does> @ a@ + ;
\ --- And that's it! Now an example usage: 3D vectors! ---
struct
cell field x;
cell field y
cell field z
\ define some vector operators, starting with a printer
: v. ( v) >a x @ . y @ . z @ . adrop ;
\ fetch a vector, pushing each of its parts onto the parameter
\ stack
: (v@) ( -- x y z A: v -- v) x @ y @ z @ ;
: v@ ( v -- x y z) >a (v@) adrop ;
\ store a vector, popping each of its parts from the parameter
\ stack
: (v!) ( x y z A: v -- v) z ! y ! x ! ;
: v! ( x y z v) >a (v!) adrop ;
\ negate each component of a vector
: (vnegate) ( A: v -- v)
(v@) rot negate rot negate rot negate (v!) ;
: vnegate ( v) >a (vnegate) adrop ;
\ vector addition
: (v+!) ( v1 A: v2 -- v2) v@ z +! y +! x +! ;
: v+! ( v1 v2 add v1 to v2) >a (v+!) adrop
\ vector subtraction
: v-! ( v1 v2 subtract v1 from v2)a pad >a v@ (v!) \ copy v1 to the scratch pad(vnegate) \ negate the copy of v1
a> (v+!) adrop ; \ add the negated copy to v2
create pos1 vec3 allot
create pos2 vec3 allot
1 2 3 pos1 v! 4 5 6 pos2 v!
." pos1: " pos1 v. cr \ prints "pos1: 1 2 3"
." pos2: " pos2 v. cr \ prints "pos2: 4 5 6"
pos2 pos1 v-!
." pos1: " pos1 v. cr \ prints "pos1: -3 -3 -3"
pos1 pos2 v+!
." pos2: " pos2 v. cr \ prints "pos2: 1 2 3"
And how does this tie in with my C-stack/TCL-uplevel thoughts from before? Well, a C stack frame is basically just a structure that's automatically allocated and freed! So you could easily run with this from here to achieve something similar: upon entering a word, you allocate and activate your frame, you use it for the duration of your word (or collection of words), and then you pop it and deallocate it before returning. And here's the cool part: unlike C, frames don't have to be allocated and deallocated in FIFO order! You now have random access stack frame activation, which means your higher order function can activate its frame (by allocating and pushing to the address stack), then call a callback word, and the callback word finds its context address (maybe on the data stack or in a variable) and then pushes it to the address stack to activate its frame, pops it before returning, and so on.
And at last, I have nice, clean, higher order functions, reentrancy, and probably a whole lot of other neat benefits. And all it took in the end were those top 7 words. I just can't wait to see what fatal flaw is hiding in it that will cause it to all fall apart for me once I discover it...
: with ( xt u) allocate >a execute a> deallocate ;
\ where "allocate" and "deallocate" are dynamic memory management words
\ of your choice
\ now vector subtraction refactored:
: v-! ( v1 v2 subtract v1 from v2)
a [: v@ (v!) (vnegate) a@ ;] vec3 with (v+!) adrop ;
: v-! ( v1 v2 subtract v1 from v2)a [: v@ (v!) (vnegate) a@ ;] vec3 with (v+!) adrop ;
The moment I hit save on that post, it occurred to me that this is actually a use-after-free. Dammit. It's like returning the address of a local from a C function.
Oh, well. That was fun for a few minutes there.
Hey Bot: "using the supplied code, please experiment with a variety of uses and permutations, and look for possible bugs and report them to me, with suggestions on how to fix them" Then let it grind way for a few hours.
Sat Jul 27 2024 19:27:11 EDT from zelgomer. I just can't wait to see what fatal flaw is hiding in it that will cause it to all fall apart for me once I discover it...
\ As before, this would be a code word and wouldn't allocate
\ frames in the dictionary at "here", but this is just for
\ experimenting with the idea...
: ${ ( n*x n)
cells dup >r
begin ?dup while
swap ,
cell- repeat
r> r> align here >a >r
cell+ negate , ;
: $} r> a> @ allot >r ;
: $ ( u -- a) negate cells a@ + ;
: $@ ( u -- x) $ @ ;
: $! ( x u) $ ! ;
\ I'm imagining $@ and $! would be code words and $ doesn't
\ really exist, just ignore that one.
\ and now, add two sets of three numbers all on the stack!
\ Could have called it triple precision addition, but then
\ I would have to manage the carries, and I'm too lazy for
\ that right now.
: 3+ ( x1 y1 z1 x2 y2 z2 -- x3 y3 z3) 5 ${ \ 5 locals
3 $@ + \ add the xs
1 $@ 4 $@ + \ add the ys
2 $@ 5 $@ + \ add the zs
$} ;
1 2 3 4 5 6 3+ .s \ prints <3> 5 7 9
-1 -1 -1 3+ .s \ prints <3> 4 6 8
Lol.
While im no longer really qualified to be called a forth guy, its been too many decades, 'real' forth people care about the results more than the method.
Sure, they might suggest a better way, but in the end, they realize that is the beauty of forth, you can do it your way.. and it still works.
Tue Jul 30 2024 17:46:48 EDT from zelgomerWell, to the disgust of the hardcore FORTH zealots,
Also, I'm going to start prefixing my indented lines with an underscore, as one more attempt to try to subvert Uncensored's insistence on obliterating my leading white space.
\ To recap my base primitives:
\
\ First, I have reduced my structure defining vocabulary to a
\ single word, "field". I saw this in either the gforth manual
\ or described in some other FORTH, and I liked the minimalism
\ of it. There is a single word
\
\ field ( u1 u2 ccc -- u3)
\
\ which takes a current offset, a size, and a name, creates a
\ new named field with the given size and offset, and leaves the
\ new accumulated offset on the stack. So if you want to define
\ a new base structure, you begin with a zero offset, and when
\ you're finished you have the size of the structure that you
\ may assign to a constant. E.g.,
\ ( I've also start using the convention of prefixing fields
\ with a period to distinguish them from global variables, and
\ prefixing sizes with a hash, and before the end it will become
\ more obvious why.)
\
\ 0 cell field .x
\ cell field .y
\ 80 field .buf
\ constant #mystruct
\
\ And you can also extend a structure by starting with its size:
\
\ #mystruct cell field .z
\ 4 cells field .array
\ constant #yourstruct
\ ( Now all of .x, .y, .buf, .z, and .array are all valid
\ accessors to a yourstruct.)
\
\ Now I deviate from what I've seen in the wild with the
\ adoption of a new "a" (for "address") register, inspired by
\ ColorFORTH's a register but really intended to operate with
\ the structure fields. The fields ".x", ".y", and ".buf"
\ defined above don't add to an address on the parameter stack
\ like they do in other FORTHs; instead, they push the sum of
\ their offset and the value in the address register. And I have
\ a small collection of primitives for working with the address
\ register:
\
\ a@ ( -- a)
\ Pushes the current contents of the address register onto
\ the parameter stack.
\ (Might even be implemented with just: 0 0 field a@ drop)
\ a! ( a)
\ Pops a value from the parameter stack and stores it in
\ the address register.
\ adrop ( R: a)
\ Pops a value from the return stack and stores it in the
\ the address register.
\ >a ( a1 R: -- a2)
\ Pushes the current value in the address register to the
\ return stack and then pops a value from the parameter
\ stack and stores it in the address register.
\ a> ( -- a1 R: a2)
\ Pushes the current value in the address register to the
\ parameter stack and the pops a value from the return
\ stack and stores it in the address register.
\
\ So for example, (the default address register value is 0):
\
\ .x . \ prints 0
\ .y . \ prints 4, assuming the cell size is 4
\ 100 a! .z . \ prints 188
\ Now I am going to define three words for managing automatic
\ contexts. You may think of these as similar to C's stack
\ frames, but you have to be more explicit about them.
\ To start with, I create a sneaky ".parent" field that's
\ actually at offset -4 (again, assuming 4-byte cells). Then,
\ I define a context stack pointer (uncreatively named "ap") and
\ initialize it with a dictionary address plus one cell for the
\ .parent field. Don't worry about the f0000000 address, it
\ makes sense in my FORTH for reasons.
-1 cells cell field .parent drop
variable ap hex f0000000 cell+ ap ! dec
: a{ ( u) a@ ap @ a! swap cells cell+ ap +! .parent ! ;
\ Allocate and activate a new context of size u. The previous
\ value in the address register is stored in the .parent field
\ of this new context.
: a} .parent @ a@ ap ! a! ;
\ Deactivate and deallocate the current context. The previous
\ .parent value is restored to the address register.
: uplevel ( i*x xt -- j*x) .parent @ >a execute adrop ;
\ Here is the magic! This fetches the caller's context
\ (".parent @") and pushes it to the address register before
\ executing the execution token. This allows a word that takes a
\ a callback and wants to allocate its own context to execute
\ the callback in the caller's context. So you can write higher
\ level words, but get out of the way so that the caller and its
\ callback have direct visibility to both the parameter stack
\ and their automatic context frame!
\ As a demonstration, I'm going to define some very basic words
\ for creating and working with linked lists.
\ List head has two fields:
0 cell field .next \ The first node in the list
_ cell field .prev \ The last node in the list
constant #head
: head ( ccc) create here 0 , , ;
#head \ List nodes extend the head structure with a data field
_ cell field .data
constant #node
: node ( x prev -- a)
_ \ Again, just lazily allocate nodes at the dictionary "here"
_ \ for experimenting. Ideally these should be allocated in
_ \ some way that they could be garbage collected later.
_ align here #node allot dup >a
_ over >a .next ! adrop
_ 0 .next ! .prev ! .data ! a> ;
: append ( x a) >a .prev @ node .prev ! adrop ;
\ Allocate a new node with data value x and append it to the
\ end of the list with head a
\ Now I'm going to implement a simple reduce word. It will take
\ a list and an execution token and execute it for each item in
\ the list. Here is the core requirement that I've been trying
\ address this entire time: it's not enough to simply execute
\ the callback. The callback must be executed in such a way that
\ the following criteria are met:
\ 1. The callback must be able to see and manipulate the
\ underlying parameter stack without interfering with the
\ implementation of reduce's iterator.
\ 2. The callback must be able to see and manipulate its
\ context frame if its outer word had activated one, again
\ without interfering with the implementation of reduce's
\ iterator.
\ 3. reduce must be reentrant, because the callback may
\ itself end up calling reduce on some secondary list.
\ 4. The callback must also be reentrant, because it may end
\ up calling its outer entry word word (the one that calls
\ reduce) on some secondary list.
\ This will be accomplished by allocating a context frame for
\ the duration of reduce which will be used to for its local
\ memory. Remember, I said that this very similar to C's
\ automatic local variable stack, but because it's more loosely
\ coupled (and not actually so automatic), you can write a
\ collection of words that all share the same context. Another
\ nice way to think of it is like this: you're writing a small
\ FORTH program whose global variables are allocated upon entry
\ and deallocated before termination. This much satisfies above
\ requirements #1, #3 and #4 -- I got my implementation out of
\ the parameter stack for the callback, and I have reentrancy.
\ Requirement #2 comes from uplevel. When it's time to invoke
\ the callback, instead of using "execute", I use "uplevel"
\ which temporarily pushes the caller's context frame only for
\ the duration of the callback, then pops back to reduce's frame
\ when it's done!
0 cell field .xt
_ cell field .node
constant #reduce
: ready ( a xt get ready to iterate) .xt ! .node ! ;
: next? ( xt -- x -1 | 0 next data value if there is one)
_ .node @ >a .next @ dup a! if
_ .data @ -1
_ else
_ 0
_ then a> .node ! ;
: once ( i*x x -- j*x one iteration of reduce) .xt @ uplevel ;
: reduce ( i*x a xt -- j*x execute xt for each node in list a)
_ #reduce a{ ready begin next? while once repeat a} ;
\ A very simple example of partial application showing that
\ reduce gets out of the way of parameter stack use.
: sum ( a -- n sum of all items in list a) 0 swap ' + reduce ;
\ And an ever-so-slightly less simple example showing that
\ reduce also gets out of the way of context frame field use. Of
\ course this could be written to only use the parameter stack,
\ but I needed something to demonstrate it.
0 cell field .count
_ cell field .sum
constant #average
: average ( a average value of items in list a)
_ #average a{
_ 0 .count ! 0 .sum !
_ \ Because reduce uses uplevel to execute the callback, I
_ \ can naturally access this word's context fields from
_ \ within the quotation to increment the count
_ [: ( x) 1 .count +! .sum +! ;] reduce
_ .count @ dup if .sum @ swap / then
_ a} ;
\ Now let's finally play with it!
head things \ create an empty list of "things"
12 things append \ and append to it 12
34 things append \ and then 34
things sum . \ prints 46
things average . \ prints 23
56 things append \ append 56
things sum . \ prints 102
things average . \ prints 34
lol
Mon Aug 05 2024 18:09:01 EDT from zelgomer. IG, stop eating my text. I want it to be displayed exactly as I enter it!
: a{ ( u) a@ ap @ a! swap cells cell+ ap +! .parent ! ;
Oops, that should have been
: a{ ( u) a@ ap @ a! swap cell aligned cell+ ap +! .parent ! ;
Assuming "aligned" aligns the value on the stack and not the dictionary pointer. I don't remember what ANS says it's supposed to do, but that's how it works in mine.
Hmm
Reason i wondered is it is default install of Armbian Desktop now ( Grrr ) and just before i removed it, thought id check its version and stuff, see if it was current. Not that i 'care' was just curious now up to date they someone out there was keeping it, as i assume its NOT Microsoft supported, to run on ARM/Linux. It was mentioning chromium and electron, implying its NOT a fat client..
Mon Sep 16 2024 11:49:09 EDT from IGnatius T FoobarThat's all it is. Microsoft has written a couple of Electron apps now. VS Code is one; the Teams client is another.
wow typos galore sorry about that.
Been spending too much time staring at freaking mandatory service now training ( hours on hours. this one was 30 hours i think ).. and my mind is going numb.
anyone use scapy python library before?
Trying to scan for wake on LAN packets, but not having much luck. Even running it on the machine that i used to broadcast the packet, zip.
End goal: Since PVE does not support WOL on VMs was going to build a scanner, let it watch for WOL packets. When it sees one check a DB and if it finds a match on the MAC, do a command line power-up of the VM. That way can keep them turned off unless i hit them via guacamole, which supports a WOL packet before it tries to connect. Not that i cant do it manually, or just leave them running forever, just thinking ahead when i go back to office, something i might try. And i could see it being useful to others in a more 'enterprise' world where they want to give out VDI like sessions to their people.. Or even kiosk like things ( which prompted that rant in bottom feeders earlier )
Ok, closer.
Switched the interface to promiscuous mode on a whim, and now filtering for simple 'ARP' i get results. So its at least scanning and just have to figure out the proper filter at this point for WoL.
End goal: Since PVE does not support WOL on VMs was going to
build a scanner, let it watch for WOL packets. When it sees one
check a DB and if it finds a match on the MAC, do a command line
power-up of the VM. That way can keep them turned off unless i
hit them via guacamole, which supports a WOL packet before it
tries to connect. Not that i cant do it manually, or just leave
them running forever, just thinking ahead when i go back to
office, something i might try. And i could see it being useful
to others in a more 'enterprise' world where they want to give
out VDI like sessions to their people.. Or even kiosk like
things ( which prompted that rant in bottom feeders earlier )
That sounds like something you could fix by having tcpdump output ARP traffic
logs into a pipe and having a script grep for known MACs in the stream. Then for every WOL check if the VM is up and start it up if not.
the scanner library will work, after i opened the network adapter up. While testing i would not have expected to have to do that, its coming from the SAME device... but whatever, it worked... Not had time to go back and look for the 'right' packet filter, but the concept is working fine now.
Wed Sep 25 2024 17:54:08 EDT from darknetuser
That sounds like something you could fix by having tcpdump output ARP traffic
logs into a pipe and having a script grep for known MACs in the stream. Then for every WOL check if the VM is up and start it up if not.
"this AI coding stuff is stupid"
Ok?
"it gave me code with a bug, and i had to ask it to fix it"
Ok. and how many seconds did it take, compared to how fast you can type, not even including the thought/planning before you can even start typing..... And i assume you have never had a bug in your code you had to spend time researching?
Or
"I had to ask it 3 times before understood what i wanted"
Ok, and that has never happened in the programming world where a PM or customer does not correctly/fully explain their needs and it takes a couple of rounds.
its yet another tool in the toolbox, use it correctly with logical expectations and things will be just fine. Or do you also use a screwdriver as a hammer and wonder why its not working right?