anything
2021-4-2 14:59:46

I’ve worked through the JSONIC language from Beautiful Racket. I then gave myself the task of replacing the delimiters @$ $@ with just parentheses. I’ve managed to build parse-trees properly. For example, (parse-to-datum (apply-tokenizer-maker make-tokenizer "(/ 3 5.0)")) produces (jsonic-program (jsonic-sexp (/ 3 5.0))), which produces expander.rkt> (jsonic-program (jsonic-sexp (/ 3 5.0))) "0.6" as expected. However, when I run this program from a file with #lang jsonic4 #lang jsonic4 (/ 3 5.0) I get ; c:\sys\tolcom\current\little-languages\jsonic4\test7.rkt::-1: /: unbound identifier ; in: / That makes me think the /-binding isn’t available in my language. With Beautiful Racket’s version, I didn’t face this problem and I think it’s because the parser produced string-s-exp as in "(+ 1 1)" which was then converted to an s-exp at compile-time [using with-pattern]. I don’t need to do this because my tokenizer is producing the s-exp [using read]. [That’s essentially the change I made.] I can’t see how this changes anything relative to racket/base bindings. In other words, I’m lost. Any help is appreciated!


soegaard2
2021-4-2 15:21:30

It’s worth checking each layer separately to see where your expectations aren’t met. Step one must be to check the tokeneizer. Can you make it print each token as it is produced?


anything
2021-4-2 15:28:20

The tokenizer does what I expect. For instance, #lang br (require jsonic4/parser jsonic4/tokenizer brag/support) (parse-to-datum (apply-tokenizer-maker make-tokenizer "(/ 3 5.0)")) produces '(jsonic-program (jsonic-sexp (/ 3 5.0))) I think the expander also does what I expect. If I enter the expander now and execute this on REPL it works because the REPL itself computes (/ 3 5.0) and so jsonic-exp sees 0.6, it converts it to a string and jsonic-program concatenate all strings and trims the resulting one. All of this is seen in the REPL. But when I run #lang jsonic4 (/ 3 5.0) in a file by itself [called test7.rkt], I get ; c:\sys\tolcom\current\little-languages\jsonic4\test7.rkt::-1: /: unbound identifier ; in: / So it’s this last mile that fails. (If I replace (/ 3 5.0) by, say, (list 1 2 3), I get list: unbound identifier.)


anything
2021-4-2 15:31:39

Maybe this is too subtle for Slack-message and I’m too clueless to present it properly. I will produce a working-example for whoever has the time to look at this. Don’t worry about this, anyone.


anything
2021-4-2 15:41:42

If you have the time only. Thank you! %wget -q -Ojsonic4.zip <https://bit.ly/2PxAuG5> %unzip jsonic4.zip Archive: jsonic4.zip inflating: jsonic4/main.rkt inflating: jsonic4/expander.rkt inflating: jsonic4/parser-test1.rkt inflating: jsonic4/parser.rkt inflating: jsonic4/tokenizer.rkt extracting: jsonic4/test7.rkt inflating: jsonic4/reader.rkt %cd jsonic4 %racket parser-test1.rkt '(jsonic-program (jsonic-sexp (/ 3 5.0))) %racket test7.rkt test7.rkt::-1: /: unbound identifier in: / location...: test7.rkt::-1 %


anything
2021-4-2 15:43:13

I actually think you’d need to raco pkg install before. I am probably running jsonic4 from other directory, but the files are the same so the behavior must be the same here too.


soegaard2
2021-4-2 16:16:09

@anything I think, “reader.rkt” is missing?


anything
2021-4-2 16:18:44

Omg, so sorry about that!


anything
2021-4-2 16:19:29

#lang br/quicklang (require "tokenizer.rkt" "parser.rkt") (define (read-syntax path port) (define parse-tree (parse path (make-tokenizer port))) (define module-datum `(module jsonic-module jsonic4/expander ,parse-tree)) (datum-&gt;syntax #f module-datum)) (provide read-syntax)


anything
2021-4-2 16:24:32

Updated the URL above as well. File reader.rkt included.


soegaard2
2021-4-2 16:52:03

I am confused by this: (define-macro (jsonic-sexp SEXP) (syntax (jsexpr-&gt;string SEXP))) When I try (jsexpr-&gt;string '(/ 4 3)) I get an error in the repl.


anything
2021-4-2 16:56:53

@soegaard2 This might very well be where the problem is because this is a core of the new code. The original from Beautiful Racket is (define-macro (jsonic-sexp SEXP-STR) (with-pattern ([SEXP-DATUM (format-datum '~a #'SEXP-STR)]) #'(jsexpr-&gt;string SEXP-DATUM))) Beautiful Racket gets a string "(/ 3 5.0)" while this new version is getting a real s-exp, not a string. What I’m trying to do with jsonic-sexp is to let Racket itself compute the s-exp (which should produce a valid jsexpr such as a number) and then convert it to a string.


anything
2021-4-2 16:58:47

The reason you get an error is because you’re passing in a list to jsexpr->string. If you remove the quote, Racket computes the division and jsexpr->string gets just a number, which is what’s suppose to happen using the language jsonic4. (But Racket doesn’t compute the division saying / is not bound, which actually confirms me what’s happening — I just didn’t expect / not to be available.)


soegaard2
2021-4-2 16:59:38

Okay, you want to allow a general expression (and not just a datum).


anything
2021-4-2 16:59:52

Precisely.


soegaard2
2021-4-2 17:03:19

The SEXP is a syntax object. It comes from directly from the reader. That means no binding information has been attached. The expander must attach relevant binding information. Since it is a general Racket expression, consider using expand-syntax and then insert the result.


anything
2021-4-2 17:04:02

Hm, that does match with the lectures I have been getting… Let me investigate it further now with this in mind. Thanks a bunch!


soegaard2
2021-4-2 17:04:39

Datums are easier - they don’t need any binding information.


anything
2021-4-2 17:10:44

You didn’t mean this, did you? (define-macro (jsonic-sexp SEXP) (syntax (jsexpr-&gt;string (expand-syntax SEXP)))) (provide jsonic-sexp) I get expander.rkt&gt; (jsonic-sexp '(/ 3 5.0)) ; expand-syntax: contract violation ; expected: syntax? ; given: '(/ 3 5.0)


soegaard2
2021-4-2 17:14:22

I was thinking of something like (define-syntax (jsonic-sexp stx) (syntax-parse stx [(_ SEXP) (with-syntax ([SEXP (expand #'SEXP)]) #'(jsexpr-&gt;string SEXP))])) It doesn’t work though.


anything
2021-4-2 17:23:42

If you can’t do it, I should not even be trying. :slightly_smiling_face: (Clearly I can’t tell what’s easy and what’s hard.)


soegaard2
2021-4-2 17:25:21

Well, I haven’t read BR yet, so I am missing some context. (define-macro (jsonic-sexp SEXP) (with-syntax ([SEXP (datum-&gt;syntax #'here (syntax-&gt;datum #'SEXP))]) #'(jsexpr-&gt;string SEXP))) This is close.


soegaard2
2021-4-2 17:25:45

Now I get the correct result - but twice?!?


soegaard2
2021-4-2 17:26:44

And I am still confusing by define-macro - why not use standard define-syntax.


soegaard2
2021-4-2 17:28:44

Ah. It works, I forgot I haded added something to jsonic-mb .


anything
2021-4-2 17:32:48

You rock. Also getting correct results here. This builds pressure for me to really understand how to use with-syntax, with-pattern. I look at your (syntax here) and I’m like… What? Lol. What’s that “here” doing there?


soegaard2
2021-4-2 17:33:31

The problem is that SEXP has no attached bindings.


soegaard2
2021-4-2 17:33:58

(datum-&gt;syntax stx datum) attaches the bindings from stx to datum.


soegaard2
2021-4-2 17:34:27

So #’here is a way to get the bindings available where jsonic-sexp is defined.


anything
2021-4-2 17:36:59

That’s making some sense. Here’s what I’m thinking. When jsonic-sexp runs, we have racket/base available, but jsonic-sexp has a s-exp coming from the user, which does not have any bindings at all in it. To put racket/base bindings in the user code, we need to enrich its syntax object, which is what you’re doing.


soegaard2
2021-4-2 17:37:43

Sounds right.


anything
2021-4-2 17:43:55

Are you using (syntax here) merely to tap into the compile-time phase used by jsonic-sexp and get its bindings? The procedure datum-&gt;syntax gives “the lexical context information of (syntax here)_”._ What’s the context information of (syntax here)? It must be the same context of jsonic-exp, which includes all of racket/base’s binding — I say.


soegaard2
2021-4-2 17:51:22

Yes.


anything
2021-4-2 18:19:11

Thanks for taking the time to look into this. This is priceless. It takes knowledge and time and other things that are also even more priceless. Thank you!


ben.knoble
2021-4-2 21:14:05

But remember when working with a language, you don’t get input evaluated (i.e., (jsonic-sexp (/ 3 5.0)) at the REPL is not what your language gets—the REPL evaluates the argument, which is not what happens when you parse the input and put this in a syntax object). I struggled with stuff like this too; you need to provide bindings for the language; IIRC this needs to happen in the expander, so that when the actual module (something like #'(#%module-begin (jsonic-program (jsonic-sexp (/ 3 5.0)))))) gets expanded the binding for / is available.


anything
2021-4-2 23:21:12

You’re quite right. I was taught that by Soegaard’s tutorial itself, but I had forgotten by then. Beautiful Racket taught me the same, but I had also forgotten it by then. It’s been weeks I’ve read both. I have been slowly working out exercises for myself and by the time I tried this one I forgot the lesson already. That’s the beginner-thing: at first we aren’t yet used to all the little parts that compose the world, so we focus on one of them and some other we already forgot part trips us up. Thanks for your insight here!


ben.knoble
2021-4-3 00:25:18

Preaching to the choir there! I’m still a racket beginner, i just remember the bindings stuff tripping me up. Otoh i was sure this was a source of confusion (what gets evaluated when), so wanted to chime in on that