Blogged by Ujihisa. Standard methods of programming and thoughts including Clojure, Vim, LLVM, Haskell, Ruby and Mathematics written by a Japanese programmer. github/ujihisa

Tuesday, January 1, 2013

Day 2 -- JavaScript in a week

previous post <-> next post

summary:

Type conversions: Boolean(), Number() and String()

https://github.com/ujihisa/jinaw/commit/f28e7f55559f4919d6718539d413f0e65bd4a9e0

(defn js-boolean
  "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Boolean"
  [value]
  (not (get #{0 'null false 'NaN 'undefined} value false)))

(defn js-number
  "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number"
  [value]
  (condp instance? value
    String (if (empty? value)
             0
             (let [x (read-string value)]
               (if (number? x) x 'NaN)))
    Long value
    'NaN))

(defn js-string
  "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String"
  [value]
  (.toString value))

Looks fairly straightforward.

Type conversions in + operator

It works both for numbers and strings.

The rule which to choose is really simple. If either one of the operands is not a number, it chooses the string's one and converts both operands to strings.

> 1 + 2
3
> '1' + 2
"12"
> 1 + '2'
"12"
> '1' + '2'
"12"
> [1] + [2]
"12"
> 1 + [2]
"12"

so

(defn evaluate [expr env]
  "assumption: env won't change"
  (if (list? expr)
    (let [[car & cdr] expr]
      (case car
        fcall (let [func (evaluate (first cdr) env)
                     args (map #(evaluate % env) (second cdr))]
                 (case func
                   console.log (println (js-string (first args)))
                   + (if (every? number? args)
                       (+ (first args) (second args))
                       (str (js-string (first args)) (js-string (second args))))
                   (prn 'must-not-happen 'missing-function func)))
        quote (get env (first cdr))
        expr))
    expr))

now the runtime outputs "1hello" by console.log(1 + 'hello').

function literal without closure

https://github.com/ujihisa/jinaw/commit/a50de0f344951e2e36a60131458d8533ea75241a

A function object which doesn't have lexical scope is represented simply as a tuple of parameter names and body (a sequence of statements.) Here just for readability in the future I'll use a hash-map which has :type, :params, and :body for function objects.

(defn evaluate [expr env]
  "assumption: env won't change"
  (if (list? expr)
    (let [[car & cdr] expr]
      (case car
        function (let [params (first cdr)
                       body (second cdr)]
                   {:type :function :params params :body body})
        fcall (let [func (evaluate (first cdr) env)
                    args (map #(evaluate % env) (second cdr))]
                (case func
                  console.log (println (js-string (first args)))
                  + (if (every? number? args)
                      (+ (first args) (second args))
                  (if (= (:type func) :function)
                    (let [applied-params (into {} (map (fn [x y] [x y])
                                                       (:params func)
                                                       args))]
                      (run- (:body func) (merge env applied-params)))
                    (prn 'must-not-happen 'missing-function func))))
        quote (get env (first cdr) 'missing-local-var)
  ...snip...

It doesn't support return yet.

No comments:

Post a Comment

Followers