diff --git a/Makefile b/Makefile index 8718c7ae..d53fbea0 100644 --- a/Makefile +++ b/Makefile @@ -22,9 +22,10 @@ mal_TEST_OPTS = --start-timeout 60 --test-timeout 120 # Settings # -IMPLS = awk bash c d clojure coffee cpp crystal cs erlang elixir es6 factor forth fsharp go groovy \ - guile haskell java julia js kotlin lua make mal ocaml matlab miniMAL nim \ - perl php ps python r racket rpython ruby rust scala swift tcl vb vimscript +IMPLS = awk bash c d clojure coffee cpp crystal cs erlang elixir es6 \ + factor forth fsharp go groovy guile haskell haxe java julia \ + js kotlin lua make mal ocaml matlab miniMAL nim perl php ps \ + python r racket rpython ruby rust scala swift tcl vb vimscript step0 = step0_repl step1 = step1_read_print @@ -112,6 +113,7 @@ go_STEP_TO_PROG = go/$($(1)) groovy_STEP_TO_PROG = groovy/$($(1)).groovy java_STEP_TO_PROG = java/src/main/java/mal/$($(1)).java haskell_STEP_TO_PROG = haskell/$($(1)) +haxe_STEP_TO_PROG = haxe/$($(1)).py julia_STEP_TO_PROG = julia/$($(1)).jl js_STEP_TO_PROG = js/$($(1)).js kotlin_STEP_TO_PROG = kotlin/$($(1)).jar @@ -167,6 +169,7 @@ fsharp_RUNSTEP = mono ../$(2) --raw $(3) go_RUNSTEP = ../$(2) $(3) groovy_RUNSTEP = groovy ../$(2) $(3) haskell_RUNSTEP = ../$(2) $(3) +haxe_RUNSTEP = python3 ../$(2) $(3) java_RUNSTEP = mvn -quiet exec:java -Dexec.mainClass="mal.$($(1))" $(if $(3), -Dexec.args="$(3)",) julia_RUNSTEP = ../$(2) $(3) js_RUNSTEP = node ../$(2) $(3) diff --git a/haxe/Dockerfile b/haxe/Dockerfile new file mode 100644 index 00000000..b8906378 --- /dev/null +++ b/haxe/Dockerfile @@ -0,0 +1,33 @@ +FROM ubuntu:vivid +MAINTAINER Joel Martin + +########################################################## +# General requirements for testing or common across many +# implementations +########################################################## + +RUN apt-get -y update + +# Required for running tests +RUN apt-get -y install make python + +# Some typical implementation and test requirements +RUN apt-get -y install curl libreadline-dev libedit-dev + +RUN mkdir -p /mal +WORKDIR /mal + +########################################################## +# Specific implementation requirements +########################################################## + +# Install Haxe +RUN apt-get -y install software-properties-common && \ + add-apt-repository -y ppa:haxe/releases && \ + apt-get -y update +RUN apt-get install -y haxe && \ + mkdir /root/haxelib && haxelib setup /root/haxelib + +# Install support for C++ compilation +RUN apt-get -y install g++ +RUN haxelib install hxcpp diff --git a/haxe/Makefile b/haxe/Makefile new file mode 100644 index 00000000..1e722bf4 --- /dev/null +++ b/haxe/Makefile @@ -0,0 +1,8 @@ +# Python step rules +s%.py: S%.hx + haxe -main $(patsubst %.hx,%,$<) -python $@ + +step1_read_print.py: types/Types.hx reader/Reader.hx printer/Printer.hx + +clean: + rm -r *.py diff --git a/haxe/Step0_repl.hx b/haxe/Step0_repl.hx new file mode 100644 index 00000000..a9dc412e --- /dev/null +++ b/haxe/Step0_repl.hx @@ -0,0 +1,38 @@ +class Step0_repl { + // READ + static function READ(str:String) { + return str; + } + + // EVAL + static function EVAL(ast:String, env:String) { + return ast; + } + + // PRINT + static function PRINT(exp:String) { + return exp; + } + + // repl + static function rep(line:String) { + return PRINT(EVAL(READ(line), "")); + } + + public static function main() { + #if js + #error "JS not supported yet" + #end + while (true) { + try { + Sys.print("user> "); + var line = Sys.stdin().readLine(); + Sys.println(rep(line)); + } catch (exc:haxe.io.Eof) { + Sys.exit(0); + } catch (exc:Dynamic) { + Sys.println(exc); + } + } + } +} diff --git a/haxe/Step1_read_print.hx b/haxe/Step1_read_print.hx new file mode 100644 index 00000000..2854100c --- /dev/null +++ b/haxe/Step1_read_print.hx @@ -0,0 +1,45 @@ +import types.Types.MalType; +import reader.*; +import printer.*; + +class Step1_read_print { + // READ + static function READ(str:String):MalType { + return Reader.read_str(str); + } + + // EVAL + static function EVAL(ast:MalType, env:String) { + return ast; + } + + // PRINT + static function PRINT(exp:MalType):String { + return Printer.pr_str(exp, true); + } + + // repl + static function rep(line:String) { + return PRINT(EVAL(READ(line), "")); + } + + public static function main() { + #if js + #error "JS not supported yet" + #end + while (true) { + try { + Sys.print("user> "); + var line = Sys.stdin().readLine(); + if (line == "") { continue; } + Sys.println(rep(line)); + } catch (exc:BlankLine) { + continue; + } catch (exc:haxe.io.Eof) { + Sys.exit(0); + } catch (exc:Dynamic) { + Sys.println(exc); + } + } + } +} diff --git a/haxe/printer/Printer.hx b/haxe/printer/Printer.hx new file mode 100644 index 00000000..bf3a0686 --- /dev/null +++ b/haxe/printer/Printer.hx @@ -0,0 +1,30 @@ +package printer; + +import types.Types.MalType; +import types.Types.MalType.*; + +class Printer { + public static function pr_str(exp:MalType, print_readably:Bool = true) { + var _r = print_readably; + return switch(exp) { + case MalNil: "nil"; + case MalTrue: "true"; + case MalFalse: "false"; + case MalInt(v): Std.string(v); + case MalString(v): + if (_r) { + '"' + v + '"'; + } else { + v; + } + case MalSymbol(v): Std.string(v); + case MalList(l): + var lst = l.map(function(e) {return pr_str(e,_r);}); + "(" + lst.join(" ") + ")"; + case MalVector(l): + var lst = l.map(function(e) {return pr_str(e,_r);}); + "[" + lst.join(" ") + "]"; + case _: throw "unknown type for printing"; + } + } +} diff --git a/haxe/reader/BlankLine.hx b/haxe/reader/BlankLine.hx new file mode 100644 index 00000000..2b110402 --- /dev/null +++ b/haxe/reader/BlankLine.hx @@ -0,0 +1,6 @@ +package reader; + +class BlankLine { + public function new() { + } +} diff --git a/haxe/reader/Reader.hx b/haxe/reader/Reader.hx new file mode 100644 index 00000000..313a4afb --- /dev/null +++ b/haxe/reader/Reader.hx @@ -0,0 +1,112 @@ +package reader; + +import types.Types.MalType; +import types.Types.MalType.*; + +class Reader { + // Reader class implementation + var tokens:Array; + var position:Int = 0; + + public function new(toks:Array) { + tokens = toks; + } + + public function next() { + return tokens[position++]; + } + + public function peek() { + if (tokens.length > position) { + return tokens[position]; + } else { + return null; + } + } + + + // Static functions grouped with Reader class + static function tokenize(str:String) { + var re = ~/[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/g; + var tokens = new Array(); + var pos = 0; + while (re.matchSub(str, pos)) { + var t = re.matched(1); + if (t == "" || t.charAt(0) == ";") { break; } + tokens.push(t); + var pos_len = re.matchedPos(); + pos = pos_len.pos + pos_len.len; + + } + return tokens; + } + + static function read_atom(rdr:Reader) { + var re_int = ~/^[0-9]*$/; + var re_str = ~/^".*"$/; + var token = rdr.next(); + return switch (token) { + case "nil": + MalNil; + case "true": + MalTrue; + case "false": + MalFalse; + case _ if (re_int.match(token)): + MalInt(Std.parseInt(token)); + case _ if (re_str.match(token)): + MalString(token.substr(1, token.length-2)); + case _: + MalSymbol(token); + } + } + + static function read_seq(rdr:Reader, start, end) { + var lst = []; + var token = rdr.next(); + if (token != start) { + throw "expected '$start'"; + } + while ((token = rdr.peek()) != end) { + if (token == null) { + throw "expected '$end', got EOF"; + } + lst.push(read_form(rdr)); + } + rdr.next(); + return lst; + } + + static function read_form(rdr:Reader):MalType { + var token = rdr.peek(); + return switch (token) { + // reader macros/transforms + case "'": rdr.next(); + MalList([MalSymbol("quote"), read_form(rdr)]); + case "`": rdr.next(); + MalList([MalSymbol("quasiquote"), read_form(rdr)]); + case "~": rdr.next(); + MalList([MalSymbol("unquote"), read_form(rdr)]); + case "~@": rdr.next(); + MalList([MalSymbol("splice-unquote"), read_form(rdr)]); + case "^": rdr.next(); + var meta = read_form(rdr); + MalList([MalSymbol("with-meta"), read_form(rdr), meta]); + case "@": rdr.next(); + MalList([MalSymbol("deref"), read_form(rdr)]); + + // list + case ")": throw("unexpected ')'"); + case "(": MalList(read_seq(rdr, '(', ')')); + case "]": throw("unexpected ']'"); + case "[": MalVector(read_seq(rdr, '[', ']')); + case _: read_atom(rdr); + } + } + + public static function read_str(str:String):MalType { + var tokens = tokenize(str); + if (tokens.length == 0) { throw(new BlankLine()); } + return read_form(new Reader(tokens)); + } +} diff --git a/haxe/types/Types.hx b/haxe/types/Types.hx new file mode 100644 index 00000000..0f13600f --- /dev/null +++ b/haxe/types/Types.hx @@ -0,0 +1,19 @@ +package types; + +enum MalType { + MalNil; + MalTrue; + MalFalse; + MalInt(val:Int); + MalString(val:String); + MalSymbol(val:String); + MalList(val:Array); + MalVector(val:Array); +} + +class Types { + public static function hello() { + trace("hello world"); + } +} +