diff options
| author | Joel Martin <github@martintribe.org> | 2014-11-15 23:15:09 -0600 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-09 16:16:47 -0600 |
| commit | ee7cd5859e56423983f025088c8cef36b7ed09dd (patch) | |
| tree | da9f2011f4feaa34c96a63407fa4e8720eb1d4dc | |
| parent | c3b508af92800f63bf99f41af68f026535f454f5 (diff) | |
| download | mal-ee7cd5859e56423983f025088c8cef36b7ed09dd.tar.gz mal-ee7cd5859e56423983f025088c8cef36b7ed09dd.zip | |
VB.Net: port of C# version.
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | README.md | 19 | ||||
| -rw-r--r-- | mal/core.mal | 1 | ||||
| -rw-r--r-- | vb/Makefile | 52 | ||||
| -rw-r--r-- | vb/core.vb | 427 | ||||
| -rw-r--r-- | vb/env.vb | 55 | ||||
| -rw-r--r-- | vb/getline.cs | 1089 | ||||
| -rw-r--r-- | vb/printer.vb | 50 | ||||
| -rw-r--r-- | vb/reader.vb | 181 | ||||
| -rw-r--r-- | vb/readline.vb | 32 | ||||
| -rw-r--r-- | vb/step0_repl.vb | 43 | ||||
| -rw-r--r-- | vb/step1_read_print.vb | 59 | ||||
| -rw-r--r-- | vb/step2_eval.vb | 134 | ||||
| -rw-r--r-- | vb/step3_env.vb | 156 | ||||
| -rw-r--r-- | vb/step4_if_fn_do.vb | 189 | ||||
| -rw-r--r-- | vb/step5_tco.vb | 198 | ||||
| -rw-r--r-- | vb/step6_file.vb | 216 | ||||
| -rw-r--r-- | vb/step7_quote.vb | 249 | ||||
| -rw-r--r-- | vb/step8_macros.vb | 289 | ||||
| -rw-r--r-- | vb/step9_try.vb | 318 | ||||
| -rw-r--r-- | vb/stepA_interop.vb | 318 | ||||
| -rw-r--r-- | vb/types.vb | 458 |
23 files changed, 4537 insertions, 3 deletions
@@ -33,3 +33,5 @@ rust/mal rust/Cargo.lock rust/.cargo r/lib +vb/*.exe +vb/*.dll @@ -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 @@ -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 |
