google / schism

A self-hosting Scheme to WebAssembly compiler
Apache License 2.0
1.27k stars 65 forks source link

define variables #109

Closed jitwit closed 4 years ago

jitwit commented 4 years ago

The added test passes but there are a few issues. The defines behave like let* and the commented out test variable fails in finding free variables.

I added a top tag to distinguish the top level variables. Maybe this isn't the right approach?

When the define bodies reference earlier ones, the symbol of the referenced variable is what appears in the result. I'm not sure, but I'm guessing something like adding a level of indirection to the global variables ora sorting them based on references might be necessary?

eholk commented 4 years ago

My feeling is that the top tag shouldn't be necessary, but it seems to keep things simple, so I'd go ahead and keep it.

When the define bodies reference earlier ones, the symbol of the referenced variable is what appears in the result.

For the symbol of the referenced variable, do you mean the global variable index?

Do you have an example of what could go wrong?

If it's something like

(define foo (cons (lambda () (bar 'foo)) '()))

(define bar (lambda (foo) foo))

that shouldn't be a problem, since we have defined all our variables before anyone made a call to bar.

If it's something like

(define a (cons 'a b))

(define b (cons 'b a))

then I'm pretty sure that's not allowed in Scheme.

jitwit commented 4 years ago

Sorry, should have given examples about what goes wrong.

(define f cons)
(define x (f y 12))
(define y "abc")
(define (do-test)
  (write x) (newline))

prints out (y . 12) instead of ("abc" . 12), when x's definition appears before y's.

Edit: nevermind about the commented out w, you were right, it works!

eholk commented 4 years ago

prints out (y . 12) instead of ("abc" . 12), when x's definition appears before y's.

Ah, so that looks like it's illegal. Just to make sure, I ran it in Chez Scheme and it throws an exception when you try to define x saying that y is not bound. I think we need to be sure to do something similar here.

jitwit commented 4 years ago

Good news! Could the check be done by putting some extra initialization/checking in the part of the start function for setting up the top level definitions?

eholk commented 4 years ago

I'd probably do the checking in the parsing phase, where we're adding a variable to the environment. It looks like we'll need some way to split the bodies of top-level function from the top-level values, since the environment is different for those two cases. The bodies of top-level functions can see all the top level defines, but the top-level values can only see the defines that have come earlier.

jitwit commented 4 years ago

Here's a version that's linear in export list size, but I'm not 100% sure it's preferable to the simpler current one... Maybe I'm missing a more natural way?

  (define (%parse-definitions definitions env-whole env-part)
    (if (null? definitions)
        '()
        (let ((def (car definitions)))
          (if (function-definition? def)
              (cons (define-function def env-whole)
                    (%parse-definitions (cdr definitions) env-whole env-part))
              (let ((env-part (memp (lambda (n.d)
                                      (eq? (definition-name def) (car n.d)))
                                    env-part)))
                (cons (define-value def env-part)
                      (%parse-definitions (cdr definitions) env-whole env-part)))))))
  (define (parse-definitions definitions env)
    (reverse (%parse-definitions (reverse definitions) env env))
    ;; (map (lambda (fn) (parse-definition fn env)) definitions)
    )