Code Monkey home page Code Monkey logo

Comments (8)

soegaard avatar soegaard commented on September 20, 2024

from urlang.

wluker avatar wluker commented on September 20, 2024

I think both option1 and option2 above will have problems if you have multiple vars named renderer at different scopes. (Not that that was the original question being asked.)

For example, the following would produce the wrong code:

(import renderer.view.style.position make-new-renderer)
(define renderer (make-new-renderer))
(:= renderer.view.style.position "option-for-outer-renderer")
(let ([renderer (make-new-renderer)])
  ;; oops; this will write to the outer renderer
  (:= renderer.view.style.position "option-for-inner-renderer"))

I think it could be made a little more robust with a macro that reworks option2 a little bit:

;; (:dot (a b c d) val) becomes a.b.c.d = val
(define-urlang-macro (:=dot stx)
  (syntax-parse stx
    ;; a.b.c.d = val where a b c can be expressions, but d has to be an id
    ([_ (dot-e:expr ...+ key-last:id) e:expr]
     (with-syntax ([key-last-str (datum->syntax #'key-last (~a (syntax->datum #'key-last)))])
       #'(let ([obj (dot dot-e ...)])
           (:= obj key-last-str e))))
    ;; todo : (:= (a b c d) val) allow d to be an expression or just a name like above
    ;; ex: (let ([d "position"]) (:=dot (renderer view style d) val))
    ;; I think I would need something like (identifier-bound? #'d)
    ;; to determine how an id should be treated. I'm not sure.
    ))

example:

(:=dot (renderer view style position) "option-value")

But this leads me to the question: Is there something like (identifier-bound? stx) available for urlang? Would it make sense to have it?

It's pretty neat that urlang can be extended with macros like this.

from urlang.

soegaard avatar soegaard commented on September 20, 2024

I like your macro. And yes - having macros available for JavaScript was the
main motivation I had when I made Urlang.

You are right that option 1 and 2 have disadvantages (but they were meant to
be used as a workaround).

I think the best solution is to extend the builtin := to handle

   (:= (dot a b c d) ...)   

or even

  (:= a.b.c.d ...)

directly.

In the mean time, I like you idea of writing a macro.

There is no identifier-bound? available for macros to use. Whether an identifier is bound is determined after macro expansion.

After fixing a bug in main.rkt (a case for generating code for ref with more than two arguments were missing), the following urlang macro seems to work.

#lang racket
(require urlang syntax/parse)

(define (join-identifiers ids)
  ; note: since we use ~a we can handle both identifiers and literal strings
  (define stx    (first (syntax-e ids)))
  (define joined (string->symbol (apply ~a (add-between (syntax->datum ids) "."))))
  (datum->syntax stx joined stx))

(define (id-or-string? x)
  (and (syntax? x)
       (or (symbol? (syntax-e x))
           (string? (syntax-e x)))))

; This version of := is correct, but the version below generates
; better code in the case that e0 ... consists of identifiers.
; In (:=dot (a b c d) val) only a needs to imported
#;(define-urlang-macro (:=dot stx)
    (syntax-parse stx
      [(_ (e0 ...+ en) e)
       (syntax/loc stx
         (array! (ref e0 ...) en e))]))

;; (:=dot (a b c d) val) becomes a.b.c.d = val
;; generates better code, but one must import the dotted identifer
(define-urlang-macro (:=dot stx)
  (syntax-parse stx
    ; the simple case consists of all identifiers or literal strings
    [(_ (x ...+) e)
     #:when (andmap id-or-string? (syntax->list #'(x ...)))
     (with-syntax ([x-dotted (join-identifiers #'(x ...))])
       (syntax/loc stx
         (:= x-dotted e)))]
    ; the general case
    [(_ (e0 ...+ en) e)
     (syntax/loc stx
       (array! (ref e0 ...) en e))]))

(urlang
 (urmodule bug
   (import a.b.c.d)
   (var a b c d)
   (:=dot (a b c d)   42)
   (:=dot (a b c "d") "val")
   (:=dot (a b c 43)  "val")
   (let ([d "x"])
     (:=dot (a b c d)   42))
   (:=dot ("a" b "c" d) 45)))

from urlang.

wluker avatar wluker commented on September 20, 2024

Very nice. I can update the original macro to accept ids, strings, or numbers, and then I want to compare the macros with some examples.

(define-urlang-macro (:=dot0 stx)
  (syntax-parse stx
    ;; a.b.c.d = val where a b c can be expressions, but d has to be an id, string, or number
    ([_ (dot-e:expr ...+ key-last) e:expr]
     #:when (or (string? (syntax->datum #'key-last))
                (identifier? #'key-last)
                (number? (syntax->datum #'key-last)))
     (define datum (syntax->datum #'key-last) )
     (define val (if (number? datum) datum (~a datum)))
     (with-syntax ([key-last-str (datum->syntax #'key-last val)])
       #'(let ([obj (dot dot-e ...)])
           (:= obj key-last-str e))))
    ;; to determine how an id should be treated. I'm not sure.
    ([_ (dot-e:expr ...+ key-last) e:expr]
     ;; else error
     (raise-syntax-error #f "last input has to be an identifier, string, or number" #'key-last))))

I think each of the macros have some good and some bad, but none of them pass all the tests.

If I name the macros in order :=dot0 :=dot1 :=dot2, I'll go through some examples I found.

Example 1: a.b[x] = "val" where x = "c"

:=dot0 fails because it uses "x" as the last key (not "c")
(let ([x "c"]) (:=dot0 (a b x) "val")) => fails : ((function(x){return ((function(obj){return (obj["x"]="val");})(a.b));})("c"));

:=dot1 works if b is an expression
(let ([x "c"]) (:=dot0 (a "b" x) "val")) => works : ((function(x){return a["b"][x]="val";})("c"));

:=dot2 fails because it should be a.b[x]="val"
(import a.b.x)
(let ([x "c"]) (:=dot2 (a "b" x) "val")) => fails : ((function(x){return (a.b.x="val");})("c"));

Example 2: a.b[0] = "val"

(:=dot0 (a b 0) "val")   => works : ((function(obj){return (obj[0]="val");})(a.b));
(:=dot1 (a "b" 0) "val") => works : a["b"][0]="val";
(:=dot1 (a "b" 0) "val") => works : a["b"][0]="val";

Example 3: a.b[0].c = "val"
What should (:=dot ...) even look like?

(:=dot0 (a (ref b 0) c) "val") => won't compile
(:=dot1 (a (ref b 0) c) "val") => not quite right : a[b[0]]["c"]="val";
(:=dot2 (a (ref b 0) c) "val") => not quite right : a[b[0]]["c"]="val";

(:= (ref (ref a "b") 0) "c" "val") => not an id in the assignment position
(:=dot0 ((ref (ref a "b") 0) c) "val") => works but hard to reason about : ((function(obj){return (obj["c"]="val");})(a["b"][0]));
(:=dot1 ((ref (ref a "b") 0) "c") "val") => not enough terms for the macro but works for more terms
(:=dot2 ((ref (ref a "b") 0) "c") "val") => not enough terms for the macro but works for more terms

This has been interesting. I think you're right about the best solution being in the compiler. {but it may not be so easy;)} I blame javascript! I think your first macro (the one I call :=dot1) would be my choice as far as the macros go. I think it would cover most uses and is more likely to generate an error than create invalid code. Maybe..

Thank you for all of your work on urlang. I think it is quite a nice tool.

from urlang.

wluker avatar wluker commented on September 20, 2024

I took your example and built on it:

;;;
;; Chained assignment macro
;; Example:
;; (:=dot (a "b" (Ref 0) "c") "val") => a.b[0].c = "val"
(define-urlang-macro (:=dot stx)
  (syntax-parse stx
    [(_ [e0 en] e)
     (syntax/loc stx
       (array! e0 en e))]
    [(_ [e0:expr e1:expr ...+ ((~datum Ref) r:expr)] body:expr)
     (syntax/loc stx
       (:=dot [(ref e0 e1 ...) r] body))]
    [(_ [e0:expr e1:expr ...+ ((~datum Ref) r:expr) en:expr ...+] body:expr)
     (syntax/loc stx
       (:=dot [(ref (ref e0 e1 ...) r) en ...] body))]
    [(_ [e0:expr ((~datum Ref) r:expr) en:expr ...] body:expr)
     (syntax/loc stx
       (:=dot [(ref e0 r) en ...] body))]
    [(_ (e0 e1 ...+ en) e)
     (syntax/loc stx
       (array! (ref e0 e1 ...) en e))]))

With some tests:

  (import new-object)
  (var [a (new-object)]
       x)
  (:=dot [a "b" "c" "d" (Ref 0)] "val")
  (:=dot [a "b" "c" (Ref x) "d"] "val")
  (:=dot [a "b" (Ref 0) "c" "d"] "val")
  (:=dot [a (Ref 0) "b" "c" "d"] "val")
  (let ([a (new-object)]
        [x 1])
    (:=dot [a "b" "c" (Ref x) "d"] "val"))

that gives:

var a=(new_object()),x;
a["b"]["b"]["b"][0]="val";
a["b"]["b"][x]["d"]="val";
a["b"][0]["c"]["d"]="val";
a[0]["b"]["b"]["d"]="val";
((function(a_1,x_2){return a_1["b"]["b"][x_2]["d"]="val";})((new_object()),1));

You can see the issue in #12 showing up here, but otherwise I think it works. ???

from urlang.

soegaard avatar soegaard commented on September 20, 2024

Hi @wluker

I have committed a fix. Does it work now?

/Jens Axel

from urlang.

wluker avatar wluker commented on September 20, 2024

That did it. Thanks!

  (var [a (new-object)]
       x)
  (:=dot [a "b" "c" "d" (Ref 0)] "val")
  (:=dot [a "b" "c" (Ref x) "d"] "val")
  (:=dot [a "b" (Ref 0) "c" "d"] "val")
  (:=dot [a (Ref 0) "b" "c" "d"] "val")
  (let ([a (new-object)]
        [x 1])
    (:=dot [a "b" "c" (Ref x) "d"] "val"))

now produces:

var a=(new_object()),x;
a["b"]["c"]["d"][0]="val";
a["b"]["c"][x]["d"]="val";
a["b"][0]["c"]["d"]="val";
a[0]["b"]["c"]["d"]="val";
((function(a_1,x_2){return a_1["b"]["c"][x_2]["d"]="val";})((new_object()),1));

As an aside... I've found that using macros with urlang has been helping me get over my "fear of macros" and is making it easier for me to get a grasp on syntax and syntax/parse. It's not as magical to me if it's just functions with pattern matching and s-expressions with syntax. Thanks for your help.

from urlang.

soegaard avatar soegaard commented on September 20, 2024

Great that it works. And you are comments on macros are spot on.

from urlang.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.