Re: [Factor-talk] How to describe variable stack effects for a call( ?

2019-06-01 Thread Luca Di Sera
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 
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 
> 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 -- 

Re: [Factor-talk] How to describe variable stack effects for a call( ?

2019-06-01 Thread John Benediktsson
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 
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
>>
>> >
>> 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 ;
>>
>>