aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-11-15 23:15:09 -0600
committerJoel Martin <github@martintribe.org>2015-01-09 16:16:47 -0600
commitee7cd5859e56423983f025088c8cef36b7ed09dd (patch)
treeda9f2011f4feaa34c96a63407fa4e8720eb1d4dc
parentc3b508af92800f63bf99f41af68f026535f454f5 (diff)
downloadmal-ee7cd5859e56423983f025088c8cef36b7ed09dd.tar.gz
mal-ee7cd5859e56423983f025088c8cef36b7ed09dd.zip
VB.Net: port of C# version.
-rw-r--r--.gitignore2
-rw-r--r--Makefile5
-rw-r--r--README.md19
-rw-r--r--mal/core.mal1
-rw-r--r--vb/Makefile52
-rw-r--r--vb/core.vb427
-rw-r--r--vb/env.vb55
-rw-r--r--vb/getline.cs1089
-rw-r--r--vb/printer.vb50
-rw-r--r--vb/reader.vb181
-rw-r--r--vb/readline.vb32
-rw-r--r--vb/step0_repl.vb43
-rw-r--r--vb/step1_read_print.vb59
-rw-r--r--vb/step2_eval.vb134
-rw-r--r--vb/step3_env.vb156
-rw-r--r--vb/step4_if_fn_do.vb189
-rw-r--r--vb/step5_tco.vb198
-rw-r--r--vb/step6_file.vb216
-rw-r--r--vb/step7_quote.vb249
-rw-r--r--vb/step8_macros.vb289
-rw-r--r--vb/step9_try.vb318
-rw-r--r--vb/stepA_interop.vb318
-rw-r--r--vb/types.vb458
23 files changed, 4537 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index f298b8c..4b638de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,5 @@ rust/mal
rust/Cargo.lock
rust/.cargo
r/lib
+vb/*.exe
+vb/*.dll
diff --git a/Makefile b/Makefile
index 3367582..c709456 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ PYTHON = python
#
IMPLS = bash c clojure coffee cs go java js make mal perl php ps \
- python r ruby rust
+ python r ruby rust vb
step0 = step0_repl
step1 = step1_read_print
@@ -65,6 +65,7 @@ python_STEP_TO_PROG = python/$($(1)).py
r_STEP_TO_PROG = r/$($(1)).r
ruby_STEP_TO_PROG = ruby/$($(1)).rb
rust_STEP_TO_PROG = rust/target/$($(1))
+vb_STEP_TO_PROG = vb/$($(1)).exe
bash_RUNSTEP = bash ../$(2) $(3)
@@ -84,10 +85,12 @@ python_RUNSTEP = $(PYTHON) ../$(2) $(3)
r_RUNSTEP = Rscript ../$(2) $(3)
ruby_RUNSTEP = ruby ../$(2) $(3)
rust_RUNSTEP = ../$(2) $(3)
+vb_RUNSTEP = mono ../$(2) --raw $(3)
# Extra options to pass to runtest.py
cs_TEST_OPTS = --redirect
mal_TEST_OPTS = --start-timeout 60 --test-timeout 120
+vb_TEST_OPTS = --redirect
# Derived lists
diff --git a/README.md b/README.md
index d7d5b8d..e3a63ae 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
## Description
Mal is an interpreter for a subset of the Clojure programming
-language. Mal is implemented from scratch in 17 different languages:
+language. Mal is implemented from scratch in 18 different languages:
* Bash shell
* C
@@ -22,6 +22,7 @@ language. Mal is implemented from scratch in 17 different languages:
* R
* Ruby
* Rust
+* Visual Basic.NET
Mal is also a learning tool. Each implementation of mal is separated
@@ -78,7 +79,7 @@ required to build and run the C# implementation.
```
cd cs
make
-mono ./stepX_YYY
+mono ./stepX_YYY.exe
```
@@ -213,6 +214,20 @@ cargo build
./target/stepX_YYY
```
+### Visual Basic.NET ###
+
+The VB.NET implementation of mal has been tested on Linux using the Mono
+VB compiler (vbnc) and the Mono runtime (version 2.10.8.1). Both are
+required to build and run the VB.NET implementation.
+
+```
+cd vb
+make
+mono ./stepX_YYY.exe
+```
+
+
+
## Running tests
The are nearly 400 generic Mal tests (for all implementations) in the
diff --git a/mal/core.mal b/mal/core.mal
index 33b6a5e..a12c7e4 100644
--- a/mal/core.mal
+++ b/mal/core.mal
@@ -4,6 +4,7 @@
["nil?" nil?]
["true?" true?]
["false?" false?]
+ ["symbol" symbol]
["symbol?" symbol?]
["pr-str" pr-str]
diff --git a/vb/Makefile b/vb/Makefile
new file mode 100644
index 0000000..d9ce097
--- /dev/null
+++ b/vb/Makefile
@@ -0,0 +1,52 @@
+#####################
+
+DEBUG =
+
+TESTS =
+
+SOURCES_BASE = readline.vb types.vb reader.vb printer.vb
+SOURCES_LISP = env.vb core.vb stepA_interop.vb
+SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
+
+#####################
+
+SRCS = step0_repl.vb step1_read_print.vb step2_eval.vb \
+ step3_env.vb step4_if_fn_do.vb step5_tco.vb step6_file.vb \
+ step7_quote.vb step8_macros.vb step9_try.vb stepA_interop.vb
+
+LIB_CS_SRCS = getline.cs
+LIB_VB_SRCS = $(filter-out step%,$(filter %.vb,$(SOURCES)))
+
+FLAGS = $(if $(strip $(DEBUG)),-debug:full,)
+
+#####################
+
+all: mal.exe $(patsubst %.vb,%.exe,$(SRCS))
+
+mal.exe: $(patsubst %.vb,%.exe,$(word $(words $(SOURCES)),$(SOURCES)))
+ cp $< $@
+
+mal_cs.dll: $(LIB_CS_SRCS)
+ mcs $(FLAGS) -target:library $+ -out:$@
+
+mal_vb.dll: mal_cs.dll $(LIB_VB_SRCS)
+ vbnc $(FLAGS) -target:library -r:mal_cs.dll $(LIB_VB_SRCS) -out:$@
+
+%.exe: %.vb mal_vb.dll
+ vbnc $(FLAGS) -r:mal_vb.dll -r:mal_cs.dll $<
+
+clean:
+ rm -f *.dll *.exe *.mdb
+
+.PHONY: stats tests $(TESTS)
+
+stats: $(SOURCES)
+ @wc $^
+stats-lisp: $(SOURCES_LISP)
+ @wc $^
+
+tests: $(TESTS)
+
+$(TESTS):
+ @echo "Running $@"; \
+ ./$@ || exit 1; \
diff --git a/vb/core.vb b/vb/core.vb
new file mode 100644
index 0000000..43fc30f
--- /dev/null
+++ b/vb/core.vb
@@ -0,0 +1,427 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports MalVal = Mal.types.MalVal
+Imports MalConstant = Mal.types.MalConstant
+Imports MalInt = Mal.types.MalInt
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalString = Mal.types.MalString
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalAtom = Mal.types.MalAtom
+Imports MalFunc = Mal.types.MalFunc
+
+Namespace Mal
+ Public Class core
+ Shared Nil As MalConstant = Mal.types.Nil
+ Shared MalTrue As MalConstant = Mal.types.MalTrue
+ Shared MalFalse As MalConstant = Mal.types.MalFalse
+
+ ' Errors/Exceptions
+ Shared Function mal_throw(a As MalList) As MalVal
+ throw New Mal.types.MalException(a(0))
+ End Function
+
+ ' General functions
+ Shared Function equal_Q(a As MalList) As MalVal
+ If Mal.types._equal_Q(a(0), a(1)) Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ ' Scalar functions
+ Shared Function nil_Q(a As MalList) As MalVal
+ If a(0) Is Nil Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ Shared Function true_Q(a As MalList) As MalVal
+ If a(0) Is MalTrue Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ Shared Function false_Q(a As MalList) As MalVal
+ If a(0) Is MalFalse Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ Shared Function symbol(a As MalList) As MalVal
+ return new MalSymbol(DirectCast(a(0),MalString))
+ End Function
+
+ Shared Function symbol_Q(a As MalList) As MalVal
+ If TypeOf a(0) Is MalSymbol Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+
+ ' Number functions
+ Shared Function lt(a As MalList) As MalVal
+ return DirectCast(a(0),MalInt) < DirectCast(a(1),MalInt)
+ End Function
+ Shared Function lte(a As MalList) As MalVal
+ return DirectCast(a(0),MalInt) <= DirectCast(a(1),MalInt)
+ End Function
+ Shared Function gt(a As MalList) As MalVal
+ return DirectCast(a(0),MalInt) > DirectCast(a(1),MalInt)
+ End Function
+ Shared Function gte(a As MalList) As MalVal
+ return DirectCast(a(0),MalInt) >= DirectCast(a(1),MalInt)
+ End Function
+ Shared Function plus(a As MalList) As MalVal
+ return DirectCast(a(0),MalInt) + DirectCast(a(1),MalInt)
+ End Function
+ Shared Function minus(a As MalList) As MalVal
+ return DirectCast(a(0),MalInt) - DirectCast(a(1),MalInt)
+ End Function
+ Shared Function mult(a As MalList) As MalVal
+ return DirectCast(a(0),MalInt) * DirectCast(a(1),MalInt)
+ End Function
+ Shared Function div(a As MalList) As MalVal
+ return DirectCast(a(0),MalInt) / DirectCast(a(1),MalInt)
+ End Function
+
+ Shared Function time_ms(a As MalList) As MalVal
+ return New MalInt(DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond)
+ End Function
+
+ ' String functions
+ Shared Function pr_str(a As MalList) As MalVal
+ return New MalString(printer._pr_str_args(a, " ", true))
+ End Function
+
+ Shared Function str(a As MalList) As MalVal
+ return new MalString(printer._pr_str_args(a, "", false))
+ End Function
+
+ Shared Function prn(a As MalList) As MalVal
+ Console.WriteLine(printer._pr_str_args(a, " ", true))
+ return Nil
+ End Function
+
+ Shared Function println(a As MalList) As MalVal
+ Console.WriteLine(printer._pr_str_args(a, " ", false))
+ return Nil
+ End Function
+
+ Shared Function mal_readline(a As MalList) As MalVal
+ Dim line As String
+ line = readline.Readline(DirectCast(a(0),MalString).getValue())
+ If line Is Nothing Then
+ return types.Nil
+ Else
+ return New MalString(line)
+ End If
+ End Function
+
+ Shared Function read_string(a As MalList) As MalVal
+ return reader.read_str(DirectCast(a(0),MalString).getValue())
+ End Function
+
+ Shared Function slurp(a As MalList) As MalVal
+ return New MalString(File.ReadAllText(DirectCast(a(0),MalString).getValue()))
+ End Function
+
+
+ ' List/Vector functions
+
+ Shared Function list(a As MalList) As MalVal
+ return New MalList(a.getValue())
+ End Function
+
+ Shared Function list_Q(a As MalList) As MalVal
+ If TypeOf a(0) Is MalList And Not TypeOf a(0) Is MalVector Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ Shared Function vector(a As MalList) As MalVal
+ return New MalVector(a.getValue())
+ End Function
+
+ Shared Function vector_Q(a As MalList) As MalVal
+ If TypeOf a(0) Is MalVector Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ ' HashMap functions
+ Shared Function hash_map(a As MalList) As MalVal
+ return New MalHashMap(a)
+ End Function
+
+ Shared Function hash_map_Q(a As MalList) As MalVal
+ If TypeOf a(0) Is MalHashMap Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ Shared Function contains_Q(a As MalList) As MalVal
+ Dim key As String = DirectCast(a(1),MalString).getValue()
+ Dim dict As Dictionary(Of String,MalVal) = DirectCast(a(0),MalHashMap).getValue()
+ If dict.ContainsKey(key) Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ Shared Function assoc(a As MalList) As MalVal
+ Dim new_hm As MalHashMap = DirectCast(a(0),MalHashMap).copy()
+ return new_hm.assoc_BANG(DirectCast(a.slice(1),MalList))
+ End Function
+
+ Shared Function dissoc(a As MalList) As MalVal
+ Dim new_hm As MalHashMap = DirectCast(a(0),MalHashMap).copy()
+ return new_hm.dissoc_BANG(DirectCast(a.slice(1),MalList))
+ End Function
+
+ Shared Function do_get(a As MalList) As MalVal
+ Dim k As String = DirectCast(a(1),MalString).getValue()
+ If a(0) Is Nil Then
+ return Nil
+ Else
+ Dim dict As Dictionary(Of String,MalVal) = DirectCast(a(0),MalHashMap).getValue()
+ If dict.ContainsKey(k) Then
+ return dict(k)
+ Else
+ return Nil
+ End If
+ End If
+ End Function
+
+ Shared Function keys(a As MalList) As MalVal
+ Dim dict As Dictionary(Of String,MalVal) = DirectCast(a(0),MalHashMap).getValue()
+ Dim key_lst As MalList = New MalList()
+ For Each key As String in dict.Keys
+ key_lst.conj_BANG(new MalString(key))
+ Next
+ return key_lst
+ End Function
+
+ Shared Function vals(a As MalList) As MalVal
+ Dim dict As Dictionary(Of String,MalVal) = DirectCast(a(0),MalHashMap).getValue()
+ Dim val_lst As MalList = New MalList()
+ For Each val As MalVal In dict.Values
+ val_lst.conj_BANG(val)
+ Next
+ return val_lst
+ End Function
+
+ ' Sequence functions
+ Shared Function sequential_Q(a As MalList) As MalVal
+ If TypeOf a(0) Is MalList Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ Shared Function cons(a As MalList) As MalVal
+ Dim lst As New List(Of MalVal)
+ lst.Add(a(0))
+ lst.AddRange(DirectCast(a(1),MalList).getValue())
+ return DirectCast(New MalList(lst),MalVal)
+ End Function
+
+ Shared Function concat(a As MalList) As MalVal
+ If a.size() = 0 Then
+ return new MalList()
+ End If
+ Dim lst As New List(Of MalVal)
+ lst.AddRange(DirectCast(a(0),MalList).getValue())
+ for i As Integer = 1 To a.size()-1
+ lst.AddRange(DirectCast(a(i),MalList).getValue())
+ Next
+ return DirectCast(new MalList(lst),MalVal)
+ End Function
+
+ Shared Function nth(a As MalList) As MalVal
+ return DirectCast(a(0),MalList)( DirectCast(a(1),MalInt).getValue() )
+ End Function
+
+ Shared Function first(a As MalList) As MalVal
+ return DirectCast(a(0),MalList)(0)
+ End Function
+
+ Shared Function rest(a As MalList) As MalVal
+ return DirectCast(a(0),MalList).rest()
+ End Function
+
+ Shared Function empty_Q(a As MalList) As MalVal
+ If DirectCast(a(0),MalList).size() = 0 Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ Shared Function count(a As MalList) As MalVal
+ return new MalInt(DirectCast(a(0),MalList).size())
+ End Function
+
+ Shared Function conj(a As MalList) As MalVal
+ Dim src_lst As List(Of MalVal) = DirectCast(a(0),MalList).getValue()
+ Dim new_lst As New List(Of MalVal)
+ new_lst.AddRange(src_lst)
+ If TypeOf a(0) Is MalVector Then
+ For i As Integer = 1 To a.size()-1
+ new_lst.Add(a(i))
+ Next
+ return new MalVector(new_lst)
+ Else
+ For i As Integer = 1 To a.size()-1
+ new_lst.Insert(0, a(i))
+ Next
+ return new MalList(new_lst)
+ End If
+ End Function
+
+
+ ' General list related functions
+ Shared Function apply(a As MalList) As MalVal
+ Dim f As MalFunc = DirectCast(a(0),MalFunc)
+ Dim lst As New List(Of MalVal)
+ lst.AddRange(a.slice(1,a.size()-1).getValue())
+ lst.AddRange(DirectCast(a(a.size()-1),MalList).getValue())
+ return f.apply(New MalList(lst))
+ End Function
+
+ Shared Function map(a As MalList) As MalVal
+ Dim f As MalFunc = DirectCast(a(0),MalFunc)
+ Dim src_lst As List(Of MalVal) = DirectCast(a(1),MalList).getValue()
+ Dim new_lst As New List(Of MalVal)
+ for i As Integer = 0 To src_lst.Count-1
+ new_lst.Add(f.apply(New MalList(src_lst(i))))
+ Next
+ return new MalList(new_lst)
+ End Function
+
+
+ ' Metadata functions
+ Shared Function atom(a As MalList) As MalVal
+ return new MalAtom(a(0))
+ End Function
+
+ Shared Function meta(a As MalList) As MalVal
+ return a(0).getMeta()
+ End Function
+
+ Shared Function with_meta(a As MalList) As MalVal
+ return DirectCast(a(0),MalVal).copy().setMeta(a(1))
+ End Function
+
+
+ ' Atom functions
+ Shared Function atom_Q(a As MalList) As MalVal
+ If TypeOf a(0) Is MalAtom Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Function
+
+ Shared Function deref(a As MalList) As MalVal
+ return DirectCast(a(0),MalAtom).getValue()
+ End Function
+
+ Shared Function reset_BANG(a As MalList) As MalVal
+ return DirectCast(a(0),MalAtom).setValue(a(1))
+ End Function
+
+ Shared Function swap_BANG(a As MalList) As MalVal
+ Dim atm As MalAtom = DirectCast(a(0),MalAtom)
+ Dim f As MalFunc = DirectCast(a(1),MalFunc)
+ Dim new_lst As New List(Of MalVal)
+ new_lst.Add(atm.getValue())
+ new_lst.AddRange(DirectCast(a.slice(2),MalList).getValue())
+ return atm.setValue(f.apply(New MalList(new_lst)))
+ End Function
+
+
+
+ Shared Function ns As Dictionary(Of String, MalVal)
+ Dim ns As New Dictionary(Of String, MalVal)
+
+ ns.Add("=", New MalFunc(AddressOf equal_Q))
+ ns.Add("throw", New MalFunc(AddressOf mal_throw))
+ ns.Add("nil?", New MalFunc(AddressOf nil_Q))
+ ns.Add("true?", New MalFunc(AddressOf true_Q))
+ ns.Add("false?", New MalFunc(AddressOf false_Q))
+ ns.Add("symbol", new MalFunc(AddressOf symbol))
+ ns.Add("symbol?", New MalFunc(AddressOf symbol_Q))
+
+ ns.Add("pr-str",New MalFunc(AddressOf pr_str))
+ ns.Add("str", New MalFunc(AddressOf str))
+ ns.Add("prn", New MalFunc(AddressOf prn))
+ ns.Add("println", New MalFunc(AddressOf println))
+ ns.Add("readline", New MalFunc(AddressOf mal_readline))
+ ns.Add("read-string", New MalFunc(AddressOf read_string))
+ ns.Add("slurp", New MalFunc(AddressOf slurp))
+ ns.Add("<", New MalFunc(AddressOf lt))
+ ns.Add("<=", New MalFunc(AddressOf lte))
+ ns.Add(">", New MalFunc(AddressOf gt))
+ ns.Add(">=", New MalFunc(AddressOf gte))
+ ns.Add("+", New MalFunc(AddressOf plus))
+ ns.Add("-", New MalFunc(AddressOf minus))
+ ns.Add("*", New MalFunc(AddressOf mult))
+ ns.Add("/", New MalFunc(AddressOf div))
+ ns.Add("time-ms", New MalFunc(AddressOf time_ms))
+
+ ns.Add("list", New MalFunc(AddressOf list))
+ ns.Add("list?", New MalFunc(AddressOf list_Q))
+ ns.Add("vector", new MalFunc(AddressOf vector))
+ ns.Add("vector?", New MalFunc(AddressOf vector_Q))
+ ns.Add("hash-map", new MalFunc(AddressOf hash_map))
+ ns.Add("map?", New MalFunc(AddressOf hash_map_Q))
+ ns.Add("contains?", New MalFunc(AddressOf contains_Q))
+ ns.Add("assoc", New MalFunc(AddressOf assoc))
+ ns.Add("dissoc", New MalFunc(AddressOf dissoc))
+ ns.Add("get", New MalFunc(AddressOf do_get))
+ ns.Add("keys", New MalFunc(AddressOf keys))
+ ns.Add("vals", New MalFunc(AddressOf vals))
+
+ ns.Add("sequential?", New MalFunc(AddressOf sequential_Q))
+ ns.Add("cons", New MalFunc(AddressOf cons))
+ ns.Add("concat", New MalFunc(AddressOf concat))
+ ns.Add("nth", New MalFunc(AddressOf nth))
+ ns.Add("first", New MalFunc(AddressOf first))
+ ns.Add("rest", New MalFunc(AddressOf rest))
+ ns.Add("empty?", New MalFunc(AddressOf empty_Q))
+ ns.Add("count",New MalFunc(AddressOf count))
+ ns.Add("conj", New MalFunc(AddressOf conj))
+ ns.Add("apply", New MalFunc(AddressOf apply))
+ ns.Add("map", New MalFunc(AddressOf map))
+
+ ns.Add("with-meta", New MalFunc(AddressOf with_meta))
+ ns.Add("meta", New MalFunc(AddressOf meta))
+ ns.Add("atom", new MalFunc(AddressOf atom))
+ ns.Add("atom?", New MalFunc(AddressOf atom_Q))
+ ns.Add("deref", New MalFunc(AddressOf deref))
+ ns.Add("reset!", New MalFunc(AddressOf reset_BANG))
+ ns.Add("swap!", New MalFunc(AddressOf swap_BANG))
+ return ns
+ End Function
+ End Class
+End Namespace
diff --git a/vb/env.vb b/vb/env.vb
new file mode 100644
index 0000000..3095344
--- /dev/null
+++ b/vb/env.vb
@@ -0,0 +1,55 @@
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+
+Namespace Mal
+ Public Class env
+ Public Class Env
+ Dim outer As Env = Nothing
+ Dim data As Dictionary(Of String, MalVal) = New Dictionary(Of String, MalVal)
+
+ Public Sub New(new_outer As Env)
+ outer = new_outer
+ End Sub
+ Public Sub New(new_outer As Env, binds As MalList, exprs As MalList)
+ outer = new_outer
+ For i As Integer = 0 To binds.size()-1
+ Dim sym As String = DirectCast(binds.nth(i),MalSymbol).getName()
+ If sym = "&" Then
+ data(DirectCast(binds.nth(i+1),MalSymbol).getName()) = exprs.slice(i)
+ Exit For
+ Else
+ data(sym) = exprs.nth(i)
+ End If
+ Next
+ End Sub
+
+ Public Function find(key As String) As Env
+ If data.ContainsKey(key) Then
+ return Me
+ Else If outer IsNot Nothing Then
+ return outer.find(key)
+ Else
+ return Nothing
+ End If
+ End Function
+
+ Public Function do_get(key As String) As MalVal
+ Dim e As Env = find(key)
+ If e Is Nothing Then
+ throw New Mal.types.MalException(
+ "'" & key & "' not found")
+ Else
+ return e.data(key)
+ End If
+ End Function
+
+ Public Function do_set(key As String, value As MalVal) As Env
+ data(key) = value
+ return Me
+ End Function
+ End Class
+ End Class
+End Namespace
diff --git a/vb/getline.cs b/vb/getline.cs
new file mode 100644
index 0000000..c11a11d
--- /dev/null
+++ b/vb/getline.cs
@@ -0,0 +1,1089 @@
+//
+// getline.cs: A command line editor
+//
+// Authors:
+// Miguel de Icaza (miguel@novell.com)
+//
+// Copyright 2008 Novell, Inc.
+//
+// Dual-licensed under the terms of the MIT X11 license or the
+// Apache License 2.0
+//
+// USE -define:DEMO to build this as a standalone file and test it
+//
+// TODO:
+// Enter an error (a = 1); Notice how the prompt is in the wrong line
+// This is caused by Stderr not being tracked by System.Console.
+// Completion support
+// Why is Thread.Interrupt not working? Currently I resort to Abort which is too much.
+//
+// Limitations in System.Console:
+// Console needs SIGWINCH support of some sort
+// Console needs a way of updating its position after things have been written
+// behind its back (P/Invoke puts for example).
+// System.Console needs to get the DELETE character, and report accordingly.
+//
+
+using System;
+using System.Text;
+using System.IO;
+using System.Threading;
+using System.Reflection;
+
+namespace Mono.Terminal {
+
+ public class LineEditor {
+
+ public class Completion {
+ public string [] Result;
+ public string Prefix;
+
+ public Completion (string prefix, string [] result)
+ {
+ Prefix = prefix;
+ Result = result;
+ }
+ }
+
+ public delegate Completion AutoCompleteHandler (string text, int pos);
+
+ //static StreamWriter log;
+
+ // The text being edited.
+ StringBuilder text;
+
+ // The text as it is rendered (replaces (char)1 with ^A on display for example).
+ StringBuilder rendered_text;
+
+ // The prompt specified, and the prompt shown to the user.
+ string prompt;
+ string shown_prompt;
+
+ // The current cursor position, indexes into "text", for an index
+ // into rendered_text, use TextToRenderPos
+ int cursor;
+
+ // The row where we started displaying data.
+ int home_row;
+
+ // The maximum length that has been displayed on the screen
+ int max_rendered;
+
+ // If we are done editing, this breaks the interactive loop
+ bool done = false;
+
+ // The thread where the Editing started taking place
+ Thread edit_thread;
+
+ // Our object that tracks history
+ History history;
+
+ // The contents of the kill buffer (cut/paste in Emacs parlance)
+ string kill_buffer = "";
+
+ // The string being searched for
+ string search;
+ string last_search;
+
+ // whether we are searching (-1= reverse; 0 = no; 1 = forward)
+ int searching;
+
+ // The position where we found the match.
+ int match_at;
+
+ // Used to implement the Kill semantics (multiple Alt-Ds accumulate)
+ KeyHandler last_handler;
+
+ delegate void KeyHandler ();
+
+ struct Handler {
+ public ConsoleKeyInfo CKI;
+ public KeyHandler KeyHandler;
+
+ public Handler (ConsoleKey key, KeyHandler h)
+ {
+ CKI = new ConsoleKeyInfo ((char) 0, key, false, false, false);
+ KeyHandler = h;
+ }
+
+ public Handler (char c, KeyHandler h)
+ {
+ KeyHandler = h;
+ // Use the "Zoom" as a flag that we only have a character.
+ CKI = new ConsoleKeyInfo (c, ConsoleKey.Zoom, false, false, false);
+ }
+
+ public Handler (ConsoleKeyInfo cki, KeyHandler h)
+ {
+ CKI = cki;
+ KeyHandler = h;
+ }
+
+ public static Handler Control (char c, KeyHandler h)
+ {
+ return new Handler ((char) (c - 'A' + 1), h);
+ }
+
+ public static Handler Alt (char c, ConsoleKey k, KeyHandler h)
+ {
+ ConsoleKeyInfo cki = new ConsoleKeyInfo ((char) c, k, false, true, false);
+ return new Handler (cki, h);
+ }
+ }
+
+ /// <summary>
+ /// Invoked when the user requests auto-completion using the tab character
+ /// </summary>
+ /// <remarks>
+ /// The result is null for no values found, an array with a single
+ /// string, in that case the string should be the text to be inserted
+ /// for example if the word at pos is "T", the result for a completion
+ /// of "ToString" should be "oString", not "ToString".
+ ///
+ /// When there are multiple results, the result should be the full
+ /// text
+ /// </remarks>
+ public AutoCompleteHandler AutoCompleteEvent;
+
+ static Handler [] handlers;
+
+ public LineEditor (string name) : this (name, 10) { }
+
+ public LineEditor (string name, int histsize)
+ {
+ handlers = new Handler [] {
+ new Handler (ConsoleKey.Home, CmdHome),
+ new Handler (ConsoleKey.End, CmdEnd),
+ new Handler (ConsoleKey.LeftArrow, CmdLeft),
+ new Handler (ConsoleKey.RightArrow, CmdRight),
+ new Handler (ConsoleKey.UpArrow, CmdHistoryPrev),
+ new Handler (ConsoleKey.DownArrow, CmdHistoryNext),
+ new Handler (ConsoleKey.Enter, CmdDone),
+ new Handler (ConsoleKey.Backspace, CmdBackspace),
+ new Handler (ConsoleKey.Delete, CmdDeleteChar),
+ new Handler (ConsoleKey.Tab, CmdTabOrComplete),
+
+ // Emacs keys
+ Handler.Control ('A', CmdHome),
+ Handler.Control ('E', CmdEnd),
+ Handler.Control ('B', CmdLeft),
+ Handler.Control ('F', CmdRight),
+ Handler.Control ('P', CmdHistoryPrev),
+ Handler.Control ('N', CmdHistoryNext),
+ Handler.Control ('K', CmdKillToEOF),
+ Handler.Control ('Y', CmdYank),
+ Handler.Control ('D', CmdDeleteChar),
+ Handler.Control ('L', CmdRefresh),
+ Handler.Control ('R', CmdReverseSearch),
+ Handler.Control ('G', delegate {} ),
+ Handler.Alt ('B', ConsoleKey.B, CmdBackwardWord),
+ Handler.Alt ('F', ConsoleKey.F, CmdForwardWord),
+
+ Handler.Alt ('D', ConsoleKey.D, CmdDeleteWord),
+ Handler.Alt ((char) 8, ConsoleKey.Backspace, CmdDeleteBackword),
+
+ // DEBUG
+ //Handler.Control ('T', CmdDebug),
+
+ // quote
+ Handler.Control ('Q', delegate { HandleChar (Console.ReadKey (true).KeyChar); })
+ };
+
+ rendered_text = new StringBuilder ();
+ text = new StringBuilder ();
+
+ history = new History (name, histsize);
+
+ //if (File.Exists ("log"))File.Delete ("log");
+ //log = File.CreateText ("log");
+ }
+
+ void CmdDebug ()
+ {
+ history.Dump ();
+ Console.WriteLine ();
+ Render ();
+ }
+
+ void Render ()
+ {
+ Console.Write (shown_prompt);
+ Console.Write (rendered_text);
+
+ int max = System.Math.Max (rendered_text.Length + shown_prompt.Length, max_rendered);
+
+ for (int i = rendered_text.Length + shown_prompt.Length; i < max_rendered; i++)
+ Console.Write (' ');
+ max_rendered = shown_prompt.Length + rendered_text.Length;
+
+ // Write one more to ensure that we always wrap around properly if we are at the
+ // end of a line.
+ Console.Write (' ');
+
+ UpdateHomeRow (max);
+ }
+
+ void UpdateHomeRow (int screenpos)
+ {
+ int lines = 1 + (screenpos / Console.WindowWidth);
+
+ home_row = Console.CursorTop - (lines - 1);
+ if (home_row < 0)
+ home_row = 0;
+ }
+
+
+ void RenderFrom (int pos)
+ {
+ int rpos = TextToRenderPos (pos);
+ int i;
+
+ for (i = rpos; i < rendered_text.Length; i++)
+ Console.Write (rendered_text [i]);
+
+ if ((shown_prompt.Length + rendered_text.Length) > max_rendered)
+ max_rendered = shown_prompt.Length + rendered_text.Length;
+ else {
+ int max_extra = max_rendered - shown_prompt.Length;
+ for (; i < max_extra; i++)
+ Console.Write (' ');
+ }
+ }
+
+ void ComputeRendered ()
+ {
+ rendered_text.Length = 0;
+
+ for (int i = 0; i < text.Length; i++){
+ int c = (int) text [i];
+ if (c < 26){
+ if (c == '\t')
+ rendered_text.Append (" ");
+ else {
+ rendered_text.Append ('^');
+ rendered_text.Append ((char) (c + (int) 'A' - 1));
+ }
+ } else
+ rendered_text.Append ((char)c);
+ }
+ }
+
+ int TextToRenderPos (int pos)
+ {
+ int p = 0;
+
+ for (int i = 0; i < pos; i++){
+ int c;
+
+ c = (int) text [i];
+
+ if (c < 26){
+ if (c == 9)
+ p += 4;
+ else
+ p += 2;
+ } else
+ p++;
+ }
+
+ return p;
+ }
+
+ int TextToScreenPos (int pos)
+ {
+ return shown_prompt.Length + TextToRenderPos (pos);
+ }
+
+ string Prompt {
+ get { return prompt; }
+ set { prompt = value; }
+ }
+
+ int LineCount {
+ get {
+ return (shown_prompt.Length + rendered_text.Length)/Console.WindowWidth;
+ }
+ }
+
+ void ForceCursor (int newpos)
+ {
+ cursor = newpos;
+
+ int actual_pos = shown_prompt.Length + TextToRenderPos (cursor);
+ int row = home_row + (actual_pos/Console.WindowWidth);
+ int col = actual_pos % Console.WindowWidth;
+
+ if (row >= Console.BufferHeight)
+ row = Console.BufferHeight-1;
+ Console.SetCursorPosition (col, row);
+
+ //log.WriteLine ("Going to cursor={0} row={1} col={2} actual={3} prompt={4} ttr={5} old={6}", newpos, row, col, actual_pos, prompt.Length, TextToRenderPos (cursor), cursor);
+ //log.Flush ();
+ }
+
+ void UpdateCursor (int newpos)
+ {
+ if (cursor == newpos)
+ return;
+
+ ForceCursor (newpos);
+ }
+
+ void InsertChar (char c)
+ {
+ int prev_lines = LineCount;
+ text = text.Insert (cursor, c);
+ ComputeRendered ();
+ if (prev_lines != LineCount){
+
+ Console.SetCursorPosition (0, home_row);
+ Render ();
+ ForceCursor (++cursor);
+ } else {
+ RenderFrom (cursor);
+ ForceCursor (++cursor);
+ UpdateHomeRow (TextToScreenPos (cursor));
+ }
+ }
+
+ //
+ // Commands
+ //
+ void CmdDone ()
+ {
+ done = true;
+ }
+
+ void CmdTabOrComplete ()
+ {
+ bool complete = false;
+
+ if (AutoCompleteEvent != null){
+ if (TabAtStartCompletes)
+ complete = true;
+ else {
+ for (int i = 0; i < cursor; i++){
+ if (!Char.IsWhiteSpace (text [i])){
+ complete = true;
+ break;
+ }
+ }
+ }
+
+ if (complete){
+ Completion completion = AutoCompleteEvent (text.ToString (), cursor);
+ string [] completions = completion.Result;
+ if (completions == null)
+ return;
+
+ int ncompletions = completions.Length;
+ if (ncompletions == 0)
+ return;
+
+ if (completions.Length == 1){
+ InsertTextAtCursor (completions [0]);
+ } else {
+ int last = -1;
+
+ for (int p = 0; p < completions [0].Length; p++){
+ char c = completions [0][p];
+
+
+ for (int i = 1; i < ncompletions; i++){
+ if (completions [i].Length < p)
+ goto mismatch;
+
+ if (completions [i][p] != c){
+ goto mismatch;
+ }
+ }
+ last = p;
+ }
+ mismatch:
+ if (last != -1){
+ InsertTextAtCursor (completions [0].Substring (0, last+1));
+ }
+ Console.WriteLine ();
+ foreach (string s in completions){
+ Console.Write (completion.Prefix);
+ Console.Write (s);
+ Console.Write (' ');
+ }
+ Console.WriteLine ();
+ Render ();
+ ForceCursor (cursor);
+ }
+ } else
+ HandleChar ('\t');
+ } else
+ HandleChar ('t');
+ }
+
+ void CmdHome ()
+ {
+ UpdateCursor (0);
+ }
+
+ void CmdEnd ()
+ {
+ UpdateCursor (text.Length);
+ }
+
+ void CmdLeft ()
+ {
+ if (cursor == 0)
+ return;
+
+ UpdateCursor (cursor-1);
+ }
+
+ void CmdBackwardWord ()
+ {
+ int p = WordBackward (cursor);
+ if (p == -1)
+ return;
+ UpdateCursor (p);
+ }
+
+ void CmdForwardWord ()
+ {
+ int p = WordForward (cursor);
+ if (p == -1)
+ return;
+ UpdateCursor (p);
+ }
+
+ void CmdRight ()
+ {
+ if (cursor == text.Length)
+ return;
+
+ UpdateCursor (cursor+1);
+ }
+
+ void RenderAfter (int p)
+ {
+ ForceCursor (p);
+ RenderFrom (p);
+ ForceCursor (cursor);
+ }
+
+ void CmdBackspace ()
+ {
+ if (cursor == 0)
+ return;
+
+ text.Remove (--cursor, 1);
+ ComputeRendered ();
+ RenderAfter (cursor);
+ }
+
+ void CmdDeleteChar ()
+ {
+ // If there is no input, this behaves like EOF
+ if (text.Length == 0){
+ done = true;
+ text = null;
+ Console.WriteLine ();
+ return;
+ }
+
+ if (cursor == text.Length)
+ return;
+ text.Remove (cursor, 1);
+ ComputeRendered ();
+ RenderAfter (cursor);
+ }
+
+ int WordForward (int p)
+ {
+ if (p >= text.Length)
+ return -1;
+
+ int i = p;
+ if (Char.IsPunctuation (text [p]) || Char.IsSymbol (text [p]) || Char.IsWhiteSpace (text[p])){
+ for (; i < text.Length; i++){
+ if (Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ for (; i < text.Length; i++){
+ if (!Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ } else {
+ for (; i < text.Length; i++){
+ if (!Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ }
+ if (i != p)
+ return i;
+ return -1;
+ }
+
+ int WordBackward (int p)
+ {
+ if (p == 0)
+ return -1;
+
+ int i = p-1;
+ if (i == 0)
+ return 0;
+
+ if (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text[i])){
+ for (; i >= 0; i--){
+ if (Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ for (; i >= 0; i--){
+ if (!Char.IsLetterOrDigit (text[i]))
+ break;
+ }
+ } else {
+ for (; i >= 0; i--){
+ if (!Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ }
+ i++;
+
+ if (i != p)
+ return i;
+
+ return -1;
+ }
+
+ void CmdDeleteWord ()
+ {
+ int pos = WordForward (cursor);
+
+ if (pos == -1)
+ return;
+
+ string k = text.ToString (cursor, pos-cursor);
+
+ if (last_handler == CmdDeleteWord)
+ kill_buffer = kill_buffer + k;
+ else
+ kill_buffer = k;
+
+ text.Remove (cursor, pos-cursor);
+ ComputeRendered ();
+ RenderAfter (cursor);
+ }
+
+ void CmdDeleteBackword ()
+ {
+ int pos = WordBackward (cursor);
+ if (pos == -1)
+ return;
+
+ string k = text.ToString (pos, cursor-pos);
+
+ if (last_handler == CmdDeleteBackword)
+ kill_buffer = k + kill_buffer;
+ else
+ kill_buffer = k;
+
+ text.Remove (pos, cursor-pos);
+ ComputeRendered ();
+ RenderAfter (pos);
+ }
+
+ //
+ // Adds the current line to the history if needed
+ //
+ void HistoryUpdateLine ()
+ {
+ history.Update (text.ToString ());
+ }
+
+ void CmdHistoryPrev ()
+ {
+ if (!history.PreviousAvailable ())
+ return;
+
+ HistoryUpdateLine ();
+
+ SetText (history.Previous ());
+ }
+
+ void CmdHistoryNext ()
+ {
+ if (!history.NextAvailable())
+ return;
+
+ history.Update (text.ToString ());
+ SetText (history.Next ());
+
+ }
+
+ void CmdKillToEOF ()
+ {
+ kill_buffer = text.ToString (cursor, text.Length-cursor);
+ text.Length = cursor;
+ ComputeRendered ();
+ RenderAfter (cursor);
+ }
+
+ void CmdYank ()
+ {
+ InsertTextAtCursor (kill_buffer);
+ }
+
+ void InsertTextAtCursor (string str)
+ {
+ int prev_lines = LineCount;
+ text.Insert (cursor, str);
+ ComputeRendered ();
+ if (prev_lines != LineCount){
+ Console.SetCursorPosition (0, home_row);
+ Render ();
+ cursor += str.Length;
+ ForceCursor (cursor);
+ } else {
+ RenderFrom (cursor);
+ cursor += str.Length;
+ ForceCursor (cursor);
+ UpdateHomeRow (TextToScreenPos (cursor));
+ }
+ }
+
+ void SetSearchPrompt (string s)
+ {
+ SetPrompt ("(reverse-i-search)`" + s + "': ");
+ }
+
+ void ReverseSearch ()
+ {
+ int p;
+
+ if (cursor == text.Length){
+ // The cursor is at the end of the string
+
+ p = text.ToString ().LastIndexOf (search);
+ if (p != -1){
+ match_at = p;
+ cursor = p;
+ ForceCursor (cursor);
+ return;
+ }
+ } else {
+ // The cursor is somewhere in the middle of the string
+ int start = (cursor == match_at) ? cursor - 1 : cursor;
+ if (start != -1){
+ p = text.ToString ().LastIndexOf (search, start);
+ if (p != -1){
+ match_at = p;
+ cursor = p;
+ ForceCursor (cursor);
+ return;
+ }
+ }
+ }
+
+ // Need to search backwards in history
+ HistoryUpdateLine ();
+ string s = history.SearchBackward (search);
+ if (s != null){
+ match_at = -1;
+ SetText (s);
+ ReverseSearch ();
+ }
+ }
+
+ void CmdReverseSearch ()
+ {
+ if (searching == 0){
+ match_at = -1;
+ last_search = search;
+ searching = -1;
+ search = "";
+ SetSearchPrompt ("");
+ } else {
+ if (search == ""){
+ if (last_search != "" && last_search != null){
+ search = last_search;
+ SetSearchPrompt (search);
+
+ ReverseSearch ();
+ }
+ return;
+ }
+ ReverseSearch ();
+ }
+ }
+
+ void SearchAppend (char c)
+ {
+ search = search + c;
+ SetSearchPrompt (search);
+
+ //
+ // If the new typed data still matches the current text, stay here
+ //
+ if (cursor < text.Length){
+ string r = text.ToString (cursor, text.Length - cursor);
+ if (r.StartsWith (search))
+ return;
+ }
+
+ ReverseSearch ();
+ }
+
+ void CmdRefresh ()
+ {
+ Console.Clear ();
+ max_rendered = 0;
+ Render ();
+ ForceCursor (cursor);
+ }
+
+ void InterruptEdit (object sender, ConsoleCancelEventArgs a)
+ {
+ // Do not abort our program:
+ a.Cancel = true;
+
+ // Interrupt the editor
+ edit_thread.Abort();
+ }
+
+ void HandleChar (char c)
+ {
+ if (searching != 0)
+ SearchAppend (c);
+ else
+ InsertChar (c);
+ }
+
+ void EditLoop ()
+ {
+ ConsoleKeyInfo cki;
+
+ while (!done){
+ ConsoleModifiers mod;
+
+ cki = Console.ReadKey (true);
+ if (cki.Key == ConsoleKey.Escape){
+ cki = Console.ReadKey (true);
+
+ mod = ConsoleModifiers.Alt;
+ } else
+ mod = cki.Modifiers;
+
+ bool handled = false;
+
+ foreach (Handler handler in handlers){
+ ConsoleKeyInfo t = handler.CKI;
+
+ if (t.Key == cki.Key && t.Modifiers == mod){
+ handled = true;
+ handler.KeyHandler ();
+ last_handler = handler.KeyHandler;
+ break;
+ } else if (t.KeyChar == cki.KeyChar && t.Key == ConsoleKey.Zoom){
+ handled = true;
+ handler.KeyHandler ();
+ last_handler = handler.KeyHandler;
+ break;
+ }
+ }
+ if (handled){
+ if (searching != 0){
+ if (last_handler != CmdReverseSearch){
+ searching = 0;
+ SetPrompt (prompt);
+ }
+ }
+ continue;
+ }
+
+ if (cki.KeyChar != (char) 0)
+ HandleChar (cki.KeyChar);
+ }
+ }
+
+ void InitText (string initial)
+ {
+ text = new StringBuilder (initial);
+ ComputeRendered ();
+ cursor = text.Length;
+ Render ();
+ ForceCursor (cursor);
+ }
+
+ void SetText (string newtext)
+ {
+ Console.SetCursorPosition (0, home_row);
+ InitText (newtext);
+ }
+
+ void SetPrompt (string newprompt)
+ {
+ shown_prompt = newprompt;
+ Console.SetCursorPosition (0, home_row);
+ Render ();
+ ForceCursor (cursor);
+ }
+
+ public string Edit (string prompt, string initial)
+ {
+ edit_thread = Thread.CurrentThread;
+ searching = 0;
+ Console.CancelKeyPress += InterruptEdit;
+
+ done = false;
+ history.CursorToEnd ();
+ max_rendered = 0;
+
+ Prompt = prompt;
+ shown_prompt = prompt;
+ InitText (initial);
+ history.Append (initial);
+
+ do {
+ try {
+ EditLoop ();
+ } catch (ThreadAbortException){
+ searching = 0;
+ Thread.ResetAbort ();
+ Console.WriteLine ();
+ SetPrompt (prompt);
+ SetText ("");
+ }
+ } while (!done);
+ Console.WriteLine ();
+
+ Console.CancelKeyPress -= InterruptEdit;
+
+ if (text == null){
+ history.Close ();
+ return null;
+ }
+
+ string result = text.ToString ();
+ if (result != "")
+ history.Accept (result);
+ else
+ history.RemoveLast ();
+
+ return result;
+ }
+
+ public void SaveHistory ()
+ {
+ if (history != null) {
+ history.Close ();
+ }
+ }
+
+ public bool TabAtStartCompletes { get; set; }
+
+ //
+ // Emulates the bash-like behavior, where edits done to the
+ // history are recorded
+ //
+ class History {
+ string [] history;
+ int head, tail;
+ int cursor, count;
+ string histfile;
+
+ public History (string app, int size)
+ {
+ if (size < 1)
+ throw new ArgumentException ("size");
+
+ if (app != null){
+ string dir = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
+ //Console.WriteLine (dir);
+ /*
+ if (!Directory.Exists (dir)){
+ try {
+ Directory.CreateDirectory (dir);
+ } catch {
+ app = null;
+ }
+ }
+ if (app != null)
+ histfile = Path.Combine (dir, app) + ".history";
+ */
+ histfile = Path.Combine (dir, ".mal-history");
+ }
+
+ history = new string [size];
+ head = tail = cursor = 0;
+
+ if (File.Exists (histfile)){
+ using (StreamReader sr = File.OpenText (histfile)){
+ string line;
+
+ while ((line = sr.ReadLine ()) != null){
+ if (line != "")
+ Append (line);
+ }
+ }
+ }
+ }
+
+ public void Close ()
+ {
+ if (histfile == null)
+ return;
+
+ try {
+ using (StreamWriter sw = File.CreateText (histfile)){
+ int start = (count == history.Length) ? head : tail;
+ for (int i = start; i < start+count; i++){
+ int p = i % history.Length;
+ sw.WriteLine (history [p]);
+ }
+ }
+ } catch {
+ // ignore
+ }
+ }
+
+ //
+ // Appends a value to the history
+ //
+ public void Append (string s)
+ {
+ //Console.WriteLine ("APPENDING {0} head={1} tail={2}", s, head, tail);
+ history [head] = s;
+ head = (head+1) % history.Length;
+ if (head == tail)
+ tail = (tail+1 % history.Length);
+ if (count != history.Length)
+ count++;
+ //Console.WriteLine ("DONE: head={1} tail={2}", s, head, tail);
+ }
+
+ //
+ // Updates the current cursor location with the string,
+ // to support editing of history items. For the current
+ // line to participate, an Append must be done before.
+ //
+ public void Update (string s)
+ {
+ history [cursor] = s;
+ }
+
+ public void RemoveLast ()
+ {
+ head = head-1;
+ if (head < 0)
+ head = history.Length-1;
+ }
+
+ public void Accept (string s)
+ {
+ int t = head-1;
+ if (t < 0)
+ t = history.Length-1;
+
+ history [t] = s;
+ }
+
+ public bool PreviousAvailable ()
+ {
+ //Console.WriteLine ("h={0} t={1} cursor={2}", head, tail, cursor);
+ if (count == 0)
+ return false;
+ int next = cursor-1;
+ if (next < 0)
+ next = count-1;
+
+ if (next == head)
+ return false;
+
+ return true;
+ }
+
+ public bool NextAvailable ()
+ {
+ if (count == 0)
+ return false;
+ int next = (cursor + 1) % history.Length;
+ if (next == head)
+ return false;
+ return true;
+ }
+
+
+ //
+ // Returns: a string with the previous line contents, or
+ // nul if there is no data in the history to move to.
+ //
+ public string Previous ()
+ {
+ if (!PreviousAvailable ())
+ return null;
+
+ cursor--;
+ if (cursor < 0)
+ cursor = history.Length - 1;
+
+ return history [cursor];
+ }
+
+ public string Next ()
+ {
+ if (!NextAvailable ())
+ return null;
+
+ cursor = (cursor + 1) % history.Length;
+ return history [cursor];
+ }
+
+ public void CursorToEnd ()
+ {
+ if (head == tail)
+ return;
+
+ cursor = head;
+ }
+
+ public void Dump ()
+ {
+ Console.WriteLine ("Head={0} Tail={1} Cursor={2} count={3}", head, tail, cursor, count);
+ for (int i = 0; i < history.Length;i++){
+ Console.WriteLine (" {0} {1}: {2}", i == cursor ? "==>" : " ", i, history[i]);
+ }
+ //log.Flush ();
+ }
+
+ public string SearchBackward (string term)
+ {
+ for (int i = 0; i < count; i++){
+ int slot = cursor-i-1;
+ if (slot < 0)
+ slot = history.Length+slot;
+ if (slot >= history.Length)
+ slot = 0;
+ if (history [slot] != null && history [slot].IndexOf (term) != -1){
+ cursor = slot;
+ return history [slot];
+ }
+ }
+
+ return null;
+ }
+
+ }
+ }
+
+#if DEMO
+ class Demo {
+ static void Main ()
+ {
+ LineEditor le = new LineEditor ("foo");
+ string s;
+
+ while ((s = le.Edit ("shell> ", "")) != null){
+ Console.WriteLine ("----> [{0}]", s);
+ }
+ }
+ }
+#endif
+}
diff --git a/vb/printer.vb b/vb/printer.vb
new file mode 100644
index 0000000..212b89c
--- /dev/null
+++ b/vb/printer.vb
@@ -0,0 +1,50 @@
+Imports System
+Imports System.Collections.Generic
+Imports System.Text.RegularExpressions
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalList = Mal.types.MalList
+
+Namespace Mal
+ Public Class printer
+ Shared Function join(value As List(Of MalVal),
+ delim As String,
+ print_readably As Boolean) As String
+ Dim strs As New List(Of String)
+ For Each mv As MalVal In value
+ strs.Add(mv.ToString(print_readably))
+ Next
+ return String.Join(delim, strs.ToArray())
+ End Function
+
+ Shared Function join(value As Dictionary(Of String, MalVal),
+ delim As String,
+ print_readably As Boolean) As String
+ Dim strs As New List(Of String)
+ For Each entry As KeyValuePair(Of String, MalVal) In value
+ If print_readably Then
+ strs.Add("""" & entry.Key.ToString() & """")
+ Else
+ strs.Add(entry.Key.ToString())
+ End If
+ strs.Add(entry.Value.ToString(print_readably))
+ Next
+ return String.Join(delim, strs.ToArray())
+ End Function
+
+ Shared Function _pr_str(mv As MalVal,
+ print_readably As Boolean) As String
+ return mv.ToString(print_readably)
+ End Function
+
+ Shared Function _pr_str_args(args As MalList,
+ sep As String,
+ print_readably As Boolean) As String
+ return join(args.getValue(), sep, print_readably)
+ End Function
+
+ Shared Function escapeString(str As String) As String
+ return Regex.Escape(str)
+ End Function
+ End Class
+End Namespace
diff --git a/vb/reader.vb b/vb/reader.vb
new file mode 100644
index 0000000..121aac2
--- /dev/null
+++ b/vb/reader.vb
@@ -0,0 +1,181 @@
+Imports System
+Imports System.Collections
+Imports System.Collections.Generic
+Imports System.Text.RegularExpressions
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalThrowable = Mal.types.MalThrowable
+Imports MalContinue = Mal.types.MalContinue
+
+Namespace Mal
+ Public Class reader
+ Public Class ParseError
+ Inherits MalThrowable
+ Public Sub New(msg As String)
+ MyBase.New(msg)
+ End Sub
+ End Class
+
+ Public Class Reader
+ Private tokens As New List(Of String)
+ Private position As Int32 = 0
+ Sub New(t As List(Of String))
+ tokens = t
+ position = 0
+ End Sub
+
+ Public Function peek() As String
+ If position >= tokens.Count Then
+ return Nothing
+ Else
+ return tokens(position)
+ End If
+ End Function
+
+ Public Function get_next() As String
+ If position >= tokens.Count Then
+ return Nothing
+ Else
+ position += 1
+ return tokens(position-1)
+ End If
+ End Function
+ End Class
+
+ Shared Function tokenize(str As String) As List(Of String)
+ Dim tokens As New List(Of String)
+ Dim pattern As String = "[\s ,]*(~@|[\[\]{}()'`~@]|""(?:[\\].|[^\\""])*""|;.*|[^\s \[\]{}()'""`~@,;]*)"
+ Dim regex As New Regex(pattern)
+ For Each match As Match In regex.Matches(str)
+ Dim token As String = match.Groups(1).Value
+ If Not token Is Nothing _
+ AndAlso Not token = "" _
+ AndAlso Not token(0) = ";" Then
+ 'Console.WriteLine("match: ^" & match.Groups[1] & "$")
+ tokens.Add(token)
+ End If
+ Next
+ return tokens
+ End Function
+
+ Shared Function read_atom(rdr As Reader) As MalVal
+ Dim token As String = rdr.get_next()
+ Dim pattern As String = "(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^("".*"")$|(^[^""]*$)"
+ Dim regex As Regex = New Regex(pattern)
+ Dim match As Match = regex.Match(token)
+ 'Console.WriteLine("token: ^" + token + "$")
+ If not match.Success Then
+ throw New ParseError("unrecognized token '" & token & "'")
+ End If
+ If match.Groups(1).Value <> String.Empty Then
+ return New Mal.types.MalInt(Integer.Parse(match.Groups(1).Value))
+ Else If match.Groups(3).Value <> String.Empty Then
+ return Mal.types.Nil
+ Else If match.Groups(4).Value <> String.Empty Then
+ return Mal.types.MalTrue
+ Else If match.Groups(5).Value <> String.Empty Then
+ return Mal.types.MalFalse
+ Else If match.Groups(6).Value <> String.Empty Then
+ Dim str As String = match.Groups(6).Value
+ return New Mal.types.MalString(
+ str.Substring(1, str.Length-2) _
+ .Replace("\""", """") _
+ .Replace("\n", Environment.NewLine))
+ Else If match.Groups(7).Value <> String.Empty Then
+ return New Mal.types.MalSymbol(match.Groups(7).Value)
+ Else
+ throw New ParseError("unrecognized '" & match.Groups(0).Value & "'")
+ End If
+ End Function
+
+ Shared Function read_list(rdr As Reader, lst As MalList,
+ start As String, last As String) As MalVal
+ Dim token As String = rdr.get_next()
+ If token(0) <> start Then
+ throw New ParseError("expected '" & start & "'")
+ End If
+
+ token = rdr.peek()
+ While token IsNot Nothing AndAlso token(0) <> last
+ lst.conj_BANG(read_form(rdr))
+ token = rdr.peek()
+ End While
+
+ If token Is Nothing Then
+ throw New ParseError("expected '" & last & "', got EOF")
+ End If
+ rdr.get_next()
+
+ return lst
+ End Function
+
+ Shared Function read_hash_map(rdr As Reader) As MalVal
+ Dim lst As MalList = DirectCast(read_list(rdr, new MalList(),
+ "{", "}"),MalList)
+ return New MalHashMap(lst)
+ End Function
+
+
+ Shared Function read_form(rdr As Reader) As MalVal
+ Dim token As String = rdr.peek()
+ If token Is Nothing Then
+ throw New MalContinue()
+ End If
+ Dim form As MalVal = Nothing
+
+ Select token
+ Case "'"
+ rdr.get_next()
+ return New MalList(New MalSymbol("quote"),
+ read_form(rdr))
+ Case "`"
+ rdr.get_next()
+ return New MalList(New MalSymbol("quasiquote"),
+ read_form(rdr))
+ Case "~"
+ rdr.get_next()
+ return New MalList(New MalSymbol("unquote"),
+ read_form(rdr))
+ Case "~@"
+ rdr.get_next()
+ return new MalList(New MalSymbol("splice-unquote"),
+ read_form(rdr))
+ Case "^"
+ rdr.get_next()
+ Dim meta As MalVal = read_form(rdr)
+ return new MalList(New MalSymbol("with-meta"),
+ read_form(rdr),
+ meta)
+ Case "@"
+ rdr.get_next()
+ return new MalList(New MalSymbol("deref"),
+ read_form(rdr))
+
+ Case "("
+ form = read_list(rdr, New MalList(), "(" , ")")
+ Case ")"
+ throw New ParseError("unexpected ')'")
+ Case "["
+ form = read_list(rdr, New MalVector(), "[" , "]")
+ Case "]"
+ throw New ParseError("unexpected ']'")
+ Case "{"
+ form = read_hash_map(rdr)
+ Case "}"
+ throw New ParseError("unexpected '}'")
+ Case Else
+ form = read_atom(rdr)
+ End Select
+ return form
+ End Function
+
+
+ Shared Function read_str(str As string) As MalVal
+ return read_form(New Reader(tokenize(str)))
+ End Function
+ End Class
+End Namespace
diff --git a/vb/readline.vb b/vb/readline.vb
new file mode 100644
index 0000000..74047ce
--- /dev/null
+++ b/vb/readline.vb
@@ -0,0 +1,32 @@
+Imports System
+Imports Mono.Terminal ' LineEditor (getline.cs)
+
+Namespace Mal
+ Public Class readline
+ Enum Modes
+ Terminal
+ Raw
+ End Enum
+
+ Public Shared mode As Modes = Modes.Terminal
+
+ Shared lineedit As LineEditor = Nothing
+
+ Public Shared Sub SetMode(new_mode As Modes)
+ mode = new_mode
+ End Sub
+
+ Public Shared Function Readline(prompt As String) As String
+ If mode = Modes.Terminal Then
+ If lineedit Is Nothing Then
+ lineedit = New LineEditor("Mal")
+ End If
+ return lineedit.Edit(prompt, "")
+ Else
+ Console.Write(prompt)
+ Console.Out.Flush()
+ return Console.ReadLine()
+ End If
+ End Function
+ End Class
+End Namespace
diff --git a/vb/step0_repl.vb b/vb/step0_repl.vb
new file mode 100644
index 0000000..ab8fe5e
--- /dev/null
+++ b/vb/step0_repl.vb
@@ -0,0 +1,43 @@
+Imports System
+Imports Mal
+
+Namespace Mal
+ class step0_repl
+ ' read
+ Shared Function READ(str As String) As String
+ Return str
+ End Function
+
+ ' eval
+ Shared Function EVAL(ast As String, env As String) As String
+ Return ast
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As String) As String
+ Return exp
+ End Function
+
+ ' repl
+ Shared Function REP(str As String, env As String) As String
+ Return PRINT(EVAL(READ(str), env))
+ End Function
+
+ Shared Function Main As Integer
+ Dim prompt As String = "user> "
+ Dim line As String
+
+ Do
+ line = Mal.readline.Readline(prompt)
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Console.WriteLine(REP(line, ""))
+ Loop While True
+ Return 0
+ End function
+ end class
+End Namespace
diff --git a/vb/step1_read_print.vb b/vb/step1_read_print.vb
new file mode 100644
index 0000000..f8b47af
--- /dev/null
+++ b/vb/step1_read_print.vb
@@ -0,0 +1,59 @@
+Imports System
+Imports System.IO
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+
+Namespace Mal
+ class step1_read_print
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function EVAL(ast As MalVal, env As String) As MalVal
+ Return ast
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), ""))
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ End If
+
+ ' repl loop
+ Dim line As String
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e as Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ end class
+End Namespace
diff --git a/vb/step2_eval.vb b/vb/step2_eval.vb
new file mode 100644
index 0000000..70bc5e3
--- /dev/null
+++ b/vb/step2_eval.vb
@@ -0,0 +1,134 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalInt = Mal.types.MalInt
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalFunc = Mal.types.MalFunc
+
+Namespace Mal
+ class step2_eval
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function eval_ast(ast As MalVal, env As Dictionary(Of String, MalVal)) As MalVal
+ If TypeOf ast Is MalSymbol Then
+ Dim sym As MalSymbol = DirectCast(ast, MalSymbol)
+ return env.Item(sym.getName())
+ Else If TypeOf ast Is MalList Then
+ Dim old_lst As MalList = DirectCast(ast, MalList)
+ Dim new_lst As MalList
+ If ast.list_Q() Then
+ new_lst = New MalList
+ Else
+ new_lst = DirectCast(New MalVector, MalList)
+ End If
+ Dim mv As MalVal
+ For Each mv in old_lst.getValue()
+ new_lst.conj_BANG(EVAL(mv, env))
+ Next
+ return new_lst
+ Else If TypeOf ast Is MalHashMap Then
+ Dim new_dict As New Dictionary(Of String, MalVal)
+ Dim entry As KeyValuePair(Of String, MalVal)
+ For Each entry in DirectCast(ast,MalHashMap).getValue()
+ new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
+ Next
+ return New MalHashMap(new_dict)
+ Else
+ return ast
+ End If
+ return ast
+ End Function
+
+ Shared Function EVAL(orig_ast As MalVal, env As Dictionary(Of String, MalVal)) As MalVal
+ 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
+ If not orig_ast.list_Q() Then
+ return eval_ast(orig_ast, env)
+ End If
+
+ ' apply list
+ Dim ast As MalList = DirectCast(orig_ast, MalList)
+ If ast.size() = 0 Then
+ return ast
+ End If
+ Dim a0 As MalVal = ast(0)
+ Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
+ Dim f As MalFunc = DirectCast(el(0), MalFunc)
+ Return f.apply(el.rest())
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared repl_env As Dictionary(Of String, MalVal)
+
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), repl_env))
+ End Function
+
+ Shared Function add(a As MalList) As MalVal
+ Return DirectCast(a.Item(0),MalInt) + DirectCast(a.Item(1),MalInt)
+ End Function
+
+ Shared Function minus(a As MalList) As MalVal
+ Return DirectCast(a.Item(0),MalInt) - DirectCast(a.Item(1),MalInt)
+ End Function
+
+ Shared Function mult(a As MalList) As MalVal
+ Return DirectCast(a.Item(0),MalInt) * DirectCast(a.Item(1),MalInt)
+ End Function
+
+ Shared Function div(a As MalList) As MalVal
+ Return DirectCast(a.Item(0),MalInt) / DirectCast(a.Item(1),MalInt)
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ repl_env = New Dictionary(Of String, MalVal)
+ repl_env.Add("+", New MalFunc(AddressOf add))
+ repl_env.Add("-", New MalFunc(AddressOf minus))
+ repl_env.Add("*", New MalFunc(AddressOf mult))
+ repl_env.Add("/", New MalFunc(AddressOf div))
+
+
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ End If
+
+ ' repl loop
+ Dim line As String
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e as Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ end class
+End Namespace
diff --git a/vb/step3_env.vb b/vb/step3_env.vb
new file mode 100644
index 0000000..2877ef1
--- /dev/null
+++ b/vb/step3_env.vb
@@ -0,0 +1,156 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalInt = Mal.types.MalInt
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalFunc = Mal.types.MalFunc
+Imports MalEnv = Mal.env.Env
+
+Namespace Mal
+ class step3_eval
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal
+ If TypeOf ast Is MalSymbol Then
+ Dim sym As MalSymbol = DirectCast(ast, MalSymbol)
+ return env.do_get(sym.getName())
+ Else If TypeOf ast Is MalList Then
+ Dim old_lst As MalList = DirectCast(ast, MalList)
+ Dim new_lst As MalList
+ If ast.list_Q() Then
+ new_lst = New MalList
+ Else
+ new_lst = DirectCast(New MalVector, MalList)
+ End If
+ Dim mv As MalVal
+ For Each mv in old_lst.getValue()
+ new_lst.conj_BANG(EVAL(mv, env))
+ Next
+ return new_lst
+ Else If TypeOf ast Is MalHashMap Then
+ Dim new_dict As New Dictionary(Of String, MalVal)
+ Dim entry As KeyValuePair(Of String, MalVal)
+ For Each entry in DirectCast(ast,MalHashMap).getValue()
+ new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
+ Next
+ return New MalHashMap(new_dict)
+ Else
+ return ast
+ End If
+ return ast
+ End Function
+
+ Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal
+ 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
+ If not orig_ast.list_Q() Then
+ return eval_ast(orig_ast, env)
+ End If
+
+ ' apply list
+ Dim ast As MalList = DirectCast(orig_ast, MalList)
+ If ast.size() = 0 Then
+ return ast
+ End If
+ Dim a0 As MalVal = ast(0)
+ Select DirectCast(a0,MalSymbol).getName()
+ Case "def!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "let*"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim key As MalSymbol
+ Dim val as MalVal
+ Dim let_env As new MalEnv(env)
+ For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2
+ key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)
+ val = DirectCast(a1,MalList)(i+1)
+ let_env.do_set(key.getName(), EVAL(val, let_env))
+ Next
+ return EVAL(a2, let_env)
+ Case Else
+ Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
+ Dim f As MalFunc = DirectCast(el(0), MalFunc)
+ Return f.apply(el.rest())
+ End Select
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared repl_env As MalEnv
+
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), repl_env))
+ End Function
+
+ Shared Function add(a As MalList) As MalVal
+ Return DirectCast(a.Item(0),MalInt) + DirectCast(a.Item(1),MalInt)
+ End Function
+
+ Shared Function minus(a As MalList) As MalVal
+ Return DirectCast(a.Item(0),MalInt) - DirectCast(a.Item(1),MalInt)
+ End Function
+
+ Shared Function mult(a As MalList) As MalVal
+ Return DirectCast(a.Item(0),MalInt) * DirectCast(a.Item(1),MalInt)
+ End Function
+
+ Shared Function div(a As MalList) As MalVal
+ Return DirectCast(a.Item(0),MalInt) / DirectCast(a.Item(1),MalInt)
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ repl_env = New MalEnv(Nothing)
+ repl_env.do_set("+", New MalFunc(AddressOf add))
+ repl_env.do_set("-", New MalFunc(AddressOf minus))
+ repl_env.do_set("*", New MalFunc(AddressOf mult))
+ repl_env.do_set("/", New MalFunc(AddressOf div))
+
+
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ End If
+
+ ' repl loop
+ Dim line As String
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e as Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ end class
+End Namespace
diff --git a/vb/step4_if_fn_do.vb b/vb/step4_if_fn_do.vb
new file mode 100644
index 0000000..2f5e16a
--- /dev/null
+++ b/vb/step4_if_fn_do.vb
@@ -0,0 +1,189 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalInt = Mal.types.MalInt
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalFunc = Mal.types.MalFunc
+Imports MalEnv = Mal.env.Env
+
+Namespace Mal
+ class step4_if_fn_do
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal
+ If TypeOf ast Is MalSymbol Then
+ Dim sym As MalSymbol = DirectCast(ast, MalSymbol)
+ return env.do_get(sym.getName())
+ Else If TypeOf ast Is MalList Then
+ Dim old_lst As MalList = DirectCast(ast, MalList)
+ Dim new_lst As MalList
+ If ast.list_Q() Then
+ new_lst = New MalList
+ Else
+ new_lst = DirectCast(New MalVector, MalList)
+ End If
+ Dim mv As MalVal
+ For Each mv in old_lst.getValue()
+ new_lst.conj_BANG(EVAL(mv, env))
+ Next
+ return new_lst
+ Else If TypeOf ast Is MalHashMap Then
+ Dim new_dict As New Dictionary(Of String, MalVal)
+ Dim entry As KeyValuePair(Of String, MalVal)
+ For Each entry in DirectCast(ast,MalHashMap).getValue()
+ new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
+ Next
+ return New MalHashMap(new_dict)
+ Else
+ return ast
+ End If
+ return ast
+ End Function
+
+ ' TODO: move to types.vb when it is ported
+ Class FClosure
+ Public ast As MalVal
+ Public params As MalList
+ Public env As MalEnv
+ Function fn(args as MalList) As MalVal
+ return EVAL(ast, new MalEnv(env, params, args))
+ End Function
+ End Class
+
+ Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal
+ 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
+ If not orig_ast.list_Q() Then
+ return eval_ast(orig_ast, env)
+ End If
+
+ ' apply list
+ Dim ast As MalList = DirectCast(orig_ast, MalList)
+ If ast.size() = 0 Then
+ return ast
+ End If
+ Dim a0 As MalVal = ast(0)
+ Dim a0sym As String
+ If TypeOf a0 is MalSymbol Then
+ a0sym = DirectCast(a0,MalSymbol).getName()
+ Else
+ a0sym = "__<*fn*>__"
+ End If
+
+ Select a0sym
+ Case "def!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "let*"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim key As MalSymbol
+ Dim val as MalVal
+ Dim let_env As new MalEnv(env)
+ For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2
+ key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)
+ val = DirectCast(a1,MalList)(i+1)
+ let_env.do_set(key.getName(), EVAL(val, let_env))
+ Next
+ return EVAL(a2, let_env)
+ Case "do"
+ Dim el As MalList = DirectCast(eval_ast(ast.rest(), env), _
+ MalLIst)
+ return el(el.size()-1)
+ Case "if"
+ Dim a1 As MalVal = ast(1)
+ Dim cond As MalVal = EVAL(a1, env)
+ If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then
+ ' eval false slot form
+ If ast.size() > 3 Then
+ Dim a3 As MalVal = ast(3)
+ return EVAL(a3, env)
+ Else
+ return Mal.types.Nil
+ End If
+ Else
+ ' eval true slot form
+ Dim a2 As MalVal = ast(2)
+ return EVAL(a2, env)
+
+ End If
+ Case "fn*"
+ Dim fc As New FClosure()
+ fc.ast = ast(2)
+ fc.params = DirectCast(ast(1),MalLIst)
+ fc.env = env
+ Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn
+ Dim mf As new MalFunc(f)
+ return DirectCast(mf,MalVal)
+ Case Else
+ Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
+ Dim f As MalFunc = DirectCast(el(0), MalFunc)
+ Return f.apply(el.rest())
+ End Select
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared repl_env As MalEnv
+
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), repl_env))
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ repl_env = New MalEnv(Nothing)
+
+ ' core.vb: defined using VB.NET
+ For Each entry As KeyValuePair(Of String,MalVal) In core.ns()
+ repl_env.do_set(entry.Key, entry.Value)
+ Next
+
+ ' core.mal: defined using the language itself
+ REP("(def! not (fn* (a) (if a false true)))")
+
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ End If
+
+ ' repl loop
+ Dim line As String
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e as Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ end class
+End Namespace
diff --git a/vb/step5_tco.vb b/vb/step5_tco.vb
new file mode 100644
index 0000000..a379cf5
--- /dev/null
+++ b/vb/step5_tco.vb
@@ -0,0 +1,198 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalInt = Mal.types.MalInt
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalFunc = Mal.types.MalFunc
+Imports MalEnv = Mal.env.Env
+
+Namespace Mal
+ class step5_tco
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal
+ If TypeOf ast Is MalSymbol Then
+ Dim sym As MalSymbol = DirectCast(ast, MalSymbol)
+ return env.do_get(sym.getName())
+ Else If TypeOf ast Is MalList Then
+ Dim old_lst As MalList = DirectCast(ast, MalList)
+ Dim new_lst As MalList
+ If ast.list_Q() Then
+ new_lst = New MalList
+ Else
+ new_lst = DirectCast(New MalVector, MalList)
+ End If
+ Dim mv As MalVal
+ For Each mv in old_lst.getValue()
+ new_lst.conj_BANG(EVAL(mv, env))
+ Next
+ return new_lst
+ Else If TypeOf ast Is MalHashMap Then
+ Dim new_dict As New Dictionary(Of String, MalVal)
+ Dim entry As KeyValuePair(Of String, MalVal)
+ For Each entry in DirectCast(ast,MalHashMap).getValue()
+ new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
+ Next
+ return New MalHashMap(new_dict)
+ Else
+ return ast
+ End If
+ return ast
+ End Function
+
+ ' TODO: move to types.vb when it is ported
+ Class FClosure
+ Public ast As MalVal
+ Public params As MalList
+ Public env As MalEnv
+ Function fn(args as MalList) As MalVal
+ return EVAL(ast, new MalEnv(env, params, args))
+ End Function
+ End Class
+
+ Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal
+ Do
+
+ 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
+ If not orig_ast.list_Q() Then
+ return eval_ast(orig_ast, env)
+ End If
+
+ ' apply list
+ Dim ast As MalList = DirectCast(orig_ast, MalList)
+ If ast.size() = 0 Then
+ return ast
+ End If
+ Dim a0 As MalVal = ast(0)
+ Dim a0sym As String
+ If TypeOf a0 is MalSymbol Then
+ a0sym = DirectCast(a0,MalSymbol).getName()
+ Else
+ a0sym = "__<*fn*>__"
+ End If
+
+ Select a0sym
+ Case "def!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "let*"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim key As MalSymbol
+ Dim val as MalVal
+ Dim let_env As new MalEnv(env)
+ For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2
+ key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)
+ val = DirectCast(a1,MalList)(i+1)
+ let_env.do_set(key.getName(), EVAL(val, let_env))
+ Next
+ orig_ast = a2
+ env = let_env
+ Case "do"
+ eval_ast(ast.slice(1, ast.size()-1), env)
+ orig_ast = ast(ast.size()-1)
+ Case "if"
+ Dim a1 As MalVal = ast(1)
+ Dim cond As MalVal = EVAL(a1, env)
+ If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then
+ ' eval false slot form
+ If ast.size() > 3 Then
+ orig_ast = ast(3)
+ Else
+ return Mal.types.Nil
+ End If
+ Else
+ ' eval true slot form
+ orig_ast = ast(2)
+
+ End If
+ Case "fn*"
+ Dim fc As New FClosure()
+ fc.ast = ast(2)
+ fc.params = DirectCast(ast(1),MalLIst)
+ fc.env = env
+ Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn
+ Dim mf As new MalFunc(ast(2), env,
+ DirectCast(ast(1),MalList), f)
+ return DirectCast(mf,MalVal)
+ Case Else
+ Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
+ Dim f As MalFunc = DirectCast(el(0), MalFunc)
+ Dim fnast As MalVal = f.getAst()
+ If not fnast Is Nothing
+ orig_ast = fnast
+ env = f.genEnv(el.rest())
+ Else
+ Return f.apply(el.rest())
+ End If
+ End Select
+
+ Loop While True
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared repl_env As MalEnv
+
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), repl_env))
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ repl_env = New MalEnv(Nothing)
+
+ ' core.vb: defined using VB.NET
+ For Each entry As KeyValuePair(Of String,MalVal) In core.ns()
+ repl_env.do_set(entry.Key, entry.Value)
+ Next
+
+ ' core.mal: defined using the language itself
+ REP("(def! not (fn* (a) (if a false true)))")
+
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ End If
+
+ ' repl loop
+ Dim line As String
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e as Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ end class
+End Namespace
diff --git a/vb/step6_file.vb b/vb/step6_file.vb
new file mode 100644
index 0000000..32cbe5b
--- /dev/null
+++ b/vb/step6_file.vb
@@ -0,0 +1,216 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalInt = Mal.types.MalInt
+Imports MalString = Mal.types.MalString
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalFunc = Mal.types.MalFunc
+Imports MalEnv = Mal.env.Env
+
+Namespace Mal
+ class step6_file
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal
+ If TypeOf ast Is MalSymbol Then
+ Dim sym As MalSymbol = DirectCast(ast, MalSymbol)
+ return env.do_get(sym.getName())
+ Else If TypeOf ast Is MalList Then
+ Dim old_lst As MalList = DirectCast(ast, MalList)
+ Dim new_lst As MalList
+ If ast.list_Q() Then
+ new_lst = New MalList
+ Else
+ new_lst = DirectCast(New MalVector, MalList)
+ End If
+ Dim mv As MalVal
+ For Each mv in old_lst.getValue()
+ new_lst.conj_BANG(EVAL(mv, env))
+ Next
+ return new_lst
+ Else If TypeOf ast Is MalHashMap Then
+ Dim new_dict As New Dictionary(Of String, MalVal)
+ Dim entry As KeyValuePair(Of String, MalVal)
+ For Each entry in DirectCast(ast,MalHashMap).getValue()
+ new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
+ Next
+ return New MalHashMap(new_dict)
+ Else
+ return ast
+ End If
+ return ast
+ End Function
+
+ ' TODO: move to types.vb when it is ported
+ Class FClosure
+ Public ast As MalVal
+ Public params As MalList
+ Public env As MalEnv
+ Function fn(args as MalList) As MalVal
+ return EVAL(ast, new MalEnv(env, params, args))
+ End Function
+ End Class
+
+ Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal
+ Do
+
+ 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
+ If not orig_ast.list_Q() Then
+ return eval_ast(orig_ast, env)
+ End If
+
+ ' apply list
+ Dim ast As MalList = DirectCast(orig_ast, MalList)
+ If ast.size() = 0 Then
+ return ast
+ End If
+ Dim a0 As MalVal = ast(0)
+ Dim a0sym As String
+ If TypeOf a0 is MalSymbol Then
+ a0sym = DirectCast(a0,MalSymbol).getName()
+ Else
+ a0sym = "__<*fn*>__"
+ End If
+
+ Select a0sym
+ Case "def!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "let*"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim key As MalSymbol
+ Dim val as MalVal
+ Dim let_env As new MalEnv(env)
+ For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2
+ key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)
+ val = DirectCast(a1,MalList)(i+1)
+ let_env.do_set(key.getName(), EVAL(val, let_env))
+ Next
+ orig_ast = a2
+ env = let_env
+ Case "do"
+ eval_ast(ast.slice(1, ast.size()-1), env)
+ orig_ast = ast(ast.size()-1)
+ Case "if"
+ Dim a1 As MalVal = ast(1)
+ Dim cond As MalVal = EVAL(a1, env)
+ If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then
+ ' eval false slot form
+ If ast.size() > 3 Then
+ orig_ast = ast(3)
+ Else
+ return Mal.types.Nil
+ End If
+ Else
+ ' eval true slot form
+ orig_ast = ast(2)
+
+ End If
+ Case "fn*"
+ Dim fc As New FClosure()
+ fc.ast = ast(2)
+ fc.params = DirectCast(ast(1),MalLIst)
+ fc.env = env
+ Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn
+ Dim mf As new MalFunc(ast(2), env,
+ DirectCast(ast(1),MalList), f)
+ return DirectCast(mf,MalVal)
+ Case Else
+ Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
+ Dim f As MalFunc = DirectCast(el(0), MalFunc)
+ Dim fnast As MalVal = f.getAst()
+ If not fnast Is Nothing
+ orig_ast = fnast
+ env = f.genEnv(el.rest())
+ Else
+ Return f.apply(el.rest())
+ End If
+ End Select
+
+ Loop While True
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared repl_env As MalEnv
+
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), repl_env))
+ End Function
+
+ Shared Function do_eval(args As MalList) As MalVal
+ Return EVAL(args(0), repl_env)
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ repl_env = New MalEnv(Nothing)
+
+ ' core.vb: defined using VB.NET
+ For Each entry As KeyValuePair(Of String,MalVal) In core.ns()
+ repl_env.do_set(entry.Key, entry.Value)
+ Next
+ repl_env.do_set("eval", new MalFunc(AddressOf do_eval))
+ Dim argv As New MalList()
+ For i As Integer = 0 To args.Length()-1
+ argv.conj_BANG(new MalString(args(i)))
+ Next
+ repl_env.do_set("*ARGV*", argv)
+
+ ' core.mal: defined using the language itself
+ REP("(def! not (fn* (a) (if a false true)))")
+ REP("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))")
+
+ Dim fileIdx As Integer = 1
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ fileIdx = 2
+ End If
+ If args.Length > fileIdx Then
+ REP("(load-file """ & args(fileIdx) & """)")
+ return 0
+ End If
+
+ ' repl loop
+ Dim line As String
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e as Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ end class
+End Namespace
diff --git a/vb/step7_quote.vb b/vb/step7_quote.vb
new file mode 100644
index 0000000..d3b6174
--- /dev/null
+++ b/vb/step7_quote.vb
@@ -0,0 +1,249 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalInt = Mal.types.MalInt
+Imports MalString = Mal.types.MalString
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalFunc = Mal.types.MalFunc
+Imports MalEnv = Mal.env.Env
+
+Namespace Mal
+ class step7_quote
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function is_pair(x As MalVal) As Boolean
+ return TypeOf x Is MalList AndAlso _
+ DirectCast(x,MalList).size() > 0
+ End Function
+
+ Shared Function quasiquote(ast As MalVal) As MalVal
+ If not is_pair(ast) Then
+ return New MalList(New MalSymbol("quote"), ast)
+ Else
+ Dim a0 As MalVal = DirectCast(ast,MalList)(0)
+ If TypeOf a0 Is MalSymbol AndAlso _
+ DirectCast(a0,MalSymbol).getName() = "unquote" Then
+ return DirectCast(ast,MalList)(1)
+ Else If is_pair(a0) Then
+ Dim a00 As MalVal = DirectCast(a0,MalList)(0)
+ If TypeOf a00 is MalSymbol AndAlso _
+ DirectCast(a00,MalSymbol).getName() = "splice-unquote" Then
+ return New MalList(New MalSymbol("concat"),
+ DirectCast(a0,MalList)(1),
+ quasiquote(DirectCast(ast,MalList).rest()))
+ End If
+ End If
+ return New MalList(New MalSymbol("cons"),
+ quasiquote(a0),
+ quasiquote(DirectCast(ast,MalList).rest()))
+ End If
+ End Function
+
+
+ Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal
+ If TypeOf ast Is MalSymbol Then
+ Dim sym As MalSymbol = DirectCast(ast, MalSymbol)
+ return env.do_get(sym.getName())
+ Else If TypeOf ast Is MalList Then
+ Dim old_lst As MalList = DirectCast(ast, MalList)
+ Dim new_lst As MalList
+ If ast.list_Q() Then
+ new_lst = New MalList
+ Else
+ new_lst = DirectCast(New MalVector, MalList)
+ End If
+ Dim mv As MalVal
+ For Each mv in old_lst.getValue()
+ new_lst.conj_BANG(EVAL(mv, env))
+ Next
+ return new_lst
+ Else If TypeOf ast Is MalHashMap Then
+ Dim new_dict As New Dictionary(Of String, MalVal)
+ Dim entry As KeyValuePair(Of String, MalVal)
+ For Each entry in DirectCast(ast,MalHashMap).getValue()
+ new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
+ Next
+ return New MalHashMap(new_dict)
+ Else
+ return ast
+ End If
+ return ast
+ End Function
+
+ ' TODO: move to types.vb when it is ported
+ Class FClosure
+ Public ast As MalVal
+ Public params As MalList
+ Public env As MalEnv
+ Function fn(args as MalList) As MalVal
+ return EVAL(ast, new MalEnv(env, params, args))
+ End Function
+ End Class
+
+ Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal
+ Do
+
+ 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
+ If not orig_ast.list_Q() Then
+ return eval_ast(orig_ast, env)
+ End If
+
+ ' apply list
+ Dim ast As MalList = DirectCast(orig_ast, MalList)
+ If ast.size() = 0 Then
+ return ast
+ End If
+ Dim a0 As MalVal = ast(0)
+ Dim a0sym As String
+ If TypeOf a0 is MalSymbol Then
+ a0sym = DirectCast(a0,MalSymbol).getName()
+ Else
+ a0sym = "__<*fn*>__"
+ End If
+
+ Select a0sym
+ Case "def!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "let*"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim key As MalSymbol
+ Dim val as MalVal
+ Dim let_env As new MalEnv(env)
+ For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2
+ key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)
+ val = DirectCast(a1,MalList)(i+1)
+ let_env.do_set(key.getName(), EVAL(val, let_env))
+ Next
+ orig_ast = a2
+ env = let_env
+ Case "quote"
+ return ast(1)
+ Case "quasiquote"
+ orig_ast = quasiquote(ast(1))
+ Case "do"
+ eval_ast(ast.slice(1, ast.size()-1), env)
+ orig_ast = ast(ast.size()-1)
+ Case "if"
+ Dim a1 As MalVal = ast(1)
+ Dim cond As MalVal = EVAL(a1, env)
+ If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then
+ ' eval false slot form
+ If ast.size() > 3 Then
+ orig_ast = ast(3)
+ Else
+ return Mal.types.Nil
+ End If
+ Else
+ ' eval true slot form
+ orig_ast = ast(2)
+
+ End If
+ Case "fn*"
+ Dim fc As New FClosure()
+ fc.ast = ast(2)
+ fc.params = DirectCast(ast(1),MalLIst)
+ fc.env = env
+ Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn
+ Dim mf As new MalFunc(ast(2), env,
+ DirectCast(ast(1),MalList), f)
+ return DirectCast(mf,MalVal)
+ Case Else
+ Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
+ Dim f As MalFunc = DirectCast(el(0), MalFunc)
+ Dim fnast As MalVal = f.getAst()
+ If not fnast Is Nothing
+ orig_ast = fnast
+ env = f.genEnv(el.rest())
+ Else
+ Return f.apply(el.rest())
+ End If
+ End Select
+
+ Loop While True
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared repl_env As MalEnv
+
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), repl_env))
+ End Function
+
+ Shared Function do_eval(args As MalList) As MalVal
+ Return EVAL(args(0), repl_env)
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ repl_env = New MalEnv(Nothing)
+
+ ' core.vb: defined using VB.NET
+ For Each entry As KeyValuePair(Of String,MalVal) In core.ns()
+ repl_env.do_set(entry.Key, entry.Value)
+ Next
+ repl_env.do_set("eval", new MalFunc(AddressOf do_eval))
+ Dim argv As New MalList()
+ For i As Integer = 0 To args.Length()-1
+ argv.conj_BANG(new MalString(args(i)))
+ Next
+ repl_env.do_set("*ARGV*", argv)
+
+ ' core.mal: defined using the language itself
+ REP("(def! not (fn* (a) (if a false true)))")
+ REP("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))")
+
+ Dim fileIdx As Integer = 1
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ fileIdx = 2
+ End If
+ If args.Length > fileIdx Then
+ REP("(load-file """ & args(fileIdx) & """)")
+ return 0
+ End If
+
+ ' repl loop
+ Dim line As String
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e as Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ end class
+End Namespace
diff --git a/vb/step8_macros.vb b/vb/step8_macros.vb
new file mode 100644
index 0000000..5a5fc77
--- /dev/null
+++ b/vb/step8_macros.vb
@@ -0,0 +1,289 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalInt = Mal.types.MalInt
+Imports MalString = Mal.types.MalString
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalFunc = Mal.types.MalFunc
+Imports MalEnv = Mal.env.Env
+
+Namespace Mal
+ class step8_macros
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function is_pair(x As MalVal) As Boolean
+ return TypeOf x Is MalList AndAlso _
+ DirectCast(x,MalList).size() > 0
+ End Function
+
+ Shared Function quasiquote(ast As MalVal) As MalVal
+ If not is_pair(ast) Then
+ return New MalList(New MalSymbol("quote"), ast)
+ Else
+ Dim a0 As MalVal = DirectCast(ast,MalList)(0)
+ If TypeOf a0 Is MalSymbol AndAlso _
+ DirectCast(a0,MalSymbol).getName() = "unquote" Then
+ return DirectCast(ast,MalList)(1)
+ Else If is_pair(a0) Then
+ Dim a00 As MalVal = DirectCast(a0,MalList)(0)
+ If TypeOf a00 is MalSymbol AndAlso _
+ DirectCast(a00,MalSymbol).getName() = "splice-unquote" Then
+ return New MalList(New MalSymbol("concat"),
+ DirectCast(a0,MalList)(1),
+ quasiquote(DirectCast(ast,MalList).rest()))
+ End If
+ End If
+ return New MalList(New MalSymbol("cons"),
+ quasiquote(a0),
+ quasiquote(DirectCast(ast,MalList).rest()))
+ End If
+ End Function
+
+ Shared Function is_macro_call(ast As MalVal, env As MalEnv) As Boolean
+ If TypeOf ast Is MalList Then
+ Dim a0 As MalVal = DirectCast(ast,MalList)(0)
+ If TypeOf a0 Is MalSymbol AndAlso _
+ env.find(DirectCast(a0,MalSymbol).getName()) IsNot Nothing Then
+ Dim mac As MalVal = env.do_get(DirectCast(a0,MalSymbol).getName())
+ If TypeOf mac Is MalFunc AndAlso _
+ DirectCast(mac,MalFunc).isMacro() Then
+ return True
+ End If
+ End If
+ End If
+ return False
+ End Function
+
+ Shared Function macroexpand(ast As MalVal, env As MalEnv) As MalVal
+ While is_macro_call(ast, env)
+ Dim a0 As MalSymbol = DirectCast(DirectCast(ast,MalList)(0),MalSymbol)
+ Dim mac As MalFunc = DirectCast(env.do_get(a0.getName()),MalFunc)
+ ast = mac.apply(DirectCast(ast,MalList).rest())
+ End While
+ return ast
+ End Function
+
+ Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal
+ If TypeOf ast Is MalSymbol Then
+ Dim sym As MalSymbol = DirectCast(ast, MalSymbol)
+ return env.do_get(sym.getName())
+ Else If TypeOf ast Is MalList Then
+ Dim old_lst As MalList = DirectCast(ast, MalList)
+ Dim new_lst As MalList
+ If ast.list_Q() Then
+ new_lst = New MalList
+ Else
+ new_lst = DirectCast(New MalVector, MalList)
+ End If
+ Dim mv As MalVal
+ For Each mv in old_lst.getValue()
+ new_lst.conj_BANG(EVAL(mv, env))
+ Next
+ return new_lst
+ Else If TypeOf ast Is MalHashMap Then
+ Dim new_dict As New Dictionary(Of String, MalVal)
+ Dim entry As KeyValuePair(Of String, MalVal)
+ For Each entry in DirectCast(ast,MalHashMap).getValue()
+ new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
+ Next
+ return New MalHashMap(new_dict)
+ Else
+ return ast
+ End If
+ return ast
+ End Function
+
+ ' TODO: move to types.vb when it is ported
+ Class FClosure
+ Public ast As MalVal
+ Public params As MalList
+ Public env As MalEnv
+ Function fn(args as MalList) As MalVal
+ return EVAL(ast, new MalEnv(env, params, args))
+ End Function
+ End Class
+
+ Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal
+ Do
+
+ 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
+ If not orig_ast.list_Q() Then
+ return eval_ast(orig_ast, env)
+ End If
+
+ ' apply list
+ Dim expanded As MalVal = macroexpand(orig_ast, env)
+ if not expanded.list_Q() Then
+ return expanded
+ End If
+ Dim ast As MalList = DirectCast(expanded, MalList)
+
+ If ast.size() = 0 Then
+ return ast
+ End If
+ Dim a0 As MalVal = ast(0)
+ Dim a0sym As String
+ If TypeOf a0 is MalSymbol Then
+ a0sym = DirectCast(a0,MalSymbol).getName()
+ Else
+ a0sym = "__<*fn*>__"
+ End If
+
+ Select a0sym
+ Case "def!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "let*"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim key As MalSymbol
+ Dim val as MalVal
+ Dim let_env As new MalEnv(env)
+ For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2
+ key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)
+ val = DirectCast(a1,MalList)(i+1)
+ let_env.do_set(key.getName(), EVAL(val, let_env))
+ Next
+ orig_ast = a2
+ env = let_env
+ Case "quote"
+ return ast(1)
+ Case "quasiquote"
+ orig_ast = quasiquote(ast(1))
+ Case "defmacro!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ DirectCast(res,MalFunc).setMacro()
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "macroexpand"
+ Dim a1 As MalVal = ast(1)
+ return macroexpand(a1, env)
+ Case "do"
+ eval_ast(ast.slice(1, ast.size()-1), env)
+ orig_ast = ast(ast.size()-1)
+ Case "if"
+ Dim a1 As MalVal = ast(1)
+ Dim cond As MalVal = EVAL(a1, env)
+ If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then
+ ' eval false slot form
+ If ast.size() > 3 Then
+ orig_ast = ast(3)
+ Else
+ return Mal.types.Nil
+ End If
+ Else
+ ' eval true slot form
+ orig_ast = ast(2)
+
+ End If
+ Case "fn*"
+ Dim fc As New FClosure()
+ fc.ast = ast(2)
+ fc.params = DirectCast(ast(1),MalLIst)
+ fc.env = env
+ Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn
+ Dim mf As new MalFunc(ast(2), env,
+ DirectCast(ast(1),MalList), f)
+ return DirectCast(mf,MalVal)
+ Case Else
+ Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
+ Dim f As MalFunc = DirectCast(el(0), MalFunc)
+ Dim fnast As MalVal = f.getAst()
+ If not fnast Is Nothing
+ orig_ast = fnast
+ env = f.genEnv(el.rest())
+ Else
+ Return f.apply(el.rest())
+ End If
+ End Select
+
+ Loop While True
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared repl_env As MalEnv
+
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), repl_env))
+ End Function
+
+ Shared Function do_eval(args As MalList) As MalVal
+ Return EVAL(args(0), repl_env)
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ repl_env = New MalEnv(Nothing)
+
+ ' core.vb: defined using VB.NET
+ For Each entry As KeyValuePair(Of String,MalVal) In core.ns()
+ repl_env.do_set(entry.Key, entry.Value)
+ Next
+ repl_env.do_set("eval", new MalFunc(AddressOf do_eval))
+ Dim argv As New MalList()
+ For i As Integer = 0 To args.Length()-1
+ argv.conj_BANG(new MalString(args(i)))
+ Next
+ repl_env.do_set("*ARGV*", argv)
+
+ ' core.mal: defined using the language itself
+ REP("(def! not (fn* (a) (if a false true)))")
+ REP("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))")
+ REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw ""odd number of forms to cond"")) (cons 'cond (rest (rest xs)))))))")
+ REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
+
+ Dim fileIdx As Integer = 1
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ fileIdx = 2
+ End If
+ If args.Length > fileIdx Then
+ REP("(load-file """ & args(fileIdx) & """)")
+ return 0
+ End If
+
+ ' repl loop
+ Dim line As String
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e as Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ end class
+End Namespace
diff --git a/vb/step9_try.vb b/vb/step9_try.vb
new file mode 100644
index 0000000..0b888b7
--- /dev/null
+++ b/vb/step9_try.vb
@@ -0,0 +1,318 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalInt = Mal.types.MalInt
+Imports MalString = Mal.types.MalString
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalFunc = Mal.types.MalFunc
+Imports MalEnv = Mal.env.Env
+
+Namespace Mal
+ Class step9_try
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function is_pair(x As MalVal) As Boolean
+ return TypeOf x Is MalList AndAlso _
+ DirectCast(x,MalList).size() > 0
+ End Function
+
+ Shared Function quasiquote(ast As MalVal) As MalVal
+ If not is_pair(ast) Then
+ return New MalList(New MalSymbol("quote"), ast)
+ Else
+ Dim a0 As MalVal = DirectCast(ast,MalList)(0)
+ If TypeOf a0 Is MalSymbol AndAlso _
+ DirectCast(a0,MalSymbol).getName() = "unquote" Then
+ return DirectCast(ast,MalList)(1)
+ Else If is_pair(a0) Then
+ Dim a00 As MalVal = DirectCast(a0,MalList)(0)
+ If TypeOf a00 is MalSymbol AndAlso _
+ DirectCast(a00,MalSymbol).getName() = "splice-unquote" Then
+ return New MalList(New MalSymbol("concat"),
+ DirectCast(a0,MalList)(1),
+ quasiquote(DirectCast(ast,MalList).rest()))
+ End If
+ End If
+ return New MalList(New MalSymbol("cons"),
+ quasiquote(a0),
+ quasiquote(DirectCast(ast,MalList).rest()))
+ End If
+ End Function
+
+ Shared Function is_macro_call(ast As MalVal, env As MalEnv) As Boolean
+ If TypeOf ast Is MalList Then
+ Dim a0 As MalVal = DirectCast(ast,MalList)(0)
+ If TypeOf a0 Is MalSymbol AndAlso _
+ env.find(DirectCast(a0,MalSymbol).getName()) IsNot Nothing Then
+ Dim mac As MalVal = env.do_get(DirectCast(a0,MalSymbol).getName())
+ If TypeOf mac Is MalFunc AndAlso _
+ DirectCast(mac,MalFunc).isMacro() Then
+ return True
+ End If
+ End If
+ End If
+ return False
+ End Function
+
+ Shared Function macroexpand(ast As MalVal, env As MalEnv) As MalVal
+ While is_macro_call(ast, env)
+ Dim a0 As MalSymbol = DirectCast(DirectCast(ast,MalList)(0),MalSymbol)
+ Dim mac As MalFunc = DirectCast(env.do_get(a0.getName()),MalFunc)
+ ast = mac.apply(DirectCast(ast,MalList).rest())
+ End While
+ return ast
+ End Function
+
+ Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal
+ If TypeOf ast Is MalSymbol Then
+ Dim sym As MalSymbol = DirectCast(ast, MalSymbol)
+ return env.do_get(sym.getName())
+ Else If TypeOf ast Is MalList Then
+ Dim old_lst As MalList = DirectCast(ast, MalList)
+ Dim new_lst As MalList
+ If ast.list_Q() Then
+ new_lst = New MalList
+ Else
+ new_lst = DirectCast(New MalVector, MalList)
+ End If
+ Dim mv As MalVal
+ For Each mv in old_lst.getValue()
+ new_lst.conj_BANG(EVAL(mv, env))
+ Next
+ return new_lst
+ Else If TypeOf ast Is MalHashMap Then
+ Dim new_dict As New Dictionary(Of String, MalVal)
+ Dim entry As KeyValuePair(Of String, MalVal)
+ For Each entry in DirectCast(ast,MalHashMap).getValue()
+ new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
+ Next
+ return New MalHashMap(new_dict)
+ Else
+ return ast
+ End If
+ return ast
+ End Function
+
+ ' TODO: move to types.vb when it is ported
+ Class FClosure
+ Public ast As MalVal
+ Public params As MalList
+ Public env As MalEnv
+ Function fn(args as MalList) As MalVal
+ return EVAL(ast, new MalEnv(env, params, args))
+ End Function
+ End Class
+
+ Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal
+ Do
+
+ 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
+ If not orig_ast.list_Q() Then
+ return eval_ast(orig_ast, env)
+ End If
+
+ ' apply list
+ Dim expanded As MalVal = macroexpand(orig_ast, env)
+ if not expanded.list_Q() Then
+ return expanded
+ End If
+ Dim ast As MalList = DirectCast(expanded, MalList)
+
+ If ast.size() = 0 Then
+ return ast
+ End If
+ Dim a0 As MalVal = ast(0)
+ Dim a0sym As String
+ If TypeOf a0 is MalSymbol Then
+ a0sym = DirectCast(a0,MalSymbol).getName()
+ Else
+ a0sym = "__<*fn*>__"
+ End If
+
+ Select a0sym
+ Case "def!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "let*"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim key As MalSymbol
+ Dim val as MalVal
+ Dim let_env As new MalEnv(env)
+ For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2
+ key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)
+ val = DirectCast(a1,MalList)(i+1)
+ let_env.do_set(key.getName(), EVAL(val, let_env))
+ Next
+ orig_ast = a2
+ env = let_env
+ Case "quote"
+ return ast(1)
+ Case "quasiquote"
+ orig_ast = quasiquote(ast(1))
+ Case "defmacro!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ DirectCast(res,MalFunc).setMacro()
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "macroexpand"
+ Dim a1 As MalVal = ast(1)
+ return macroexpand(a1, env)
+ Case "try*"
+ Try
+ return EVAL(ast(1), env)
+ Catch e As Exception
+ If ast.size() > 2 Then
+ Dim exc As MalVal
+ Dim a2 As MalVal = ast(2)
+ Dim a20 As MalVal = DirectCast(a2,MalList)(0)
+ If DirectCast(a20,MalSymbol).getName() = "catch*" Then
+ If TypeOf e Is Mal.types.MalException Then
+ exc = DirectCast(e,Mal.types.MalException).getValue()
+ Else
+ exc = New MalString(e.StackTrace)
+ End If
+ return EVAL(
+ DirectCast(a2,MalList)(2),
+ New MalEnv(env,
+ DirectCast(a2,MalList).slice(1,2),
+ New MalList(exc)))
+ End If
+ Throw e
+ End If
+ End Try
+ Case "do"
+ eval_ast(ast.slice(1, ast.size()-1), env)
+ orig_ast = ast(ast.size()-1)
+ Case "if"
+ Dim a1 As MalVal = ast(1)
+ Dim cond As MalVal = EVAL(a1, env)
+ If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then
+ ' eval false slot form
+ If ast.size() > 3 Then
+ orig_ast = ast(3)
+ Else
+ return Mal.types.Nil
+ End If
+ Else
+ ' eval true slot form
+ orig_ast = ast(2)
+
+ End If
+ Case "fn*"
+ Dim fc As New FClosure()
+ fc.ast = ast(2)
+ fc.params = DirectCast(ast(1),MalLIst)
+ fc.env = env
+ Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn
+ Dim mf As new MalFunc(ast(2), env,
+ DirectCast(ast(1),MalList), f)
+ return DirectCast(mf,MalVal)
+ Case Else
+ Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
+ Dim f As MalFunc = DirectCast(el(0), MalFunc)
+ Dim fnast As MalVal = f.getAst()
+ If not fnast Is Nothing
+ orig_ast = fnast
+ env = f.genEnv(el.rest())
+ Else
+ Return f.apply(el.rest())
+ End If
+ End Select
+
+ Loop While True
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared repl_env As MalEnv
+
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), repl_env))
+ End Function
+
+ Shared Function do_eval(args As MalList) As MalVal
+ Return EVAL(args(0), repl_env)
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ repl_env = New MalEnv(Nothing)
+
+ ' core.vb: defined using VB.NET
+ For Each entry As KeyValuePair(Of String,MalVal) In core.ns()
+ repl_env.do_set(entry.Key, entry.Value)
+ Next
+ repl_env.do_set("eval", new MalFunc(AddressOf do_eval))
+ Dim argv As New MalList()
+ For i As Integer = 0 To args.Length()-1
+ argv.conj_BANG(new MalString(args(i)))
+ Next
+ repl_env.do_set("*ARGV*", argv)
+
+ ' core.mal: defined using the language itself
+ REP("(def! *host-language* ""VB.NET"")")
+ REP("(def! not (fn* (a) (if a false true)))")
+ REP("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))")
+ REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw ""odd number of forms to cond"")) (cons 'cond (rest (rest xs)))))))")
+ REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
+
+ Dim fileIdx As Integer = 1
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ fileIdx = 2
+ End If
+ If args.Length > fileIdx Then
+ REP("(load-file """ & args(fileIdx) & """)")
+ return 0
+ End If
+
+ ' repl loop
+ Dim line As String
+ REP("(println (str ""Mal ["" *host-language* ""]""))")
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e As Mal.types.MalException
+ Console.WriteLine("Error: " & _
+ printer._pr_str(e.getValue(), False))
+ Continue Do
+ Catch e As Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ End Class
+End Namespace
diff --git a/vb/stepA_interop.vb b/vb/stepA_interop.vb
new file mode 100644
index 0000000..713954d
--- /dev/null
+++ b/vb/stepA_interop.vb
@@ -0,0 +1,318 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports Mal
+Imports MalVal = Mal.types.MalVal
+Imports MalInt = Mal.types.MalInt
+Imports MalString = Mal.types.MalString
+Imports MalSymbol = Mal.types.MalSymbol
+Imports MalList = Mal.types.MalList
+Imports MalVector = Mal.types.MalVector
+Imports MalHashMap = Mal.types.MalHashMap
+Imports MalFunc = Mal.types.MalFunc
+Imports MalEnv = Mal.env.Env
+
+Namespace Mal
+ Class stepA_interop
+ ' read
+ Shared Function READ(str As String) As MalVal
+ Return reader.read_str(str)
+ End Function
+
+ ' eval
+ Shared Function is_pair(x As MalVal) As Boolean
+ return TypeOf x Is MalList AndAlso _
+ DirectCast(x,MalList).size() > 0
+ End Function
+
+ Shared Function quasiquote(ast As MalVal) As MalVal
+ If not is_pair(ast) Then
+ return New MalList(New MalSymbol("quote"), ast)
+ Else
+ Dim a0 As MalVal = DirectCast(ast,MalList)(0)
+ If TypeOf a0 Is MalSymbol AndAlso _
+ DirectCast(a0,MalSymbol).getName() = "unquote" Then
+ return DirectCast(ast,MalList)(1)
+ Else If is_pair(a0) Then
+ Dim a00 As MalVal = DirectCast(a0,MalList)(0)
+ If TypeOf a00 is MalSymbol AndAlso _
+ DirectCast(a00,MalSymbol).getName() = "splice-unquote" Then
+ return New MalList(New MalSymbol("concat"),
+ DirectCast(a0,MalList)(1),
+ quasiquote(DirectCast(ast,MalList).rest()))
+ End If
+ End If
+ return New MalList(New MalSymbol("cons"),
+ quasiquote(a0),
+ quasiquote(DirectCast(ast,MalList).rest()))
+ End If
+ End Function
+
+ Shared Function is_macro_call(ast As MalVal, env As MalEnv) As Boolean
+ If TypeOf ast Is MalList Then
+ Dim a0 As MalVal = DirectCast(ast,MalList)(0)
+ If TypeOf a0 Is MalSymbol AndAlso _
+ env.find(DirectCast(a0,MalSymbol).getName()) IsNot Nothing Then
+ Dim mac As MalVal = env.do_get(DirectCast(a0,MalSymbol).getName())
+ If TypeOf mac Is MalFunc AndAlso _
+ DirectCast(mac,MalFunc).isMacro() Then
+ return True
+ End If
+ End If
+ End If
+ return False
+ End Function
+
+ Shared Function macroexpand(ast As MalVal, env As MalEnv) As MalVal
+ While is_macro_call(ast, env)
+ Dim a0 As MalSymbol = DirectCast(DirectCast(ast,MalList)(0),MalSymbol)
+ Dim mac As MalFunc = DirectCast(env.do_get(a0.getName()),MalFunc)
+ ast = mac.apply(DirectCast(ast,MalList).rest())
+ End While
+ return ast
+ End Function
+
+ Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal
+ If TypeOf ast Is MalSymbol Then
+ Dim sym As MalSymbol = DirectCast(ast, MalSymbol)
+ return env.do_get(sym.getName())
+ Else If TypeOf ast Is MalList Then
+ Dim old_lst As MalList = DirectCast(ast, MalList)
+ Dim new_lst As MalList
+ If ast.list_Q() Then
+ new_lst = New MalList
+ Else
+ new_lst = DirectCast(New MalVector, MalList)
+ End If
+ Dim mv As MalVal
+ For Each mv in old_lst.getValue()
+ new_lst.conj_BANG(EVAL(mv, env))
+ Next
+ return new_lst
+ Else If TypeOf ast Is MalHashMap Then
+ Dim new_dict As New Dictionary(Of String, MalVal)
+ Dim entry As KeyValuePair(Of String, MalVal)
+ For Each entry in DirectCast(ast,MalHashMap).getValue()
+ new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
+ Next
+ return New MalHashMap(new_dict)
+ Else
+ return ast
+ End If
+ return ast
+ End Function
+
+ ' TODO: move to types.vb when it is ported
+ Class FClosure
+ Public ast As MalVal
+ Public params As MalList
+ Public env As MalEnv
+ Function fn(args as MalList) As MalVal
+ return EVAL(ast, new MalEnv(env, params, args))
+ End Function
+ End Class
+
+ Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal
+ Do
+
+ 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
+ If not orig_ast.list_Q() Then
+ return eval_ast(orig_ast, env)
+ End If
+
+ ' apply list
+ Dim expanded As MalVal = macroexpand(orig_ast, env)
+ if not expanded.list_Q() Then
+ return expanded
+ End If
+ Dim ast As MalList = DirectCast(expanded, MalList)
+
+ If ast.size() = 0 Then
+ return ast
+ End If
+ Dim a0 As MalVal = ast(0)
+ Dim a0sym As String
+ If TypeOf a0 is MalSymbol Then
+ a0sym = DirectCast(a0,MalSymbol).getName()
+ Else
+ a0sym = "__<*fn*>__"
+ End If
+
+ Select a0sym
+ Case "def!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "let*"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim key As MalSymbol
+ Dim val as MalVal
+ Dim let_env As new MalEnv(env)
+ For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2
+ key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)
+ val = DirectCast(a1,MalList)(i+1)
+ let_env.do_set(key.getName(), EVAL(val, let_env))
+ Next
+ orig_ast = a2
+ env = let_env
+ Case "quote"
+ return ast(1)
+ Case "quasiquote"
+ orig_ast = quasiquote(ast(1))
+ Case "defmacro!"
+ Dim a1 As MalVal = ast(1)
+ Dim a2 As MalVal = ast(2)
+ Dim res As MalVal = EVAL(a2, env)
+ DirectCast(res,MalFunc).setMacro()
+ env.do_set(DirectCast(a1,MalSymbol).getName(), res)
+ return res
+ Case "macroexpand"
+ Dim a1 As MalVal = ast(1)
+ return macroexpand(a1, env)
+ Case "try*"
+ Try
+ return EVAL(ast(1), env)
+ Catch e As Exception
+ If ast.size() > 2 Then
+ Dim exc As MalVal
+ Dim a2 As MalVal = ast(2)
+ Dim a20 As MalVal = DirectCast(a2,MalList)(0)
+ If DirectCast(a20,MalSymbol).getName() = "catch*" Then
+ If TypeOf e Is Mal.types.MalException Then
+ exc = DirectCast(e,Mal.types.MalException).getValue()
+ Else
+ exc = New MalString(e.StackTrace)
+ End If
+ return EVAL(
+ DirectCast(a2,MalList)(2),
+ New MalEnv(env,
+ DirectCast(a2,MalList).slice(1,2),
+ New MalList(exc)))
+ End If
+ Throw e
+ End If
+ End Try
+ Case "do"
+ eval_ast(ast.slice(1, ast.size()-1), env)
+ orig_ast = ast(ast.size()-1)
+ Case "if"
+ Dim a1 As MalVal = ast(1)
+ Dim cond As MalVal = EVAL(a1, env)
+ If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then
+ ' eval false slot form
+ If ast.size() > 3 Then
+ orig_ast = ast(3)
+ Else
+ return Mal.types.Nil
+ End If
+ Else
+ ' eval true slot form
+ orig_ast = ast(2)
+
+ End If
+ Case "fn*"
+ Dim fc As New FClosure()
+ fc.ast = ast(2)
+ fc.params = DirectCast(ast(1),MalLIst)
+ fc.env = env
+ Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn
+ Dim mf As new MalFunc(ast(2), env,
+ DirectCast(ast(1),MalList), f)
+ return DirectCast(mf,MalVal)
+ Case Else
+ Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
+ Dim f As MalFunc = DirectCast(el(0), MalFunc)
+ Dim fnast As MalVal = f.getAst()
+ If not fnast Is Nothing
+ orig_ast = fnast
+ env = f.genEnv(el.rest())
+ Else
+ Return f.apply(el.rest())
+ End If
+ End Select
+
+ Loop While True
+ End Function
+
+ ' print
+ Shared Function PRINT(exp As MalVal) As String
+ return printer._pr_str(exp, TRUE)
+ End Function
+
+ ' repl
+ Shared repl_env As MalEnv
+
+ Shared Function REP(str As String) As String
+ Return PRINT(EVAL(READ(str), repl_env))
+ End Function
+
+ Shared Function do_eval(args As MalList) As MalVal
+ Return EVAL(args(0), repl_env)
+ End Function
+
+ Shared Function Main As Integer
+ Dim args As String() = Environment.GetCommandLineArgs()
+
+ repl_env = New MalEnv(Nothing)
+
+ ' core.vb: defined using VB.NET
+ For Each entry As KeyValuePair(Of String,MalVal) In core.ns()
+ repl_env.do_set(entry.Key, entry.Value)
+ Next
+ repl_env.do_set("eval", new MalFunc(AddressOf do_eval))
+ Dim argv As New MalList()
+ For i As Integer = 0 To args.Length()-1
+ argv.conj_BANG(new MalString(args(i)))
+ Next
+ repl_env.do_set("*ARGV*", argv)
+
+ ' core.mal: defined using the language itself
+ REP("(def! *host-language* ""VB.NET"")")
+ REP("(def! not (fn* (a) (if a false true)))")
+ REP("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))")
+ REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw ""odd number of forms to cond"")) (cons 'cond (rest (rest xs)))))))")
+ REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
+
+ Dim fileIdx As Integer = 1
+ If args.Length > 1 AndAlso args(1) = "--raw" Then
+ Mal.readline.SetMode(Mal.readline.Modes.Raw)
+ fileIdx = 2
+ End If
+ If args.Length > fileIdx Then
+ REP("(load-file """ & args(fileIdx) & """)")
+ return 0
+ End If
+
+ ' repl loop
+ Dim line As String
+ REP("(println (str ""Mal ["" *host-language* ""]""))")
+ Do
+ Try
+ line = Mal.readline.Readline("user> ")
+ If line is Nothing Then
+ Exit Do
+ End If
+ If line = "" Then
+ Continue Do
+ End If
+ Catch e As IOException
+ Console.WriteLine("IOException: " & e.Message)
+ End Try
+ Try
+ Console.WriteLine(REP(line))
+ Catch e As Mal.types.MalException
+ Console.WriteLine("Error: " & _
+ printer._pr_str(e.getValue(), False))
+ Continue Do
+ Catch e As Exception
+ Console.WriteLine("Error: " & e.Message)
+ Console.WriteLine(e.StackTrace)
+ Continue Do
+ End Try
+ Loop While True
+ End function
+ End Class
+End Namespace
diff --git a/vb/types.vb b/vb/types.vb
new file mode 100644
index 0000000..303a02e
--- /dev/null
+++ b/vb/types.vb
@@ -0,0 +1,458 @@
+Imports System
+Imports System.Collections.Generic
+Imports System.Text.RegularExpressions
+Imports Mal
+
+namespace Mal
+ Public Class types
+ '
+ ' Exceptiosn/Errors
+ '
+ Public Class MalThrowable
+ Inherits Exception
+ Public Sub New()
+ MyBase.New()
+ End Sub
+ Public Sub New(msg As String)
+ MyBase.New(msg)
+ End Sub
+ End Class
+ Public Class MalError
+ Inherits MalThrowable
+ Public Sub New(msg As String)
+ MyBase.New(msg)
+ End Sub
+ End Class
+ Public Class MalContinue
+ Inherits MalThrowable
+ End Class
+
+ ' Thrown by throw function
+ Public Class MalException
+ Inherits MalThrowable
+ Private value As MalVal
+
+ 'string Message
+ Public Sub New(new_value As MalVal)
+ value = new_value
+ End Sub
+ Public Sub New(new_value As String)
+ MyBase.New(new_value)
+ value = New MalString(new_value)
+ End Sub
+ Public Function getValue() As MalVal
+ return value
+ End Function
+ End Class
+
+ '
+ ' General functions
+ '
+ Public Shared Function _equal_Q(a As MalVal, b As MalVal) As Boolean
+ Dim ota As Type = a.GetType()
+ Dim otb As Type = b.GetType()
+ If not (ota = otb Or
+ (TypeOf a Is MalList and TypeOf b Is MalList)) Then
+ return False
+ Else
+ If TypeOf a Is MalInt Then
+ return DirectCast(a,MalInt).getValue() =
+ DirectCast(b,MalInt).getValue()
+ Else If TypeOf a Is MalSymbol Then
+ return DirectCast(a,MalSymbol).getName() =
+ DirectCast(b,MalSymbol).getName()
+ Else If TypeOf a Is MalString Then
+ return DirectCast(a,MalString).getValue() =
+ DirectCast(b,MalString).getValue()
+ Else If TypeOf a Is MalList Then
+ If DirectCast(a,MalList).size() <>
+ DirectCast(b,MalList).size()
+ return False
+ End If
+ for i As Integer = 0 To DirectCast(a,MalList).size()-1
+ If not _equal_Q(DirectCast(a,MalList)(i),
+ DirectCast(b,MalList)(i))
+ return False
+ End If
+ Next
+ return True
+ Else
+ return a Is b
+ End If
+ End If
+ End Function
+
+
+ Public MustInherit Class MalVal
+ Private meta As MalVal = Nil
+ Public Overridable Function copy() As MalVal
+ return DirectCast(Me.MemberwiseClone(),MalVal)
+ End Function
+
+ ' Default is just to call regular toString()
+ Public Overridable Function ToString() As String
+ throw New MalException("ToString called on abstract MalVal")
+ End Function
+ Public Overridable Function ToString(print_readably As Boolean) As String
+ return Me.ToString()
+ End Function
+ Public Function getMeta() As MalVal
+ return meta
+ End Function
+ Public Function setMeta(m As MalVal) As MalVal
+ meta = m
+ return Me
+ End Function
+ Public Overridable Function list_Q() As Boolean
+ return False
+ End Function
+ End Class
+
+ Public Class MalConstant
+ Inherits MalVal
+ Private value As String
+ Public Sub New(name As String)
+ value = name
+ End Sub
+ Public Shadows Function copy() As MalConstant
+ return Me
+ End Function
+
+ Public Overrides Function ToString() As String
+ return value
+ End Function
+ Public Overrides Function ToString(print_readably As Boolean) As String
+ return value
+ End Function
+ End Class
+
+ Public Shared Nil As MalConstant = New MalConstant("nil")
+ Public Shared MalTrue As MalConstant = New MalConstant("true")
+ Public Shared MalFalse As MalConstant = New MalConstant("false")
+
+ Public Class MalInt
+ Inherits MalVal
+ Private value As Int64
+ Public Sub New(v As Int64)
+ value = v
+ End Sub
+ Public Shadows Function copy() As MalInt
+ return Me
+ End Function
+
+ Public Function getValue() As Int64
+ return value
+ End Function
+ Public Overrides Function ToString() As String
+ return value.ToString()
+ End Function
+ Public Overrides Function ToString(print_readably As Boolean) As String
+ return value.ToString()
+ End Function
+ Public Shared Operator <(a As MalInt, b As Malint) As MalConstant
+ If a.getValue() < b.getValue() Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Operator
+ Public Shared Operator <=(a As MalInt, b As Malint) As MalConstant
+ If a.getValue() <= b.getValue() Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Operator
+ Public Shared Operator >(a As MalInt, b As Malint) As MalConstant
+ If a.getValue() > b.getValue() Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Operator
+ Public Shared Operator >=(a As MalInt, b As Malint) As MalConstant
+ If a.getValue() >= b.getValue() Then
+ return MalTrue
+ Else
+ return MalFalse
+ End If
+ End Operator
+ Public Shared Operator +(a As MalInt, b As Malint) As MalInt
+ return new MalInt(a.getValue() + b.getValue())
+ End Operator
+ Public Shared Operator -(a As MalInt, b As Malint) As MalInt
+ return new MalInt(a.getValue() - b.getValue())
+ End Operator
+ Public Shared Operator *(a As MalInt, b As Malint) As MalInt
+ return new MalInt(a.getValue() * b.getValue())
+ End Operator
+ Public Shared Operator /(a As MalInt, b As Malint) As MalInt
+ return new MalInt(a.getValue() / b.getValue())
+ End Operator
+ End Class
+
+ Public Class MalSymbol
+ Inherits MalVal
+ Private value As String
+ Public Sub New(v As String)
+ value = v
+ End Sub
+ Public Sub New(v As MalString)
+ value = v.getValue()
+ End Sub
+ Public Shadows Function copy() As MalSymbol
+ return Me
+ End Function
+
+ Public Function getName() As String
+ return value
+ End Function
+ Public Overrides Function ToString() As String
+ return value
+ End Function
+ Public Overrides Function ToString(print_readably As Boolean) As String
+ return value
+ End Function
+ End Class
+
+ Public Class MalString
+ Inherits MalVal
+ Private value As String
+ Public Sub New(v As String)
+ value = v
+ End Sub
+ Public Shadows Function copy() As MalString
+ return Me
+ End Function
+
+ Public Function getValue() As String
+ return value
+ End Function
+ Public Overrides Function ToString() As String
+ return """" & value & """"
+ End Function
+ Public Overrides Function ToString(print_readably As Boolean) As String
+ If print_readably Then
+ return """" & _
+ value.Replace("\", "\\") _
+ .Replace("""", "\""") _
+ .Replace(Environment.NewLine, "\n") & _
+ """"
+ Else
+ return value
+ End If
+ End Function
+ End Class
+
+
+ Public Class MalList
+ Inherits MalVal
+ Public start As String = "("
+ Public last As String = ")"
+ Private value As List(Of MalVal)
+ Public Sub New()
+ value = New List(Of MalVal)
+ End Sub
+ Public Sub New(val As List(Of MalVal))
+ value = val
+ End Sub
+ Public Sub New(ParamArray mvs() As MalVal)
+ value = New List(Of MalVal)
+ conj_BANG(mvs)
+ End Sub
+
+ Public Function getValue() As List(Of MalVal)
+ return value
+ End Function
+ Public Overrides Function list_Q() As Boolean
+ return True
+ End Function
+
+ Public Overrides Function ToString() As String
+ return start & printer.join(value, " ", true) & last
+ End Function
+ Public Overrides Function ToString(print_readably As Boolean) As String
+ return start & printer.join(value, " ", print_readably) & last
+ End Function
+
+ Public Function conj_BANG(ParamArray mvs() As MalVal) As MalList
+ For i As Integer = 0 To mvs.Length-1
+ value.Add(mvs(i))
+ Next
+ return Me
+ End Function
+
+ Public Function size() As Int64
+ return value.Count
+ End Function
+ Public Function nth(ByVal idx As Integer) As MalVal
+ If value.Count > idx Then
+ return value(idx)
+ Else
+ return Nil
+ End If
+ End Function
+ Default Public ReadOnly Property Item(idx As Integer) As MalVal
+ Get
+ If value.Count > idx then
+ return value(idx)
+ Else
+ return Nil
+ End If
+ End Get
+ End Property
+ Public Function rest() As MalList
+ If size() > 0 Then
+ return New MalList(value.GetRange(1, value.Count-1))
+ Else
+ return New MalList()
+ End If
+ End Function
+ Public Overridable Function slice(start As Int64) As MalList
+ return New MalList(value.GetRange(start, value.Count-start))
+ End Function
+ Public Overridable Function slice(start As Int64, last As Int64) As MalList
+ return New MalList(value.GetRange(start, last-start))
+ End Function
+ End Class
+
+ Public Class MalVector
+ Inherits MalList
+' ' Same implementation except for instantiation methods
+ Public Sub New()
+ MyBase.New()
+ start = "["
+ last = "]"
+ End Sub
+ Public Sub New(val As List(Of MalVal))
+ MyBase.New(val)
+ start = "["
+ last = "]"
+ End Sub
+
+ Public Overrides Function list_Q() As Boolean
+ return False
+ End Function
+
+ Public Overrides Function slice(start As Int64, last As Int64) As MalList
+ Dim val As List(Of MalVal) = Me.getValue()
+ return New MalVector(val.GetRange(start, val.Count-start))
+ End Function
+ End Class
+
+ Public Class MalHashMap
+ Inherits MalVal
+ Private value As Dictionary(Of string, MalVal)
+ Public Sub New(val As Dictionary(Of String, MalVal))
+ value = val
+ End Sub
+ Public Sub New(lst As MalList)
+ value = New Dictionary(Of String, MalVal)
+ assoc_BANG(lst)
+ End Sub
+ Public Shadows Function copy() As MalHashMap
+ Dim new_self As MalHashMap = DirectCast(Me.MemberwiseClone(),MalHashMap)
+ new_self.value = New Dictionary(Of String, MalVal)(value)
+ return new_self
+ End Function
+
+ Public Function getValue() As Dictionary(Of String, MalVal)
+ return value
+ End Function
+
+ Public Overrides Function ToString() As String
+ return "{" & printer.join(value, " ", true) & "}"
+ End Function
+ Public Overrides Function ToString(print_readably As Boolean) As String
+ return "{" & printer.join(value, " ", print_readably) & "}"
+ End Function
+
+ Public Function assoc_BANG(lst As MalList) As MalHashMap
+ For i As Integer = 0 To lst.size()-1 Step 2
+ value(DirectCast(lst(i),MalString).getValue()) = lst(i+1)
+ Next
+ return Me
+ End Function
+
+ Public Function dissoc_BANG(lst As MalList) As MalHashMap
+ for i As Integer = 0 To lst.size()-1
+ value.Remove(DirectCast(lst.nth(i),MalString).getValue())
+ Next
+ return Me
+ End Function
+ End Class
+
+ Public Class MalAtom
+ Inherits MalVal
+ Private value As MalVal
+ Public Sub New(val As MalVal)
+ value = val
+ End Sub
+ 'Public MalAtom copy() { return New MalAtom(value) }
+ Public Function getValue() As MalVal
+ return value
+ End Function
+ Public Function setValue(val As MalVal) As MalVal
+ value = val
+ return value
+ End Function
+ Public Overrides Function ToString() As String
+ return "(atom " & printer._pr_str(value, true) & ")"
+ End Function
+ Public Overrides Function ToString(print_readably As Boolean) As String
+ return "(atom " & printer._pr_str(value, print_readably) & ")"
+ End Function
+ End Class
+
+ Public Class MalFunc
+ Inherits MalVal
+ Private fn As Func(Of MalList, MalVal) = Nothing
+ Private ast As MalVal = Nothing
+ Private env As Mal.env.Env = Nothing
+ Private fparams As MalList
+ Private macro As Boolean = False
+ Public Sub New(new_fn As Func(Of MalList, MalVal))
+ fn = new_fn
+ End Sub
+ Public Sub New(new_ast As MalVal, new_env As Mal.env.Env,
+ new_fparams As MalList, new_fn As Func(Of MalList, MalVal))
+ fn = new_fn
+ ast = new_ast
+ env = new_env
+ fparams = new_fparams
+ End Sub
+
+ Public Overrides Function ToString() As String
+ If Not ast Is Nothing Then
+ return "<fn* " & Mal.printer._pr_str(fparams,true) &
+ " " & Mal.printer._pr_str(ast, true) & ">"
+ Else
+ return "<builtin_function " & fn.ToString() & ">"
+ End If
+ End Function
+
+ Public Function apply(args As MalList) As MalVal
+ return fn(args)
+ End Function
+
+ Public Function getAst() As MalVal
+ return ast
+ End Function
+ Public Function getEnv() As Mal.env.Env
+ return env
+ End Function
+ Public Function getFParams() As MalList
+ return fparams
+ End Function
+ Public Function genEnv(args As MalList) As Mal.env.Env
+ return New Mal.env.Env(env, fparams, args)
+ End Function
+ Public Function isMacro() As Boolean
+ return macro
+ End Function
+ Public Sub setMacro()
+ macro = true
+ End Sub
+ End Class
+ End Class
+End Namespace