Tests: add testing Dockerfile. Impl fixes.
authorJoel Martin <github@martintribe.org>
Thu, 12 Mar 2015 03:22:35 +0000 (22:22 -0500)
committerJoel Martin <github@martintribe.org>
Thu, 12 Mar 2015 03:22:35 +0000 (22:22 -0500)
- tests/docker/Dockerfile: specifies full docker image containing all
  tools/languages (except matlab).
- tests/docker-build.sh: build above image.
- tests/docker-run.sh: launch above image.
    Example: ./tests/docker-run.sh make test^js^step2
- Various fixes across multiple languages:
    - Unicode fixes for bash and R on Ubuntu Utopic
    - readline history fixes for when ~/.mal-history is not available
      or readable/writable. No fatal errors.
    - fixes to work with perl 5.20 (and still perl 5.18)

31 files changed:
.gitignore
bash/printer.sh
bash/reader.sh
bash/types.sh
c/readline.c
clojure/src/readline.clj
coffee/node_readline.coffee
haskell/Readline.hs
js/node_readline.js
lua/readline.lua
make/readline.mk
perl/printer.pm
perl/step2_eval.pl
perl/step3_env.pl
perl/step4_if_fn_do.pl
perl/step5_tco.pl
perl/step6_file.pl
perl/step7_quote.pl
perl/step8_macros.pl
perl/step9_try.pl
perl/stepA_mal.pl
perl/types.pm
php/readline.php
python/mal_readline.py
r/printer.r
r/readline.r
racket/readline.rkt
ruby/mal_readline.rb
tests/docker-build.sh [new file with mode: 0644]
tests/docker-run.sh [new file with mode: 0755]
tests/docker/Dockerfile [new file with mode: 0644]

index 6c346cf..cb3427d 100644 (file)
@@ -48,3 +48,8 @@ haskell/*.hi
 haskell/*.o
 lua/lib
 lua/linenoise.so
+nim/nimcache
+.lein
+.m2
+.ivy2
+.sbt
index 0d23028..ca40c77 100644 (file)
@@ -38,6 +38,8 @@ _raw_string_pr_str () {
     local print_readably="${2}"
     if [[ "${s:0:1}" = "${__keyw}" ]]; then
         r=":${s:1}"
+    elif [[ "${s:0:2}" = "${__keyw}" ]]; then
+        r=":${s:2}"
     elif [ "${print_readably}" == "yes" ]; then
         s="${s//\\/\\\\}"
         r="\"${s//\"/\\\"}\""
index a00e7a1..3fcd36c 100644 (file)
@@ -149,10 +149,10 @@ READ_STR () {
 READLINE_EOF=
 READLINE_HISTORY_FILE=${HOME}/.mal-history
 READLINE () {
-    history -r "${READLINE_HISTORY_FILE}"
+    history -r "${READLINE_HISTORY_FILE}" 2>/dev/null || true
     read -r -e -p "${1}" r || return "$?"
     history -s -- "${r}"
-    history -a "${READLINE_HISTORY_FILE}"
+    history -a "${READLINE_HISTORY_FILE}" 2>/dev/null || true
 }
 
 fi
index 4c2c824..5c12302 100644 (file)
@@ -8,7 +8,7 @@ __mal_types_included=true
 declare -A ANON
 
 __obj_magic=__5bal7
-__keyw=$(echo -en "\u029e")
+__keyw=$(echo -en "\xCA\x9E") # \u029E
 __obj_hash_code=${__obj_hash_code:-0}
 
 __new_obj_hash_code () {
index b981ee7..fdf0fdb 100644 (file)
@@ -45,8 +45,10 @@ int append_to_history() {
 #else
     HIST_ENTRY *he = history_get(history_length-1);
     FILE *fp = fopen(hf, "a");
-    fprintf(fp, "%s\n", he->line);
-    fclose(fp);
+    if (fp) {
+        fprintf(fp, "%s\n", he->line);
+        fclose(fp);
+    }
 #endif
     free(hf);
 }
index a510e13..7c12ac1 100644 (file)
@@ -1,5 +1,6 @@
 (ns readline
     (:require [clojure.string :refer [split]]
+              [clojure.java.io :refer [file]]
               [net.n01se.clojure-jna :as jna]))
 
 (defonce history-loaded (atom nil))
 (defn readline [prompt & [lib]]
   (when (not @history-loaded)
     (reset! history-loaded true)
-    (load-history HISTORY-FILE))
+    (when (.canRead (file HISTORY-FILE))
+      (load-history HISTORY-FILE)))
   (let [line (readline-call prompt)]
     (when line
       (add-history line)
-      (spit HISTORY-FILE (str line "\n") :append true))
+      (when (.canWrite (file HISTORY-FILE))
+        (spit HISTORY-FILE (str line "\n") :append true)))
     line))
index 31e5ca0..87c8d37 100644 (file)
@@ -29,7 +29,10 @@ exports.readline = rlwrap.readline = (prompt = 'user> ') ->
   line = rllib.readline prompt
   if line
     rllib.add_history line
-    fs.appendFileSync HISTORY_FILE, line + "\n"
+    try
+      fs.appendFileSync HISTORY_FILE, line + "\n"
+    catch exc
+      true
 
   line
 
index bbde009..077f26f 100644 (file)
@@ -8,9 +8,11 @@ import qualified System.Console.Readline as RL
 -- BSD license
 --import qualified System.Console.Editline.Readline as RL
 
-import System.Directory (getHomeDirectory)
+import Control.Monad (when)
+import System.Directory (getHomeDirectory, doesFileExist)
 
 import System.IO (hGetLine, hFlush, hIsEOF, stdin, stdout)
+import System.IO.Error (tryIOError)
 
 history_file = do
     home <- getHomeDirectory
@@ -18,15 +20,19 @@ history_file = do
 
 load_history = do
     hfile <- history_file
-    content <- readFile hfile
-    mapM RL.addHistory (lines content)
+    fileExists <- doesFileExist hfile
+    when fileExists $ do
+        content <- readFile hfile
+        mapM RL.addHistory (lines content)
+        return ()
+    return ()
 
 readline prompt = do
     hfile <- history_file
     maybeLine <- RL.readline prompt
     case maybeLine of 
          Just line -> do
-             appendFile hfile (line ++ "\n")
              RL.addHistory line
+             res <- tryIOError (appendFile hfile (line ++ "\n"))
              return maybeLine
          _ -> return maybeLine
index 0585e0f..2045d66 100644 (file)
@@ -34,7 +34,11 @@ exports.readline = rlwrap.readline = function(prompt) {
     var line = rllib.readline(prompt);
     if (line) {
         rllib.add_history(line);
-        fs.appendFileSync(HISTORY_FILE, line + "\n");
+        try {
+            fs.appendFileSync(HISTORY_FILE, line + "\n");
+        } catch (exc) {
+            // ignored
+        }
     }
 
     return line;
index 5acdb54..ba390a7 100644 (file)
@@ -10,9 +10,13 @@ M.raw = false
 function M.readline(prompt)
     if not history_loaded then
         history_loaded = true
-        for line in io.lines(history_file) do
-            LN.historyadd(line)
-        end
+        xpcall(function()
+            for line in io.lines(history_file) do
+                LN.historyadd(line)
+            end
+        end, function(exc)
+            return true -- ignore the error
+        end)
     end
 
     if M.raw then
@@ -23,9 +27,13 @@ function M.readline(prompt)
     end
     if line then
         LN.historyadd(line)
-        local f = io.open(history_file, "a")
-        f:write(line.."\n")
-        f:close()
+        xpcall(function()
+            local f = io.open(history_file, "a")
+            f:write(line.."\n")
+            f:close()
+        end, function(exc)
+            return true -- ignore the error
+        end)
     end
     return line
 end
index 1208f5c..69f5960 100644 (file)
@@ -10,6 +10,6 @@ __mal_readline_included := true
 # have readline history.
 READLINE_EOF :=
 READLINE_HISTORY_FILE := $${HOME}/.mal-history
-READLINE = $(eval __readline_temp := $(shell history -r $(READLINE_HISTORY_FILE); read -u 0 -r -e -p $(if $(1),$(1),"user> ") line && history -s -- "$${line}" && history -a $(READLINE_HISTORY_FILE) && echo "$${line}" || echo "__||EOF||__"))$(if $(filter __||EOF||__,$(__readline_temp)),$(eval READLINE_EOF := yes),$(__readline_temp))
+READLINE = $(eval __readline_temp := $(shell history -r $(READLINE_HISTORY_FILE); read -u 0 -r -e -p $(if $(1),$(1),"user> ") line && history -s -- "$${line}" && echo "$${line}" || echo "__||EOF||__"; history -a $(READLINE_HISTORY_FILE) 2>/dev/null || true))$(if $(filter __||EOF||__,$(__readline_temp)),$(eval READLINE_EOF := yes),$(__readline_temp))
 
 endif
index 7b00b1e..441dbe3 100644 (file)
@@ -22,7 +22,8 @@ sub _pr_str {
         }
         when(/^HashMap/) {
             my @elems = ();
-            foreach my $key (keys $obj->{val}) {
+
+            foreach my $key (keys %{ $obj->{val} }) {
                 push(@elems, _pr_str(String->new($key), $_r));
                 push(@elems, _pr_str($obj->{val}->{$key}, $_r));
             }
index 858a385..b280a9f 100644 (file)
@@ -38,7 +38,7 @@ sub eval_ast {
         }
         when (/^HashMap/) {
             my $new_hm = {};
-            foreach my $k (keys($ast->{val})) {
+            foreach my $k (keys( %{ $ast->{val} })) {
                 $new_hm->{$k} = EVAL($ast->get($k), $env);
             }
             return HashMap->new($new_hm);
index 1c34ab6..79795bd 100644 (file)
@@ -35,7 +35,7 @@ sub eval_ast {
         }
         when (/^HashMap/) {
             my $new_hm = {};
-            foreach my $k (keys($ast->{val})) {
+            foreach my $k (keys( %{ $ast->{val} })) {
                 $new_hm->{$k} = EVAL($ast->get($k), $env);
             }
             return HashMap->new($new_hm);
index 64ad314..01eb6b2 100644 (file)
@@ -36,7 +36,7 @@ sub eval_ast {
         }
         when (/^HashMap/) {
             my $new_hm = {};
-            foreach my $k (keys($ast->{val})) {
+            foreach my $k (keys( %{ $ast->{val} })) {
                 $new_hm->{$k} = EVAL($ast->get($k), $env);
             }
             return HashMap->new($new_hm);
index 53255c2..4ef42ed 100644 (file)
@@ -36,7 +36,7 @@ sub eval_ast {
         }
         when (/^HashMap/) {
             my $new_hm = {};
-            foreach my $k (keys($ast->{val})) {
+            foreach my $k (keys( %{ $ast->{val} })) {
                 $new_hm->{$k} = EVAL($ast->get($k), $env);
             }
             return HashMap->new($new_hm);
index 48b835f..70ec3dd 100644 (file)
@@ -36,7 +36,7 @@ sub eval_ast {
         }
         when (/^HashMap/) {
             my $new_hm = {};
-            foreach my $k (keys($ast->{val})) {
+            foreach my $k (keys( %{ $ast->{val} })) {
                 $new_hm->{$k} = EVAL($ast->get($k), $env);
             }
             return HashMap->new($new_hm);
index 89133a2..58652ec 100644 (file)
@@ -59,7 +59,7 @@ sub eval_ast {
         }
         when (/^HashMap/) {
             my $new_hm = {};
-            foreach my $k (keys($ast->{val})) {
+            foreach my $k (keys( %{ $ast->{val} })) {
                 $new_hm->{$k} = EVAL($ast->get($k), $env);
             }
             return HashMap->new($new_hm);
index 4e4a48d..c6d2dff 100644 (file)
@@ -82,7 +82,7 @@ sub eval_ast {
         }
         when (/^HashMap/) {
             my $new_hm = {};
-            foreach my $k (keys($ast->{val})) {
+            foreach my $k (keys( %{ $ast->{val} })) {
                 $new_hm->{$k} = EVAL($ast->get($k), $env);
             }
             return HashMap->new($new_hm);
index ec823bc..a6dce80 100644 (file)
@@ -83,7 +83,7 @@ sub eval_ast {
         }
         when (/^HashMap/) {
             my $new_hm = {};
-            foreach my $k (keys($ast->{val})) {
+            foreach my $k (keys( %{ $ast->{val} })) {
                 $new_hm->{$k} = EVAL($ast->get($k), $env);
             }
             return HashMap->new($new_hm);
index 5305fbc..e8a8f2e 100644 (file)
@@ -83,7 +83,7 @@ sub eval_ast {
         }
         when (/^HashMap/) {
             my $new_hm = {};
-            foreach my $k (keys($ast->{val})) {
+            foreach my $k (keys( %{ $ast->{val} })) {
                 $new_hm->{$k} = EVAL($ast->get($k), $env);
             }
             return HashMap->new($new_hm);
index e2d919c..d7f859b 100644 (file)
@@ -102,13 +102,13 @@ sub _false_Q { return $_[0] eq $false }
 
 {
     package Integer;
-    sub new  { my $class = shift; bless \$_[0] => $class }
+    sub new  { my $class = shift; bless \do { my $x=$_[0] }, $class }
 }
 
 
 {
     package Symbol;
-    sub new  { my $class = shift; bless \$_[0] => $class }
+    sub new  { my $class = shift; bless \do { my $x=$_[0] }, $class }
 }
 sub _symbol_Q { (ref $_[0]) =~ /^Symbol/ }
 
index 5e33501..a312109 100644 (file)
@@ -9,12 +9,14 @@ function mal_readline($prompt) {
     // Load the history file
     if (! $history_loaded) {
         $history_loaded = true;
-        if ($file = fopen($HISTORY_FILE, "r")) {
-            while (!feof($file)) {
-                $line = fgets($file);
-                if ($line) { readline_add_history($line); }
+        if (is_readable($HISTORY_FILE)) {
+            if ($file = fopen($HISTORY_FILE, "r")) {
+                while (!feof($file)) {
+                    $line = fgets($file);
+                    if ($line) { readline_add_history($line); }
+                }
+                fclose($file);
             }
-            fclose($file);
         }
     }
 
@@ -23,9 +25,11 @@ function mal_readline($prompt) {
     readline_add_history($line);
 
     // Append to the history file
-    if ($file = fopen($HISTORY_FILE, "a")) {
-        fputs($file, $line . "\n");
-        fclose($file);
+    if (is_writable($HISTORY_FILE)) {
+        if ($file = fopen($HISTORY_FILE, "a")) {
+            fputs($file, $line . "\n");
+            fclose($file);
+        }
     }
 
     return $line;
index fc22b58..181a0ae 100644 (file)
@@ -25,6 +25,8 @@ def readline(prompt="user> "):
         pyreadline.add_history(line)
         with open(histfile, "a") as hf:
             hf.write(line + "\n")
-        return line
+    except IOError:
+        pass
     except EOFError:
         return None
+    return line
index 71deef7..ed8cbd4 100644 (file)
@@ -29,6 +29,9 @@ if(!exists("..types..")) source("types.r")
         "character"={
             if (substring(exp,1,1) == "\u029e") {
                 concat(":", substring(exp,2))
+            } else if (substring(exp,1,8) == "<U+029E>") {
+                # terrible hack, appears in 3.1.1 on Utopic
+                concat(":", substring(exp,9))
             } else if (print_readably) {
                 paste("\"",
                       gsub("\\n", "\\\\n",
index 79ababc..795984c 100644 (file)
@@ -25,16 +25,20 @@ readline <- function(prompt) {
     if (!.state$rl_history_loaded) {
         .state$rl_history_loaded <- TRUE
 
-        lines <- scan(HISTORY_FILE, what="", sep="\n", quiet=TRUE)
-        for(add_line in lines) {
-            .dyncall(.call_add_history, "Z)v", add_line)
+        if (file.access(HISTORY_FILE, 4) == 0) {
+            lines <- scan(HISTORY_FILE, what="", sep="\n", quiet=TRUE)
+            for(add_line in lines) {
+                .dyncall(.call_add_history, "Z)v", add_line)
+            }
         }
     }
 
     line <- .readline(prompt)
     if (is.null(line)) return(NULL)
     .dyncall(.call_add_history, "Z)v", line)
-    write(line, file=HISTORY_FILE, append=TRUE)
+    if (file.access(HISTORY_FILE, 2) == 0) {
+        write(line, file=HISTORY_FILE, append=TRUE)
+    }
 
     line
 }
index 7f92169..fe2a72b 100644 (file)
 (define HISTORY-FILE (format "~a/.mal-history" (find-system-path 'home-dir)))
 
 (define (load-history path)
-  (map 
-    (lambda (line) (readline:add-history line))
-    (string-split
-      (port->string (open-input-file path))
-      #px"\n")))
+  (with-handlers
+    ([exn:fail? (lambda (e) #t)])
+    (map
+      (lambda (line) (readline:add-history line))
+      (string-split
+        (port->string (open-input-file path))
+        #px"\n"))))
 
 (define (readline prompt)
   (when (not history-loaded)
       nil
       (begin
         (readline:add-history line)
-        (with-output-to-file
-          HISTORY-FILE
-          (lambda () (printf "~a~n" line))
-          #:exists 'append)
+        (with-handlers
+          ([exn:fail? (lambda (e) #t)])
+          (with-output-to-file
+            HISTORY-FILE
+            (lambda () (printf "~a~n" line))
+            #:exists 'append))
         line))))
index 3799783..89b6777 100644 (file)
@@ -6,11 +6,15 @@ $histfile = "#{ENV['HOME']}/.mal-history"
 def _readline(prompt)
     if !$history_loaded && File.exist?($histfile)
         $history_loaded = true
-        File.readlines($histfile).each {|l| Readline::HISTORY.push(l.chomp)}
+        if File.readable?($histfile)
+            File.readlines($histfile).each {|l| Readline::HISTORY.push(l.chomp)}
+        end
     end
 
     if line = Readline.readline(prompt, true)
-        File.open($histfile, 'a+') {|f| f.write(line+"\n")}
+        if File.writable?($histfile)
+            File.open($histfile, 'a+') {|f| f.write(line+"\n")}
+        end
         return line
     else
         return nil
diff --git a/tests/docker-build.sh b/tests/docker-build.sh
new file mode 100644 (file)
index 0000000..0bd5555
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+IMAGE_NAME=${IMAGE_NAME:-mal-test-ubuntu-utopic}
+GIT_TOP=$(git rev-parse --show-toplevel)
+
+docker build -t "${IMAGE_NAME}" "${GIT_TOP}/tests/"
diff --git a/tests/docker-run.sh b/tests/docker-run.sh
new file mode 100755 (executable)
index 0000000..1666d7d
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+IMAGE_NAME=${IMAGE_NAME:-mal-test-ubuntu-utopic}
+GIT_TOP=$(git rev-parse --show-toplevel)
+docker run -it --rm -u ${EUID} \
+    --volume=${GIT_TOP}:/mal \
+    ${IMAGE_NAME} \
+    "${@}"
diff --git a/tests/docker/Dockerfile b/tests/docker/Dockerfile
new file mode 100644 (file)
index 0000000..4a19e73
--- /dev/null
@@ -0,0 +1,133 @@
+FROM ubuntu:utopic
+MAINTAINER Joel Martin <github@martintribe.org>
+
+ENV DEBIAN_FRONTEND noninteractive
+
+RUN echo "deb http://dl.bintray.com/sbt/debian /" > /etc/apt/sources.list.d/sbt.list
+RUN apt-get -y update
+
+#
+# General dependencies
+#
+VOLUME /mal
+
+RUN apt-get -y install make wget curl git
+
+# Deps for compiled languages (C, Go, Rust, Nim, etc)
+RUN apt-get -y install gcc pkg-config 
+
+# Deps for Java-based languages (Clojure, Scala, Java)
+RUN apt-get -y install openjdk-7-jre-headless
+ENV MAVEN_OPTS -Duser.home=/mal
+
+# Deps for Mono-based languages (C#, VB.Net)
+RUN apt-get -y install mono-runtime mono-mcs mono-vbnc
+
+# Deps for node.js languages (JavaScript, CoffeeScript, miniMAL, etc)
+RUN apt-get -y install nodejs npm
+RUN ln -sf nodejs /usr/bin/node
+
+
+#
+# Implementation specific installs
+#
+
+# Bash
+RUN apt-get -y install bash
+
+# C
+RUN apt-get -y install libglib2.0 libglib2.0-dev
+RUN apt-get -y install libffi-dev libreadline-dev libedit2 libedit-dev
+
+# Clojure
+ADD https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein \
+    /usr/local/bin/lein
+RUN sudo chmod 0755 /usr/local/bin/lein
+ENV LEIN_HOME /mal/.lein
+
+# CoffeeScript
+RUN npm install -g coffee-script
+RUN touch /.coffee_history && chmod go+w /.coffee_history
+
+# C#
+RUN apt-get -y install mono-mcs
+
+# Forth
+RUN apt-get -y install gforth
+
+# Go
+RUN apt-get -y install golang
+
+# Haskell
+RUN apt-get -y install ghc haskell-platform libghc-readline-dev libghc-editline-dev
+
+# Java
+RUN apt-get -y install maven2
+
+# JavaScript
+# Already satisfied above
+
+# Lua
+RUN apt-get -y install lua5.1 lua-rex-pcre luarocks
+RUN luarocks install linenoise
+
+# Mal
+# N/A: self-hosted on other language implementations
+
+# GNU Make
+# Already satisfied as a based dependency for testing
+
+# miniMAL
+RUN npm install -g minimal-lisp
+
+# Nim
+RUN git clone -b devel git://github.com/Araq/Nim.git /tmp/Nim
+RUN cd /tmp/Nim && git clone -b devel --depth 1 git://github.com/nim-lang/csources
+RUN cd /tmp/Nim/csources && sh build.sh
+RUN cd /tmp/Nim && bin/nim c koch
+RUN cd /tmp/Nim && ./koch boot -d:release
+RUN cd /tmp/Nim && ./koch install /usr/local/bin
+RUN rm -r /tmp/Nim
+
+# OCaml
+RUN apt-get -y install ocaml-batteries-included
+
+# perl
+RUN apt-get -y install perl
+
+# PHP
+RUN apt-get -y install php5-cli
+
+# PostScript/ghostscript
+RUN apt-get -y install ghostscript
+
+# python
+RUN apt-get -y install python
+
+# R
+RUN apt-get -y install r-base-core
+
+# Racket
+RUN apt-get -y install racket
+
+# Ruby
+RUN apt-get -y install ruby
+
+# Rust
+RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh
+
+# Scala
+RUN apt-get -y --force-yes install sbt
+RUN apt-get -y install scala
+ENV SBT_OPTS -Duser.home=/mal
+
+# VB.Net
+RUN apt-get -y install mono-vbnc
+
+# TODO: move this up with Clojure
+ENV LEIN_JVM_OPTS -Duser.home=/mal
+
+ENV DEBIAN_FRONTEND newt
+ENV HOME /
+
+WORKDIR /mal