tbrooke
2019-8-23 07:43:05

@tbrooke has joined the channel


sturgman
2019-8-23 14:49:18

Hello all! I am trying to understand how to customize pretty-printing and I was thinking to myself… how do I display the contents of a pretty-print-style-tablehttps://docs.racket-lang.org/reference/pretty-print.html#%28def._%28%28lib._racket%2Fpretty..rkt%29._pretty-print-current-style-table%29%29


sturgman
2019-8-23 14:50:55

but I can’t… pretty-print-current-style-table only yields… #<procedure:parameter-procedure>


sturgman
2019-8-23 14:51:54

If I try calling it with (pretty-print-current-style-table), I get: #<pretty-print-style-table>


sturgman
2019-8-23 14:53:08

How can I see what is inside one of these?


sturgman
2019-8-23 14:53:39

Clearly, there is a lack of understanding in my brain regarding this…


soegaard2
2019-8-23 15:34:13

@sturgman The style-table is represented as a struct. If a struct is declared with #:transparent, then the printer can “see inside” and print the fields. If not, it can just see what kind of struct it is. In this case we can cheat and look at the code. We find: (define-struct pretty-print-style-table (hash)) So in principle, we ought to write pretty-print-style-table-hash to get the hash table. But racket/pretty doesn’t export the accessor. The reason? Well maybe at some point the pretty printer will changed to store the styles in a different way, and then it is problematic, if others have used the internal representation.

The intention is that you use pretty-print-extend-style-table to change the styles.

If you just want to play around with style-tables, you can add a (provide (struct-out pretty-print-style-table)) and a #:transparent to the struct definition (and then run raco setup).

https://github.com/racket/racket/blob/master/racket/collects/racket/pretty.rkt


sturgman
2019-8-23 15:40:09

Thank you @soegaard2! That is so interesting… I understand the desire to hide the details of the style table… but how is one supposed to use pretty-print-extend-style-table? Is there an example somewhere? What is the format of that thing?


sturgman
2019-8-23 15:42:19

Thanks for pointing out where to find the source as well!


soegaard2
2019-8-23 15:45:37
#lang racket

(require racket/pretty)


(pretty-print-current-style-table
 (pretty-print-extend-style-table
  (pretty-print-current-style-table)
  '(foo bar)
  '(lambda define)))

(pretty-print-columns 20)
(pretty-print '(bar (x 1)
                    "this is is a long string"))

soegaard2
2019-8-23 15:46:00

This makes foo prints like lambda is printed, and bar as define is printed.


sturgman
2019-8-23 15:49:50

Ahh… that clarifies the explanation in the documentation as well… so the extent of customization here is to customize how new things are pretty printed but not how the default things are pretty printed…


soegaard2
2019-8-23 15:51:08

Yes. That’s my understanding.


soegaard2
2019-8-23 15:51:44

But … there is an alternative if you want to change the way structs you define yourself is printed.


soegaard2
2019-8-23 15:52:19

You can add a #:methods gen:write and provide your own printer for that particular struct type.


sturgman
2019-8-23 15:52:59

Sounds good… thanks a lot for your help!


sydney.lambda
2019-8-23 17:16:30

I’ve been following this tutorial on compiling Scheme: https://generalproblem.net/lets_build_a_compiler/01-starting-out/ at the same time as the tutorial it is based off, “An Incremental Approach to Compiler Construction”. They differ in some ways, with the main one for the stage I’m at being that the former uses the same word-size as my system, so I’m using that representation.

      type      \| 31            bit             0
-------------------------------------------------
        integer \| iiiiiiiiiiiiiiiiiiiiiiiiiiiii00
        boolean \| 0000000000000000000000b00001111
           char \| 000000000000000cccccccc00000111
   pair pointer \| pppppppppppppppppppppppppppp001
 vector pointer \| pppppppppppppppppppppppppppp010
 string pointer \| pppppppppppppppppppppppppppp011
 symbol pointer \| pppppppppppppppppppppppppppp101
closure pointer \| pppppppppppppppppppppppppppp110

However, that representation has no way of tagging/representing the empty list! not only that, but it seems all possible tags are taken! Can anybody think of a way to work around this to provide a tag/identity/representation for the empty list?

The best solution I can think of is to reserve address 0000000000000000000000000000000 somehow and add the list-pointer (or any, I suppose, but just for symmetry) identification tag yielding 0000000000000000000000000000001. That way, the empty list is just a comparison against that literal binary value - no need to mask and dereference like the other actual pointer types as there’s nothing actually “there”; that that memory location is just reserved/unusable so as to enable the use of it as a literal identity value. Otherwise you’d risk confusing it with an “actual” pointer to a list stored in the same location.

Any ideas? Thanks :)


soegaard2
2019-8-23 17:21:36

sydney.lambda
2019-8-23 17:24:00

@soegaard2 looks to be exactly what I needed. Thanks :)


sydney.lambda
2019-8-23 17:36:24

On a slightly different note, I’ve been working on my 6502 emulator a bit today, and started to think that I must find a way to define the various opcodes without having such a ridiculous amount of repeated code. It’s easy enough for simple cases like (roughly using fancy-app notation for definitions formed by partial-application): (define (LOAD register-lens processor mode) ;do stuff, set flags etc.) (define LOAD-IMM (LOAD _ _ identity) ; operand is directly loaded into register, no modification (define LDA-IMM (LOAD-IMM Processor-A-lens _)) (define LDY-IMM (LOAD-IMM Processor-Y-lens _)) (define LDX-IMM .... but - and this may just be down to my inexperience, it seems to get to the point where you’re making simple functions seem very complex out of a need to make them “sufficiently generic” (even when they may be very specific) because another derived function is defined in terms of it, and so it must accommodate this.

Now, sorry for the rambling (I hope this actually made sense), here’s the actual question: In a situation like this, do you think it makes sense (i.e. isn’t cheating or bad form) to use macros to define all the various functions in full using templates, even if it does produce a lot of duplicate code and doesn’t necessarily make the best use of composition and modularity, because it prevents the cognitive load of having to piece all these excessively-generic functions together that successively implement eachother by being combined in more-and-more specific ways?


notjack
2019-8-23 17:39:36

@sydney.lambda I think that’s reasonable. Make sure the generated code doesn’t unnecessarily duplicate code though, otherwise the macros will explode code size. Generating functions that just call slightly more generic functions like you’re already doing is a great way to do that, so you’re on the right track.


notjack
2019-8-23 17:40:34

Really there’s only so much you can do when making an API to an assembler. ISAs usually have a bajillion instructions that are only slight variations of each other.


notjack
2019-8-23 17:42:28

RISC-V assembly has a notion of "pseudo-instructions* which are like assembly-level macros. Maybe 6502 has something similar? Not my area of expertise though.


notjack
2019-8-23 17:42:59

Also, are those lenses you’re using? Neat!


soegaard2
2019-8-23 17:44:07

I agree with notjack, that using macros is the perfect tool to automatically generate many, similar functions.

However in this case it is better to 1) read the next opcode, 2) find the adressing mode, 3) compute the effective address (based on the adressing mode), and 4) dispatch to, say, LDA which receives the effective address.


sydney.lambda
2019-8-23 17:50:50

I’m * fetching the byte at the PC to get the operator (and therefore the addressing mode also encoded in the byte) * then passing the processor-state-struct to the function-representation of the opcode * which grabs however many bytes it needs (either just the PC+1, or the PC+1 & the PC+2) * does what it needs to do (load/store/update flags) * and finally increments the PC based on how many operands were “consumed”, or, the instruction’s length. does that differ from the method you describe @soegaard2 ? I think it’s does, but I’m not sure I understand how to do it the way you describe I’m afraid. Of the things I’m less-read on, how the processor /actually/ executes instructions isn’t something I’m knowledgeable about, only “what” happens, the result it produces, and where to go from there, if that makes sense.


sydney.lambda
2019-8-23 17:53:22

@notjack thanks for the clarification, good to know I’m not completely on the wrong track haha. I’ll look into the RISC-V stuff - sounds fairly interesting regardless. And yes, lenses indeed! the Racket library is great in my experience; struct-copy started to get very unwieldy when I added the memory vector and status-flag updating routines.


samdphillips
2019-8-23 17:54:17

Alternatively you could probably encode the empty list as pppppppppppppppppppppppppppp001 All p set to zero

Since there probably won’t be a pointer to a pair at the 1 address. :stuck_out_tongue:


sydney.lambda
2019-8-23 17:59:40

(here’s just the very-much-experimental modes themselves which I intended to somehow use to parameterise the opcodes http://pasterack.org/pastes/44332) (just realised IND modes are incorrect after pasting, oops…)


soegaard2
2019-8-23 18:00:18

@sydney.lambda Maybe I am reading your snippet wrong, but I got the impression that you have a function for each mode, LDA-IMM, LDA-ZEROPAGE, LDA-ABSOLUTE etc. I was just suggesting that it is simpler to have one LDA, which receives the effective operand.

And by effective operand I mean: LDA #$20 effective operand is #$20 LDA $20 effective operand is contents of $20 LDA $20,x effective operatind is contents of $20+x etc


sydney.lambda
2019-8-23 18:04:00

Sorry, I’ve changed things around that many times I’m probably being very confusing. At the moment, the tentative plan is to have a generic LOAD instruction, which takes a register-lens (Processor-A-lens, Processor-Y-lens…) and one of the modes in the paste above to parameterize it in some way I haven’t yet fully formulated, but I think can work almost entirely by working on the operand/s (use it as an immediate value, use it as a zero-page address, use it as a zero-page address+x etc.) That is to say, I think we’re thinking along the same lines, other than having a high-level LOAD (or, LOAD-REG) function that takes a register-lens.


sydney.lambda
2019-8-23 18:06:11

Thank you both for your advice, much appreciated :)


rokitna
2019-8-23 20:12:51

looks like 0000000000000000000000000011111 isn’t being used for anything in that table, so that could be another alternative


sydney.lambda
2019-8-23 20:59:36

It seems I underestimated just how many extra possibilities were left! Thank you both, I appreciate the help :)


sydney.lambda
2019-8-23 21:11:04

@samdphillips That’s bloody perfect! I managed to get a decent-enough version by converting a C++ algorithm, but it doesn’t handle variable-length arguments or preserve order like your version. I think I understand most of the cases, but could you explain a little how the first one applies? I’m a bit mystified as to how a single operator and operand results in just dropping the operator. Is it because (other than unary negation and division which I didn’t specify) single-operand numerical-operator applications are just the identity? That is, (+ 1) is just 1, and (* 5) is just 5? Thanks :)


samdphillips
2019-8-23 21:20:42

Hah, yeah I think that is an underspecification. It’s mostly there to handle recursing on the tail for the >2 case. There is probably a better way to handle the >2 case though. Maybe a function like: ;; combine-forth :: forth-expr op lisp-expr -> forth-expr (define (combine-forth fexpr op expr) (append fexpr (forthify expr) (list op)))


samdphillips
2019-8-23 22:02:50

I’ve updated it to handle the case of a single argument: https://gist.github.com/samdphillips/a62bcda26e7577ac2eba4dec6326f958