Thanks a lot for taking the time to respond. Your response was really helpful. Nonetheless, it actually made me quite a bit more confused about row-variable stack effects and runtime calls. I actually wrote a huge response with quite a few questions and, while I was writing it, the documentation digging I had to do to argument my questions actually got me to respond to them completely.
Furthermore, with this improved understanding, I found a solution that satisfied my initial requirements and which is as simple as using a single quotation. H{ { 0 [ parse-x eval-x ] } ... } As my interface is designed to have an lc3 at the top of the stack permanently and each parser-evaluator pair goes from an lc3 instruction to a modified lc3, in conjunction the pair always has ( x x -- x ) stack effect and only requires that the associated words are internally compatible ( which is always true as I'm manually making the pairs ). Now, I've made some tries and everything seems to work correctly, am I right in thinking that I should not incur in any problem as long as the invariants ( parsers go from x to n-args, evaluators go from lc3 n-args to lc3, parser-evaluator pairs always have the same number of n-args)? So thanks a lot, especially for instilling in me a constructive train of thoughts which helped me learn a lot. Best Regards, Di Sera Luca Il giorno sab 1 giu 2019 alle ore 15:35 John Benediktsson <mrj...@gmail.com> ha scritto: > You will need at some point to specify the arity of your functions. > > You can call quotations "dynamically" like this... but if the stack > checker can't infer what the quotation's stack effect is, you need to > specify in the ``call(`` what the effect is so that it will be checked at > runtime: > > IN: scratchpad : foo ( x quot -- y ) call( x -- y ) ; > > IN: scratchpad 2 [ sq ] foo . > 4 > > Seems like you would just need to specify in your opcode map what the > arity of your words are and do something like: > > CONSTANT: ops H{ > { "add" { [ + ] 2 } } > { "sq" { [ sq ] 1 } } > } > > : foo ( stack opcode -- output ) > '[ _ ops at first2 { > { 1 [ call( x -- y ) ] } > { 2 [ call( x y -- z ) ] } > } case > ] with-datastack ; > > IN: scratchpad { 2 } "sq" foo . > { 4 } > > IN: scratchpad { 2 3 } "add" foo . > { 5 } > > There are a few other ways / approaches but basically that's an idea for > you... > > Hope it helps! > > > > On Fri, May 31, 2019 at 8:47 AM Luca Di Sera <bloodtype.si...@gmail.com> > wrote: > >> I'm working on an implementation of the lc3 -vm ( >> https://justinmeiners.github.io/lc3-vm/ ). >> >> This a basic didactical architecture. >> >> In the code, I have a TUPLE: that is used to keep the state of the vm. >> >> TUPLE: lc3 { ram array } { registers array } { pc integer } { cond >>> bit-array } ; >>> >> >> An instance of this TUPLE is passed around by some of the code to execute >> instructions and change the internal state. >> >> Instructions are coded as 16bit values where the highest 4 bits represent >> an op-code that identifies the instruction to execute and the rest of the >> bits contain informations about the necessary data to execute it. >> In my code I'm using a map of pairs to dispatch to the correct ( >> quotated- ) words depending on the opcode. >> >> CONSTANT: OPCODE_MAP H{ >>> { 0 { [ parse-br ] [ eval-br ] } } >>> { 1 "add" } >>> { 2 "ld" } >>> { 3 "st" } >>> { 4 "jsr" } >>> { 5 "and" } >>> { 6 "ldr" } >>> { 7 "str" } >>> { 8 "rti" } >>> { 9 "not" } >>> { 10 "ldi" } >>> { 11 "sti" } >>> { 12 "jmp" } >>> { 14 "lea" } >>> { 15 "trap" } >>> } >>> >>> : parser-evaluator ( opcode -- parser evaluator ) >>> OPCODE_MAP at [ first ] [ second ] bi ; >>> >> >> The strings are there as temporary values that I still have to build. >> >> Not all instructions encode informations in the same way. Some of them >> may include 4 pieces of informations of different width and type and some >> others may contain more or less of them. >> To correctly retrieve informations and to execute the correct instruction >> I'm using those parser-evaluator pairs. >> >> All parsers, which cut instructions into the correct pieces of >> informations, expects the stack to have an instruction at its top to >> consume while pushing a variable number of values in its place. >> All evaluators, which actually execute the given instruction modifying >> the state of an lc3, expects n values on the stack to consume and will >> push-out a modified lc3. >> >> Now, those parser-evaluator pairs are call( [ed] at runtime. >> After many tries, I was able to construct a piece of code that would >> compile ( Stack-effects are not completely clear to me yet ). >> Unfortunately, when a parser-evaluator pair is actually called the program >> will stop as the expressed stack effect of call( is considered incorrect. >> >> : _eval ( lc3 parser: ( instruction -- ..a ) evaluator: ( ..a -- lc3 ) -- >>> lc3 ) >>> [ call( instruction -- ..a ) ] [ call( ..a -- lc3 ) ] bi* ; inline >>> >> >> (U) Quotation: [ set-namestack init-catchstack self quot>> call => stop ] >>> (O) Word: listener-thread >>> (O) Word: listener >>> (O) Word: (listener) >>> (O) Word: listener-step >>> (U) Quotation: [ >>> [ ~quotation~ dip swap ~quotation~ dip ] dip swap >>> [ call get-datastack ] dip => swap [ set-datastack ] dip >>> ] >>> (U) Quotation: [ call => get-datastack ] >>> (O) Word: eval >>> (O) Word: wrong-values >>> (O) Method: M\ object throw >>> (U) Quotation: [ >>> OBJ-CURRENT-THREAD special-object error-thread set-global >>> current-continuation => error-continuation set-global >>> [ original-error set-global ] [ rethrow ] bi >>> ] >>> >> >> After some tries, I'm at a kind of roadblock that I seem unable to >> untangle. >> How can I express, if at all possible, this kind of polymorphism in >> factor ? What are some alternative ways to produce the same effect ? >> >> For reference this is the relevant code: >> >> USING: lc3-vm.core.lc3.private lc3-vm.core.lc3 sequences bit-arrays >>> locals kernel assocs ; >>> IN: lc3-vm.core.instructions >>> >>> <PRIVATE >>> >>> CONSTANT: OPCODE_LENGHT 4 >>> >>> : instruction>opcode ( instruction -- instruction opcode ) >>> OPCODE_LENGHT cut* bit-array>integer ; >>> >>> : parse-br ( instruction -- n z p offset ) >>> 1 cut 1 cut 1 cut [ [ first ] tri@ ] dip bit-array>integer ; >>> >>> : n-and-cond-n? ( lc3 n -- ? ) >>> [ cond-n? ] dip and ; >>> >>> : z-and-cond-z? ( lc3 z -- ? ) >>> [ cond-z? ] dip and ; >>> >>> : p-and-cond-p? ( lc3 p -- ? ) >>> [ cond-p? ] dip and ; >>> >>> :: br-should-jump ( lc3 n z p -- ? ) >>> lc3 n n-and-cond-n? lc3 z z-and-cond-z? lc3 p p-and-cond-p? or or ; >>> >>> :: eval-br ( lc3 n z p offset -- lc3 ) >>> lc3 n z p br-should-jump [ lc3 offset jump-by ] [ lc3 ] if ; >>> >>> CONSTANT: OPCODE_MAP H{ >>> { 0 { [ parse-br ] [ eval-br ] } } >>> { 1 "add" } >>> { 2 "ld" } >>> { 3 "st" } >>> { 4 "jsr" } >>> { 5 "and" } >>> { 6 "ldr" } >>> { 7 "str" } >>> { 8 "rti" } >>> { 9 "not" } >>> { 10 "ldi" } >>> { 11 "sti" } >>> { 12 "jmp" } >>> { 14 "lea" } >>> { 15 "trap" } >>> } >>> >>> : parser-evaluator ( opcode -- parser evaluator ) >>> OPCODE_MAP at [ first ] [ second ] bi ; >>> >>> >>> : _eval ( lc3 parser: ( instruction -- ..a ) evaluator: ( ..a -- lc3 ) >>> -- lc3 ) >>> [ call( instruction -- ..a ) ] [ call( ..a -- lc3 ) ] bi* ; inline >>> >>> PRIVATE> >>> >>> ALIAS: instruction 16bit >>> >>> : eval ( instruction -- lc3 ) >>> instruction>opcode parser-evaluator _eval ; >>> >> >> A preemptive thanks to anyone who will take the time to help me get a >> better understanding of this issue. >> >> Best Regards, >> Di Sera Luca >> _______________________________________________ >> Factor-talk mailing list >> Factor-talk@lists.sourceforge.net >> https://lists.sourceforge.net/lists/listinfo/factor-talk >> > _______________________________________________ > Factor-talk mailing list > Factor-talk@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/factor-talk >
_______________________________________________ Factor-talk mailing list Factor-talk@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/factor-talk